mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Pica/VertexShader: Cleanup flow control logic and implement CMP/IFU instructions.
This commit is contained in:
		
							parent
							
								
									22afb9d830
								
							
						
					
					
						commit
						6bd41de276
					
				
					 1 changed files with 57 additions and 51 deletions
				
			
		|  | @ -2,6 +2,8 @@ | ||||||
| // Licensed under GPLv2
 | // Licensed under GPLv2
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <stack> | ||||||
|  | 
 | ||||||
| #include <boost/range/algorithm.hpp> | #include <boost/range/algorithm.hpp> | ||||||
| 
 | 
 | ||||||
| #include <common/file_util.h> | #include <common/file_util.h> | ||||||
|  | @ -65,9 +67,6 @@ const std::array<u32, 1024>& GetSwizzlePatterns() | ||||||
|     return swizzle_data; |     return swizzle_data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: Is there actually a limit on hardware?
 |  | ||||||
| const int if_stack_size = 8; |  | ||||||
| 
 |  | ||||||
| struct VertexShaderState { | struct VertexShaderState { | ||||||
|     u32* program_counter; |     u32* program_counter; | ||||||
| 
 | 
 | ||||||
|  | @ -84,14 +83,14 @@ struct VertexShaderState { | ||||||
|     enum { |     enum { | ||||||
|         INVALID_ADDRESS = 0xFFFFFFFF |         INVALID_ADDRESS = 0xFFFFFFFF | ||||||
|     }; |     }; | ||||||
|     u32 call_stack[8]; // TODO: What is the maximal call stack depth?
 |  | ||||||
|     u32* call_stack_pointer; |  | ||||||
| 
 | 
 | ||||||
|     struct IfStackElement { |     struct CallStackElement { | ||||||
|         u32 else_addr; |         u32 final_address; | ||||||
|         u32 else_instructions; |         u32 return_address; | ||||||
|     } if_stack[if_stack_size]; |     }; | ||||||
|     IfStackElement* if_stack_pointer; | 
 | ||||||
|  |     // TODO: Is there a maximal size for this?
 | ||||||
|  |     std::stack<CallStackElement> call_stack; | ||||||
| 
 | 
 | ||||||
|     struct { |     struct { | ||||||
|         u32 max_offset; // maximum program counter ever reached
 |         u32 max_offset; // maximum program counter ever reached
 | ||||||
|  | @ -101,12 +100,27 @@ struct VertexShaderState { | ||||||
| 
 | 
 | ||||||
| static void ProcessShaderCode(VertexShaderState& state) { | static void ProcessShaderCode(VertexShaderState& state) { | ||||||
|     while (true) { |     while (true) { | ||||||
|         bool increment_pc = true; |         if (!state.call_stack.empty()) { | ||||||
|  |             if (state.program_counter - shader_memory.data() == state.call_stack.top().final_address) { | ||||||
|  |                 state.program_counter = &shader_memory[state.call_stack.top().return_address]; | ||||||
|  |                 state.call_stack.pop(); | ||||||
|  | 
 | ||||||
|  |                 // TODO: Is "trying again" accurate to hardware?
 | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         bool exit_loop = false; |         bool exit_loop = false; | ||||||
|         const Instruction& instr = *(const Instruction*)state.program_counter; |         const Instruction& instr = *(const Instruction*)state.program_counter; | ||||||
|         const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; |         const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; | ||||||
| 
 | 
 | ||||||
|         state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + (state.program_counter - shader_memory.data())); |         auto call = [&](std::stack<VertexShaderState::CallStackElement>& stack, u32 offset, u32 num_instructions, u32 return_offset) { | ||||||
|  |             state.program_counter = &shader_memory[offset] - 1; // -1 to make sure when incrementing the PC we end up at the correct offset
 | ||||||
|  |             stack.push({ offset + num_instructions, return_offset }); | ||||||
|  |         }; | ||||||
|  |         u32 binary_offset = state.program_counter - shader_memory.data(); | ||||||
|  | 
 | ||||||
|  |         state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + binary_offset); | ||||||
| 
 | 
 | ||||||
|         auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { |         auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { | ||||||
|             switch (source_reg.GetRegisterType()) { |             switch (source_reg.GetRegisterType()) { | ||||||
|  | @ -328,30 +342,33 @@ static void ProcessShaderCode(VertexShaderState& state) { | ||||||
|         default: |         default: | ||||||
|             // Handle each instruction on its own
 |             // Handle each instruction on its own
 | ||||||
|             switch (instr.opcode) { |             switch (instr.opcode) { | ||||||
|             // NOP is currently used as a heuristic for leaving from a function.
 |             case Instruction::OpCode::END: | ||||||
|             // TODO: This is completely incorrect.
 |                 exit_loop = true; | ||||||
|             case Instruction::OpCode::NOP: |  | ||||||
|                 if (*state.call_stack_pointer == VertexShaderState::INVALID_ADDRESS) { |  | ||||||
|                     exit_loop = true; |  | ||||||
|                 } else { |  | ||||||
|                     // Jump back to call stack position, invalidate call stack entry, move up call stack pointer
 |  | ||||||
|                     state.program_counter = &shader_memory[*state.call_stack_pointer]; |  | ||||||
|                     *state.call_stack_pointer-- = VertexShaderState::INVALID_ADDRESS; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             case Instruction::OpCode::CALL: |             case Instruction::OpCode::CALL: | ||||||
|                 increment_pc = false; |                 call(state.call_stack, | ||||||
| 
 |                      instr.flow_control.dest_offset, | ||||||
|                 _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); |                      instr.flow_control.num_instructions, | ||||||
| 
 |                      binary_offset + 1); | ||||||
|                 *++state.call_stack_pointer = state.program_counter - shader_memory.data(); |  | ||||||
|                 state.program_counter = &shader_memory[instr.flow_control.dest_offset]; |  | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             case Instruction::OpCode::END: |             case Instruction::OpCode::NOP: | ||||||
|                 // TODO
 |                 break; | ||||||
|  | 
 | ||||||
|  |             case Instruction::OpCode::IFU: | ||||||
|  |                 if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) { | ||||||
|  |                     call(state.call_stack, | ||||||
|  |                          binary_offset + 1, | ||||||
|  |                          instr.flow_control.dest_offset - binary_offset - 1, | ||||||
|  |                          instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||||||
|  |                 } else { | ||||||
|  |                     call(state.call_stack, | ||||||
|  |                          instr.flow_control.dest_offset, | ||||||
|  |                          instr.flow_control.num_instructions, | ||||||
|  |                          instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             case Instruction::OpCode::IFC: |             case Instruction::OpCode::IFC: | ||||||
|  | @ -381,12 +398,15 @@ static void ProcessShaderCode(VertexShaderState& state) { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (results[2]) { |                 if (results[2]) { | ||||||
|                     ++state.if_stack_pointer; |                     call(state.call_stack, | ||||||
| 
 |                          binary_offset + 1, | ||||||
|                     state.if_stack_pointer->else_addr = instr.flow_control.dest_offset; |                          instr.flow_control.dest_offset - binary_offset - 1, | ||||||
|                     state.if_stack_pointer->else_instructions = instr.flow_control.num_instructions; |                          instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||||||
|                 } else { |                 } else { | ||||||
|                     state.program_counter = &shader_memory[instr.flow_control.dest_offset] - 1; |                     call(state.call_stack, | ||||||
|  |                          instr.flow_control.dest_offset, | ||||||
|  |                          instr.flow_control.num_instructions, | ||||||
|  |                          instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 break; |                 break; | ||||||
|  | @ -401,15 +421,7 @@ static void ProcessShaderCode(VertexShaderState& state) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (increment_pc) |         ++state.program_counter; | ||||||
|             ++state.program_counter; |  | ||||||
| 
 |  | ||||||
|         if (state.if_stack_pointer >= &state.if_stack[0]) { |  | ||||||
|             if (state.program_counter - shader_memory.data() == state.if_stack_pointer->else_addr) { |  | ||||||
|                 state.program_counter += state.if_stack_pointer->else_instructions; |  | ||||||
|                 state.if_stack_pointer--; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if (exit_loop) |         if (exit_loop) | ||||||
|             break; |             break; | ||||||
|  | @ -462,12 +474,6 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) | ||||||
| 
 | 
 | ||||||
|     state.conditional_code[0] = false; |     state.conditional_code[0] = false; | ||||||
|     state.conditional_code[1] = false; |     state.conditional_code[1] = false; | ||||||
|     boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS); |  | ||||||
|     state.call_stack_pointer = &state.call_stack[0]; |  | ||||||
| 
 |  | ||||||
|     std::fill(state.if_stack, state.if_stack + sizeof(state.if_stack) / sizeof(state.if_stack[0]), |  | ||||||
|               VertexShaderState::IfStackElement{VertexShaderState::INVALID_ADDRESS, VertexShaderState::INVALID_ADDRESS}); |  | ||||||
|     state.if_stack_pointer = state.if_stack - 1; // Meh. TODO: Make this less ugly
 |  | ||||||
| 
 | 
 | ||||||
|     ProcessShaderCode(state); |     ProcessShaderCode(state); | ||||||
|     DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(), |     DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(), | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue