diff options
| author | 2014-12-13 21:30:13 +0100 | |
|---|---|---|
| committer | 2014-12-20 18:06:55 +0100 | |
| commit | 6bd41de276a97fee1d4f07789a33ff49d494a20d (patch) | |
| tree | 97d0e3afb45f0ccee1b8b30058e7f879807f710d /src/video_core/vertex_shader.cpp | |
| parent | Pica/VertexShader: Run instruction handlers according to the effective opcode. (diff) | |
| download | yuzu-6bd41de276a97fee1d4f07789a33ff49d494a20d.tar.gz yuzu-6bd41de276a97fee1d4f07789a33ff49d494a20d.tar.xz yuzu-6bd41de276a97fee1d4f07789a33ff49d494a20d.zip | |
Pica/VertexShader: Cleanup flow control logic and implement CMP/IFU instructions.
Diffstat (limited to 'src/video_core/vertex_shader.cpp')
| -rw-r--r-- | src/video_core/vertex_shader.cpp | 106 |
1 files changed, 56 insertions, 50 deletions
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index dd406f9ca..af9332975 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <stack> | ||
| 6 | |||
| 5 | #include <boost/range/algorithm.hpp> | 7 | #include <boost/range/algorithm.hpp> |
| 6 | 8 | ||
| 7 | #include <common/file_util.h> | 9 | #include <common/file_util.h> |
| @@ -65,9 +67,6 @@ const std::array<u32, 1024>& GetSwizzlePatterns() | |||
| 65 | return swizzle_data; | 67 | return swizzle_data; |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | // TODO: Is there actually a limit on hardware? | ||
| 69 | const int if_stack_size = 8; | ||
| 70 | |||
| 71 | struct VertexShaderState { | 70 | struct VertexShaderState { |
| 72 | u32* program_counter; | 71 | u32* program_counter; |
| 73 | 72 | ||
| @@ -84,14 +83,14 @@ struct VertexShaderState { | |||
| 84 | enum { | 83 | enum { |
| 85 | INVALID_ADDRESS = 0xFFFFFFFF | 84 | INVALID_ADDRESS = 0xFFFFFFFF |
| 86 | }; | 85 | }; |
| 87 | u32 call_stack[8]; // TODO: What is the maximal call stack depth? | ||
| 88 | u32* call_stack_pointer; | ||
| 89 | 86 | ||
| 90 | struct IfStackElement { | 87 | struct CallStackElement { |
| 91 | u32 else_addr; | 88 | u32 final_address; |
| 92 | u32 else_instructions; | 89 | u32 return_address; |
| 93 | } if_stack[if_stack_size]; | 90 | }; |
| 94 | IfStackElement* if_stack_pointer; | 91 | |
| 92 | // TODO: Is there a maximal size for this? | ||
| 93 | std::stack<CallStackElement> call_stack; | ||
| 95 | 94 | ||
| 96 | struct { | 95 | struct { |
| 97 | u32 max_offset; // maximum program counter ever reached | 96 | u32 max_offset; // maximum program counter ever reached |
| @@ -101,12 +100,27 @@ struct VertexShaderState { | |||
| 101 | 100 | ||
| 102 | static void ProcessShaderCode(VertexShaderState& state) { | 101 | static void ProcessShaderCode(VertexShaderState& state) { |
| 103 | while (true) { | 102 | while (true) { |
| 104 | bool increment_pc = true; | 103 | if (!state.call_stack.empty()) { |
| 104 | if (state.program_counter - shader_memory.data() == state.call_stack.top().final_address) { | ||
| 105 | state.program_counter = &shader_memory[state.call_stack.top().return_address]; | ||
| 106 | state.call_stack.pop(); | ||
| 107 | |||
| 108 | // TODO: Is "trying again" accurate to hardware? | ||
| 109 | continue; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 105 | bool exit_loop = false; | 113 | bool exit_loop = false; |
| 106 | const Instruction& instr = *(const Instruction*)state.program_counter; | 114 | const Instruction& instr = *(const Instruction*)state.program_counter; |
| 107 | const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; | 115 | const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; |
| 108 | 116 | ||
| 109 | state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + (state.program_counter - shader_memory.data())); | 117 | auto call = [&](std::stack<VertexShaderState::CallStackElement>& stack, u32 offset, u32 num_instructions, u32 return_offset) { |
| 118 | state.program_counter = &shader_memory[offset] - 1; // -1 to make sure when incrementing the PC we end up at the correct offset | ||
| 119 | stack.push({ offset + num_instructions, return_offset }); | ||
| 120 | }; | ||
| 121 | u32 binary_offset = state.program_counter - shader_memory.data(); | ||
| 122 | |||
| 123 | state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + binary_offset); | ||
| 110 | 124 | ||
| 111 | auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { | 125 | auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { |
| 112 | switch (source_reg.GetRegisterType()) { | 126 | switch (source_reg.GetRegisterType()) { |
| @@ -328,30 +342,33 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 328 | default: | 342 | default: |
| 329 | // Handle each instruction on its own | 343 | // Handle each instruction on its own |
| 330 | switch (instr.opcode) { | 344 | switch (instr.opcode) { |
| 331 | // NOP is currently used as a heuristic for leaving from a function. | 345 | case Instruction::OpCode::END: |
| 332 | // TODO: This is completely incorrect. | 346 | exit_loop = true; |
| 333 | case Instruction::OpCode::NOP: | ||
| 334 | if (*state.call_stack_pointer == VertexShaderState::INVALID_ADDRESS) { | ||
| 335 | exit_loop = true; | ||
| 336 | } else { | ||
| 337 | // Jump back to call stack position, invalidate call stack entry, move up call stack pointer | ||
| 338 | state.program_counter = &shader_memory[*state.call_stack_pointer]; | ||
| 339 | *state.call_stack_pointer-- = VertexShaderState::INVALID_ADDRESS; | ||
| 340 | } | ||
| 341 | |||
| 342 | break; | 347 | break; |
| 343 | 348 | ||
| 344 | case Instruction::OpCode::CALL: | 349 | case Instruction::OpCode::CALL: |
| 345 | increment_pc = false; | 350 | call(state.call_stack, |
| 346 | 351 | instr.flow_control.dest_offset, | |
| 347 | _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); | 352 | instr.flow_control.num_instructions, |
| 353 | binary_offset + 1); | ||
| 354 | break; | ||
| 348 | 355 | ||
| 349 | *++state.call_stack_pointer = state.program_counter - shader_memory.data(); | 356 | case Instruction::OpCode::NOP: |
| 350 | state.program_counter = &shader_memory[instr.flow_control.dest_offset]; | ||
| 351 | break; | 357 | break; |
| 352 | 358 | ||
| 353 | case Instruction::OpCode::END: | 359 | case Instruction::OpCode::IFU: |
| 354 | // TODO | 360 | if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) { |
| 361 | call(state.call_stack, | ||
| 362 | binary_offset + 1, | ||
| 363 | instr.flow_control.dest_offset - binary_offset - 1, | ||
| 364 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 365 | } else { | ||
| 366 | call(state.call_stack, | ||
| 367 | instr.flow_control.dest_offset, | ||
| 368 | instr.flow_control.num_instructions, | ||
| 369 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 370 | } | ||
| 371 | |||
| 355 | break; | 372 | break; |
| 356 | 373 | ||
| 357 | case Instruction::OpCode::IFC: | 374 | case Instruction::OpCode::IFC: |
| @@ -381,12 +398,15 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 381 | } | 398 | } |
| 382 | 399 | ||
| 383 | if (results[2]) { | 400 | if (results[2]) { |
| 384 | ++state.if_stack_pointer; | 401 | call(state.call_stack, |
| 385 | 402 | binary_offset + 1, | |
| 386 | state.if_stack_pointer->else_addr = instr.flow_control.dest_offset; | 403 | instr.flow_control.dest_offset - binary_offset - 1, |
| 387 | state.if_stack_pointer->else_instructions = instr.flow_control.num_instructions; | 404 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); |
| 388 | } else { | 405 | } else { |
| 389 | state.program_counter = &shader_memory[instr.flow_control.dest_offset] - 1; | 406 | call(state.call_stack, |
| 407 | instr.flow_control.dest_offset, | ||
| 408 | instr.flow_control.num_instructions, | ||
| 409 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 390 | } | 410 | } |
| 391 | 411 | ||
| 392 | break; | 412 | break; |
| @@ -401,15 +421,7 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 401 | break; | 421 | break; |
| 402 | } | 422 | } |
| 403 | 423 | ||
| 404 | if (increment_pc) | 424 | ++state.program_counter; |
| 405 | ++state.program_counter; | ||
| 406 | |||
| 407 | if (state.if_stack_pointer >= &state.if_stack[0]) { | ||
| 408 | if (state.program_counter - shader_memory.data() == state.if_stack_pointer->else_addr) { | ||
| 409 | state.program_counter += state.if_stack_pointer->else_instructions; | ||
| 410 | state.if_stack_pointer--; | ||
| 411 | } | ||
| 412 | } | ||
| 413 | 425 | ||
| 414 | if (exit_loop) | 426 | if (exit_loop) |
| 415 | break; | 427 | break; |
| @@ -462,12 +474,6 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) | |||
| 462 | 474 | ||
| 463 | state.conditional_code[0] = false; | 475 | state.conditional_code[0] = false; |
| 464 | state.conditional_code[1] = false; | 476 | state.conditional_code[1] = false; |
| 465 | boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS); | ||
| 466 | state.call_stack_pointer = &state.call_stack[0]; | ||
| 467 | |||
| 468 | std::fill(state.if_stack, state.if_stack + sizeof(state.if_stack) / sizeof(state.if_stack[0]), | ||
| 469 | VertexShaderState::IfStackElement{VertexShaderState::INVALID_ADDRESS, VertexShaderState::INVALID_ADDRESS}); | ||
| 470 | state.if_stack_pointer = state.if_stack - 1; // Meh. TODO: Make this less ugly | ||
| 471 | 477 | ||
| 472 | ProcessShaderCode(state); | 478 | ProcessShaderCode(state); |
| 473 | DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(), | 479 | DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(), |