Starting to drive command buffer actions down to a graphics driver.

This commit is contained in:
Ben Vanik 2013-10-06 21:09:58 -07:00
parent 371075f154
commit 651954ccae
12 changed files with 307 additions and 84 deletions

View File

@ -0,0 +1,29 @@
/**
******************************************************************************
* 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;
GraphicsDriver::GraphicsDriver(xe_memory_ref memory) {
memory_ = xe_memory_retain(memory);
memset(&register_file_, 0, sizeof(register_file_));
}
GraphicsDriver::~GraphicsDriver() {
xe_memory_release(memory_);
}
xe_memory_ref GraphicsDriver::memory() {
return xe_memory_retain(memory_);
}

View File

@ -0,0 +1,79 @@
/**
******************************************************************************
* 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/core.h>
#include <xenia/gpu/xenos/registers.h>
namespace xe {
namespace gpu {
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;
class GraphicsDriver {
public:
virtual ~GraphicsDriver();
xe_memory_ref memory();
xenos::RegisterFile* register_file() { return &register_file_; };
virtual void Initialize() = 0;
virtual void InvalidateState(
uint32_t mask) = 0;
virtual void SetShader(
XE_GPU_SHADER_TYPE type,
uint32_t address,
uint32_t start,
uint32_t size_dwords) = 0;
virtual void DrawIndexed(
XE_GPU_PRIMITIVE_TYPE prim_type,
uint32_t index_count) = 0;
protected:
GraphicsDriver(xe_memory_ref memory);
xe_memory_ref memory_;
xenos::RegisterFile register_file_;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GRAPHICS_DRIVER_H_

View File

@ -8,13 +8,16 @@
*/ */
#include <xenia/gpu/graphics_system.h> #include <xenia/gpu/graphics_system.h>
#include <xenia/cpu/processor.h> #include <xenia/cpu/processor.h>
#include <xenia/gpu/graphics_driver.h>
#include <xenia/gpu/ring_buffer_worker.h> #include <xenia/gpu/ring_buffer_worker.h>
#include <xenia/gpu/xenos/registers.h>
using namespace xe; using namespace xe;
using namespace xe::gpu; using namespace xe::gpu;
using namespace xe::gpu::xenos;
GraphicsSystem::GraphicsSystem(const CreationParams* params) : GraphicsSystem::GraphicsSystem(const CreationParams* params) :
@ -22,6 +25,9 @@ GraphicsSystem::GraphicsSystem(const CreationParams* params) :
memory_ = xe_memory_retain(params->memory); memory_ = xe_memory_retain(params->memory);
worker_ = new RingBufferWorker(memory_); worker_ = new RingBufferWorker(memory_);
// Set during Initialize();
driver_ = 0;
} }
GraphicsSystem::~GraphicsSystem() { GraphicsSystem::~GraphicsSystem() {
@ -51,7 +57,8 @@ void GraphicsSystem::SetInterruptCallback(uint32_t callback,
} }
void GraphicsSystem::InitializeRingBuffer(uint32_t ptr, uint32_t page_count) { void GraphicsSystem::InitializeRingBuffer(uint32_t ptr, uint32_t page_count) {
worker_->Initialize(ptr, page_count); XEASSERTNOTNULL(driver_);
worker_->Initialize(driver_, ptr, page_count);
} }
void GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr, void GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr,
@ -62,26 +69,31 @@ void GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr,
uint64_t GraphicsSystem::ReadRegister(uint32_t r) { uint64_t GraphicsSystem::ReadRegister(uint32_t r) {
XELOGGPU("ReadRegister(%.4X)", r); XELOGGPU("ReadRegister(%.4X)", r);
RegisterFile* regs = driver_->register_file();
switch (r) { switch (r) {
case 0x6544: // ? vblank pending? case 0x6544: // ? vblank pending?
return 1; return 1;
} }
return 0; XEASSERT(r >= 0 && r < kXEGpuRegisterCount);
return regs->values[r].u32;
} }
void GraphicsSystem::WriteRegister(uint32_t r, uint64_t value) { void GraphicsSystem::WriteRegister(uint32_t r, uint64_t value) {
XELOGGPU("WriteRegister(%.4X, %.8X)", r, value); XELOGGPU("WriteRegister(%.4X, %.8X)", r, value);
RegisterFile* regs = driver_->register_file();
switch (r) { switch (r) {
case 0x0714: // CP_RB_WPTR case 0x0714: // CP_RB_WPTR
worker_->UpdateWritePointer((uint32_t)value); worker_->UpdateWritePointer((uint32_t)value);
break; break;
default:
XELOGW("Unknown GPU register %.4X write: %.8X", r, value);
break;
} }
XEASSERT(r >= 0 && r < kXEGpuRegisterCount);
XELOGW("Unknown GPU register %.4X write: %.8X", r, value);
regs->values[r].u32 = (uint32_t)value;
} }
void GraphicsSystem::DispatchInterruptCallback() { void GraphicsSystem::DispatchInterruptCallback() {

View File

@ -13,16 +13,17 @@
#include <xenia/core.h> #include <xenia/core.h>
namespace xe { namespace xe {
namespace cpu { namespace cpu {
class Processor; class Processor;
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe
namespace xe { namespace xe {
namespace gpu { namespace gpu {
class GraphicsDriver;
class RingBufferWorker; class RingBufferWorker;
@ -53,6 +54,8 @@ public:
void DispatchInterruptCallback(); void DispatchInterruptCallback();
public: public:
// TODO(benvanik): have an HasRegisterHandler() so that the JIT can
// just poke the register file directly.
static uint64_t ReadRegisterThunk(GraphicsSystem* this_ptr, uint32_t r) { static uint64_t ReadRegisterThunk(GraphicsSystem* this_ptr, uint32_t r) {
return this_ptr->ReadRegister(r); return this_ptr->ReadRegister(r);
} }
@ -67,6 +70,7 @@ protected:
xe_memory_ref memory_; xe_memory_ref memory_;
shared_ptr<cpu::Processor> processor_; shared_ptr<cpu::Processor> processor_;
GraphicsDriver* driver_;
RingBufferWorker* worker_; RingBufferWorker* worker_;
uint32_t interrupt_callback_; uint32_t interrupt_callback_;

View File

@ -0,0 +1,57 @@
/**
******************************************************************************
* 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;
NopGraphicsDriver::NopGraphicsDriver(xe_memory_ref memory) :
GraphicsDriver(memory) {
}
NopGraphicsDriver::~NopGraphicsDriver() {
}
void NopGraphicsDriver::Initialize() {
}
void NopGraphicsDriver::InvalidateState(
uint32_t mask) {
if (mask == XE_GPU_INVALIDATE_MASK_ALL) {
XELOGGPU("NOP: (invalidate all)");
}
if (mask & XE_GPU_INVALIDATE_MASK_VERTEX_SHADER) {
XELOGGPU("NOP: invalidate vertex shader");
}
if (mask & XE_GPU_INVALIDATE_MASK_PIXEL_SHADER) {
XELOGGPU("NOP: invalidate pixel shader");
}
}
void NopGraphicsDriver::SetShader(
XE_GPU_SHADER_TYPE type,
uint32_t address,
uint32_t start,
uint32_t size_dwords) {
XELOGGPU("NOP: set shader %d at %0.8X (%db)",
type, address, size_dwords * 4);
}
void NopGraphicsDriver::DrawIndexed(
XE_GPU_PRIMITIVE_TYPE prim_type,
uint32_t index_count) {
XELOGGPU("NOP: draw indexed %d (%d indicies)",
prim_type, index_count);
}

View File

@ -0,0 +1,51 @@
/**
******************************************************************************
* 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/core.h>
#include <xenia/gpu/graphics_driver.h>
#include <xenia/gpu/nop/nop-private.h>
namespace xe {
namespace gpu {
namespace nop {
class NopGraphicsDriver : public GraphicsDriver {
public:
NopGraphicsDriver(xe_memory_ref memory);
virtual ~NopGraphicsDriver();
virtual void Initialize();
virtual void InvalidateState(
uint32_t mask);
virtual void SetShader(
XE_GPU_SHADER_TYPE type,
uint32_t address,
uint32_t start,
uint32_t size_dwords);
virtual void DrawIndexed(
XE_GPU_PRIMITIVE_TYPE prim_type,
uint32_t index_count);
protected:
};
} // namespace nop
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_NOP_NOP_GRAPHICS_DRIVER_H_

View File

@ -10,6 +10,7 @@
#include <xenia/gpu/nop/nop_graphics_system.h> #include <xenia/gpu/nop/nop_graphics_system.h>
#include <xenia/gpu/gpu-private.h> #include <xenia/gpu/gpu-private.h>
#include <xenia/gpu/nop/nop_graphics_driver.h>
using namespace xe; using namespace xe;
@ -42,6 +43,9 @@ NopGraphicsSystem::~NopGraphicsSystem() {
} }
void NopGraphicsSystem::Initialize() { void NopGraphicsSystem::Initialize() {
XEASSERTNULL(driver_);
driver_ = new NopGraphicsDriver(memory_);
XEASSERTNULL(timer_queue_); XEASSERTNULL(timer_queue_);
XEASSERTNULL(vsync_timer_); XEASSERTNULL(vsync_timer_);

View File

@ -4,6 +4,8 @@
'nop-private.h', 'nop-private.h',
'nop.cc', 'nop.cc',
'nop.h', 'nop.h',
'nop_graphics_driver.cc',
'nop_graphics_driver.h',
'nop_graphics_system.cc', 'nop_graphics_system.cc',
'nop_graphics_system.h', 'nop_graphics_system.h',
], ],

View File

@ -9,6 +9,7 @@
#include <xenia/gpu/ring_buffer_worker.h> #include <xenia/gpu/ring_buffer_worker.h>
#include <xenia/gpu/graphics_driver.h>
#include <xenia/gpu/xenos/packets.h> #include <xenia/gpu/xenos/packets.h>
#include <xenia/gpu/xenos/registers.h> #include <xenia/gpu/xenos/registers.h>
@ -19,7 +20,7 @@ using namespace xe::gpu::xenos;
RingBufferWorker::RingBufferWorker(xe_memory_ref memory) : RingBufferWorker::RingBufferWorker(xe_memory_ref memory) :
memory_(memory) { memory_(memory), driver_(0) {
running_ = true; running_ = true;
write_ptr_index_event_ = CreateEvent( write_ptr_index_event_ = CreateEvent(
NULL, FALSE, FALSE, NULL); NULL, FALSE, FALSE, NULL);
@ -37,7 +38,9 @@ RingBufferWorker::~RingBufferWorker() {
CloseHandle(write_ptr_index_event_); CloseHandle(write_ptr_index_event_);
} }
void RingBufferWorker::Initialize(uint32_t ptr, uint32_t page_count) { void RingBufferWorker::Initialize(GraphicsDriver* driver,
uint32_t ptr, uint32_t page_count) {
driver_ = driver;
primary_buffer_ptr_ = ptr; primary_buffer_ptr_ = ptr;
primary_buffer_size_ = page_count * 4 * 1024; primary_buffer_size_ = page_count * 4 * 1024;
read_ptr_index_ = 0; read_ptr_index_ = 0;
@ -100,6 +103,7 @@ void RingBufferWorker::ThreadStart() {
void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) { void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) {
uint8_t* p = xe_memory_addr(memory_); uint8_t* p = xe_memory_addr(memory_);
RegisterFile* regs = driver_->register_file();
// Adjust pointer base. // Adjust pointer base.
ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (ptr & 0x1FFFFFFF); ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (ptr & 0x1FFFFFFF);
@ -138,7 +142,10 @@ void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) {
const char* reg_name = xenos::GetRegisterName(base_index + m); const char* reg_name = xenos::GetRegisterName(base_index + m);
XELOGGPU(" %.8X -> %.4X %s", reg_data, base_index + m, XELOGGPU(" %.8X -> %.4X %s", reg_data, base_index + m,
reg_name ? reg_name : ""); reg_name ? reg_name : "");
// TODO(benvanik): process register writes. // TODO(benvanik): exec write handler (if special).
if (base_index + m < kXEGpuRegisterCount) {
regs->values[base_index + m].u32 = reg_data;
}
} }
n += 1 + count; n += 1 + count;
} }
@ -158,7 +165,14 @@ void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) {
reg_name_1 ? reg_name_1 : ""); reg_name_1 ? reg_name_1 : "");
XELOGGPU(" %.8X -> %.4X %s", reg_data_2, reg_index_2, XELOGGPU(" %.8X -> %.4X %s", reg_data_2, reg_index_2,
reg_name_2 ? reg_name_2 : ""); reg_name_2 ? reg_name_2 : "");
// TODO(benvanik): process register writes. // TODO(benvanik): exec write handler (if special).
if (reg_index_1 < kXEGpuRegisterCount) {
regs->values[reg_index_1].u32 = reg_data_1;
}
if (reg_index_2 < kXEGpuRegisterCount) {
regs->values[reg_index_2].u32 = reg_data_2;
}
n += 1 + 2; n += 1 + 2;
} }
break; break;
@ -242,56 +256,31 @@ void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) {
// initiate fetch of index buffer and draw // initiate fetch of index buffer and draw
{ {
XELOGGPU("Packet(%.8X): PM4_DRAW_INDX", packet); XELOGGPU("Packet(%.8X): PM4_DRAW_INDX", packet);
LOG_DATA(count);
// d0 = viz query info // d0 = viz query info
uint32_t d0 = XEGETUINT32BE(packet_base + 1 * 4); uint32_t d0 = XEGETUINT32BE(packet_base + 1 * 4);
uint32_t d1 = XEGETUINT32BE(packet_base + 2 * 4); uint32_t d1 = XEGETUINT32BE(packet_base + 2 * 4);
uint32_t index_count = d1 >> 16; uint32_t index_count = d1 >> 16;
uint32_t prim_type = d1 & 0x3F; uint32_t prim_type = d1 & 0x3F;
// Not sure what the other bits mean - 'SrcSel=AutoIndex'? // Not sure what the other bits mean - 'SrcSel=AutoIndex'?
const char* prim_type_name = "UNKNOWN"; driver_->DrawIndexed(
switch (prim_type) { (XE_GPU_PRIMITIVE_TYPE)prim_type,
case 1: index_count);
prim_type_name = "pointlist";
break;
case 4:
prim_type_name = "trilist";
break;
case 8:
prim_type_name = "rectlist";
break;
default:
XEASSERTALWAYS();
break;
}
XELOGGPU(" %d indices of %s", index_count, prim_type_name);
LOG_DATA(count);
} }
break; break;
case PM4_DRAW_INDX_2: case PM4_DRAW_INDX_2:
// draw using supplied indices in packet // draw using supplied indices in packet
{ {
XELOGGPU("Packet(%.8X): PM4_DRAW_INDX_2", packet); XELOGGPU("Packet(%.8X): PM4_DRAW_INDX_2", packet);
LOG_DATA(count);
uint32_t d0 = XEGETUINT32BE(packet_base + 1 * 4); uint32_t d0 = XEGETUINT32BE(packet_base + 1 * 4);
uint32_t index_count = d0 >> 16; uint32_t index_count = d0 >> 16;
uint32_t prim_type = d0 & 0x3F; uint32_t prim_type = d0 & 0x3F;
// Not sure what the other bits mean - 'SrcSel=AutoIndex'? // Not sure what the other bits mean - 'SrcSel=AutoIndex'?
const char* prim_type_name = "UNKNOWN"; // TODO(benvanik): verify this matches DRAW_INDX
switch (prim_type) { driver_->DrawIndexed(
case 1: (XE_GPU_PRIMITIVE_TYPE)prim_type,
prim_type_name = "pointlist"; index_count);
break;
case 4:
prim_type_name = "trilist";
break;
case 8:
prim_type_name = "rectlist";
break;
default:
XEASSERTALWAYS();
break;
}
XELOGGPU(" %d indices of %s", index_count, prim_type_name);
LOG_DATA(count);
} }
break; break;
@ -299,6 +288,7 @@ void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) {
// load sequencer instruction memory (pointer-based) // load sequencer instruction memory (pointer-based)
{ {
XELOGGPU("Packet(%.8X): PM4_IM_LOAD", packet); XELOGGPU("Packet(%.8X): PM4_IM_LOAD", packet);
LOG_DATA(count);
uint32_t addr_type = XEGETUINT32BE(packet_base + 1 * 4); uint32_t addr_type = XEGETUINT32BE(packet_base + 1 * 4);
uint32_t type = addr_type & 0x3; uint32_t type = addr_type & 0x3;
uint32_t addr = addr_type & ~0x3; uint32_t addr = addr_type & ~0x3;
@ -306,20 +296,11 @@ void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) {
uint32_t start = start_size >> 16; uint32_t start = start_size >> 16;
uint32_t size = start_size & 0xFFFF; // dwords uint32_t size = start_size & 0xFFFF; // dwords
XEASSERT(start == 0); XEASSERT(start == 0);
switch (type) { driver_->SetShader(
case 0: (XE_GPU_SHADER_TYPE)type,
XELOGGPU(" vertex shader"); TRANSLATE_ADDR(addr),
break; start,
case 1: size);
XELOGGPU(" pixel shader");
break;
default:
XEASSERTALWAYS();
break;
}
XELOGGPU(" %0.8X, %db / %dw",
TRANSLATE_ADDR(addr), size * 4, size);
LOG_DATA(count);
} }
break; break;
case PM4_IM_LOAD_IMMEDIATE: case PM4_IM_LOAD_IMMEDIATE:
@ -331,27 +312,23 @@ void RingBufferWorker::ExecuteSegment(uint32_t ptr, uint32_t length) {
uint32_t start = start_size >> 16; uint32_t start = start_size >> 16;
uint32_t size = start_size & 0xFFFF; // dwords uint32_t size = start_size & 0xFFFF; // dwords
XEASSERT(start == 0); XEASSERT(start == 0);
switch (type) {
case 0:
XELOGGPU(" vertex shader");
break;
case 1:
XELOGGPU(" pixel shader");
break;
default:
XEASSERTALWAYS();
break;
}
XELOGGPU(" %db / %dw", size * 4, size);
LOG_DATA(count); LOG_DATA(count);
driver_->SetShader(
(XE_GPU_SHADER_TYPE)type,
ptr + n * 4 + 3 * 4,
start,
size);
} }
break; break;
case PM4_INVALIDATE_STATE: case PM4_INVALIDATE_STATE:
// selective invalidation of state pointers // selective invalidation of state pointers
XELOGGPU("Packet(%.8X): PM4_INVALIDATE_STATE", packet); {
LOG_DATA(count); XELOGGPU("Packet(%.8X): PM4_INVALIDATE_STATE", packet);
//*cmd++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ LOG_DATA(count);
uint32_t mask = XEGETUINT32BE(packet_base + 1 * 4);
driver_->InvalidateState(mask);
}
break; break;
default: default:

View File

@ -12,10 +12,13 @@
#include <xenia/core.h> #include <xenia/core.h>
#include <xenia/gpu/xenos/registers.h>
namespace xe { namespace xe {
namespace gpu { namespace gpu {
class GraphicsDriver;
class RingBufferWorker { class RingBufferWorker {
public: public:
@ -24,7 +27,8 @@ public:
xe_memory_ref memory(); xe_memory_ref memory();
void Initialize(uint32_t ptr, uint32_t page_count); void Initialize(GraphicsDriver* driver,
uint32_t ptr, uint32_t page_count);
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size); void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size);
void UpdateWritePointer(uint32_t value); void UpdateWritePointer(uint32_t value);
@ -41,6 +45,8 @@ protected:
xe_thread_ref thread_; xe_thread_ref thread_;
bool running_; bool running_;
GraphicsDriver* driver_;
uint32_t primary_buffer_ptr_; uint32_t primary_buffer_ptr_;
uint32_t primary_buffer_size_; uint32_t primary_buffer_size_;

View File

@ -5,6 +5,8 @@
'gpu-private.h', 'gpu-private.h',
'gpu.cc', 'gpu.cc',
'gpu.h', 'gpu.h',
'graphics_driver.cc',
'graphics_driver.h',
'graphics_system.cc', 'graphics_system.cc',
'graphics_system.h', 'graphics_system.h',
'ring_buffer_worker.cc', 'ring_buffer_worker.cc',

View File

@ -18,7 +18,7 @@ namespace gpu {
namespace xenos { namespace xenos {
static const uint32_t kXEGpuRegisterCount = 0x3000; static const uint32_t kXEGpuRegisterCount = 0x5003;
enum Registers { enum Registers {
@ -33,13 +33,13 @@ const char* GetRegisterName(uint32_t index);
union RegisterValue { union RegisterValue {
uint32_t dword_value; uint32_t u32;
float float_value; float f32;
}; };
struct RegisterFile { struct RegisterFile {
RegisterValue registers[kXEGpuRegisterCount]; RegisterValue values[kXEGpuRegisterCount];
}; };