Main window, empty GPU files.

This commit is contained in:
Ben Vanik 2014-12-20 22:17:57 -08:00
parent 7faf9d6bd3
commit 577ab0a4f1
57 changed files with 4403 additions and 7598 deletions

View File

@ -20,6 +20,7 @@
'poly.h',
'string.cc',
'string.h',
'threading.cc',
'threading.h',
],

18
src/poly/threading.cc Normal file
View File

@ -0,0 +1,18 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <poly/threading.h>
namespace poly {
namespace threading {
//
} // namespace threading
} // namespace poly

View File

@ -10,8 +10,11 @@
#ifndef POLY_THREADING_H_
#define POLY_THREADING_H_
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <mutex>
#include <string>
#include <thread>
@ -20,6 +23,27 @@
namespace poly {
namespace threading {
class Fence {
public:
Fence() : signaled_(false) {}
void Signal() {
std::unique_lock<std::mutex> lock(mutex_);
signaled_.store(true);
cond_.notify_all();
}
void Wait() {
std::unique_lock<std::mutex> lock(mutex_);
while (!signaled_.load()) {
cond_.wait(lock);
}
}
private:
std::mutex mutex_;
std::condition_variable cond_;
std::atomic<bool> signaled_;
};
// Gets the current high-performance tick count.
uint64_t ticks();
@ -35,7 +59,7 @@ void set_name(const std::string& name);
void set_name(std::thread::native_handle_type handle, const std::string& name);
// Yields the current thread to the scheduler. Maybe.
void Yield();
void MaybeYield();
// Sleeps the current thread for at least as long as the given duration.
void Sleep(std::chrono::microseconds duration);

View File

@ -32,7 +32,7 @@ void set_name(std::thread::native_handle_type handle, const std::string& name) {
// ?
}
void Yield() { pthread_yield_np(); }
void MaybeYield() { pthread_yield_np(); }
void Sleep(std::chrono::microseconds duration) {
timespec rqtp = {duration.count() / 1000000, duration.count() % 1000};

View File

@ -30,7 +30,7 @@ void set_name(std::thread::native_handle_type handle, const std::string& name) {
pthread_setname_np(pthread_self(), name.c_str());
}
void Yield() { pthread_yield_np(); }
void MaybeYield() { pthread_yield_np(); }
void Sleep(std::chrono::microseconds duration) {
timespec rqtp = {duration.count() / 1000000, duration.count() % 1000};

View File

@ -61,7 +61,7 @@ void set_name(std::thread::native_handle_type handle, const std::string& name) {
set_name(GetThreadId(handle), name);
}
void Yield() { SwitchToThread(); }
void MaybeYield() { SwitchToThread(); }
void Sleep(std::chrono::microseconds duration) {
if (duration.count() < 100) {

View File

@ -23,6 +23,7 @@ class Loop {
virtual void Post(std::function<void()> fn) = 0;
virtual void Quit() = 0;
virtual void AwaitQuit() = 0;
};
} // namespace ui

View File

@ -9,6 +9,8 @@
#include <poly/ui/win32/win32_loop.h>
#include <poly/assert.h>
namespace poly {
namespace ui {
namespace win32 {
@ -26,10 +28,18 @@ class PostedFn {
};
Win32Loop::Win32Loop() : thread_id_(0) {
thread_ = std::thread([this]() {
poly::threading::Fence init_fence;
thread_ = std::thread([&]() {
poly::threading::set_name("Win32 Loop");
thread_id_ = GetCurrentThreadId();
init_fence.Signal();
ThreadMain();
quit_fence_.Signal();
});
init_fence.Wait();
}
Win32Loop::~Win32Loop() = default;
@ -57,16 +67,22 @@ void Win32Loop::ThreadMain() {
}
void Win32Loop::Post(std::function<void()> fn) {
assert_true(thread_id_ != 0);
PostThreadMessage(thread_id_, kWmWin32LoopPost,
reinterpret_cast<WPARAM>(this),
reinterpret_cast<LPARAM>(new PostedFn(std::move(fn))));
}
void Win32Loop::Quit() {
assert_true(thread_id_ != 0);
PostThreadMessage(thread_id_, kWmWin32LoopQuit,
reinterpret_cast<WPARAM>(this), 0);
}
void Win32Loop::AwaitQuit() {
quit_fence_.Wait();
}
} // namespace win32
} // namespace ui
} // namespace poly

View File

@ -13,8 +13,12 @@
#include <windows.h>
#include <windowsx.h>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <poly/threading.h>
#include <poly/ui/loop.h>
namespace poly {
@ -24,17 +28,19 @@ namespace win32 {
class Win32Loop : public Loop {
public:
Win32Loop();
~Win32Loop();
~Win32Loop() override;
void Post(std::function<void()> fn) override;
void Quit() override;
void AwaitQuit() override;
private:
void ThreadMain();
std::thread thread_;
DWORD thread_id_;
poly::threading::Fence quit_fence_;
};
} // namespace win32

View File

@ -24,6 +24,11 @@ Win32Window::Win32Window(const std::wstring& title)
Win32Window::~Win32Window() {}
bool Win32Window::Initialize() {
CreateHWND();
return true;
}
bool Win32Window::CreateHWND() {
HINSTANCE hInstance = GetModuleHandle(nullptr);
@ -62,6 +67,7 @@ bool Win32Window::CreateHWND() {
}
main_menu_ = CreateMenu();
AppendMenu(main_menu_, MF_STRING, 0, L"TODO");
SetMenu(hwnd_, main_menu_);
// Disable flicks.
@ -130,13 +136,25 @@ bool Win32Window::set_title(const std::wstring& title) {
return true;
}
// bool Win32Window::SetSize(uint32_t width, uint32_t height) {
// RECT rc = {0, 0, static_cast<LONG>(width), static_cast<LONG>(height)};
// AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
// // TODO(benvanik): center?
// MoveWindow(handle_, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE);
// return true;
// }
void Win32Window::Resize(int32_t width, int32_t height) {
RECT rc = {0, 0, width, height};
bool has_menu = main_menu_ ? true : false;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu);
Window::Resize(rc.right - rc.left, rc.bottom - rc.top);
}
void Win32Window::Resize(int32_t left, int32_t top, int32_t right,
int32_t bottom) {
RECT rc = {left, top, right, bottom};
bool has_menu = main_menu_ ? true : false;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu);
Window::Resize(rc.left, rc.top, rc.right, rc.bottom);
}
void Win32Window::ResizeToFill(int32_t pad_left, int32_t pad_top,
int32_t pad_right, int32_t pad_bottom) {
// TODO(benvanik): fullscreen.
}
void Win32Window::OnClose() {
if (!closing_ && hwnd_) {

View File

@ -24,8 +24,16 @@ class Win32Window : public Window<Win32Control> {
Win32Window(const std::wstring& title);
~Win32Window() override;
bool Initialize() override;
bool set_title(const std::wstring& title) override;
void Resize(int32_t width, int32_t height) override;
void Resize(int32_t left, int32_t top, int32_t right,
int32_t bottom) override;
void ResizeToFill(int32_t pad_left, int32_t pad_top, int32_t pad_right,
int32_t pad_bottom) override;
protected:
bool CreateHWND() override;

View File

@ -71,10 +71,8 @@ X_STATUS Emulator::Setup() {
X_STATUS result = X_STATUS_UNSUCCESSFUL;
// Create the main window. Other parts will hook into this.
main_window_ = std::make_unique<ui::MainWindow>();
if (!main_window_->Initialize()) {
return result;
}
main_window_ = std::make_unique<ui::MainWindow>(this);
main_window_->Start();
debug_agent_.reset(new DebugAgent(this));
result = debug_agent_->Initialize();

View File

@ -1,56 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/buffer_resource.h>
using namespace std;
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
BufferResource::BufferResource(const MemoryRange& memory_range)
: PagedResource(memory_range) {
}
BufferResource::~BufferResource() = default;
int BufferResource::Prepare() {
if (!handle()) {
if (CreateHandle()) {
XELOGE("Unable to create buffer handle");
return 1;
}
}
//if (!dirtied_) {
// return 0;
//}
//dirtied_ = false;
// pass dirty regions?
return InvalidateRegion(memory_range_);
}
IndexBufferResource::IndexBufferResource(const MemoryRange& memory_range,
const Info& info)
: BufferResource(memory_range),
info_(info) {
}
IndexBufferResource::~IndexBufferResource() = default;
VertexBufferResource::VertexBufferResource(const MemoryRange& memory_range,
const Info& info)
: BufferResource(memory_range),
info_(info) {
}
VertexBufferResource::~VertexBufferResource() = default;

View File

@ -1,99 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_BUFFER_RESOURCE_H_
#define XENIA_GPU_BUFFER_RESOURCE_H_
#include <xenia/gpu/resource.h>
#include <xenia/gpu/xenos/ucode.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
class BufferResource : public PagedResource {
public:
BufferResource(const MemoryRange& memory_range);
~BufferResource() override;
virtual int Prepare();
protected:
virtual int CreateHandle() = 0;
virtual int InvalidateRegion(const MemoryRange& memory_range) = 0;
};
enum IndexFormat {
INDEX_FORMAT_16BIT = 0,
INDEX_FORMAT_32BIT = 1,
};
class IndexBufferResource : public BufferResource {
public:
struct Info {
IndexFormat format;
xenos::XE_GPU_ENDIAN endianness;
};
IndexBufferResource(const MemoryRange& memory_range,
const Info& info);
~IndexBufferResource() override;
const Info& info() const { return info_; }
bool Equals(const void* info_ptr, size_t info_length) override {
return info_length == sizeof(Info) &&
memcmp(info_ptr, &info_, info_length) == 0;
}
protected:
Info info_;
};
class VertexBufferResource : public BufferResource {
public:
struct DeclElement {
xenos::instr_fetch_vtx_t vtx_fetch;
uint32_t format;
uint32_t offset_words;
uint32_t size_words;
bool is_signed;
bool is_normalized;
};
struct Info {
uint32_t stride_words;
uint32_t element_count;
DeclElement elements[16];
};
VertexBufferResource(const MemoryRange& memory_range,
const Info& info);
~VertexBufferResource() override;
const Info& info() const { return info_; }
bool Equals(const void* info_ptr, size_t info_length) override {
return info_length == sizeof(Info) &&
memcmp(info_ptr, &info_, info_length) == 0;
}
protected:
Info info_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_BUFFER_RESOURCE_H_

View File

@ -1,804 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/command_processor.h>
#include <algorithm>
#include <xenia/gpu/gpu-private.h>
#include <xenia/gpu/graphics_driver.h>
#include <xenia/gpu/graphics_system.h>
#include <xenia/gpu/xenos/packets.h>
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
#define XETRACECP(fmt, ...) if (FLAGS_trace_ring_buffer) XELOGGPU(fmt, ##__VA_ARGS__)
CommandProcessor::CommandProcessor(
GraphicsSystem* graphics_system, Memory* memory) :
graphics_system_(graphics_system), memory_(memory), driver_(0) {
write_ptr_index_event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
primary_buffer_ptr_ = 0;
primary_buffer_size_ = 0;
read_ptr_index_ = 0;
read_ptr_update_freq_ = 0;
read_ptr_writeback_ptr_ = 0;
write_ptr_index_ = 0;
write_ptr_max_index_ = 0;
LARGE_INTEGER perf_counter;
QueryPerformanceCounter(&perf_counter);
time_base_ = perf_counter.QuadPart;
counter_ = 0;
}
CommandProcessor::~CommandProcessor() {
SetEvent(write_ptr_index_event_);
CloseHandle(write_ptr_index_event_);
}
uint64_t CommandProcessor::QueryTime() {
LARGE_INTEGER perf_counter;
QueryPerformanceCounter(&perf_counter);
return perf_counter.QuadPart - time_base_;
}
void CommandProcessor::Initialize(GraphicsDriver* driver,
uint32_t ptr, uint32_t page_count) {
driver_ = driver;
primary_buffer_ptr_ = ptr;
// Not sure this is correct, but it's a way to take the page_count back to
// the number of bytes allocated by the physical alloc.
uint32_t original_size = 1 << (0x1C - page_count - 1);
primary_buffer_size_ = original_size;
read_ptr_index_ = 0;
}
void CommandProcessor::EnableReadPointerWriteBack(uint32_t ptr,
uint32_t block_size) {
// CP_RB_RPTR_ADDR Ring Buffer Read Pointer Address 0x70C
// ptr = RB_RPTR_ADDR, pointer to write back the address to.
read_ptr_writeback_ptr_ = (primary_buffer_ptr_ & ~0x1FFFFFFF) + ptr;
// CP_RB_CNTL Ring Buffer Control 0x704
// block_size = RB_BLKSZ, number of quadwords read between updates of the
// read pointer.
read_ptr_update_freq_ = (uint32_t)pow(2.0, (double)block_size) / 4;
}
void CommandProcessor::UpdateWritePointer(uint32_t value) {
write_ptr_max_index_ =
std::max(static_cast<uint32_t>(write_ptr_max_index_), value);
write_ptr_index_ = value;
SetEvent(write_ptr_index_event_);
}
void CommandProcessor::Pump() {
uint8_t* p = memory_->membase();
while (write_ptr_index_ == 0xBAADF00D ||
read_ptr_index_ == write_ptr_index_) {
// Check if the pointer has moved.
// We wait a short bit here to yield time. Since we are also running the
// main window display we don't want to pause too long, though.
// YieldProcessor();
const int wait_time_ms = 1;
if (WaitForSingleObject(write_ptr_index_event_,
wait_time_ms) == WAIT_TIMEOUT) {
return;
}
}
// Bring local so we don't have to worry about them changing out from under
// us.
uint32_t write_ptr_index = write_ptr_index_;
uint32_t write_ptr_max_index = write_ptr_max_index_;
if (read_ptr_index_ == write_ptr_index) {
return;
}
// Process the new commands.
XETRACECP("Command processor thread work");
// Execute. Note that we handle wraparound transparently.
ExecutePrimaryBuffer(read_ptr_index_, write_ptr_index);
read_ptr_index_ = write_ptr_index;
// TODO(benvanik): use read_ptr_update_freq_ and only issue after moving
// that many indices.
if (read_ptr_writeback_ptr_) {
poly::store_and_swap<uint32_t>(p + read_ptr_writeback_ptr_, read_ptr_index_);
}
}
void CommandProcessor::ExecutePrimaryBuffer(
uint32_t start_index, uint32_t end_index) {
SCOPE_profile_cpu_f("gpu");
// Adjust pointer base.
uint32_t ptr = primary_buffer_ptr_ + start_index * 4;
ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (ptr & 0x1FFFFFFF);
uint32_t end_ptr = primary_buffer_ptr_ + end_index * 4;
end_ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (end_ptr & 0x1FFFFFFF);
XETRACECP("[%.8X] ExecutePrimaryBuffer(%dw -> %dw)",
ptr, start_index, end_index);
// Execute commands!
PacketArgs args;
args.ptr = ptr;
args.base_ptr = primary_buffer_ptr_;
args.max_address = primary_buffer_ptr_ + primary_buffer_size_;
args.ptr_mask = (primary_buffer_size_ / 4) - 1;
uint32_t n = 0;
while (args.ptr != end_ptr) {
n += ExecutePacket(args);
assert_true(args.ptr < args.max_address);
}
if (end_index > start_index) {
assert_true(n == (end_index - start_index));
}
XETRACECP(" ExecutePrimaryBuffer End");
}
void CommandProcessor::ExecuteIndirectBuffer(uint32_t ptr, uint32_t length) {
XETRACECP("[%.8X] ExecuteIndirectBuffer(%dw)", ptr, length);
// Execute commands!
PacketArgs args;
args.ptr = ptr;
args.base_ptr = ptr;
args.max_address = ptr + length * 4;
args.ptr_mask = 0;
for (uint32_t n = 0; n < length;) {
n += ExecutePacket(args);
assert_true(n <= length);
}
XETRACECP(" ExecuteIndirectBuffer End");
}
#define LOG_DATA(count) \
for (uint32_t __m = 0; __m < count; __m++) { \
XETRACECP("[%.8X] %.8X", \
packet_ptr + (1 + __m) * 4, \
poly::load_and_swap<uint32_t>(packet_base + 1 * 4 + __m * 4)); \
}
void CommandProcessor::AdvancePtr(PacketArgs& args, uint32_t n) {
args.ptr = args.ptr + n * 4;
if (args.ptr_mask) {
args.ptr =
args.base_ptr + (((args.ptr - args.base_ptr) / 4) & args.ptr_mask) * 4;
}
}
#define ADVANCE_PTR(n) AdvancePtr(args, n)
#define PEEK_PTR() \
poly::load_and_swap<uint32_t>(p + args.ptr)
#define READ_PTR() \
poly::load_and_swap<uint32_t>(p + args.ptr); ADVANCE_PTR(1);
uint32_t CommandProcessor::ExecutePacket(PacketArgs& args) {
uint8_t* p = memory_->membase();
RegisterFile* regs = driver_->register_file();
uint32_t packet_ptr = args.ptr;
const uint8_t* packet_base = p + packet_ptr;
const uint32_t packet = PEEK_PTR();
ADVANCE_PTR(1);
const uint32_t packet_type = packet >> 30;
if (packet == 0) {
XETRACECP("[%.8X] Packet(%.8X): 0?",
packet_ptr, packet);
return 1;
}
switch (packet_type) {
case 0x00:
{
// Type-0 packet.
// Write count registers in sequence to the registers starting at
// (base_index << 2).
XETRACECP("[%.8X] Packet(%.8X): set registers:",
packet_ptr, packet);
uint32_t count = ((packet >> 16) & 0x3FFF) + 1;
uint32_t base_index = (packet & 0x7FFF);
uint32_t write_one_reg = (packet >> 15) & 0x1;
for (uint32_t m = 0; m < count; m++) {
uint32_t reg_data = PEEK_PTR();
uint32_t target_index = write_one_reg ? base_index : base_index + m;
const char* reg_name = regs->GetRegisterName(target_index);
XETRACECP("[%.8X] %.8X -> %.4X %s",
args.ptr,
reg_data, target_index, reg_name ? reg_name : "");
ADVANCE_PTR(1);
WriteRegister(packet_ptr, target_index, reg_data);
}
return 1 + count;
}
break;
case 0x01:
{
// Type-1 packet.
// Contains two registers of data. Type-0 should be more common.
XETRACECP("[%.8X] Packet(%.8X): set registers:",
packet_ptr, packet);
uint32_t reg_index_1 = packet & 0x7FF;
uint32_t reg_index_2 = (packet >> 11) & 0x7FF;
uint32_t reg_ptr_1 = args.ptr;
uint32_t reg_data_1 = READ_PTR();
uint32_t reg_ptr_2 = args.ptr;
uint32_t reg_data_2 = READ_PTR();
const char* reg_name_1 = regs->GetRegisterName(reg_index_1);
const char* reg_name_2 = regs->GetRegisterName(reg_index_2);
XETRACECP("[%.8X] %.8X -> %.4X %s",
reg_ptr_1,
reg_data_1, reg_index_1, reg_name_1 ? reg_name_1 : "");
XETRACECP("[%.8X] %.8X -> %.4X %s",
reg_ptr_2,
reg_data_2, reg_index_2, reg_name_2 ? reg_name_2 : "");
WriteRegister(packet_ptr, reg_index_1, reg_data_1);
WriteRegister(packet_ptr, reg_index_2, reg_data_2);
return 1 + 2;
}
break;
case 0x02:
// Type-2 packet.
// No-op. Do nothing.
XETRACECP("[%.8X] Packet(%.8X): padding",
packet_ptr, packet);
return 1;
case 0x03:
{
// Type-3 packet.
uint32_t count = ((packet >> 16) & 0x3FFF) + 1;
uint32_t opcode = (packet >> 8) & 0x7F;
// & 1 == predicate, maybe?
switch (opcode) {
case PM4_ME_INIT:
// initialize CP's micro-engine
XETRACECP("[%.8X] Packet(%.8X): PM4_ME_INIT",
packet_ptr, packet);
LOG_DATA(count);
ADVANCE_PTR(count);
break;
case PM4_NOP:
// skip N 32-bit words to get to the next packet
// No-op, ignore some data.
XETRACECP("[%.8X] Packet(%.8X): PM4_NOP",
packet_ptr, packet);
LOG_DATA(count);
ADVANCE_PTR(count);
break;
case PM4_INTERRUPT:
// generate interrupt from the command stream
{
XETRACECP("[%.8X] Packet(%.8X): PM4_INTERRUPT",
packet_ptr, packet);
LOG_DATA(count);
uint32_t cpu_mask = READ_PTR();
for (int n = 0; n < 6; n++) {
if (cpu_mask & (1 << n)) {
graphics_system_->DispatchInterruptCallback(1, n);
}
}
}
break;
case PM4_XE_SWAP:
// Xenia-specific VdSwap hook.
// VdSwap will post this to tell us we need to swap the screen/fire an interrupt.
XETRACECP("[%.8X] Packet(%.8X): PM4_XE_SWAP",
packet_ptr, packet);
LOG_DATA(count);
ADVANCE_PTR(count);
graphics_system_->Swap();
break;
case PM4_INDIRECT_BUFFER:
// indirect buffer dispatch
{
uint32_t list_ptr = READ_PTR();
uint32_t list_length = READ_PTR();
XETRACECP("[%.8X] Packet(%.8X): PM4_INDIRECT_BUFFER %.8X (%dw)",
packet_ptr, packet, list_ptr, list_length);
ExecuteIndirectBuffer(GpuToCpu(list_ptr), list_length);
}
break;
case PM4_WAIT_REG_MEM:
// wait until a register or memory location is a specific value
{
XETRACECP("[%.8X] Packet(%.8X): PM4_WAIT_REG_MEM",
packet_ptr, packet);
LOG_DATA(count);
uint32_t wait_info = READ_PTR();
uint32_t poll_reg_addr = READ_PTR();
uint32_t ref = READ_PTR();
uint32_t mask = READ_PTR();
uint32_t wait = READ_PTR();
bool matched = false;
do {
uint32_t value;
if (wait_info & 0x10) {
// Memory.
XE_GPU_ENDIAN endianness = (XE_GPU_ENDIAN)(poll_reg_addr & 0x3);
poll_reg_addr &= ~0x3;
value = poly::load<uint32_t>(p + GpuToCpu(packet_ptr, poll_reg_addr));
value = GpuSwap(value, endianness);
} else {
// Register.
assert_true(poll_reg_addr < RegisterFile::kRegisterCount);
value = regs->values[poll_reg_addr].u32;
if (poll_reg_addr == XE_GPU_REG_COHER_STATUS_HOST) {
MakeCoherent();
value = regs->values[poll_reg_addr].u32;
}
}
switch (wait_info & 0x7) {
case 0x0: // Never.
matched = false;
break;
case 0x1: // Less than reference.
matched = (value & mask) < ref;
break;
case 0x2: // Less than or equal to reference.
matched = (value & mask) <= ref;
break;
case 0x3: // Equal to reference.
matched = (value & mask) == ref;
break;
case 0x4: // Not equal to reference.
matched = (value & mask) != ref;
break;
case 0x5: // Greater than or equal to reference.
matched = (value & mask) >= ref;
break;
case 0x6: // Greater than reference.
matched = (value & mask) > ref;
break;
case 0x7: // Always
matched = true;
break;
}
if (!matched) {
// Wait.
if (wait >= 0x100) {
Sleep(wait / 0x100);
} else {
SwitchToThread();
}
}
} while (!matched);
}
break;
case PM4_REG_RMW:
// register read/modify/write
// ? (used during shader upload and edram setup)
{
XETRACECP("[%.8X] Packet(%.8X): PM4_REG_RMW",
packet_ptr, packet);
LOG_DATA(count);
uint32_t rmw_info = READ_PTR();
uint32_t and_mask = READ_PTR();
uint32_t or_mask = READ_PTR();
uint32_t value = regs->values[rmw_info & 0x1FFF].u32;
if ((rmw_info >> 30) & 0x1) {
// | reg
value |= regs->values[or_mask & 0x1FFF].u32;
} else {
// | imm
value |= or_mask;
}
if ((rmw_info >> 31) & 0x1) {
// & reg
value &= regs->values[and_mask & 0x1FFF].u32;
} else {
// & imm
value &= and_mask;
}
WriteRegister(packet_ptr, rmw_info & 0x1FFF, value);
}
break;
case PM4_COND_WRITE:
// conditional write to memory or register
{
XETRACECP("[%.8X] Packet(%.8X): PM4_COND_WRITE",
packet_ptr, packet);
LOG_DATA(count);
uint32_t wait_info = READ_PTR();
uint32_t poll_reg_addr = READ_PTR();
uint32_t ref = READ_PTR();
uint32_t mask = READ_PTR();
uint32_t write_reg_addr = READ_PTR();
uint32_t write_data = READ_PTR();
uint32_t value;
if (wait_info & 0x10) {
// Memory.
XE_GPU_ENDIAN endianness = (XE_GPU_ENDIAN)(poll_reg_addr & 0x3);
poll_reg_addr &= ~0x3;
value = poly::load<uint32_t>(p + GpuToCpu(packet_ptr, poll_reg_addr));
value = GpuSwap(value, endianness);
} else {
// Register.
assert_true(poll_reg_addr < RegisterFile::kRegisterCount);
value = regs->values[poll_reg_addr].u32;
}
bool matched = false;
switch (wait_info & 0x7) {
case 0x0: // Never.
matched = false;
break;
case 0x1: // Less than reference.
matched = (value & mask) < ref;
break;
case 0x2: // Less than or equal to reference.
matched = (value & mask) <= ref;
break;
case 0x3: // Equal to reference.
matched = (value & mask) == ref;
break;
case 0x4: // Not equal to reference.
matched = (value & mask) != ref;
break;
case 0x5: // Greater than or equal to reference.
matched = (value & mask) >= ref;
break;
case 0x6: // Greater than reference.
matched = (value & mask) > ref;
break;
case 0x7: // Always
matched = true;
break;
}
if (matched) {
// Write.
if (wait_info & 0x100) {
// Memory.
XE_GPU_ENDIAN endianness = (XE_GPU_ENDIAN)(write_reg_addr & 0x3);
write_reg_addr &= ~0x3;
write_data = GpuSwap(write_data, endianness);
poly::store(p + GpuToCpu(packet_ptr, write_reg_addr),
write_data);
} else {
// Register.
WriteRegister(packet_ptr, write_reg_addr, write_data);
}
}
}
break;
case PM4_EVENT_WRITE:
// generate an event that creates a write to memory when completed
{
XETRACECP("[%.8X] Packet(%.8X): PM4_EVENT_WRITE (unimplemented!)",
packet_ptr, packet);
LOG_DATA(count);
uint32_t initiator = READ_PTR();
if (count == 1) {
// Just an event flag? Where does this write?
} else {
// Write to an address.
assert_always();
ADVANCE_PTR(count - 1);
}
}
break;
case PM4_EVENT_WRITE_SHD:
// generate a VS|PS_done event
{
XETRACECP("[%.8X] Packet(%.8X): PM4_EVENT_WRITE_SHD",
packet_ptr, packet);
LOG_DATA(count);
uint32_t initiator = READ_PTR();
uint32_t address = READ_PTR();
uint32_t value = READ_PTR();
// Writeback initiator.
WriteRegister(packet_ptr, XE_GPU_REG_VGT_EVENT_INITIATOR,
initiator & 0x3F);
uint32_t data_value;
if ((initiator >> 31) & 0x1) {
// Write counter (GPU vblank counter?).
data_value = counter_;
} else {
// Write value.
data_value = value;
}
XE_GPU_ENDIAN endianness = (XE_GPU_ENDIAN)(address & 0x3);
address &= ~0x3;
data_value = GpuSwap(data_value, endianness);
poly::store(p + GpuToCpu(address), data_value);
}
break;
case PM4_DRAW_INDX:
// initiate fetch of index buffer and draw
{
XETRACECP("[%.8X] Packet(%.8X): PM4_DRAW_INDX",
packet_ptr, packet);
LOG_DATA(count);
// d0 = viz query info
uint32_t d0 = READ_PTR();
uint32_t d1 = READ_PTR();
uint32_t index_count = d1 >> 16;
uint32_t prim_type = d1 & 0x3F;
uint32_t src_sel = (d1 >> 6) & 0x3;
if (!driver_->PrepareDraw(draw_command_)) {
draw_command_.prim_type = (XE_GPU_PRIMITIVE_TYPE)prim_type;
draw_command_.start_index = 0;
draw_command_.index_count = index_count;
draw_command_.base_vertex = 0;
if (src_sel == 0x0) {
// Indexed draw.
// TODO(benvanik): detect subregions of larger index buffers!
uint32_t index_base = READ_PTR();
uint32_t index_size = READ_PTR();
uint32_t endianness = index_size >> 30;
index_size &= 0x00FFFFFF;
bool index_32bit = (d1 >> 11) & 0x1;
index_size *= index_32bit ? 4 : 2;
driver_->PrepareDrawIndexBuffer(
draw_command_,
index_base, index_size,
(XE_GPU_ENDIAN)endianness,
index_32bit ? INDEX_FORMAT_32BIT : INDEX_FORMAT_16BIT);
} else if (src_sel == 0x2) {
// Auto draw.
draw_command_.index_buffer = nullptr;
} else {
// Unknown source select.
assert_always();
}
driver_->Draw(draw_command_);
} else {
if (src_sel == 0x0) {
ADVANCE_PTR(2); // skip
}
}
}
break;
case PM4_DRAW_INDX_2:
// draw using supplied indices in packet
{
XETRACECP("[%.8X] Packet(%.8X): PM4_DRAW_INDX_2",
packet_ptr, packet);
LOG_DATA(count);
uint32_t d0 = READ_PTR();
uint32_t index_count = d0 >> 16;
uint32_t prim_type = d0 & 0x3F;
uint32_t src_sel = (d0 >> 6) & 0x3;
assert_true(src_sel == 0x2); // 'SrcSel=AutoIndex'
if (!driver_->PrepareDraw(draw_command_)) {
draw_command_.prim_type = (XE_GPU_PRIMITIVE_TYPE)prim_type;
draw_command_.start_index = 0;
draw_command_.index_count = index_count;
draw_command_.base_vertex = 0;
draw_command_.index_buffer = nullptr;
driver_->Draw(draw_command_);
}
}
break;
case PM4_SET_CONSTANT:
// load constant into chip and to memory
{
XETRACECP("[%.8X] Packet(%.8X): PM4_SET_CONSTANT",
packet_ptr, packet);
// PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg)))
// reg - 0x2000
uint32_t offset_type = READ_PTR();
uint32_t index = offset_type & 0x7FF;
uint32_t type = (offset_type >> 16) & 0xFF;
switch (type) {
case 0x4: // REGISTER
index += 0x2000; // registers
for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = READ_PTR();
const char* reg_name = regs->GetRegisterName(index);
XETRACECP("[%.8X] %.8X -> %.4X %s",
packet_ptr + (1 + n) * 4,
data, index, reg_name ? reg_name : "");
WriteRegister(packet_ptr, index, data);
}
break;
default:
assert_always();
break;
}
}
break;
case PM4_LOAD_ALU_CONSTANT:
// load constants from memory
{
XETRACECP("[%.8X] Packet(%.8X): PM4_LOAD_ALU_CONSTANT",
packet_ptr, packet);
uint32_t address = READ_PTR();
address &= 0x3FFFFFFF;
uint32_t offset_type = READ_PTR();
uint32_t index = offset_type & 0x7FF;
uint32_t size = READ_PTR();
size &= 0xFFF;
index += 0x4000; // alu constants
for (uint32_t n = 0; n < size; n++, index++) {
uint32_t data = poly::load_and_swap<uint32_t>(
p + GpuToCpu(packet_ptr, address + n * 4));
const char* reg_name = regs->GetRegisterName(index);
XETRACECP("[%.8X] %.8X -> %.4X %s",
packet_ptr,
data, index, reg_name ? reg_name : "");
WriteRegister(packet_ptr, index, data);
}
}
break;
case PM4_IM_LOAD:
// load sequencer instruction memory (pointer-based)
{
XETRACECP("[%.8X] Packet(%.8X): PM4_IM_LOAD",
packet_ptr, packet);
LOG_DATA(count);
uint32_t addr_type = READ_PTR();
uint32_t type = addr_type & 0x3;
uint32_t addr = addr_type & ~0x3;
uint32_t start_size = READ_PTR();
uint32_t start = start_size >> 16;
uint32_t size = start_size & 0xFFFF; // dwords
assert_true(start == 0);
driver_->LoadShader((XE_GPU_SHADER_TYPE)type,
GpuToCpu(packet_ptr, addr), size * 4, start);
}
break;
case PM4_IM_LOAD_IMMEDIATE:
// load sequencer instruction memory (code embedded in packet)
{
XETRACECP("[%.8X] Packet(%.8X): PM4_IM_LOAD_IMMEDIATE",
packet_ptr, packet);
LOG_DATA(count);
uint32_t type = READ_PTR();
uint32_t start_size = READ_PTR();
uint32_t start = start_size >> 16;
uint32_t size = start_size & 0xFFFF; // dwords
assert_true(start == 0);
// TODO(benvanik): figure out if this could wrap.
assert_true(args.ptr + size * 4 < args.max_address);
driver_->LoadShader((XE_GPU_SHADER_TYPE)type,
args.ptr, size * 4, start);
ADVANCE_PTR(size);
}
break;
case PM4_INVALIDATE_STATE:
// selective invalidation of state pointers
{
XETRACECP("[%.8X] Packet(%.8X): PM4_INVALIDATE_STATE",
packet_ptr, packet);
LOG_DATA(count);
uint32_t mask = READ_PTR();
//driver_->InvalidateState(mask);
}
break;
case PM4_SET_BIN_MASK_LO:
{
uint32_t value = READ_PTR();
XETRACECP("[%.8X] Packet(%.8X): PM4_SET_BIN_MASK_LO = %.8X",
packet_ptr, packet, value);
}
break;
case PM4_SET_BIN_MASK_HI:
{
uint32_t value = READ_PTR();
XETRACECP("[%.8X] Packet(%.8X): PM4_SET_BIN_MASK_HI = %.8X",
packet_ptr, packet, value);
}
break;
case PM4_SET_BIN_SELECT_LO:
{
uint32_t value = READ_PTR();
XETRACECP("[%.8X] Packet(%.8X): PM4_SET_BIN_SELECT_LO = %.8X",
packet_ptr, packet, value);
}
break;
case PM4_SET_BIN_SELECT_HI:
{
uint32_t value = READ_PTR();
XETRACECP("[%.8X] Packet(%.8X): PM4_SET_BIN_SELECT_HI = %.8X",
packet_ptr, packet, value);
}
break;
// Ignored packets - useful if breaking on the default handler below.
case 0x50: // 0xC0015000 usually 2 words, 0xFFFFFFFF / 0x00000000
XETRACECP("[%.8X] Packet(%.8X): unknown!",
packet_ptr, packet);
LOG_DATA(count);
ADVANCE_PTR(count);
break;
default:
XETRACECP("[%.8X] Packet(%.8X): unknown!",
packet_ptr, packet);
LOG_DATA(count);
ADVANCE_PTR(count);
break;
}
return 1 + count;
}
break;
}
return 0;
}
void CommandProcessor::WriteRegister(
uint32_t packet_ptr, uint32_t index, uint32_t value) {
RegisterFile* regs = driver_->register_file();
assert_true(index < RegisterFile::kRegisterCount);
regs->values[index].u32 = value;
// If this is a COHER register, set the dirty flag.
// This will block the command processor the next time it WAIT_MEM_REGs and
// allow us to synchronize the memory.
if (index == XE_GPU_REG_COHER_STATUS_HOST) {
regs->values[index].u32 |= 0x80000000ul;
}
// Scratch register writeback.
if (index >= XE_GPU_REG_SCRATCH_REG0 && index <= XE_GPU_REG_SCRATCH_REG7) {
uint32_t scratch_reg = index - XE_GPU_REG_SCRATCH_REG0;
if ((1 << scratch_reg) & regs->values[XE_GPU_REG_SCRATCH_UMSK].u32) {
// Enabled - write to address.
uint8_t* p = memory_->membase();
uint32_t scratch_addr = regs->values[XE_GPU_REG_SCRATCH_ADDR].u32;
uint32_t mem_addr = scratch_addr + (scratch_reg * 4);
poly::store_and_swap<uint32_t>(p + GpuToCpu(primary_buffer_ptr_, mem_addr), value);
}
}
}
void CommandProcessor::MakeCoherent() {
// Status host often has 0x01000000 or 0x03000000.
// This is likely toggling VC (vertex cache) or TC (texture cache).
// Or, it also has a direction in here maybe - there is probably
// some way to check for dest coherency (what all the COHER_DEST_BASE_*
// registers are for).
// Best docs I've found on this are here:
// http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/10/R6xx_R7xx_3D.pdf
// http://cgit.freedesktop.org/xorg/driver/xf86-video-radeonhd/tree/src/r6xx_accel.c?id=3f8b6eccd9dba116cc4801e7f80ce21a879c67d2#n454
RegisterFile* regs = driver_->register_file();
auto status_host = regs->values[XE_GPU_REG_COHER_STATUS_HOST].u32;
auto base_host = regs->values[XE_GPU_REG_COHER_BASE_HOST].u32;
auto size_host = regs->values[XE_GPU_REG_COHER_SIZE_HOST].u32;
if (!(status_host & 0x80000000ul)) {
return;
}
// TODO(benvanik): notify resource cache of base->size and type.
XETRACECP("Make %.8X -> %.8X (%db) coherent",
base_host, base_host + size_host, size_host);
driver_->resource_cache()->SyncRange(base_host, size_host);
// Mark coherent.
status_host &= ~0x80000000ul;
regs->values[XE_GPU_REG_COHER_STATUS_HOST].u32 = status_host;
}

View File

@ -1,81 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_COMMAND_PROCESSOR_H_
#define XENIA_GPU_COMMAND_PROCESSOR_H_
#include <xenia/common.h>
#include <xenia/gpu/draw_command.h>
#include <xenia/gpu/register_file.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
class GraphicsDriver;
class GraphicsSystem;
class CommandProcessor {
public:
CommandProcessor(GraphicsSystem* graphics_system, Memory* memory);
virtual ~CommandProcessor();
Memory* memory() const { return memory_; }
uint64_t QueryTime();
uint32_t counter() const { return counter_; }
void increment_counter() { counter_++; }
void Initialize(GraphicsDriver* driver, uint32_t ptr, uint32_t page_count);
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size);
void UpdateWritePointer(uint32_t value);
void Pump();
private:
typedef struct {
uint32_t ptr;
uint32_t base_ptr;
uint32_t max_address;
uint32_t ptr_mask;
} PacketArgs;
void AdvancePtr(PacketArgs& args, uint32_t n);
void ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index);
void ExecuteIndirectBuffer(uint32_t ptr, uint32_t length);
uint32_t ExecutePacket(PacketArgs& args);
void WriteRegister(uint32_t packet_ptr, uint32_t index, uint32_t value);
void MakeCoherent();
Memory* memory_;
GraphicsSystem* graphics_system_;
GraphicsDriver* driver_;
uint64_t time_base_;
uint32_t counter_;
uint32_t primary_buffer_ptr_;
uint32_t primary_buffer_size_;
uint32_t read_ptr_index_;
uint32_t read_ptr_update_freq_;
uint32_t read_ptr_writeback_ptr_;
HANDLE write_ptr_index_event_;
volatile uint32_t write_ptr_index_;
volatile uint32_t write_ptr_max_index_;
DrawCommand draw_command_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_COMMAND_PROCESSOR_H_

View File

@ -1,74 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_DRAW_COMMAND_H_
#define XENIA_GPU_DRAW_COMMAND_H_
#include <xenia/common.h>
#include <xenia/gpu/buffer_resource.h>
#include <xenia/gpu/sampler_state_resource.h>
#include <xenia/gpu/shader_resource.h>
#include <xenia/gpu/texture_resource.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
// TODO(benvanik): move more of the enums in here?
struct DrawCommand {
xenos::XE_GPU_PRIMITIVE_TYPE prim_type;
uint32_t start_index;
uint32_t index_count;
uint32_t base_vertex;
VertexShaderResource* vertex_shader;
PixelShaderResource* pixel_shader;
// TODO(benvanik): dirty tracking/max ranges/etc.
struct {
float* values;
size_t count;
} float4_constants;
struct {
uint32_t* values;
size_t count;
} loop_constants;
struct {
uint32_t* values;
size_t count;
} bool_constants;
// Index buffer, if present. If index_count > 0 then auto draw.
IndexBufferResource* index_buffer;
// Vertex buffers.
struct {
uint32_t input_index;
VertexBufferResource* buffer;
uint32_t stride;
uint32_t offset;
} vertex_buffers[96];
size_t vertex_buffer_count;
// Texture samplers.
struct SamplerInput {
uint32_t input_index;
TextureResource* texture;
SamplerStateResource* sampler_state;
};
SamplerInput vertex_shader_samplers[32];
size_t vertex_shader_sampler_count;
SamplerInput pixel_shader_samplers[32];
size_t pixel_shader_sampler_count;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_DRAW_COMMAND_H_

View File

@ -7,22 +7,22 @@
******************************************************************************
*/
#ifndef XENIA_GPU_NOP_NOP_GPU_PRIVATE_H_
#define XENIA_GPU_NOP_NOP_GPU_PRIVATE_H_
#ifndef XENIA_GPU_GL4_GL4_GPU_PRIVATE_H_
#define XENIA_GPU_GL4_GL4_GPU_PRIVATE_H_
// GL headers
#include <xenia/common.h>
#include <xenia/gpu/nop/nop_gpu.h>
#include <xenia/gpu/gl4/gl4_gpu.h>
namespace xe {
namespace gpu {
namespace nop {
namespace gl4 {
//
} // namespace nop
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_NOP_NOP_PRIVATE_H_
#endif // XENIA_GPU_GL4_GL4_GPU_PRIVATE_H_

View File

@ -2,20 +2,19 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/nop/nop_gpu.h>
#include <xenia/gpu/gl4/gl4_gpu.h>
#include <xenia/gpu/nop/nop_graphics_system.h>
//#include <xenia/gpu/gl4/gl4_graphics_system.h>
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::nop;
namespace xe {
namespace gpu {
namespace gl4 {
namespace {
void InitializeIfNeeded();
void CleanupOnShutdown();
@ -32,9 +31,18 @@ void InitializeIfNeeded() {
}
void CleanupOnShutdown() {}
class GL4GraphicsSystem : public GraphicsSystem {
public:
GL4GraphicsSystem(Emulator* emulator) : GraphicsSystem(emulator) {}
~GL4GraphicsSystem() override = default;
};
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator) {
InitializeIfNeeded();
return std::make_unique<GL4GraphicsSystem>(emulator);
}
std::unique_ptr<GraphicsSystem> xe::gpu::nop::Create(Emulator* emulator) {
InitializeIfNeeded();
return std::make_unique<NopGraphicsSystem>(emulator);
}
} // namespace gl4
} // namespace gpu
} // namespace xe

View File

@ -2,31 +2,27 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_NOP_NOP_GPU_H_
#define XENIA_GPU_NOP_NOP_GPU_H_
#ifndef XENIA_GPU_GL4_GL4_GPU_H_
#define XENIA_GPU_GL4_GL4_GPU_H_
#include <memory>
#include <xenia/common.h>
namespace xe {
class Emulator;
} // namespace xe
#include <xenia/gpu/graphics_system.h>
namespace xe {
namespace gpu {
class GraphicsSystem;
namespace nop {
namespace gl4 {
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator);
} // namespace nop
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_NOP_NOP_GPU_H_
#endif // XENIA_GPU_GL4_GL4_GPU_H_

View File

@ -0,0 +1,8 @@
# Copyright 2014 Ben Vanik. All Rights Reserved.
{
'sources': [
'gl4_gpu-private.h',
'gl4_gpu.cc',
'gl4_gpu.h',
],
}

View File

@ -12,13 +12,9 @@
#include <gflags/gflags.h>
DECLARE_string(gpu);
DECLARE_bool(trace_ring_buffer);
DECLARE_string(dump_shaders);
DECLARE_uint64(max_draw_elements);
#endif // XENIA_GPU_PRIVATE_H_

View File

@ -10,49 +10,34 @@
#include <xenia/gpu/gpu.h>
#include <xenia/gpu/gpu-private.h>
using namespace xe;
using namespace xe::gpu;
// TODO(benvanik): based on platform.
#include <xenia/gpu/gl4/gl4_gpu.h>
DEFINE_string(gpu, "any", "Graphics system. Use: [any, nop, d3d11]");
namespace xe {
namespace gpu {
DEFINE_string(gpu, "any", "Graphics system. Use: [any, gl4]");
DEFINE_bool(trace_ring_buffer, false, "Trace GPU ring buffer packets.");
DEFINE_string(dump_shaders, "",
"Path to write GPU shaders to as they are compiled.");
DEFINE_uint64(max_draw_elements, 0,
"Maximum element count; anything over this is ignored.");
#include <xenia/gpu/nop/nop_gpu.h>
std::unique_ptr<GraphicsSystem> xe::gpu::CreateNop(Emulator* emulator) {
return xe::gpu::nop::Create(emulator);
}
#if XE_PLATFORM_WIN32
#include <xenia/gpu/d3d11/d3d11_gpu.h>
std::unique_ptr<GraphicsSystem> xe::gpu::CreateD3D11(Emulator* emulator) {
return xe::gpu::d3d11::Create(emulator);
}
#endif // WIN32
std::unique_ptr<GraphicsSystem> xe::gpu::Create(Emulator* emulator) {
if (FLAGS_gpu.compare("nop") == 0) {
return CreateNop(emulator);
#if XE_PLATFORM_WIN32
} else if (FLAGS_gpu.compare("d3d11") == 0) {
return CreateD3D11(emulator);
#endif // WIN32
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator) {
if (FLAGS_gpu.compare("gl4") == 0) {
return xe::gpu::gl4::Create(emulator);
} else {
// Create best available.
std::unique_ptr<GraphicsSystem> best;
#if XE_PLATFORM_WIN32
best = CreateD3D11(emulator);
best = xe::gpu::gl4::Create(emulator);
if (best) {
return best;
}
#endif // WIN32
// Fallback to nop.
return CreateNop(emulator);
// Nothing!
return nullptr;
}
}
} // namespace gpu
} // namespace xe

View File

@ -23,11 +23,7 @@ namespace gpu {
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator);
std::unique_ptr<GraphicsSystem> CreateNop(Emulator* emulator);
#if XE_PLATFORM_WIN32
std::unique_ptr<GraphicsSystem> CreateD3D11(Emulator* emulator);
#endif // WIN32
std::unique_ptr<GraphicsSystem> CreateGL4(Emulator* emulator);
} // namespace gpu
} // namespace xe

View File

@ -1,307 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/graphics_driver.h>
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
GraphicsDriver::GraphicsDriver(Memory* memory) : memory_(memory) {}
GraphicsDriver::~GraphicsDriver() = default;
int GraphicsDriver::LoadShader(XE_GPU_SHADER_TYPE type,
uint32_t address, uint32_t length,
uint32_t start) {
MemoryRange memory_range(
memory_->Translate(address),
address, length);
ShaderResource* shader = nullptr;
if (type == XE_GPU_SHADER_TYPE_VERTEX) {
VertexShaderResource::Info info;
shader = vertex_shader_ = resource_cache()->FetchVertexShader(memory_range,
info);
if (!vertex_shader_) {
XELOGE("Unable to fetch vertex shader");
return 1;
}
} else {
PixelShaderResource::Info info;
shader = pixel_shader_ = resource_cache()->FetchPixelShader(memory_range,
info);
if (!pixel_shader_) {
XELOGE("Unable to fetch pixel shader");
return 1;
}
}
if (!shader->is_prepared()) {
// Disassemble.
const char* source = shader->disasm_src();
XELOGGPU("Set shader %d at %0.8X (%db):\n%s",
type, address, length,
source ? source : "<failed to disassemble>");
}
return 0;
}
int GraphicsDriver::PrepareDraw(DrawCommand& command) {
SCOPE_profile_cpu_f("gpu");
// Ignore copies for now.
uint32_t enable_mode = register_file_[XE_GPU_REG_RB_MODECONTROL].u32 & 0x7;
if (enable_mode != 4) {
XELOGW("GPU: ignoring draw with enable mode %d", enable_mode);
return 1;
}
// Reset the things we don't modify so that we have clean state.
command.prim_type = XE_GPU_PRIMITIVE_TYPE_POINT_LIST;
command.index_count = 0;
command.index_buffer = nullptr;
// Generic stuff.
command.start_index = register_file_[XE_GPU_REG_VGT_INDX_OFFSET].u32;
command.base_vertex = 0;
int ret;
ret = PopulateState(command);
if (ret) {
XELOGE("Unable to prepare draw state");
return ret;
}
ret = PopulateConstantBuffers(command);
if (ret) {
XELOGE("Unable to prepare draw constant buffers");
return ret;
}
ret = PopulateShaders(command);
if (ret) {
XELOGE("Unable to prepare draw shaders");
return ret;
}
ret = PopulateInputAssembly(command);
if (ret) {
XELOGE("Unable to prepare draw input assembly");
return ret;
}
ret = PopulateSamplers(command);
if (ret) {
XELOGE("Unable to prepare draw samplers");
return ret;
}
return 0;
}
int GraphicsDriver::PrepareDrawIndexBuffer(
DrawCommand& command,
uint32_t address, uint32_t length,
xenos::XE_GPU_ENDIAN endianness,
IndexFormat format) {
SCOPE_profile_cpu_f("gpu");
MemoryRange memory_range(memory_->Translate(address), address, length);
IndexBufferResource::Info info;
info.endianness = endianness;
info.format = format;
command.index_buffer =
resource_cache()->FetchIndexBuffer(memory_range, info);
if (!command.index_buffer) {
return 1;
}
return 0;
}
int GraphicsDriver::PopulateState(DrawCommand& command) {
return 0;
}
int GraphicsDriver::PopulateConstantBuffers(DrawCommand& command) {
command.float4_constants.count = 512;
command.float4_constants.values =
&register_file_[XE_GPU_REG_SHADER_CONSTANT_000_X].f32;
command.loop_constants.count = 32;
command.loop_constants.values =
&register_file_[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].u32;
command.bool_constants.count = 8;
command.bool_constants.values =
&register_file_[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].u32;
return 0;
}
int GraphicsDriver::PopulateShaders(DrawCommand& command) {
SCOPE_profile_cpu_f("gpu");
if (!vertex_shader_) {
XELOGE("No vertex shader bound; ignoring");
return 1;
}
if (!pixel_shader_) {
XELOGE("No pixel shader bound; ignoring");
return 1;
}
xe_gpu_program_cntl_t program_cntl;
program_cntl.dword_0 = register_file_[XE_GPU_REG_SQ_PROGRAM_CNTL].u32;
if (!vertex_shader_->is_prepared()) {
if (vertex_shader_->Prepare(program_cntl)) {
XELOGE("Unable to prepare vertex shader");
return 1;
}
}
if (!pixel_shader_->is_prepared()) {
if (pixel_shader_->Prepare(program_cntl, vertex_shader_)) {
XELOGE("Unable to prepare pixel shader");
return 1;
}
}
command.vertex_shader = vertex_shader_;
command.pixel_shader = pixel_shader_;
return 0;
}
int GraphicsDriver::PopulateInputAssembly(DrawCommand& command) {
SCOPE_profile_cpu_f("gpu");
const auto& buffer_inputs = command.vertex_shader->buffer_inputs();
command.vertex_buffer_count = buffer_inputs.count;
for (size_t n = 0; n < buffer_inputs.count; n++) {
const auto& desc = buffer_inputs.descs[n];
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + (desc.fetch_slot / 3) * 6;
auto group = reinterpret_cast<xe_gpu_fetch_group_t*>(&register_file_.values[r]);
xe_gpu_vertex_fetch_t* fetch = nullptr;
switch (desc.fetch_slot % 3) {
case 0:
fetch = &group->vertex_fetch_0;
break;
case 1:
fetch = &group->vertex_fetch_1;
break;
case 2:
fetch = &group->vertex_fetch_2;
break;
}
assert_not_null(fetch);
assert_true(fetch->type == 0x3); // must be of type vertex
// TODO(benvanik): some games have type 2, which is texture - maybe
// fetch_slot wrong?
assert_not_zero(fetch->size);
const auto& info = desc.info;
MemoryRange memory_range;
memory_range.guest_base = fetch->address << 2;
memory_range.host_base = memory_->Translate(memory_range.guest_base);
memory_range.length = fetch->size * 4;
// TODO(benvanik): if the memory range is within the command buffer, we
// should use a cached transient buffer.
auto buffer = resource_cache()->FetchVertexBuffer(memory_range, info);
if (!buffer) {
XELOGE("Unable to create vertex fetch buffer");
return 1;
}
command.vertex_buffers[n].input_index = desc.input_index;
command.vertex_buffers[n].buffer = buffer;
command.vertex_buffers[n].stride = desc.info.stride_words * 4;
command.vertex_buffers[n].offset = 0;
}
return 0;
}
int GraphicsDriver::PopulateSamplers(DrawCommand& command) {
SCOPE_profile_cpu_f("gpu");
// Vertex texture samplers.
const auto& vertex_sampler_inputs = command.vertex_shader->sampler_inputs();
command.vertex_shader_sampler_count = vertex_sampler_inputs.count;
for (size_t i = 0; i < command.vertex_shader_sampler_count; ++i) {
if (PopulateSamplerSet(vertex_sampler_inputs.descs[i],
command.vertex_shader_samplers[i])) {
return 1;
}
}
// Pixel shader texture sampler.
const auto& pixel_sampler_inputs = command.pixel_shader->sampler_inputs();
command.pixel_shader_sampler_count = pixel_sampler_inputs.count;
for (size_t i = 0; i < command.pixel_shader_sampler_count; ++i) {
if (PopulateSamplerSet(pixel_sampler_inputs.descs[i],
command.pixel_shader_samplers[i])) {
return 1;
}
}
return 0;
}
int GraphicsDriver::PopulateSamplerSet(
const ShaderResource::SamplerDesc& src_input,
DrawCommand::SamplerInput& dst_input) {
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + src_input.fetch_slot * 6;
const auto group = (const xe_gpu_fetch_group_t*)&register_file_.values[r];
const xenos::xe_gpu_texture_fetch_t& fetch = group->texture_fetch;
if (fetch.type != 0x2) {
return 0;
}
dst_input.input_index = src_input.input_index;
dst_input.texture = nullptr;
dst_input.sampler_state = nullptr;
TextureResource::Info info;
if (!TextureResource::Info::Prepare(fetch, info)) {
XELOGE("D3D11: unable to parse texture fetcher info");
return 0; // invalid texture used
}
if (info.format == DXGI_FORMAT_UNKNOWN) {
XELOGW("D3D11: unknown texture format %d", info.format);
return 0; // invalid texture used
}
// TODO(benvanik): quick validate without refetching intraframe.
// Fetch texture from the cache.
MemoryRange memory_range;
memory_range.guest_base = fetch.address << 12;
memory_range.host_base = memory_->Translate(memory_range.guest_base);
memory_range.length = info.input_length;
auto texture = resource_cache()->FetchTexture(memory_range, info);
if (!texture) {
XELOGW("D3D11: unable to fetch texture");
return 0; // invalid texture used
}
SamplerStateResource::Info sampler_info;
if (!SamplerStateResource::Info::Prepare(fetch,
src_input.tex_fetch,
sampler_info)) {
XELOGW("D3D11: unable to parse sampler info");
return 0; // invalid texture used
}
auto sampler_state = resource_cache()->FetchSamplerState(sampler_info);
if (!sampler_state) {
XELOGW("D3D11: unable to fetch sampler");
return 0; // invalid texture used
}
dst_input.texture = texture;
dst_input.sampler_state = sampler_state;
return 0;
}

View File

@ -1,67 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_GRAPHICS_DRIVER_H_
#define XENIA_GPU_GRAPHICS_DRIVER_H_
#include <xenia/common.h>
#include <xenia/gpu/draw_command.h>
#include <xenia/gpu/register_file.h>
#include <xenia/gpu/resource_cache.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
class GraphicsDriver {
public:
virtual ~GraphicsDriver();
Memory* memory() const { return memory_; }
virtual ResourceCache* resource_cache() const = 0;
RegisterFile* register_file() { return &register_file_; };
virtual int Initialize() = 0;
int LoadShader(xenos::XE_GPU_SHADER_TYPE type,
uint32_t address, uint32_t length,
uint32_t start);
int PrepareDraw(DrawCommand& command);
int PrepareDrawIndexBuffer(DrawCommand& command,
uint32_t address, uint32_t length,
xenos::XE_GPU_ENDIAN endianness,
IndexFormat format);
virtual int Draw(const DrawCommand& command) = 0;
virtual int Resolve() = 0;
private:
int PopulateState(DrawCommand& command);
int PopulateConstantBuffers(DrawCommand& command);
int PopulateShaders(DrawCommand& command);
int PopulateInputAssembly(DrawCommand& command);
int PopulateSamplers(DrawCommand& command);
int PopulateSamplerSet(const ShaderResource::SamplerDesc& src_input,
DrawCommand::SamplerInput& dst_input);
protected:
GraphicsDriver(Memory* memory);
Memory* memory_;
RegisterFile register_file_;
VertexShaderResource* vertex_shader_;
PixelShaderResource* pixel_shader_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GRAPHICS_DRIVER_H_

View File

@ -12,111 +12,26 @@
#include <poly/poly.h>
#include <xenia/emulator.h>
#include <xenia/cpu/processor.h>
#include <xenia/gpu/command_processor.h>
#include <xenia/gpu/gpu-private.h>
#include <xenia/gpu/graphics_driver.h>
#include <xenia/gpu/register_file.h>
namespace xe {
namespace gpu {
using namespace xe;
using namespace xe::cpu;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
GraphicsSystem::GraphicsSystem(Emulator* emulator)
: emulator_(emulator),
memory_(emulator->memory()),
interrupt_callback_(0),
interrupt_callback_data_(0) {}
GraphicsSystem::GraphicsSystem(Emulator* emulator) :
emulator_(emulator), memory_(emulator->memory()),
running_(false), driver_(nullptr),
command_processor_(nullptr),
interrupt_callback_(0), interrupt_callback_data_(0),
thread_wait_(nullptr) {
// Create the run loop used for any windows/etc.
// This must be done on the thread we create the driver.
run_loop_ = xe_run_loop_create();
thread_wait_ = CreateEvent(NULL, TRUE, FALSE, NULL);
}
GraphicsSystem::~GraphicsSystem() {
CloseHandle(thread_wait_);
}
GraphicsSystem::~GraphicsSystem() {}
X_STATUS GraphicsSystem::Setup() {
processor_ = emulator_->processor();
// Create worker.
command_processor_ = new CommandProcessor(this, memory_);
// Let the processor know we want register access callbacks.
emulator_->memory()->AddMappedRange(
0x7FC80000,
0xFFFF0000,
0x0000FFFF,
this,
reinterpret_cast<MMIOReadCallback>(MMIOReadRegisterThunk),
reinterpret_cast<MMIOWriteCallback>(MMIOWriteRegisterThunk));
// Create worker thread.
// This will initialize the graphics system.
// Init needs to happen there so that any thread-local stuff
// is created on the right thread.
running_ = true;
thread_ = std::thread(std::bind(&GraphicsSystem::ThreadStart, this));
WaitForSingleObject(thread_wait_, INFINITE);
return X_STATUS_SUCCESS;
}
void GraphicsSystem::ThreadStart() {
poly::threading::set_name("GraphicsSystemThread");
xe::Profiler::ThreadEnter("GraphicsSystemThread");
xe_run_loop_ref run_loop = xe_run_loop_retain(run_loop_);
// Initialize driver and ringbuffer.
Initialize();
assert_not_null(driver_);
SetEvent(thread_wait_);
// Main run loop.
while (running_) {
// Peek main run loop.
{
SCOPE_profile_cpu_i("gpu", "GraphicsSystemRunLoopPump");
if (xe_run_loop_pump(run_loop)) {
break;
}
}
if (!running_) {
break;
}
// Pump worker.
command_processor_->Pump();
if (!running_) {
break;
}
// Pump graphics system.
Pump();
}
running_ = false;
xe_run_loop_release(run_loop);
xe::Profiler::ThreadExit();
}
void GraphicsSystem::Initialize() {
}
void GraphicsSystem::Shutdown() {
running_ = false;
thread_.join();
delete command_processor_;
xe_run_loop_release(run_loop_);
}
void GraphicsSystem::Shutdown() {}
void GraphicsSystem::SetInterruptCallback(uint32_t callback,
uint32_t user_data) {
@ -126,70 +41,15 @@ void GraphicsSystem::SetInterruptCallback(uint32_t callback,
}
void GraphicsSystem::InitializeRingBuffer(uint32_t ptr, uint32_t page_count) {
// TODO(benvanik): an event?
while (!driver_) {
Sleep(0);
}
assert_not_null(driver_);
command_processor_->Initialize(driver_, ptr, page_count);
//
}
void GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr,
uint32_t block_size) {
command_processor_->EnableReadPointerWriteBack(ptr, block_size);
//
}
uint64_t GraphicsSystem::ReadRegister(uint64_t addr) {
uint32_t r = addr & 0xFFFF;
if (FLAGS_trace_ring_buffer) {
XELOGGPU("ReadRegister(%.4X)", r);
}
RegisterFile* regs = driver_->register_file();
switch (r) {
case 0x6530: // ????
return 1;
case 0x6544: // ? vblank pending?
return 1;
case 0x6584: // ????
return 1;
}
assert_true(r >= 0 && r < RegisterFile::kRegisterCount);
return regs->values[r].u32;
}
void GraphicsSystem::WriteRegister(uint64_t addr, uint64_t value) {
uint32_t r = addr & 0xFFFF;
if (FLAGS_trace_ring_buffer) {
XELOGGPU("WriteRegister(%.4X, %.8X)", r, value);
}
RegisterFile* regs = driver_->register_file();
switch (r) {
case 0x0714: // CP_RB_WPTR
command_processor_->UpdateWritePointer((uint32_t)value);
break;
case 0x6110: // ? swap related?
XELOGW("Unimplemented GPU register %.4X write: %.8X", r, value);
return;
default:
XELOGW("Unknown GPU register %.4X write: %.8X", r, value);
break;
}
assert_true(r >= 0 && r < RegisterFile::kRegisterCount);
regs->values[r].u32 = (uint32_t)value;
}
void GraphicsSystem::MarkVblank() {
command_processor_->increment_counter();
}
void GraphicsSystem::DispatchInterruptCallback(
uint32_t source, uint32_t cpu) {
void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
// Pick a CPU, if needed. We're going to guess 2. Because.
if (cpu == 0xFFFFFFFF) {
cpu = 2;
@ -202,7 +62,10 @@ void GraphicsSystem::DispatchInterruptCallback(
if (!interrupt_callback_) {
return;
}
uint64_t args[] = { source, interrupt_callback_data_ };
processor_->ExecuteInterrupt(
cpu, interrupt_callback_, args, poly::countof(args));
uint64_t args[] = {source, interrupt_callback_data_};
processor_->ExecuteInterrupt(cpu, interrupt_callback_, args,
poly::countof(args));
}
} // namespace gpu
} // namespace xe

View File

@ -20,9 +20,6 @@
namespace xe {
namespace gpu {
class CommandProcessor;
class GraphicsDriver;
class GraphicsSystem {
public:
virtual ~GraphicsSystem();
@ -38,45 +35,17 @@ class GraphicsSystem {
void InitializeRingBuffer(uint32_t ptr, uint32_t page_count);
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size);
virtual uint64_t ReadRegister(uint64_t addr);
virtual void WriteRegister(uint64_t addr, uint64_t value);
void MarkVblank();
void DispatchInterruptCallback(uint32_t source, uint32_t cpu = 0xFFFFFFFF);
virtual void Swap() = 0;
protected:
virtual void Initialize();
virtual void Pump() = 0;
private:
void ThreadStart();
static uint64_t MMIOReadRegisterThunk(GraphicsSystem* gs, uint64_t addr) {
return gs->ReadRegister(addr);
}
static void MMIOWriteRegisterThunk(GraphicsSystem* gs, uint64_t addr,
uint64_t value) {
gs->WriteRegister(addr, value);
}
protected:
GraphicsSystem(Emulator* emulator);
void DispatchInterruptCallback(uint32_t source, uint32_t cpu);
Emulator* emulator_;
Memory* memory_;
cpu::Processor* processor_;
xe_run_loop_ref run_loop_;
std::thread thread_;
std::atomic<bool> running_;
GraphicsDriver* driver_;
CommandProcessor* command_processor_;
uint32_t interrupt_callback_;
uint32_t interrupt_callback_data_;
HANDLE thread_wait_;
};
} // namespace gpu

View File

@ -1,38 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/nop/nop_graphics_driver.h>
#include <xenia/gpu/gpu-private.h>
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::nop;
using namespace xe::gpu::xenos;
NopGraphicsDriver::NopGraphicsDriver(Memory* memory)
: GraphicsDriver(memory), resource_cache_(nullptr) {
}
NopGraphicsDriver::~NopGraphicsDriver() {
}
int NopGraphicsDriver::Initialize() {
return 0;
}
int NopGraphicsDriver::Draw(const DrawCommand& command) {
return 0;
}
int NopGraphicsDriver::Resolve() {
return 0;
}

View File

@ -1,43 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_NOP_NOP_GRAPHICS_DRIVER_H_
#define XENIA_GPU_NOP_NOP_GRAPHICS_DRIVER_H_
#include <xenia/common.h>
#include <xenia/gpu/graphics_driver.h>
#include <xenia/gpu/nop/nop_gpu-private.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
namespace nop {
class NopGraphicsDriver : public GraphicsDriver {
public:
NopGraphicsDriver(Memory* memory);
virtual ~NopGraphicsDriver();
ResourceCache* resource_cache() const override { return resource_cache_; }
int Initialize() override;
int Draw(const DrawCommand& command) override;
int Resolve() override;
protected:
ResourceCache* resource_cache_;
};
} // namespace nop
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_NOP_NOP_GRAPHICS_DRIVER_H_

View File

@ -1,72 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/nop/nop_graphics_system.h>
#include <xenia/gpu/gpu-private.h>
#include <xenia/gpu/nop/nop_graphics_driver.h>
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::nop;
namespace {
void __stdcall NopGraphicsSystemVsyncCallback(NopGraphicsSystem* gs, BOOLEAN) {
gs->MarkVblank();
gs->DispatchInterruptCallback(0);
}
}
NopGraphicsSystem::NopGraphicsSystem(Emulator* emulator) :
GraphicsSystem(emulator),
timer_queue_(NULL),
vsync_timer_(NULL) {
}
NopGraphicsSystem::~NopGraphicsSystem() {
}
void NopGraphicsSystem::Initialize() {
GraphicsSystem::Initialize();
assert_null(driver_);
driver_ = new NopGraphicsDriver(memory_);
assert_null(timer_queue_);
assert_null(vsync_timer_);
timer_queue_ = CreateTimerQueue();
CreateTimerQueueTimer(
&vsync_timer_,
timer_queue_,
(WAITORTIMERCALLBACK)NopGraphicsSystemVsyncCallback,
this,
16,
100,
WT_EXECUTEINTIMERTHREAD);
}
void NopGraphicsSystem::Pump() {
}
void NopGraphicsSystem::Shutdown() {
if (vsync_timer_) {
DeleteTimerQueueTimer(timer_queue_, vsync_timer_, NULL);
}
if (timer_queue_) {
DeleteTimerQueueEx(timer_queue_, NULL);
}
GraphicsSystem::Shutdown();
}

View File

@ -1,43 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_NOP_NOP_GRAPHICS_SYSTEM_H_
#define XENIA_GPU_NOP_NOP_GRAPHICS_SYSTEM_H_
#include <xenia/common.h>
#include <xenia/gpu/graphics_system.h>
#include <xenia/gpu/nop/nop_gpu-private.h>
namespace xe {
namespace gpu {
namespace nop {
class NopGraphicsSystem : public GraphicsSystem {
public:
NopGraphicsSystem(Emulator* emulator);
virtual ~NopGraphicsSystem();
virtual void Shutdown();
void Swap() override {}
protected:
virtual void Initialize();
virtual void Pump();
private:
HANDLE timer_queue_;
HANDLE vsync_timer_;
};
} // namespace nop
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_NOP_NOP_GRAPHICS_SYSTEM_H_

View File

@ -1,12 +0,0 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'nop_gpu-private.h',
'nop_gpu.cc',
'nop_gpu.h',
'nop_graphics_driver.cc',
'nop_graphics_driver.h',
'nop_graphics_system.cc',
'nop_graphics_system.h',
],
}

View File

@ -9,8 +9,8 @@
#include <xenia/gpu/register_file.h>
using namespace xe;
using namespace xe::gpu;
namespace xe {
namespace gpu {
RegisterFile::RegisterFile() { memset(values, 0, sizeof(values)); }
@ -19,9 +19,12 @@ const char* RegisterFile::GetRegisterName(uint32_t index) {
#define XE_GPU_REGISTER(index, type, name) \
case index: \
return #name;
#include <xenia/gpu/xenos/register_table.inc>
#include <xenia/gpu/register_table.inc>
#undef XE_GPU_REGISTER
default:
return NULL;
return nullptr;
}
}
} // namespace gpu
} // namespace xe

View File

@ -16,28 +16,25 @@ namespace xe {
namespace gpu {
enum Register {
#define XE_GPU_REGISTER(index, type, name) \
XE_GPU_REG_##name = index,
#include <xenia/gpu/xenos/register_table.inc>
#define XE_GPU_REGISTER(index, type, name) XE_GPU_REG_##name = index,
#include <xenia/gpu/register_table.inc>
#undef XE_GPU_REGISTER
};
class RegisterFile {
public:
public:
RegisterFile();
const char* GetRegisterName(uint32_t index);
static const size_t kRegisterCount = 0x5003;
union RegisterValue {
uint32_t u32;
float f32;
uint32_t u32;
float f32;
};
RegisterValue values[kRegisterCount];
RegisterValue& operator[](Register reg) {
return values[reg];
}
RegisterValue& operator[](Register reg) { return values[reg]; }
};
} // namespace gpu

View File

@ -1,37 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/resource.h>
using namespace std;
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
HashedResource::HashedResource(const MemoryRange& memory_range)
: memory_range_(memory_range) {
}
HashedResource::~HashedResource() = default;
PagedResource::PagedResource(const MemoryRange& memory_range)
: memory_range_(memory_range), dirtied_(true) {
}
PagedResource::~PagedResource() = default;
void PagedResource::MarkDirty(uint32_t lo_address, uint32_t hi_address) {
dirtied_ = true;
}
StaticResource::StaticResource() = default;
StaticResource::~StaticResource() = default;

View File

@ -1,100 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_RESOURCE_H_
#define XENIA_GPU_RESOURCE_H_
#include <xenia/common.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
struct MemoryRange {
uint8_t* host_base;
uint32_t guest_base;
uint32_t length;
MemoryRange() : host_base(nullptr), guest_base(0), length(0) {}
MemoryRange(const MemoryRange& other)
: host_base(other.host_base), guest_base(other.guest_base),
length(other.length) {}
MemoryRange(uint8_t* _host_base, uint32_t _guest_base, uint32_t _length)
: host_base(_host_base), guest_base(_guest_base), length(_length) {}
};
class Resource {
public:
virtual ~Resource() = default;
virtual void* handle() const = 0;
template <typename T>
T* handle_as() {
return reinterpret_cast<T*>(handle());
}
protected:
Resource() = default;
// last use/LRU stuff
};
class HashedResource : public Resource {
public:
~HashedResource() override;
const MemoryRange& memory_range() const { return memory_range_; }
protected:
HashedResource(const MemoryRange& memory_range);
MemoryRange memory_range_;
// key
};
class PagedResource : public Resource {
public:
~PagedResource() override;
const MemoryRange& memory_range() const { return memory_range_; }
template <typename T>
bool Equals(const T& info) {
return Equals(&info, sizeof(info));
}
virtual bool Equals(const void* info_ptr, size_t info_length) = 0;
bool is_dirty() const { return true; }
void MarkDirty(uint32_t lo_address, uint32_t hi_address);
protected:
PagedResource(const MemoryRange& memory_range);
MemoryRange memory_range_;
bool dirtied_;
// dirtied pages list
};
class StaticResource : public Resource {
public:
~StaticResource() override;
protected:
StaticResource();
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_RESOURCE_H_

View File

@ -1,120 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/resource_cache.h>
#include <algorithm>
#include <xenia/core/hash.h>
using namespace std;
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
ResourceCache::ResourceCache(Memory* memory)
: memory_(memory) {
}
ResourceCache::~ResourceCache() {
for (auto it = resources_.begin(); it != resources_.end(); ++it) {
Resource* resource = *it;
delete resource;
}
resources_.clear();
}
VertexShaderResource* ResourceCache::FetchVertexShader(
const MemoryRange& memory_range,
const VertexShaderResource::Info& info) {
return FetchHashedResource<VertexShaderResource>(
memory_range, info, &ResourceCache::CreateVertexShader);
}
PixelShaderResource* ResourceCache::FetchPixelShader(
const MemoryRange& memory_range,
const PixelShaderResource::Info& info) {
return FetchHashedResource<PixelShaderResource>(
memory_range, info, &ResourceCache::CreatePixelShader);
}
TextureResource* ResourceCache::FetchTexture(
const MemoryRange& memory_range,
const TextureResource::Info& info) {
auto resource = FetchPagedResource<TextureResource>(
memory_range, info, &ResourceCache::CreateTexture);
if (!resource) {
return nullptr;
}
if (resource->Prepare()) {
XELOGE("Unable to prepare texture");
return nullptr;
}
return resource;
}
SamplerStateResource* ResourceCache::FetchSamplerState(
const SamplerStateResource::Info& info) {
auto key = info.hash();
auto it = static_resources_.find(key);
if (it != static_resources_.end()) {
return static_cast<SamplerStateResource*>(it->second);
}
auto resource = CreateSamplerState(info);
if (resource->Prepare()) {
XELOGE("Unable to prepare sampler state");
return nullptr;
}
static_resources_.insert({ key, resource });
resources_.push_back(resource);
return resource;
}
IndexBufferResource* ResourceCache::FetchIndexBuffer(
const MemoryRange& memory_range,
const IndexBufferResource::Info& info) {
auto resource = FetchPagedResource<IndexBufferResource>(
memory_range, info, &ResourceCache::CreateIndexBuffer);
if (!resource) {
return nullptr;
}
if (resource->Prepare()) {
XELOGE("Unable to prepare index buffer");
return nullptr;
}
return resource;
}
VertexBufferResource* ResourceCache::FetchVertexBuffer(
const MemoryRange& memory_range,
const VertexBufferResource::Info& info) {
auto resource = FetchPagedResource<VertexBufferResource>(
memory_range, info, &ResourceCache::CreateVertexBuffer);
if (!resource) {
return nullptr;
}
if (resource->Prepare()) {
XELOGE("Unable to prepare vertex buffer");
return nullptr;
}
return resource;
}
uint64_t ResourceCache::HashRange(const MemoryRange& memory_range) {
// We could do something smarter here to potentially early exit.
return hash64(memory_range.host_base, memory_range.length);
}
void ResourceCache::SyncRange(uint32_t address, int length) {
SCOPE_profile_cpu_f("gpu");
// TODO(benvanik): something interesting?
//uint32_t lo_address = address % 0x20000000;
//uint32_t hi_address = lo_address + length;
}

View File

@ -1,124 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_RESOURCE_CACHE_H_
#define XENIA_GPU_RESOURCE_CACHE_H_
#include <map>
#include <unordered_map>
#include <xenia/common.h>
#include <xenia/gpu/buffer_resource.h>
#include <xenia/gpu/resource.h>
#include <xenia/gpu/sampler_state_resource.h>
#include <xenia/gpu/shader_resource.h>
#include <xenia/gpu/texture_resource.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
class ResourceCache {
public:
virtual ~ResourceCache();
VertexShaderResource* FetchVertexShader(
const MemoryRange& memory_range,
const VertexShaderResource::Info& info);
PixelShaderResource* FetchPixelShader(
const MemoryRange& memory_range,
const PixelShaderResource::Info& info);
TextureResource* FetchTexture(
const MemoryRange& memory_range,
const TextureResource::Info& info);
SamplerStateResource* FetchSamplerState(
const SamplerStateResource::Info& info);
IndexBufferResource* FetchIndexBuffer(
const MemoryRange& memory_range,
const IndexBufferResource::Info& info);
VertexBufferResource* FetchVertexBuffer(
const MemoryRange& memory_range,
const VertexBufferResource::Info& info);
uint64_t HashRange(const MemoryRange& memory_range);
void SyncRange(uint32_t address, int length);
protected:
ResourceCache(Memory* memory);
template <typename T, typename V>
T* FetchHashedResource(const MemoryRange& memory_range,
const typename T::Info& info,
const V& factory) {
// TODO(benvanik): if there's no way it's changed and it's been checked,
// just lookup. This way we don't rehash 100x a frame.
auto key = HashRange(memory_range);
auto it = hashed_resources_.find(key);
if (it != hashed_resources_.end()) {
return static_cast<T*>(it->second);
}
auto resource = (this->*factory)(memory_range, info);
hashed_resources_.insert({ key, resource });
resources_.push_back(resource);
return resource;
}
template <typename T, typename V>
T* FetchPagedResource(const MemoryRange& memory_range,
const typename T::Info& info,
const V& factory) {
uint32_t lo_address = memory_range.guest_base % 0x20000000;
auto key = uint64_t(lo_address);
auto range = paged_resources_.equal_range(key);
for (auto it = range.first; it != range.second; ++it) {
if (it->second->memory_range().length == memory_range.length &&
it->second->Equals(info)) {
return static_cast<T*>(it->second);
}
}
auto resource = (this->*factory)(memory_range, info);
paged_resources_.insert({ key, resource });
resources_.push_back(resource);
return resource;
}
virtual VertexShaderResource* CreateVertexShader(
const MemoryRange& memory_range,
const VertexShaderResource::Info& info) = 0;
virtual PixelShaderResource* CreatePixelShader(
const MemoryRange& memory_range,
const PixelShaderResource::Info& info) = 0;
virtual TextureResource* CreateTexture(
const MemoryRange& memory_range,
const TextureResource::Info& info) = 0;
virtual SamplerStateResource* CreateSamplerState(
const SamplerStateResource::Info& info) = 0;
virtual IndexBufferResource* CreateIndexBuffer(
const MemoryRange& memory_range,
const IndexBufferResource::Info& info) = 0;
virtual VertexBufferResource* CreateVertexBuffer(
const MemoryRange& memory_range,
const VertexBufferResource::Info& info) = 0;
private:
Memory* memory_;
std::vector<Resource*> resources_;
std::unordered_map<uint64_t, HashedResource*> hashed_resources_;
std::unordered_map<uint64_t, StaticResource*> static_resources_;
std::multimap<uint64_t, PagedResource*> paged_resources_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_RESOURCE_CACHE_H_

View File

@ -1,32 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/sampler_state_resource.h>
using namespace std;
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
bool SamplerStateResource::Info::Prepare(
const xe_gpu_texture_fetch_t& fetch, const instr_fetch_tex_t& fetch_instr,
Info& out_info) {
out_info.min_filter = static_cast<instr_tex_filter_t>(
fetch_instr.min_filter == 3 ? fetch.min_filter : fetch_instr.min_filter);
out_info.mag_filter = static_cast<instr_tex_filter_t>(
fetch_instr.mag_filter == 3 ? fetch.mag_filter : fetch_instr.mag_filter);
out_info.mip_filter = static_cast<instr_tex_filter_t>(
fetch_instr.mip_filter == 3 ? fetch.mip_filter : fetch_instr.mip_filter);
out_info.clamp_u = fetch.clamp_x;
out_info.clamp_v = fetch.clamp_y;
out_info.clamp_w = fetch.clamp_z;
return true;
}

View File

@ -1,68 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_SAMPLER_STATE_RESOURCE_H_
#define XENIA_GPU_SAMPLER_STATE_RESOURCE_H_
#include <xenia/core/hash.h>
#include <xenia/gpu/resource.h>
#include <xenia/gpu/xenos/ucode.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
class SamplerStateResource : public StaticResource {
public:
struct Info {
xenos::instr_tex_filter_t min_filter;
xenos::instr_tex_filter_t mag_filter;
xenos::instr_tex_filter_t mip_filter;
uint32_t clamp_u;
uint32_t clamp_v;
uint32_t clamp_w;
uint64_t hash() const {
return hash_combine(0,
min_filter, mag_filter, mip_filter,
clamp_u, clamp_v, clamp_w);
}
bool Equals(const Info& other) const {
return min_filter == other.min_filter &&
mag_filter == other.mag_filter &&
mip_filter == other.mip_filter &&
clamp_u == other.clamp_u &&
clamp_v == other.clamp_v &&
clamp_w == other.clamp_w;
}
static bool Prepare(const xenos::xe_gpu_texture_fetch_t& fetch,
const xenos::instr_fetch_tex_t& fetch_instr,
Info& out_info);
};
SamplerStateResource(const Info& info) : info_(info) {}
virtual ~SamplerStateResource() = default;
const Info& info() const { return info_; }
virtual int Prepare() = 0;
protected:
Info info_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_SAMPLER_STATE_RESOURCE_H_

View File

@ -1,280 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/shader_resource.h>
#include <poly/math.h>
#include <xenia/gpu/xenos/ucode_disassembler.h>
const bool kAssertOnZeroShaders = false;
using namespace std;
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
ShaderResource::ShaderResource(const MemoryRange& memory_range,
const Info& info, xenos::XE_GPU_SHADER_TYPE type)
: HashedResource(memory_range),
info_(info),
type_(type),
is_prepared_(false),
disasm_src_(nullptr) {
memset(&alloc_counts_, 0, sizeof(alloc_counts_));
memset(&buffer_inputs_, 0, sizeof(buffer_inputs_));
memset(&sampler_inputs_, 0, sizeof(sampler_inputs_));
// Verify.
dword_count_ = memory_range.length / 4;
assert_true(dword_count_ <= 512);
// Copy bytes and swap.
size_t byte_size = dword_count_ * sizeof(uint32_t);
dwords_ = (uint32_t*)malloc(byte_size);
bool any_nonzero = false;
for (uint32_t n = 0; n < dword_count_; n++) {
dwords_[n] = poly::load_and_swap<uint32_t>(memory_range.host_base + n * 4);
any_nonzero = any_nonzero || dwords_[n] != 0;
}
if (kAssertOnZeroShaders) {
assert_true(any_nonzero);
}
// Disassemble, for debugging.
disasm_src_ = DisassembleShader(type_, dwords_, dword_count_);
// Gather input/output registers/etc.
GatherIO();
}
ShaderResource::~ShaderResource() {
free(disasm_src_);
free(dwords_);
}
void ShaderResource::GatherIO() {
// Process all execution blocks.
instr_cf_t cfa;
instr_cf_t cfb;
for (int idx = 0; idx < dword_count_; idx += 3) {
uint32_t dword_0 = dwords_[idx + 0];
uint32_t dword_1 = dwords_[idx + 1];
uint32_t dword_2 = dwords_[idx + 2];
cfa.dword_0 = dword_0;
cfa.dword_1 = dword_1 & 0xFFFF;
cfb.dword_0 = (dword_1 >> 16) | (dword_2 << 16);
cfb.dword_1 = dword_2 >> 16;
if (cfa.opc == ALLOC) {
GatherAlloc(&cfa.alloc);
} else if (cfa.is_exec()) {
GatherExec(&cfa.exec);
}
if (cfb.opc == ALLOC) {
GatherAlloc(&cfb.alloc);
} else if (cfb.is_exec()) {
GatherExec(&cfb.exec);
}
if (cfa.opc == EXEC_END || cfb.opc == EXEC_END) {
break;
}
}
}
void ShaderResource::GatherAlloc(const instr_cf_alloc_t* cf) {
allocs_.push_back(*cf);
switch (cf->buffer_select) {
case SQ_POSITION:
// Position (SV_POSITION).
alloc_counts_.positions += cf->size + 1;
break;
case SQ_PARAMETER_PIXEL:
// Output to PS (if VS), or frag output (if PS).
alloc_counts_.params += cf->size + 1;
break;
case SQ_MEMORY:
// MEMEXPORT?
alloc_counts_.memories += cf->size + 1;
break;
}
}
void ShaderResource::GatherExec(const instr_cf_exec_t* cf) {
execs_.push_back(*cf);
uint32_t sequence = cf->serialize;
for (uint32_t i = 0; i < cf->count; i++) {
uint32_t alu_off = (cf->address + i);
int sync = sequence & 0x2;
if (sequence & 0x1) {
const instr_fetch_t* fetch =
(const instr_fetch_t*)(dwords_ + alu_off * 3);
switch (fetch->opc) {
case VTX_FETCH:
GatherVertexFetch(&fetch->vtx);
break;
case TEX_FETCH:
GatherTextureFetch(&fetch->tex);
break;
case TEX_GET_BORDER_COLOR_FRAC:
case TEX_GET_COMP_TEX_LOD:
case TEX_GET_GRADIENTS:
case TEX_GET_WEIGHTS:
case TEX_SET_TEX_LOD:
case TEX_SET_GRADIENTS_H:
case TEX_SET_GRADIENTS_V:
default:
assert_always();
break;
}
} else {
// TODO(benvanik): gather registers used, predicate bits used, etc.
const instr_alu_t* alu = (const instr_alu_t*)(dwords_ + alu_off * 3);
if (alu->vector_write_mask) {
if (alu->export_data && alu->vector_dest == 63) {
alloc_counts_.point_size = true;
}
}
if (alu->scalar_write_mask || !alu->vector_write_mask) {
if (alu->export_data && alu->scalar_dest == 63) {
alloc_counts_.point_size = true;
}
}
}
sequence >>= 2;
}
}
void ShaderResource::GatherVertexFetch(const instr_fetch_vtx_t* vtx) {
assert_true(type_ == XE_GPU_SHADER_TYPE_VERTEX);
// dst_reg/dst_swiz
// src_reg/src_swiz
// format = a2xx_sq_surfaceformat
// format_comp_all ? signed : unsigned
// num_format_all ? normalized
// stride
// offset
// const_index/const_index_sel -- fetch constant register
// num_format_all ? integer : fraction
// exp_adjust_all - [-32,31] - (2^exp_adjust_all)*fetch - 0 = default
// Sometimes games have fetches that just produce constants. We can
// ignore those.
uint32_t dst_swiz = vtx->dst_swiz;
bool fetches_any_data = false;
for (int i = 0; i < 4; i++) {
if ((dst_swiz & 0x7) == 4) {
// 0.0
} else if ((dst_swiz & 0x7) == 5) {
// 1.0
} else if ((dst_swiz & 0x7) == 6) {
// ?
} else if ((dst_swiz & 0x7) == 7) {
// Previous register value.
} else {
fetches_any_data = true;
break;
}
dst_swiz >>= 3;
}
if (!fetches_any_data) {
return;
}
uint32_t fetch_slot = vtx->const_index * 3 + vtx->const_index_sel;
auto& inputs = buffer_inputs_;
VertexBufferResource::DeclElement* el = nullptr;
for (size_t n = 0; n < inputs.count; n++) {
auto& desc = inputs.descs[n];
auto& info = desc.info;
if (desc.fetch_slot == fetch_slot) {
assert_true(info.element_count <= poly::countof(info.elements));
// It may not hold that all strides are equal, but I hope it does.
assert_true(!vtx->stride || info.stride_words == vtx->stride);
el = &info.elements[info.element_count++];
break;
}
}
if (!el) {
assert_not_zero(vtx->stride);
assert_true(inputs.count + 1 < poly::countof(inputs.descs));
auto& desc = inputs.descs[inputs.count++];
desc.input_index = inputs.count - 1;
desc.fetch_slot = fetch_slot;
desc.info.stride_words = vtx->stride;
el = &desc.info.elements[desc.info.element_count++];
}
el->vtx_fetch = *vtx;
el->format = vtx->format;
el->is_normalized = vtx->num_format_all == 0;
el->is_signed = vtx->format_comp_all == 1;
el->offset_words = vtx->offset;
el->size_words = 0;
switch (el->format) {
case FMT_8_8_8_8:
case FMT_2_10_10_10:
case FMT_10_11_11:
case FMT_11_11_10:
el->size_words = 1;
break;
case FMT_16_16:
case FMT_16_16_FLOAT:
el->size_words = 1;
break;
case FMT_16_16_16_16:
case FMT_16_16_16_16_FLOAT:
el->size_words = 2;
break;
case FMT_32:
case FMT_32_FLOAT:
el->size_words = 1;
break;
case FMT_32_32:
case FMT_32_32_FLOAT:
el->size_words = 2;
break;
case FMT_32_32_32_FLOAT:
el->size_words = 3;
break;
case FMT_32_32_32_32:
case FMT_32_32_32_32_FLOAT:
el->size_words = 4;
break;
default:
XELOGE("Unknown vertex format: %d", el->format);
assert_always();
break;
}
}
void ShaderResource::GatherTextureFetch(const xenos::instr_fetch_tex_t* tex) {
// TODO(benvanik): check dest_swiz to see if we are writing anything.
assert_true(sampler_inputs_.count + 1 < poly::countof(sampler_inputs_.descs));
auto& input = sampler_inputs_.descs[sampler_inputs_.count++];
input.input_index = sampler_inputs_.count - 1;
input.fetch_slot = tex->const_idx & 0xF; // ?
input.tex_fetch = *tex;
// Format mangling, size estimation, etc.
}
VertexShaderResource::VertexShaderResource(const MemoryRange& memory_range,
const Info& info)
: ShaderResource(memory_range, info, XE_GPU_SHADER_TYPE_VERTEX) {}
VertexShaderResource::~VertexShaderResource() = default;
PixelShaderResource::PixelShaderResource(const MemoryRange& memory_range,
const Info& info)
: ShaderResource(memory_range, info, XE_GPU_SHADER_TYPE_PIXEL) {}
PixelShaderResource::~PixelShaderResource() = default;

View File

@ -1,130 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_SHADER_RESOURCE_H_
#define XENIA_GPU_SHADER_RESOURCE_H_
#include <vector>
#include <xenia/gpu/buffer_resource.h>
#include <xenia/gpu/resource.h>
#include <xenia/gpu/xenos/ucode.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
class ShaderResource : public HashedResource {
public:
struct Info {
// type, etc?
};
~ShaderResource() override;
const Info& info() const { return info_; }
xenos::XE_GPU_SHADER_TYPE type() const { return type_; }
const uint32_t* dwords() const { return dwords_; }
const size_t dword_count() const { return dword_count_; }
bool is_prepared() const { return is_prepared_; }
const char* disasm_src() const { return disasm_src_; }
struct BufferDesc {
uint32_t input_index;
uint32_t fetch_slot;
VertexBufferResource::Info info;
// xenos::instr_fetch_vtx_t vtx_fetch; for each el
};
struct BufferInputs {
uint32_t count;
BufferDesc descs[32];
};
const BufferInputs& buffer_inputs() { return buffer_inputs_; }
struct SamplerDesc {
uint32_t input_index;
uint32_t fetch_slot;
uint32_t format;
xenos::instr_fetch_tex_t tex_fetch;
};
struct SamplerInputs {
uint32_t count;
SamplerDesc descs[32];
};
const SamplerInputs& sampler_inputs() { return sampler_inputs_; }
struct AllocCounts {
uint32_t positions;
uint32_t params;
uint32_t memories;
bool point_size;
};
const AllocCounts& alloc_counts() const { return alloc_counts_; }
const std::vector<xenos::instr_cf_exec_t>& execs() const { return execs_; }
const std::vector<xenos::instr_cf_alloc_t>& allocs() const { return allocs_; }
private:
void GatherIO();
void GatherAlloc(const xenos::instr_cf_alloc_t* cf);
void GatherExec(const xenos::instr_cf_exec_t* cf);
void GatherVertexFetch(const xenos::instr_fetch_vtx_t* vtx);
void GatherTextureFetch(const xenos::instr_fetch_tex_t* tex);
protected:
ShaderResource(const MemoryRange& memory_range,
const Info& info,
xenos::XE_GPU_SHADER_TYPE type);
Info info_;
xenos::XE_GPU_SHADER_TYPE type_;
size_t dword_count_;
uint32_t* dwords_;
char* disasm_src_;
AllocCounts alloc_counts_;
std::vector<xenos::instr_cf_exec_t> execs_;
std::vector<xenos::instr_cf_alloc_t> allocs_;
BufferInputs buffer_inputs_;
SamplerInputs sampler_inputs_;
bool is_prepared_;
};
class VertexShaderResource : public ShaderResource {
public:
VertexShaderResource(const MemoryRange& memory_range,
const Info& info);
~VertexShaderResource() override;
// buffer_inputs() matching VertexBufferResource::Info
virtual int Prepare(const xenos::xe_gpu_program_cntl_t& program_cntl) = 0;
};
class PixelShaderResource : public ShaderResource {
public:
PixelShaderResource(const MemoryRange& memory_range,
const Info& info);
~PixelShaderResource() override;
virtual int Prepare(const xenos::xe_gpu_program_cntl_t& program_cntl,
VertexShaderResource* vertex_shader) = 0;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_SHADER_RESOURCE_H_

View File

@ -1,42 +1,21 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'buffer_resource.cc',
'buffer_resource.h',
'command_processor.cc',
'command_processor.h',
'draw_command.h',
'gpu-private.h',
'gpu.cc',
'gpu.h',
'graphics_driver.cc',
'graphics_driver.h',
'graphics_system.cc',
'graphics_system.h',
'register_file.cc',
'register_file.h',
'resource.cc',
'resource.h',
'resource_cache.cc',
'resource_cache.h',
'sampler_state_resource.cc',
'sampler_state_resource.h',
'shader_resource.cc',
'shader_resource.h',
'texture_resource.cc',
'texture_resource.h',
'register_table.inc',
'ucode.h',
'ucode_disassembler.cc',
'ucode_disassembler.h',
'xenos.h',
],
'includes': [
'nop/sources.gypi',
'xenos/sources.gypi',
],
'conditions': [
['OS == "win"', {
'includes': [
'd3d11/sources.gypi',
],
}],
'gl4/sources.gypi',
],
}

View File

@ -1,363 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/texture_resource.h>
#include <poly/math.h>
#include <xenia/gpu/xenos/ucode.h>
#include <xenia/gpu/xenos/xenos.h>
using namespace std;
using namespace xe;
using namespace xe::gpu;
using namespace xe::gpu::xenos;
bool TextureResource::Info::Prepare(const xe_gpu_texture_fetch_t& fetch,
Info& info) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/cc308051(v=vs.85).aspx
// a2xx_sq_surfaceformat
info.dimension = (TextureDimension)fetch.dimension;
switch (info.dimension) {
case TEXTURE_DIMENSION_1D:
info.width = fetch.size_1d.width;
break;
case TEXTURE_DIMENSION_2D:
info.width = fetch.size_2d.width;
info.height = fetch.size_2d.height;
break;
case TEXTURE_DIMENSION_3D:
case TEXTURE_DIMENSION_CUBE:
info.width = fetch.size_3d.width;
info.height = fetch.size_3d.height;
info.depth = fetch.size_3d.depth;
break;
}
info.block_size = 0;
info.texel_pitch = 0;
info.endianness = (XE_GPU_ENDIAN)fetch.endianness;
info.is_tiled = fetch.tiled;
info.is_compressed = false;
info.input_length = 0;
info.format = DXGI_FORMAT_UNKNOWN;
switch (fetch.format) {
case FMT_8:
switch (fetch.swizzle) {
case XE_GPU_SWIZZLE_RRR1:
info.format = DXGI_FORMAT_R8_UNORM;
break;
case XE_GPU_SWIZZLE_000R:
info.format = DXGI_FORMAT_A8_UNORM;
break;
default:
XELOGW("D3D11: unhandled swizzle for FMT_8");
info.format = DXGI_FORMAT_A8_UNORM;
break;
}
info.block_size = 1;
info.texel_pitch = 1;
break;
case FMT_1_5_5_5:
switch (fetch.swizzle) {
case XE_GPU_SWIZZLE_BGRA:
info.format = DXGI_FORMAT_B5G5R5A1_UNORM;
break;
default:
XELOGW("D3D11: unhandled swizzle for FMT_1_5_5_5");
info.format = DXGI_FORMAT_B5G5R5A1_UNORM;
break;
}
info.block_size = 1;
info.texel_pitch = 2;
break;
case FMT_8_8_8_8:
case FMT_8_8_8_8_AS_16_16_16_16:
switch (fetch.swizzle) {
case XE_GPU_SWIZZLE_RGBA:
info.format = DXGI_FORMAT_R8G8B8A8_UNORM;
break;
case XE_GPU_SWIZZLE_BGRA:
info.format = DXGI_FORMAT_B8G8R8A8_UNORM;
break;
case XE_GPU_SWIZZLE_RGB1:
info.format = DXGI_FORMAT_R8G8B8A8_UNORM; // ?
break;
case XE_GPU_SWIZZLE_BGR1:
info.format = DXGI_FORMAT_B8G8R8X8_UNORM;
break;
default:
XELOGW("D3D11: unhandled swizzle for FMT_8_8_8_8");
info.format = DXGI_FORMAT_R8G8B8A8_UNORM;
break;
}
info.block_size = 1;
info.texel_pitch = 4;
break;
case FMT_4_4_4_4:
switch (fetch.swizzle) {
case XE_GPU_SWIZZLE_BGRA:
info.format = DXGI_FORMAT_B4G4R4A4_UNORM; // only supported on Windows 8+
break;
default:
XELOGW("D3D11: unhandled swizzle for FMT_4_4_4_4");
info.format = DXGI_FORMAT_B4G4R4A4_UNORM; // only supported on Windows 8+
break;
}
info.block_size = 1;
info.texel_pitch = 2;
break;
case FMT_16_16_16_16_FLOAT:
switch (fetch.swizzle) {
case XE_GPU_SWIZZLE_RGBA:
info.format = DXGI_FORMAT_R16G16B16A16_FLOAT;
break;
default:
XELOGW("D3D11: unhandled swizzle for FMT_16_16_16_16_FLOAT");
info.format = DXGI_FORMAT_R16G16B16A16_FLOAT;
break;
}
info.block_size = 1;
info.texel_pitch = 8;
break;
case FMT_32_FLOAT:
switch (fetch.swizzle) {
case XE_GPU_SWIZZLE_R111:
info.format = DXGI_FORMAT_R32_FLOAT;
break;
default:
XELOGW("D3D11: unhandled swizzle for FMT_32_FLOAT");
info.format = DXGI_FORMAT_R32_FLOAT;
break;
}
info.block_size = 1;
info.texel_pitch = 4;
break;
case FMT_DXT1:
info.format = DXGI_FORMAT_BC1_UNORM;
info.block_size = 4;
info.texel_pitch = 8;
info.is_compressed = true;
break;
case FMT_DXT2_3:
case FMT_DXT4_5:
info.format = (fetch.format == FMT_DXT4_5 ? DXGI_FORMAT_BC3_UNORM : DXGI_FORMAT_BC2_UNORM);
info.block_size = 4;
info.texel_pitch = 16;
info.is_compressed = true;
break;
case FMT_DXT1_AS_16_16_16_16:
// TODO(benvanik): conversion?
info.format = DXGI_FORMAT_BC1_UNORM;
info.block_size = 4;
info.texel_pitch = 8;
info.is_compressed = true;
break;
case FMT_DXT2_3_AS_16_16_16_16:
case FMT_DXT4_5_AS_16_16_16_16:
// TODO(benvanik): conversion?
info.format = DXGI_FORMAT_BC3_UNORM;
info.block_size = 4;
info.texel_pitch = 16;
info.is_compressed = true;
break;
case FMT_1_REVERSE:
case FMT_1:
case FMT_5_6_5:
case FMT_6_5_5:
case FMT_2_10_10_10:
case FMT_8_A:
case FMT_8_B:
case FMT_8_8:
case FMT_Cr_Y1_Cb_Y0:
case FMT_Y1_Cr_Y0_Cb:
case FMT_5_5_5_1:
case FMT_8_8_8_8_A:
case FMT_10_11_11:
case FMT_11_11_10:
case FMT_24_8:
case FMT_24_8_FLOAT:
case FMT_16:
case FMT_16_16:
case FMT_16_16_16_16:
case FMT_16_EXPAND:
case FMT_16_16_EXPAND:
case FMT_16_16_16_16_EXPAND:
case FMT_16_FLOAT:
case FMT_16_16_FLOAT:
case FMT_32:
case FMT_32_32:
case FMT_32_32_32_32:
case FMT_32_32_FLOAT:
case FMT_32_32_32_32_FLOAT:
case FMT_32_AS_8:
case FMT_32_AS_8_8:
case FMT_16_MPEG:
case FMT_16_16_MPEG:
case FMT_8_INTERLACED:
case FMT_32_AS_8_INTERLACED:
case FMT_32_AS_8_8_INTERLACED:
case FMT_16_INTERLACED:
case FMT_16_MPEG_INTERLACED:
case FMT_16_16_MPEG_INTERLACED:
case FMT_DXN:
case FMT_2_10_10_10_AS_16_16_16_16:
case FMT_10_11_11_AS_16_16_16_16:
case FMT_11_11_10_AS_16_16_16_16:
case FMT_32_32_32_FLOAT:
case FMT_DXT3A:
case FMT_DXT5A:
case FMT_CTX1:
case FMT_DXT3A_AS_1_1_1_1:
info.format = DXGI_FORMAT_UNKNOWN;
break;
}
if (info.format == DXGI_FORMAT_UNKNOWN) {
return false;
}
// Must be called here when we know the format.
switch (info.dimension) {
case TEXTURE_DIMENSION_1D:
info.CalculateTextureSizes1D(fetch);
break;
case TEXTURE_DIMENSION_2D:
info.CalculateTextureSizes2D(fetch);
break;
case TEXTURE_DIMENSION_3D:
// TODO(benvanik): calculate size.
return false;
case TEXTURE_DIMENSION_CUBE:
// TODO(benvanik): calculate size.
return false;
}
return true;
}
void TextureResource::Info::CalculateTextureSizes1D(
const xe_gpu_texture_fetch_t& fetch) {
// ?
size_1d.width = fetch.size_1d.width;
}
void TextureResource::Info::CalculateTextureSizes2D(
const xe_gpu_texture_fetch_t& fetch) {
size_2d.logical_width = 1 + fetch.size_2d.width;
size_2d.logical_height = 1 + fetch.size_2d.height;
size_2d.block_width = size_2d.logical_width / block_size;
size_2d.block_height = size_2d.logical_height / block_size;
if (!is_compressed) {
// must be 32x32 but also must have a pitch that is a multiple of 256 bytes
uint32_t bytes_per_block = block_size * block_size * texel_pitch;
uint32_t width_multiple = 32;
if (bytes_per_block) {
uint32_t minimum_multiple = 256 / bytes_per_block;
if (width_multiple < minimum_multiple) {
width_multiple = minimum_multiple;
}
}
size_2d.input_width = poly::round_up(size_2d.logical_width, width_multiple);
size_2d.input_height = poly::round_up(size_2d.logical_height, 32);
size_2d.output_width = size_2d.logical_width;
size_2d.output_height = size_2d.logical_height;
} else {
// must be 128x128
size_2d.input_width = poly::round_up(size_2d.logical_width, 128);
size_2d.input_height = poly::round_up(size_2d.logical_height, 128);
size_2d.output_width = poly::next_pow2(size_2d.logical_width);
size_2d.output_height = poly::next_pow2(size_2d.logical_height);
}
size_2d.logical_pitch = (size_2d.logical_width / block_size) * texel_pitch;
size_2d.input_pitch = (size_2d.input_width / block_size) * texel_pitch;
if (!is_tiled) {
input_length = size_2d.block_height * size_2d.logical_pitch;
} else {
input_length = size_2d.block_height * size_2d.logical_pitch; // ?
}
}
TextureResource::TextureResource(const MemoryRange& memory_range,
const Info& info)
: PagedResource(memory_range),
info_(info) {
}
TextureResource::~TextureResource() {
}
int TextureResource::Prepare() {
if (!handle()) {
if (CreateHandle()) {
XELOGE("Unable to create texture handle");
return 1;
}
}
//if (!dirtied_) {
// return 0;
//}
//dirtied_ = false;
// pass dirty regions?
return InvalidateRegion(memory_range_);
}
void TextureResource::TextureSwap(uint8_t* dest, const uint8_t* src,
uint32_t pitch) const {
// TODO(benvanik): optimize swapping paths.
switch (info_.endianness) {
case XE_GPU_ENDIAN_8IN16:
for (uint32_t i = 0; i < pitch; i += 2, src += 2, dest += 2) {
*(uint16_t*)dest = poly::byte_swap(*(uint16_t*)src);
}
break;
case XE_GPU_ENDIAN_8IN32: // Swap bytes.
for (uint32_t i = 0; i < pitch; i += 4, src += 4, dest += 4) {
*(uint32_t*)dest = poly::byte_swap(*(uint32_t*)src);
}
break;
case XE_GPU_ENDIAN_16IN32: // Swap half words.
for (uint32_t i = 0; i < pitch; i += 4, src += 4, dest += 4) {
uint32_t value = *(uint32_t*)src;
*(uint32_t*)dest = ((value >> 16) & 0xFFFF) | (value << 16);
}
break;
default:
case XE_GPU_ENDIAN_NONE:
memcpy(dest, src, pitch);
break;
}
}
// https://code.google.com/p/crunch/source/browse/trunk/inc/crn_decomp.h#4104
uint32_t TextureResource::TiledOffset2DOuter(uint32_t y, uint32_t width,
uint32_t log_bpp) const {
uint32_t macro = ((y >> 5) * (width >> 5)) << (log_bpp + 7);
uint32_t micro = ((y & 6) << 2) << log_bpp;
return macro +
((micro & ~15) << 1) +
(micro & 15) +
((y & 8) << (3 + log_bpp)) +
((y & 1) << 4);
}
uint32_t TextureResource::TiledOffset2DInner(uint32_t x, uint32_t y, uint32_t bpp,
uint32_t base_offset) const {
uint32_t macro = (x >> 5) << (bpp + 7);
uint32_t micro = (x & 7) << bpp;
uint32_t offset = base_offset + (macro + ((micro & ~15) << 1) + (micro & 15));
return ((offset & ~511) << 3) + ((offset & 448) << 2) + (offset & 63) +
((y & 16) << 7) + (((((y & 8) >> 2) + (x >> 3)) & 3) << 6);
}

View File

@ -1,110 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_TEXTURE_RESOURCE_H_
#define XENIA_GPU_TEXTURE_RESOURCE_H_
#include <xenia/gpu/resource.h>
#include <xenia/gpu/xenos/xenos.h>
// TODO(benvanik): replace DXGI constants with xenia constants.
#include <d3d11.h>
namespace xe {
namespace gpu {
enum TextureDimension {
TEXTURE_DIMENSION_1D = 0,
TEXTURE_DIMENSION_2D = 1,
TEXTURE_DIMENSION_3D = 2,
TEXTURE_DIMENSION_CUBE = 3,
};
class TextureResource : public PagedResource {
public:
struct Info {
TextureDimension dimension;
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t block_size;
uint32_t texel_pitch;
xenos::XE_GPU_ENDIAN endianness;
bool is_tiled;
bool is_compressed;
uint32_t input_length;
// TODO(benvanik): replace with our own constants.
DXGI_FORMAT format;
union {
struct {
uint32_t width;
} size_1d;
struct {
uint32_t logical_width;
uint32_t logical_height;
uint32_t block_width;
uint32_t block_height;
uint32_t input_width;
uint32_t input_height;
uint32_t output_width;
uint32_t output_height;
uint32_t logical_pitch;
uint32_t input_pitch;
} size_2d;
struct {
} size_3d;
struct {
} size_cube;
};
static bool Prepare(const xenos::xe_gpu_texture_fetch_t& fetch,
Info& out_info);
private:
void CalculateTextureSizes1D(const xenos::xe_gpu_texture_fetch_t& fetch);
void CalculateTextureSizes2D(const xenos::xe_gpu_texture_fetch_t& fetch);
};
TextureResource(const MemoryRange& memory_range,
const Info& info);
~TextureResource() override;
const Info& info() const { return info_; }
bool Equals(const void* info_ptr, size_t info_length) override {
return info_length == sizeof(Info) &&
memcmp(info_ptr, &info_, info_length) == 0;
}
virtual int Prepare();
protected:
virtual int CreateHandle() = 0;
virtual int InvalidateRegion(const MemoryRange& memory_range) = 0;
void TextureSwap(uint8_t* dest, const uint8_t* src, uint32_t pitch) const;
uint32_t TiledOffset2DOuter(uint32_t y, uint32_t width,
uint32_t log_bpp) const;
uint32_t TiledOffset2DInner(uint32_t x, uint32_t y, uint32_t bpp,
uint32_t base_offset) const;
Info info_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_TEXTURE_RESOURCE_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,27 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_XENOS_UCODE_DISASSEMBLER_H_
#define XENIA_GPU_XENOS_UCODE_DISASSEMBLER_H_
#include <xenia/common.h>
#include <xenia/gpu/xenos/ucode.h>
#include <xenia/gpu/xenos/xenos.h>
namespace xe {
namespace gpu {
namespace xenos {
char* DisassembleShader(
XE_GPU_SHADER_TYPE type,
const uint32_t* dwords, size_t dword_count);
} // namespace xenos
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_XENOS_UCODE_DISASSEMBLER_H_
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_UCODE_DISASSEMBLER_H_
#define XENIA_GPU_UCODE_DISASSEMBLER_H_
#include <string>
#include <xenia/gpu/ucode.h>
#include <xenia/gpu/xenos.h>
namespace xe {
namespace gpu {
std::string DisassembleShader(xenos::ShaderType type, const uint32_t* dwords,
size_t dword_count);
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_UCODE_DISASSEMBLER_H_

View File

@ -1,239 +1,303 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_XENOS_XENOS_H_
#define XENIA_GPU_XENOS_XENOS_H_
#include <xenia/common.h>
#include <xenia/gpu/xenos/ucode.h>
namespace xe {
namespace gpu {
namespace xenos {
typedef enum {
XE_GPU_SHADER_TYPE_VERTEX = 0x00,
XE_GPU_SHADER_TYPE_PIXEL = 0x01,
} XE_GPU_SHADER_TYPE;
typedef enum {
XE_GPU_INVALIDATE_MASK_VERTEX_SHADER = 1 << 8,
XE_GPU_INVALIDATE_MASK_PIXEL_SHADER = 1 << 9,
XE_GPU_INVALIDATE_MASK_ALL = 0x7FFF,
} XE_GPU_INVALIDATE_MASK;
typedef enum {
XE_GPU_PRIMITIVE_TYPE_POINT_LIST = 0x01,
XE_GPU_PRIMITIVE_TYPE_LINE_LIST = 0x02,
XE_GPU_PRIMITIVE_TYPE_LINE_STRIP = 0x03,
XE_GPU_PRIMITIVE_TYPE_TRIANGLE_LIST = 0x04,
XE_GPU_PRIMITIVE_TYPE_TRIANGLE_FAN = 0x05,
XE_GPU_PRIMITIVE_TYPE_TRIANGLE_STRIP = 0x06,
XE_GPU_PRIMITIVE_TYPE_UNKNOWN_07 = 0x07,
XE_GPU_PRIMITIVE_TYPE_RECTANGLE_LIST = 0x08,
XE_GPU_PRIMITIVE_TYPE_LINE_LOOP = 0x0C,
XE_GPU_PRIMITIVE_TYPE_QUAD_LIST = 0x0D,
} XE_GPU_PRIMITIVE_TYPE;
typedef enum {
XE_GPU_ENDIAN_NONE = 0x0,
XE_GPU_ENDIAN_8IN16 = 0x1,
XE_GPU_ENDIAN_8IN32 = 0x2,
XE_GPU_ENDIAN_16IN32 = 0x3,
} XE_GPU_ENDIAN;
#define XE_GPU_MAKE_SWIZZLE(x, y, z, w) \
(((XE_GPU_SWIZZLE_##x) << 0) | ((XE_GPU_SWIZZLE_##y) << 3) | ((XE_GPU_SWIZZLE_##z) << 6) | ((XE_GPU_SWIZZLE_##w) << 9))
typedef enum {
XE_GPU_SWIZZLE_X = 0,
XE_GPU_SWIZZLE_R = 0,
XE_GPU_SWIZZLE_Y = 1,
XE_GPU_SWIZZLE_G = 1,
XE_GPU_SWIZZLE_Z = 2,
XE_GPU_SWIZZLE_B = 2,
XE_GPU_SWIZZLE_W = 3,
XE_GPU_SWIZZLE_A = 3,
XE_GPU_SWIZZLE_0 = 4,
XE_GPU_SWIZZLE_1 = 5,
XE_GPU_SWIZZLE_RGBA = XE_GPU_MAKE_SWIZZLE(R, G, B, A),
XE_GPU_SWIZZLE_BGRA = XE_GPU_MAKE_SWIZZLE(B, G, R, A),
XE_GPU_SWIZZLE_RGB1 = XE_GPU_MAKE_SWIZZLE(R, G, B, 1),
XE_GPU_SWIZZLE_BGR1 = XE_GPU_MAKE_SWIZZLE(B, G, R, 1),
XE_GPU_SWIZZLE_000R = XE_GPU_MAKE_SWIZZLE(0, 0, 0, R),
XE_GPU_SWIZZLE_RRR1 = XE_GPU_MAKE_SWIZZLE(R, R, R, 1),
XE_GPU_SWIZZLE_R111 = XE_GPU_MAKE_SWIZZLE(R, 1, 1, 1),
XE_GPU_SWIZZLE_R000 = XE_GPU_MAKE_SWIZZLE(R, 0, 0, 0),
} XE_GPU_SWIZZLE;
inline uint32_t GpuSwap(uint32_t value, XE_GPU_ENDIAN endianness) {
switch (endianness) {
default:
case XE_GPU_ENDIAN_NONE: // No swap.
return value;
case XE_GPU_ENDIAN_8IN16: // Swap bytes in half words.
return ((value << 8) & 0xFF00FF00) |
((value >> 8) & 0x00FF00FF);
case XE_GPU_ENDIAN_8IN32: // Swap bytes.
// NOTE: we are likely doing two swaps here. Wasteful. Oh well.
return poly::byte_swap(value);
case XE_GPU_ENDIAN_16IN32: // Swap half words.
return ((value >> 16) & 0xFFFF) | (value << 16);
}
}
inline uint32_t GpuToCpu(uint32_t p) {
return p;
}
inline uint32_t GpuToCpu(uint32_t base, uint32_t p) {
// Some AMD docs say relative to base ptr, some say just this.
// Some games use some crazy shift magic, but it seems to nop.
uint32_t upper = 0;//base & 0xFF000000;
uint32_t lower = p & 0x01FFFFFF;
return upper + lower;// -(((base >> 20) + 0x200) & 0x1000);
}
// XE_GPU_REG_SQ_PROGRAM_CNTL
typedef union {
XEPACKEDSTRUCTANONYMOUS({
uint32_t vs_regs : 6;
uint32_t : 2;
uint32_t ps_regs : 6;
uint32_t : 2;
uint32_t vs_resource : 1;
uint32_t ps_resource : 1;
uint32_t param_gen : 1;
uint32_t unknown0 : 1;
uint32_t vs_export_count : 4;
uint32_t vs_export_mode : 3;
uint32_t ps_export_depth : 1;
uint32_t ps_export_count : 3;
uint32_t gen_index_vtx : 1;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
});
} xe_gpu_program_cntl_t;
// XE_GPU_REG_SHADER_CONSTANT_FETCH_*
XEPACKEDUNION(xe_gpu_vertex_fetch_t, {
XEPACKEDSTRUCTANONYMOUS({
uint32_t type : 2;
uint32_t address : 30;
uint32_t endian : 2;
uint32_t size : 24;
uint32_t unk1 : 6;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
uint32_t dword_1;
});
});
// XE_GPU_REG_SHADER_CONSTANT_FETCH_*
XEPACKEDUNION(xe_gpu_texture_fetch_t, {
XEPACKEDSTRUCTANONYMOUS({
uint32_t type : 2; // dword_0
uint32_t sign_x : 2;
uint32_t sign_y : 2;
uint32_t sign_z : 2;
uint32_t sign_w : 2;
uint32_t clamp_x : 3;
uint32_t clamp_y : 3;
uint32_t clamp_z : 3;
uint32_t unk0 : 3;
uint32_t pitch : 9;
uint32_t tiled : 1;
uint32_t format : 6; // dword_1
uint32_t endianness : 2;
uint32_t unk1 : 4;
uint32_t address : 20;
union { // dword_2
struct {
uint32_t width : 24;
uint32_t unused : 8;
} size_1d;
struct {
uint32_t width : 13;
uint32_t height : 13;
uint32_t unused : 6;
} size_2d;
struct {
uint32_t width : 13;
uint32_t height : 13;
uint32_t depth : 6;
} size_stack;
struct {
uint32_t width : 11;
uint32_t height : 11;
uint32_t depth : 10;
} size_3d;
};
uint32_t unk3_0 : 1; // dword_3
uint32_t swizzle : 12; // xyzw, 3b each (XE_GPU_SWIZZLE)
uint32_t unk3_1 : 6;
uint32_t mag_filter : 2;
uint32_t min_filter : 2;
uint32_t mip_filter : 2;
uint32_t unk3_2 : 6;
uint32_t border : 1;
uint32_t unk4; // dword_4
uint32_t unk5 : 9; // dword_5
uint32_t dimension : 2;
uint32_t unk5b : 21;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
uint32_t dword_1;
uint32_t dword_2;
uint32_t dword_3;
uint32_t dword_4;
uint32_t dword_5;
});
});
// XE_GPU_REG_SHADER_CONSTANT_FETCH_*
XEPACKEDUNION(xe_gpu_fetch_group_t, {
xe_gpu_texture_fetch_t texture_fetch;
XEPACKEDSTRUCTANONYMOUS({
xe_gpu_vertex_fetch_t vertex_fetch_0;
xe_gpu_vertex_fetch_t vertex_fetch_1;
xe_gpu_vertex_fetch_t vertex_fetch_2;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
uint32_t dword_1;
uint32_t dword_2;
uint32_t dword_3;
uint32_t dword_4;
uint32_t dword_5;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t type_0 : 2;
uint32_t : 30;
uint32_t : 32;
uint32_t type_1 : 2;
uint32_t : 30;
uint32_t : 32;
uint32_t type_2 : 2;
uint32_t : 30;
uint32_t : 32;
});
});
} // namespace xenos
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_XENOS_XENOS_H_
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_XENOS_H_
#define XENIA_GPU_XENOS_H_
#include <xenia/common.h>
#include <xenia/gpu/ucode.h>
namespace xe {
namespace gpu {
namespace xenos {
enum class ShaderType : uint32_t {
kVertex = 0,
kPixel = 1,
};
typedef enum {
XE_GPU_INVALIDATE_MASK_VERTEX_SHADER = 1 << 8,
XE_GPU_INVALIDATE_MASK_PIXEL_SHADER = 1 << 9,
XE_GPU_INVALIDATE_MASK_ALL = 0x7FFF,
} XE_GPU_INVALIDATE_MASK;
enum class PrimitiveType : uint32_t {
kNone = 0x00,
kPointList = 0x01,
kLineList = 0x02,
kLineStrip = 0x03,
kTriangleList = 0x04,
kTriangleFan = 0x05,
kTriangleStrip = 0x06,
kUnknown0x07 = 0x07,
kRectangleList = 0x08,
kLineLoop = 0x0C,
kQuadList = 0x0D,
};
enum class Endian : uint32_t {
kUnspecified = 0,
k8in16 = 1,
k8in32 = 2,
k16in32 = 3,
};
#define XE_GPU_MAKE_SWIZZLE(x, y, z, w) \
(((XE_GPU_SWIZZLE_##x) << 0) | ((XE_GPU_SWIZZLE_##y) << 3) | ((XE_GPU_SWIZZLE_##z) << 6) | ((XE_GPU_SWIZZLE_##w) << 9))
typedef enum {
XE_GPU_SWIZZLE_X = 0,
XE_GPU_SWIZZLE_R = 0,
XE_GPU_SWIZZLE_Y = 1,
XE_GPU_SWIZZLE_G = 1,
XE_GPU_SWIZZLE_Z = 2,
XE_GPU_SWIZZLE_B = 2,
XE_GPU_SWIZZLE_W = 3,
XE_GPU_SWIZZLE_A = 3,
XE_GPU_SWIZZLE_0 = 4,
XE_GPU_SWIZZLE_1 = 5,
XE_GPU_SWIZZLE_RGBA = XE_GPU_MAKE_SWIZZLE(R, G, B, A),
XE_GPU_SWIZZLE_BGRA = XE_GPU_MAKE_SWIZZLE(B, G, R, A),
XE_GPU_SWIZZLE_RGB1 = XE_GPU_MAKE_SWIZZLE(R, G, B, 1),
XE_GPU_SWIZZLE_BGR1 = XE_GPU_MAKE_SWIZZLE(B, G, R, 1),
XE_GPU_SWIZZLE_000R = XE_GPU_MAKE_SWIZZLE(0, 0, 0, R),
XE_GPU_SWIZZLE_RRR1 = XE_GPU_MAKE_SWIZZLE(R, R, R, 1),
XE_GPU_SWIZZLE_R111 = XE_GPU_MAKE_SWIZZLE(R, 1, 1, 1),
XE_GPU_SWIZZLE_R000 = XE_GPU_MAKE_SWIZZLE(R, 0, 0, 0),
} XE_GPU_SWIZZLE;
inline uint32_t GpuSwap(uint32_t value, Endian endianness) {
switch (endianness) {
default:
case Endian::kUnspecified:
// No swap.
return value;
case Endian::k8in16:
// Swap bytes in half words.
return ((value << 8) & 0xFF00FF00) | ((value >> 8) & 0x00FF00FF);
case Endian::k8in32:
// Swap bytes.
// NOTE: we are likely doing two swaps here. Wasteful. Oh well.
return poly::byte_swap(value);
case Endian::k16in32:
// Swap half words.
return ((value >> 16) & 0xFFFF) | (value << 16);
}
}
inline uint32_t GpuToCpu(uint32_t p) {
return p;
}
inline uint32_t GpuToCpu(uint32_t base, uint32_t p) {
// Some AMD docs say relative to base ptr, some say just this.
// Some games use some crazy shift magic, but it seems to nop.
uint32_t upper = 0;//base & 0xFF000000;
uint32_t lower = p & 0x01FFFFFF;
return upper + lower;// -(((base >> 20) + 0x200) & 0x1000);
}
// XE_GPU_REG_SQ_PROGRAM_CNTL
typedef union {
XEPACKEDSTRUCTANONYMOUS({
uint32_t vs_regs : 6;
uint32_t : 2;
uint32_t ps_regs : 6;
uint32_t : 2;
uint32_t vs_resource : 1;
uint32_t ps_resource : 1;
uint32_t param_gen : 1;
uint32_t unknown0 : 1;
uint32_t vs_export_count : 4;
uint32_t vs_export_mode : 3;
uint32_t ps_export_depth : 1;
uint32_t ps_export_count : 3;
uint32_t gen_index_vtx : 1;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
});
} xe_gpu_program_cntl_t;
// XE_GPU_REG_SHADER_CONSTANT_FETCH_*
XEPACKEDUNION(xe_gpu_vertex_fetch_t, {
XEPACKEDSTRUCTANONYMOUS({
uint32_t type : 2;
uint32_t address : 30;
uint32_t endian : 2;
uint32_t size : 24;
uint32_t unk1 : 6;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
uint32_t dword_1;
});
});
// XE_GPU_REG_SHADER_CONSTANT_FETCH_*
XEPACKEDUNION(xe_gpu_texture_fetch_t, {
XEPACKEDSTRUCTANONYMOUS({
uint32_t type : 2; // dword_0
uint32_t sign_x : 2;
uint32_t sign_y : 2;
uint32_t sign_z : 2;
uint32_t sign_w : 2;
uint32_t clamp_x : 3;
uint32_t clamp_y : 3;
uint32_t clamp_z : 3;
uint32_t unk0 : 3;
uint32_t pitch : 9;
uint32_t tiled : 1;
uint32_t format : 6; // dword_1
uint32_t endianness : 2;
uint32_t unk1 : 4;
uint32_t address : 20;
union { // dword_2
struct {
uint32_t width : 24;
uint32_t unused : 8;
} size_1d;
struct {
uint32_t width : 13;
uint32_t height : 13;
uint32_t unused : 6;
} size_2d;
struct {
uint32_t width : 13;
uint32_t height : 13;
uint32_t depth : 6;
} size_stack;
struct {
uint32_t width : 11;
uint32_t height : 11;
uint32_t depth : 10;
} size_3d;
};
uint32_t unk3_0 : 1; // dword_3
uint32_t swizzle : 12; // xyzw, 3b each (XE_GPU_SWIZZLE)
uint32_t unk3_1 : 6;
uint32_t mag_filter : 2;
uint32_t min_filter : 2;
uint32_t mip_filter : 2;
uint32_t unk3_2 : 6;
uint32_t border : 1;
uint32_t unk4; // dword_4
uint32_t unk5 : 9; // dword_5
uint32_t dimension : 2;
uint32_t unk5b : 21;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
uint32_t dword_1;
uint32_t dword_2;
uint32_t dword_3;
uint32_t dword_4;
uint32_t dword_5;
});
});
// XE_GPU_REG_SHADER_CONSTANT_FETCH_*
XEPACKEDUNION(xe_gpu_fetch_group_t, {
xe_gpu_texture_fetch_t texture_fetch;
XEPACKEDSTRUCTANONYMOUS({
xe_gpu_vertex_fetch_t vertex_fetch_0;
xe_gpu_vertex_fetch_t vertex_fetch_1;
xe_gpu_vertex_fetch_t vertex_fetch_2;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
uint32_t dword_1;
uint32_t dword_2;
uint32_t dword_3;
uint32_t dword_4;
uint32_t dword_5;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t type_0 : 2;
uint32_t : 30;
uint32_t : 32;
uint32_t type_1 : 2;
uint32_t : 30;
uint32_t : 32;
uint32_t type_2 : 2;
uint32_t : 30;
uint32_t : 32;
});
});
// Opcodes (IT_OPCODE) for Type-3 commands in the ringbuffer.
// https://github.com/freedreno/amd-gpu/blob/master/include/api/gsl_pm4types.h
// Not sure if all of these are used.
enum Type3Opcode {
PM4_ME_INIT = 0x48, // initialize CP's micro-engine
PM4_NOP = 0x10, // skip N 32-bit words to get to the next packet
PM4_INDIRECT_BUFFER = 0x3f, // indirect buffer dispatch. prefetch parser uses this packet type to determine whether to pre-fetch the IB
PM4_INDIRECT_BUFFER_PFD = 0x37, // indirect buffer dispatch. same as IB, but init is pipelined
PM4_WAIT_FOR_IDLE = 0x26, // wait for the IDLE state of the engine
PM4_WAIT_REG_MEM = 0x3c, // wait until a register or memory location is a specific value
PM4_WAIT_REG_EQ = 0x52, // wait until a register location is equal to a specific value
PM4_WAT_REG_GTE = 0x53, // wait until a register location is >= a specific value
PM4_WAIT_UNTIL_READ = 0x5c, // wait until a read completes
PM4_WAIT_IB_PFD_COMPLETE = 0x5d, // wait until all base/size writes from an IB_PFD packet have completed
PM4_REG_RMW = 0x21, // register read/modify/write
PM4_REG_TO_MEM = 0x3e, // reads register in chip and writes to memory
PM4_MEM_WRITE = 0x3d, // write N 32-bit words to memory
PM4_MEM_WRITE_CNTR = 0x4f, // write CP_PROG_COUNTER value to memory
PM4_COND_EXEC = 0x44, // conditional execution of a sequence of packets
PM4_COND_WRITE = 0x45, // conditional write to memory or register
PM4_EVENT_WRITE = 0x46, // generate an event that creates a write to memory when completed
PM4_EVENT_WRITE_SHD = 0x58, // generate a VS|PS_done event
PM4_EVENT_WRITE_CFL = 0x59, // generate a cache flush done event
PM4_EVENT_WRITE_ZPD = 0x5b, // generate a z_pass done event
PM4_DRAW_INDX = 0x22, // initiate fetch of index buffer and draw
PM4_DRAW_INDX_2 = 0x36, // draw using supplied indices in packet
PM4_DRAW_INDX_BIN = 0x34, // initiate fetch of index buffer and binIDs and draw
PM4_DRAW_INDX_2_BIN = 0x35, // initiate fetch of bin IDs and draw using supplied indices
PM4_VIZ_QUERY = 0x23, // begin/end initiator for viz query extent processing
PM4_SET_STATE = 0x25, // fetch state sub-blocks and initiate shader code DMAs
PM4_SET_CONSTANT = 0x2d, // load constant into chip and to memory
PM4_LOAD_ALU_CONSTANT = 0x2f, // load constants from memory
PM4_IM_LOAD = 0x27, // load sequencer instruction memory (pointer-based)
PM4_IM_LOAD_IMMEDIATE = 0x2b, // load sequencer instruction memory (code embedded in packet)
PM4_LOAD_CONSTANT_CONTEXT = 0x2e, // load constants from a location in memory
PM4_INVALIDATE_STATE = 0x3b, // selective invalidation of state pointers
PM4_SET_SHADER_BASES = 0x4A, // dynamically changes shader instruction memory partition
PM4_SET_BIN_BASE_OFFSET = 0x4B, // program an offset that will added to the BIN_BASE value of the 3D_DRAW_INDX_BIN packet
PM4_SET_BIN_MASK = 0x50, // sets the 64-bit BIN_MASK register in the PFP
PM4_SET_BIN_SELECT = 0x51, // sets the 64-bit BIN_SELECT register in the PFP
PM4_CONTEXT_UPDATE = 0x5e, // updates the current context, if needed
PM4_INTERRUPT = 0x54, // generate interrupt from the command stream
PM4_XE_SWAP = 0x55, // Xenia only: VdSwap uses this to trigger a swap.
PM4_IM_STORE = 0x2c, // copy sequencer instruction memory to system memory
// Tiled rendering:
// https://www.google.com/patents/US20060055701
PM4_SET_BIN_MASK_LO = 0x60,
PM4_SET_BIN_MASK_HI = 0x61,
PM4_SET_BIN_SELECT_LO = 0x62,
PM4_SET_BIN_SELECT_HI = 0x63,
};
} // namespace xenos
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_XENOS_H_

View File

@ -1,87 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_XENOS_PACKETS_H_
#define XENIA_GPU_XENOS_PACKETS_H_
#include <xenia/common.h>
namespace xe {
namespace gpu {
namespace xenos {
// Opcodes (IT_OPCODE) for Type-3 commands in the ringbuffer.
// https://github.com/freedreno/amd-gpu/blob/master/include/api/gsl_pm4types.h
// Not sure if all of these are used.
enum Type3Opcode {
PM4_ME_INIT = 0x48, // initialize CP's micro-engine
PM4_NOP = 0x10, // skip N 32-bit words to get to the next packet
PM4_INDIRECT_BUFFER = 0x3f, // indirect buffer dispatch. prefetch parser uses this packet type to determine whether to pre-fetch the IB
PM4_INDIRECT_BUFFER_PFD = 0x37, // indirect buffer dispatch. same as IB, but init is pipelined
PM4_WAIT_FOR_IDLE = 0x26, // wait for the IDLE state of the engine
PM4_WAIT_REG_MEM = 0x3c, // wait until a register or memory location is a specific value
PM4_WAIT_REG_EQ = 0x52, // wait until a register location is equal to a specific value
PM4_WAT_REG_GTE = 0x53, // wait until a register location is >= a specific value
PM4_WAIT_UNTIL_READ = 0x5c, // wait until a read completes
PM4_WAIT_IB_PFD_COMPLETE = 0x5d, // wait until all base/size writes from an IB_PFD packet have completed
PM4_REG_RMW = 0x21, // register read/modify/write
PM4_REG_TO_MEM = 0x3e, // reads register in chip and writes to memory
PM4_MEM_WRITE = 0x3d, // write N 32-bit words to memory
PM4_MEM_WRITE_CNTR = 0x4f, // write CP_PROG_COUNTER value to memory
PM4_COND_EXEC = 0x44, // conditional execution of a sequence of packets
PM4_COND_WRITE = 0x45, // conditional write to memory or register
PM4_EVENT_WRITE = 0x46, // generate an event that creates a write to memory when completed
PM4_EVENT_WRITE_SHD = 0x58, // generate a VS|PS_done event
PM4_EVENT_WRITE_CFL = 0x59, // generate a cache flush done event
PM4_EVENT_WRITE_ZPD = 0x5b, // generate a z_pass done event
PM4_DRAW_INDX = 0x22, // initiate fetch of index buffer and draw
PM4_DRAW_INDX_2 = 0x36, // draw using supplied indices in packet
PM4_DRAW_INDX_BIN = 0x34, // initiate fetch of index buffer and binIDs and draw
PM4_DRAW_INDX_2_BIN = 0x35, // initiate fetch of bin IDs and draw using supplied indices
PM4_VIZ_QUERY = 0x23, // begin/end initiator for viz query extent processing
PM4_SET_STATE = 0x25, // fetch state sub-blocks and initiate shader code DMAs
PM4_SET_CONSTANT = 0x2d, // load constant into chip and to memory
PM4_LOAD_ALU_CONSTANT = 0x2f, // load constants from memory
PM4_IM_LOAD = 0x27, // load sequencer instruction memory (pointer-based)
PM4_IM_LOAD_IMMEDIATE = 0x2b, // load sequencer instruction memory (code embedded in packet)
PM4_LOAD_CONSTANT_CONTEXT = 0x2e, // load constants from a location in memory
PM4_INVALIDATE_STATE = 0x3b, // selective invalidation of state pointers
PM4_SET_SHADER_BASES = 0x4A, // dynamically changes shader instruction memory partition
PM4_SET_BIN_BASE_OFFSET = 0x4B, // program an offset that will added to the BIN_BASE value of the 3D_DRAW_INDX_BIN packet
PM4_SET_BIN_MASK = 0x50, // sets the 64-bit BIN_MASK register in the PFP
PM4_SET_BIN_SELECT = 0x51, // sets the 64-bit BIN_SELECT register in the PFP
PM4_CONTEXT_UPDATE = 0x5e, // updates the current context, if needed
PM4_INTERRUPT = 0x54, // generate interrupt from the command stream
PM4_XE_SWAP = 0x55, // Xenia only: VdSwap uses this to trigger a swap.
PM4_IM_STORE = 0x2c, // copy sequencer instruction memory to system memory
// Tiled rendering:
// https://www.google.com/patents/US20060055701
PM4_SET_BIN_MASK_LO = 0x60,
PM4_SET_BIN_MASK_HI = 0x61,
PM4_SET_BIN_SELECT_LO = 0x62,
PM4_SET_BIN_SELECT_HI = 0x63,
};
} // namespace xenos
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_XENOS_PACKETS_H_

View File

@ -1,11 +0,0 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'packets.h',
'register_table.inc',
'ucode.h',
'ucode_disassembler.cc',
'ucode_disassembler.h',
'xenos.h',
],
}

View File

@ -10,8 +10,8 @@
#include <xenia/common.h>
#include <xenia/cpu/cpu.h>
#include <xenia/emulator.h>
#include <xenia/gpu/gpu.h>
#include <xenia/gpu/xenos/packets.h>
#include <xenia/gpu/graphics_system.h>
#include <xenia/gpu/xenos.h>
#include <xenia/kernel/kernel_state.h>
#include <xenia/kernel/util/shim_utils.h>
#include <xenia/kernel/xboxkrnl_private.h>

View File

@ -10,30 +10,68 @@
#include <xenia/ui/main_window.h>
#include <poly/logging.h>
#include <poly/threading.h>
namespace xe {
namespace ui {
MainWindow::MainWindow() : PlatformWindow(L"xenia") {}
MainWindow::MainWindow(Emulator* emulator)
: PlatformWindow(L"xenia"), emulator_(emulator) {}
MainWindow::~MainWindow() {}
MainWindow::~MainWindow() = default;
void MainWindow::Start() {
loop_.Post([this]() {
poly::threading::Fence fence;
loop_.Post([&]() {
if (!Initialize()) {
PFATAL("Failed to initialize main window");
exit(1);
}
fence.Signal();
});
fence.Wait();
}
bool MainWindow::Initialize() {
if (!Window::Initialize()) {
if (!PlatformWindow::Initialize()) {
return false;
}
//
Resize(1280, 720);
return true;
}
void MainWindow::OnClose() {
loop_.Quit();
// TODO(benvanik): proper exit.
PLOGI("User-initiated death!");
exit(1);
}
X_STATUS MainWindow::LaunchPath(std::wstring path) {
X_STATUS result;
// Launch based on file type.
// This is a silly guess based on file extension.
// NOTE: this blocks!
auto file_system_type = emulator_->file_system()->InferType(path);
switch (file_system_type) {
case kernel::fs::FileSystemType::STFS_TITLE:
result = emulator_->LaunchSTFSTitle(path);
break;
case kernel::fs::FileSystemType::XEX_FILE:
result = emulator_->LaunchXexFile(path);
break;
case kernel::fs::FileSystemType::DISC_IMAGE:
result = emulator_->LaunchDiscImage(path);
break;
}
return result;
}
} // namespace ui
} // namespace xe

View File

@ -11,6 +11,7 @@
#define XENIA_UI_MAIN_WINDOW_H_
#include <poly/ui/window.h>
#include <xenia/emulator.h>
// TODO(benvanik): only on windows.
#include <poly/ui/win32/win32_loop.h>
@ -24,16 +25,22 @@ using PlatformWindow = poly::ui::win32::Win32Window;
class MainWindow : public PlatformWindow {
public:
MainWindow();
~MainWindow();
explicit MainWindow(Emulator* emulator);
~MainWindow() override;
Emulator* emulator() const { return emulator_; }
PlatformLoop* loop() { return &loop_; }
void Start();
X_STATUS LaunchPath(std::wstring path);
private:
bool Initialize();
void OnClose() override;
Emulator* emulator_;
PlatformLoop loop_;
};

View File

@ -11,33 +11,16 @@
#include <poly/main.h>
#include <xenia/emulator.h>
#include <xenia/kernel/kernel.h>
using namespace xe;
#include <xenia/ui/main_window.h>
DEFINE_string(target, "", "Specifies the target .xex or .iso to execute.");
namespace xe {
int xenia_main(std::vector<std::wstring>& args) {
Profiler::Initialize();
Profiler::ThreadEnter("main");
// Grab path from the flag or unnamed argument.
if (!FLAGS_target.size() && args.size() < 2) {
google::ShowUsageWithFlags("xenia");
PFATAL("Pass a file to launch.");
return 1;
}
std::wstring path;
if (FLAGS_target.size()) {
// Passed as a named argument.
// TODO(benvanik): find something better than gflags that supports unicode.
path = poly::to_wstring(FLAGS_target);
} else {
// Passed as an unnamed argument.
path = args[1];
}
// Normalize the path and make absolute.
std::wstring abs_path = poly::to_absolute_path(path);
// Create the emulator.
auto emulator = std::make_unique<Emulator>(L"");
X_STATUS result = emulator->Setup();
@ -46,29 +29,37 @@ int xenia_main(std::vector<std::wstring>& args) {
return 1;
}
// Launch based on file type.
// This is a silly guess based on file extension.
auto file_system_type = emulator->file_system()->InferType(abs_path);
switch (file_system_type) {
case kernel::fs::FileSystemType::STFS_TITLE:
result = emulator->LaunchSTFSTitle(abs_path);
break;
case kernel::fs::FileSystemType::XEX_FILE:
result = emulator->LaunchXexFile(abs_path);
break;
case kernel::fs::FileSystemType::DISC_IMAGE:
result = emulator->LaunchDiscImage(abs_path);
break;
}
if (XFAILED(result)) {
XELOGE("Failed to launch target: %.8X", result);
return 1;
// Grab path from the flag or unnamed argument.
if (FLAGS_target.size() && args.size() >= 2) {
std::wstring path;
if (FLAGS_target.size()) {
// Passed as a named argument.
// TODO(benvanik): find something better than gflags that supports
// unicode.
path = poly::to_wstring(FLAGS_target);
} else {
// Passed as an unnamed argument.
path = args[1];
}
// Normalize the path and make absolute.
std::wstring abs_path = poly::to_absolute_path(path);
result = emulator->main_window()->LaunchPath(abs_path);
if (XFAILED(result)) {
XELOGE("Failed to launch target: %.8X", result);
return 1;
}
}
// Wait until we are exited.
emulator->main_window()->loop()->AwaitQuit();
emulator.reset();
Profiler::Dump();
Profiler::Shutdown();
return 0;
}
DEFINE_ENTRY_POINT(L"xenia", L"xenia some.xex", xenia_main);
} // namespace xe
DEFINE_ENTRY_POINT(L"xenia", L"xenia some.xex", xe::xenia_main);