diff options
| author | 2015-05-24 00:55:35 -0400 | |
|---|---|---|
| committer | 2015-05-31 01:52:39 -0400 | |
| commit | 02c9fe202c4027d7e4a6287137f4b254b8830494 (patch) | |
| tree | 0fe719f6ccd73baf60eb72bf675252bcaed9a24b /src/video_core | |
| parent | vertex_shader: Implement SLT/SLTI instructions. (diff) | |
| download | yuzu-02c9fe202c4027d7e4a6287137f4b254b8830494.tar.gz yuzu-02c9fe202c4027d7e4a6287137f4b254b8830494.tar.xz yuzu-02c9fe202c4027d7e4a6287137f4b254b8830494.zip | |
Pica: Implement command buffer execution registers.
Diffstat (limited to 'src/video_core')
| -rw-r--r-- | src/video_core/command_processor.cpp | 70 | ||||
| -rw-r--r-- | src/video_core/pica.h | 50 |
2 files changed, 76 insertions, 44 deletions
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 29ba6b769..b46fadd9f 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -56,7 +56,17 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 56 | // Trigger IRQ | 56 | // Trigger IRQ |
| 57 | case PICA_REG_INDEX(trigger_irq): | 57 | case PICA_REG_INDEX(trigger_irq): |
| 58 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); | 58 | GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); |
| 59 | return; | 59 | break; |
| 60 | |||
| 61 | case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c): | ||
| 62 | case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d): | ||
| 63 | { | ||
| 64 | unsigned index = id - PICA_REG_INDEX(command_buffer.trigger[0]); | ||
| 65 | u32* head_ptr = (u32*)Memory::GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index)); | ||
| 66 | g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr; | ||
| 67 | g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32); | ||
| 68 | break; | ||
| 69 | } | ||
| 60 | 70 | ||
| 61 | // It seems like these trigger vertex rendering | 71 | // It seems like these trigger vertex rendering |
| 62 | case PICA_REG_INDEX(trigger_draw): | 72 | case PICA_REG_INDEX(trigger_draw): |
| @@ -363,38 +373,34 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 363 | g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); | 373 | g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id)); |
| 364 | } | 374 | } |
| 365 | 375 | ||
| 366 | static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { | ||
| 367 | const CommandHeader& header = *(const CommandHeader*)(&first_command_word[1]); | ||
| 368 | |||
| 369 | u32* read_pointer = (u32*)first_command_word; | ||
| 370 | |||
| 371 | const u32 write_mask = ((header.parameter_mask & 0x1) ? (0xFFu << 0) : 0u) | | ||
| 372 | ((header.parameter_mask & 0x2) ? (0xFFu << 8) : 0u) | | ||
| 373 | ((header.parameter_mask & 0x4) ? (0xFFu << 16) : 0u) | | ||
| 374 | ((header.parameter_mask & 0x8) ? (0xFFu << 24) : 0u); | ||
| 375 | |||
| 376 | WritePicaReg(header.cmd_id, *read_pointer, write_mask); | ||
| 377 | read_pointer += 2; | ||
| 378 | |||
| 379 | for (unsigned int i = 1; i < 1+header.extra_data_length; ++i) { | ||
| 380 | u32 cmd = header.cmd_id + ((header.group_commands) ? i : 0); | ||
| 381 | WritePicaReg(cmd, *read_pointer, write_mask); | ||
| 382 | ++read_pointer; | ||
| 383 | } | ||
| 384 | |||
| 385 | // align read pointer to 8 bytes | ||
| 386 | if ((first_command_word - read_pointer) % 2) | ||
| 387 | ++read_pointer; | ||
| 388 | |||
| 389 | return read_pointer - first_command_word; | ||
| 390 | } | ||
| 391 | |||
| 392 | void ProcessCommandList(const u32* list, u32 size) { | 376 | void ProcessCommandList(const u32* list, u32 size) { |
| 393 | u32* read_pointer = (u32*)list; | 377 | g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = list; |
| 394 | u32 list_length = size / sizeof(u32); | 378 | g_state.cmd_list.length = size / sizeof(u32); |
| 395 | 379 | ||
| 396 | while (read_pointer < list + list_length) { | 380 | while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) { |
| 397 | read_pointer += ExecuteCommandBlock(read_pointer); | 381 | // Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF |
| 382 | static const u32 expand_bits_to_bytes[] = { | ||
| 383 | 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, | ||
| 384 | 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, | ||
| 385 | 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, | ||
| 386 | 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff | ||
| 387 | }; | ||
| 388 | |||
| 389 | // Align read pointer to 8 bytes | ||
| 390 | if ((g_state.cmd_list.head_ptr - g_state.cmd_list.current_ptr) % 2 != 0) | ||
| 391 | ++g_state.cmd_list.current_ptr; | ||
| 392 | |||
| 393 | u32 value = *g_state.cmd_list.current_ptr++; | ||
| 394 | const CommandHeader header = { *g_state.cmd_list.current_ptr++ }; | ||
| 395 | const u32 write_mask = expand_bits_to_bytes[header.parameter_mask]; | ||
| 396 | u32 cmd = header.cmd_id; | ||
| 397 | |||
| 398 | WritePicaReg(cmd, value, write_mask); | ||
| 399 | |||
| 400 | for (unsigned i = 0; i < header.extra_data_length; ++i) { | ||
| 401 | u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0); | ||
| 402 | WritePicaReg(cmd, *g_state.cmd_list.current_ptr++, write_mask); | ||
| 403 | } | ||
| 398 | } | 404 | } |
| 399 | } | 405 | } |
| 400 | 406 | ||
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 6ebeb08f7..fbc95a4b6 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -708,7 +708,33 @@ struct Regs { | |||
| 708 | u32 set_value[3]; | 708 | u32 set_value[3]; |
| 709 | } vs_default_attributes_setup; | 709 | } vs_default_attributes_setup; |
| 710 | 710 | ||
| 711 | INSERT_PADDING_WORDS(0x28); | 711 | INSERT_PADDING_WORDS(0x2); |
| 712 | |||
| 713 | struct { | ||
| 714 | // There are two channels that can be used to configure the next command buffer, which | ||
| 715 | // can be then executed by writing to the "trigger" registers. There are two reasons why a | ||
| 716 | // game might use this feature: | ||
| 717 | // 1) With this, an arbitrary number of additional command buffers may be executed in | ||
| 718 | // sequence without requiring any intervention of the CPU after the initial one is | ||
| 719 | // kicked off. | ||
| 720 | // 2) Games can configure these registers to provide a command list subroutine mechanism. | ||
| 721 | |||
| 722 | BitField< 0, 20, u32> size[2]; ///< Size (in bytes / 8) of each channel's command buffer | ||
| 723 | BitField< 0, 28, u32> addr[2]; ///< Physical address / 8 of each channel's command buffer | ||
| 724 | u32 trigger[2]; ///< Triggers execution of the channel's command buffer when written to | ||
| 725 | |||
| 726 | unsigned GetSize(unsigned index) const { | ||
| 727 | ASSERT(index < 2); | ||
| 728 | return 8 * size[index]; | ||
| 729 | } | ||
| 730 | |||
| 731 | PAddr GetPhysicalAddress(unsigned index) const { | ||
| 732 | ASSERT(index < 2); | ||
| 733 | return (PAddr)(8 * addr[index]); | ||
| 734 | } | ||
| 735 | } command_buffer; | ||
| 736 | |||
| 737 | INSERT_PADDING_WORDS(0x20); | ||
| 712 | 738 | ||
| 713 | enum class TriangleTopology : u32 { | 739 | enum class TriangleTopology : u32 { |
| 714 | List = 0, | 740 | List = 0, |
| @@ -861,6 +887,7 @@ struct Regs { | |||
| 861 | ADD_FIELD(trigger_draw); | 887 | ADD_FIELD(trigger_draw); |
| 862 | ADD_FIELD(trigger_draw_indexed); | 888 | ADD_FIELD(trigger_draw_indexed); |
| 863 | ADD_FIELD(vs_default_attributes_setup); | 889 | ADD_FIELD(vs_default_attributes_setup); |
| 890 | ADD_FIELD(command_buffer); | ||
| 864 | ADD_FIELD(triangle_topology); | 891 | ADD_FIELD(triangle_topology); |
| 865 | ADD_FIELD(vs_bool_uniforms); | 892 | ADD_FIELD(vs_bool_uniforms); |
| 866 | ADD_FIELD(vs_int_uniforms); | 893 | ADD_FIELD(vs_int_uniforms); |
| @@ -938,6 +965,7 @@ ASSERT_REG_POSITION(num_vertices, 0x228); | |||
| 938 | ASSERT_REG_POSITION(trigger_draw, 0x22e); | 965 | ASSERT_REG_POSITION(trigger_draw, 0x22e); |
| 939 | ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); | 966 | ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); |
| 940 | ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); | 967 | ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); |
| 968 | ASSERT_REG_POSITION(command_buffer, 0x238); | ||
| 941 | ASSERT_REG_POSITION(triangle_topology, 0x25e); | 969 | ASSERT_REG_POSITION(triangle_topology, 0x25e); |
| 942 | ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | 970 | ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); |
| 943 | ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); | 971 | ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); |
| @@ -1053,21 +1081,12 @@ private: | |||
| 1053 | float value; | 1081 | float value; |
| 1054 | }; | 1082 | }; |
| 1055 | 1083 | ||
| 1056 | union CommandHeader { | ||
| 1057 | CommandHeader(u32 h) : hex(h) {} | ||
| 1058 | |||
| 1059 | u32 hex; | ||
| 1060 | |||
| 1061 | BitField< 0, 16, u32> cmd_id; | ||
| 1062 | BitField<16, 4, u32> parameter_mask; | ||
| 1063 | BitField<20, 11, u32> extra_data_length; | ||
| 1064 | BitField<31, 1, u32> group_commands; | ||
| 1065 | }; | ||
| 1066 | |||
| 1067 | /// Struct used to describe current Pica state | 1084 | /// Struct used to describe current Pica state |
| 1068 | struct State { | 1085 | struct State { |
| 1086 | /// Pica registers | ||
| 1069 | Regs regs; | 1087 | Regs regs; |
| 1070 | 1088 | ||
| 1089 | /// Vertex shader memory | ||
| 1071 | struct { | 1090 | struct { |
| 1072 | struct { | 1091 | struct { |
| 1073 | Math::Vec4<float24> f[96]; | 1092 | Math::Vec4<float24> f[96]; |
| @@ -1080,6 +1099,13 @@ struct State { | |||
| 1080 | std::array<u32, 1024> program_code; | 1099 | std::array<u32, 1024> program_code; |
| 1081 | std::array<u32, 1024> swizzle_data; | 1100 | std::array<u32, 1024> swizzle_data; |
| 1082 | } vs; | 1101 | } vs; |
| 1102 | |||
| 1103 | /// Current Pica command list | ||
| 1104 | struct { | ||
| 1105 | const u32* head_ptr; | ||
| 1106 | const u32* current_ptr; | ||
| 1107 | u32 length; | ||
| 1108 | } cmd_list; | ||
| 1083 | }; | 1109 | }; |
| 1084 | 1110 | ||
| 1085 | /// Initialize Pica state | 1111 | /// Initialize Pica state |