Main window, empty GPU files.
This commit is contained in:
parent
7faf9d6bd3
commit
577ab0a4f1
|
@ -20,6 +20,7 @@
|
|||
'poly.h',
|
||||
'string.cc',
|
||||
'string.h',
|
||||
'threading.cc',
|
||||
'threading.h',
|
||||
],
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -23,6 +23,7 @@ class Loop {
|
|||
virtual void Post(std::function<void()> fn) = 0;
|
||||
|
||||
virtual void Quit() = 0;
|
||||
virtual void AwaitQuit() = 0;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright 2014 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'gl4_gpu-private.h',
|
||||
'gl4_gpu.cc',
|
||||
'gl4_gpu.h',
|
||||
],
|
||||
}
|
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
®ister_file_[XE_GPU_REG_SHADER_CONSTANT_000_X].f32;
|
||||
command.loop_constants.count = 32;
|
||||
command.loop_constants.values =
|
||||
®ister_file_[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].u32;
|
||||
command.bool_constants.count = 8;
|
||||
command.bool_constants.values =
|
||||
®ister_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*>(®ister_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*)®ister_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;
|
||||
}
|
|
@ -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 ®ister_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_
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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();
|
||||
}
|
|
@ -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_
|
|
@ -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',
|
||||
],
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
|
@ -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_
|
|
@ -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',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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',
|
||||
],
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue