forked from suyu/suyu
Pica: Implement command buffer execution registers.
This commit is contained in:
parent
4ac6c1a3b5
commit
02c9fe202c
2 changed files with 75 additions and 43 deletions
|
@ -56,7 +56,17 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
|
||||||
// Trigger IRQ
|
// Trigger IRQ
|
||||||
case PICA_REG_INDEX(trigger_irq):
|
case PICA_REG_INDEX(trigger_irq):
|
||||||
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
|
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
|
||||||
return;
|
break;
|
||||||
|
|
||||||
|
case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c):
|
||||||
|
case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d):
|
||||||
|
{
|
||||||
|
unsigned index = id - PICA_REG_INDEX(command_buffer.trigger[0]);
|
||||||
|
u32* head_ptr = (u32*)Memory::GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index));
|
||||||
|
g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr;
|
||||||
|
g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// It seems like these trigger vertex rendering
|
// It seems like these trigger vertex rendering
|
||||||
case PICA_REG_INDEX(trigger_draw):
|
case PICA_REG_INDEX(trigger_draw):
|
||||||
|
@ -363,38 +373,34 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
|
||||||
g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id));
|
g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) {
|
|
||||||
const CommandHeader& header = *(const CommandHeader*)(&first_command_word[1]);
|
|
||||||
|
|
||||||
u32* read_pointer = (u32*)first_command_word;
|
|
||||||
|
|
||||||
const u32 write_mask = ((header.parameter_mask & 0x1) ? (0xFFu << 0) : 0u) |
|
|
||||||
((header.parameter_mask & 0x2) ? (0xFFu << 8) : 0u) |
|
|
||||||
((header.parameter_mask & 0x4) ? (0xFFu << 16) : 0u) |
|
|
||||||
((header.parameter_mask & 0x8) ? (0xFFu << 24) : 0u);
|
|
||||||
|
|
||||||
WritePicaReg(header.cmd_id, *read_pointer, write_mask);
|
|
||||||
read_pointer += 2;
|
|
||||||
|
|
||||||
for (unsigned int i = 1; i < 1+header.extra_data_length; ++i) {
|
|
||||||
u32 cmd = header.cmd_id + ((header.group_commands) ? i : 0);
|
|
||||||
WritePicaReg(cmd, *read_pointer, write_mask);
|
|
||||||
++read_pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// align read pointer to 8 bytes
|
|
||||||
if ((first_command_word - read_pointer) % 2)
|
|
||||||
++read_pointer;
|
|
||||||
|
|
||||||
return read_pointer - first_command_word;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessCommandList(const u32* list, u32 size) {
|
void ProcessCommandList(const u32* list, u32 size) {
|
||||||
u32* read_pointer = (u32*)list;
|
g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = list;
|
||||||
u32 list_length = size / sizeof(u32);
|
g_state.cmd_list.length = size / sizeof(u32);
|
||||||
|
|
||||||
while (read_pointer < list + list_length) {
|
while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) {
|
||||||
read_pointer += ExecuteCommandBlock(read_pointer);
|
// Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF
|
||||||
|
static const u32 expand_bits_to_bytes[] = {
|
||||||
|
0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff,
|
||||||
|
0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff,
|
||||||
|
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
|
||||||
|
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff
|
||||||
|
};
|
||||||
|
|
||||||
|
// Align read pointer to 8 bytes
|
||||||
|
if ((g_state.cmd_list.head_ptr - g_state.cmd_list.current_ptr) % 2 != 0)
|
||||||
|
++g_state.cmd_list.current_ptr;
|
||||||
|
|
||||||
|
u32 value = *g_state.cmd_list.current_ptr++;
|
||||||
|
const CommandHeader header = { *g_state.cmd_list.current_ptr++ };
|
||||||
|
const u32 write_mask = expand_bits_to_bytes[header.parameter_mask];
|
||||||
|
u32 cmd = header.cmd_id;
|
||||||
|
|
||||||
|
WritePicaReg(cmd, value, write_mask);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < header.extra_data_length; ++i) {
|
||||||
|
u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
|
||||||
|
WritePicaReg(cmd, *g_state.cmd_list.current_ptr++, write_mask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -708,7 +708,33 @@ struct Regs {
|
||||||
u32 set_value[3];
|
u32 set_value[3];
|
||||||
} vs_default_attributes_setup;
|
} vs_default_attributes_setup;
|
||||||
|
|
||||||
INSERT_PADDING_WORDS(0x28);
|
INSERT_PADDING_WORDS(0x2);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
// There are two channels that can be used to configure the next command buffer, which
|
||||||
|
// can be then executed by writing to the "trigger" registers. There are two reasons why a
|
||||||
|
// game might use this feature:
|
||||||
|
// 1) With this, an arbitrary number of additional command buffers may be executed in
|
||||||
|
// sequence without requiring any intervention of the CPU after the initial one is
|
||||||
|
// kicked off.
|
||||||
|
// 2) Games can configure these registers to provide a command list subroutine mechanism.
|
||||||
|
|
||||||
|
BitField< 0, 20, u32> size[2]; ///< Size (in bytes / 8) of each channel's command buffer
|
||||||
|
BitField< 0, 28, u32> addr[2]; ///< Physical address / 8 of each channel's command buffer
|
||||||
|
u32 trigger[2]; ///< Triggers execution of the channel's command buffer when written to
|
||||||
|
|
||||||
|
unsigned GetSize(unsigned index) const {
|
||||||
|
ASSERT(index < 2);
|
||||||
|
return 8 * size[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
PAddr GetPhysicalAddress(unsigned index) const {
|
||||||
|
ASSERT(index < 2);
|
||||||
|
return (PAddr)(8 * addr[index]);
|
||||||
|
}
|
||||||
|
} command_buffer;
|
||||||
|
|
||||||
|
INSERT_PADDING_WORDS(0x20);
|
||||||
|
|
||||||
enum class TriangleTopology : u32 {
|
enum class TriangleTopology : u32 {
|
||||||
List = 0,
|
List = 0,
|
||||||
|
@ -861,6 +887,7 @@ struct Regs {
|
||||||
ADD_FIELD(trigger_draw);
|
ADD_FIELD(trigger_draw);
|
||||||
ADD_FIELD(trigger_draw_indexed);
|
ADD_FIELD(trigger_draw_indexed);
|
||||||
ADD_FIELD(vs_default_attributes_setup);
|
ADD_FIELD(vs_default_attributes_setup);
|
||||||
|
ADD_FIELD(command_buffer);
|
||||||
ADD_FIELD(triangle_topology);
|
ADD_FIELD(triangle_topology);
|
||||||
ADD_FIELD(vs_bool_uniforms);
|
ADD_FIELD(vs_bool_uniforms);
|
||||||
ADD_FIELD(vs_int_uniforms);
|
ADD_FIELD(vs_int_uniforms);
|
||||||
|
@ -938,6 +965,7 @@ ASSERT_REG_POSITION(num_vertices, 0x228);
|
||||||
ASSERT_REG_POSITION(trigger_draw, 0x22e);
|
ASSERT_REG_POSITION(trigger_draw, 0x22e);
|
||||||
ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f);
|
ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f);
|
||||||
ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232);
|
ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232);
|
||||||
|
ASSERT_REG_POSITION(command_buffer, 0x238);
|
||||||
ASSERT_REG_POSITION(triangle_topology, 0x25e);
|
ASSERT_REG_POSITION(triangle_topology, 0x25e);
|
||||||
ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0);
|
ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0);
|
||||||
ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1);
|
ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1);
|
||||||
|
@ -1053,21 +1081,12 @@ private:
|
||||||
float value;
|
float value;
|
||||||
};
|
};
|
||||||
|
|
||||||
union CommandHeader {
|
|
||||||
CommandHeader(u32 h) : hex(h) {}
|
|
||||||
|
|
||||||
u32 hex;
|
|
||||||
|
|
||||||
BitField< 0, 16, u32> cmd_id;
|
|
||||||
BitField<16, 4, u32> parameter_mask;
|
|
||||||
BitField<20, 11, u32> extra_data_length;
|
|
||||||
BitField<31, 1, u32> group_commands;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Struct used to describe current Pica state
|
/// Struct used to describe current Pica state
|
||||||
struct State {
|
struct State {
|
||||||
|
/// Pica registers
|
||||||
Regs regs;
|
Regs regs;
|
||||||
|
|
||||||
|
/// Vertex shader memory
|
||||||
struct {
|
struct {
|
||||||
struct {
|
struct {
|
||||||
Math::Vec4<float24> f[96];
|
Math::Vec4<float24> f[96];
|
||||||
|
@ -1080,6 +1099,13 @@ struct State {
|
||||||
std::array<u32, 1024> program_code;
|
std::array<u32, 1024> program_code;
|
||||||
std::array<u32, 1024> swizzle_data;
|
std::array<u32, 1024> swizzle_data;
|
||||||
} vs;
|
} vs;
|
||||||
|
|
||||||
|
/// Current Pica command list
|
||||||
|
struct {
|
||||||
|
const u32* head_ptr;
|
||||||
|
const u32* current_ptr;
|
||||||
|
u32 length;
|
||||||
|
} cmd_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Initialize Pica state
|
/// Initialize Pica state
|
||||||
|
|
Loading…
Reference in a new issue