mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	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
 | ||||
|         case PICA_REG_INDEX(trigger_irq): | ||||
|             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
 | ||||
|         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)); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     u32* read_pointer = (u32*)list; | ||||
|     u32 list_length = size / sizeof(u32); | ||||
|     g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = list; | ||||
|     g_state.cmd_list.length = size / sizeof(u32); | ||||
| 
 | ||||
|     while (read_pointer < list + list_length) { | ||||
|         read_pointer += ExecuteCommandBlock(read_pointer); | ||||
|     while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) { | ||||
|         // 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]; | ||||
|     } 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 { | ||||
|         List        = 0, | ||||
|  | @ -861,6 +887,7 @@ struct Regs { | |||
|         ADD_FIELD(trigger_draw); | ||||
|         ADD_FIELD(trigger_draw_indexed); | ||||
|         ADD_FIELD(vs_default_attributes_setup); | ||||
|         ADD_FIELD(command_buffer); | ||||
|         ADD_FIELD(triangle_topology); | ||||
|         ADD_FIELD(vs_bool_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_indexed, 0x22f); | ||||
| ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232); | ||||
| ASSERT_REG_POSITION(command_buffer, 0x238); | ||||
| ASSERT_REG_POSITION(triangle_topology, 0x25e); | ||||
| ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | ||||
| ASSERT_REG_POSITION(vs_int_uniforms, 0x2b1); | ||||
|  | @ -1053,21 +1081,12 @@ private: | |||
|     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 State { | ||||
|     /// Pica registers
 | ||||
|     Regs regs; | ||||
| 
 | ||||
|     /// Vertex shader memory
 | ||||
|     struct { | ||||
|         struct { | ||||
|             Math::Vec4<float24> f[96]; | ||||
|  | @ -1080,6 +1099,13 @@ struct State { | |||
|         std::array<u32, 1024> program_code; | ||||
|         std::array<u32, 1024> swizzle_data; | ||||
|     } vs; | ||||
| 
 | ||||
|     /// Current Pica command list
 | ||||
|     struct { | ||||
|         const u32* head_ptr; | ||||
|         const u32* current_ptr; | ||||
|         u32 length; | ||||
|     } cmd_list; | ||||
| }; | ||||
| 
 | ||||
| /// Initialize Pica state
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue