diff --git a/include/PICA/shader.hpp b/include/PICA/shader.hpp index 7136312f..195733e0 100644 --- a/include/PICA/shader.hpp +++ b/include/PICA/shader.hpp @@ -17,7 +17,8 @@ namespace ShaderOpcodes { MUL = 0x08, MOVA = 0x12, MOV = 0x13, - END = 0x22 + END = 0x22, + LOOP = 0x29 }; } @@ -25,6 +26,13 @@ class PICAShader { using f24 = Floats::f24; using vec4f = OpenGL::Vector; + struct Loop { + u32 startingPC; // PC at the start of the loop + u32 endingPC; // PC at the end of the loop + u32 iterations; // How many iterations of the loop to run + u32 increment; // How much to increment the loop counter after each iteration + }; + int bufferIndex; // Index of the next instruction to overwrite for shader uploads int opDescriptorIndex; // Index of the next operand descriptor we'll overwrite u32 floatUniformIndex = 0; // Which float uniform are we writing to? ([0, 95] range) @@ -35,9 +43,12 @@ class PICAShader { std::array operandDescriptors; std::array tempRegisters; // General purpose registers the shader can use for temp values OpenGL::Vector addrRegister; // Address register - u32 pc = 0; // Program counter: Index of the next instruction we're going to execute - u32 loopCounter; + + u32 pc = 0; // Program counter: Index of the next instruction we're going to execute + u32 loopIndex = 0; // The index of our loop stack (0 = empty, 4 = full) + std::array loopInfo; + ShaderType type; vec4f getSource(u32 source); @@ -46,6 +57,7 @@ class PICAShader { // Shader opcodes void add(u32 instruction); void dp4(u32 instruction); + void loop(u32 instruction); void mov(u32 instruction); void mova(u32 instruction); void mul(u32 instruction); diff --git a/src/core/PICA/shader_interpreter.cpp b/src/core/PICA/shader_interpreter.cpp index b1b1b6d7..af75f47e 100644 --- a/src/core/PICA/shader_interpreter.cpp +++ b/src/core/PICA/shader_interpreter.cpp @@ -11,11 +11,25 @@ void PICAShader::run() { case ShaderOpcodes::ADD: add(instruction); break; case ShaderOpcodes::DP4: dp4(instruction); break; case ShaderOpcodes::END: return; // Stop running shader + case ShaderOpcodes::LOOP: loop(instruction); break; case ShaderOpcodes::MOV: mov(instruction); break; case ShaderOpcodes::MOVA: mova(instruction); break; case ShaderOpcodes::MUL: mul(instruction); break; default:Helpers::panic("Unimplemented PICA instruction %08X (Opcode = %02X)", instruction, opcode); } + + // Handle loop + if (loopIndex != 0) { + auto& loop = loopInfo[loopIndex - 1]; + if (pc == loop.endingPC) { // Check if the loop needs to start over + loop.iterations -= 1; + if (loop.iterations == 0) // If the loop ended, go one level down on the loop stack + loopIndex -= 1; + + loopCounter += loop.increment; + pc = loop.startingPC; + } + } } } @@ -153,4 +167,19 @@ void PICAShader::dp4(u32 instruction) { destVector[3 - i] = dot; } } +} + +void PICAShader::loop(u32 instruction) { + if (loopIndex >= 4) [[unlikely]] + Helpers::panic("[PICA] Overflowed loop stack"); + + u32 dest = (instruction >> 10) & 0xfff; + auto& uniform = intUniforms[(instruction >> 22) & 3]; // The uniform we'll get loop info from + loopCounter = uniform.y(); + auto& loop = loopInfo[loopIndex++]; + + loop.startingPC = pc; + loop.endingPC = dest + 1; // Loop is inclusive so we need + 1 here + loop.iterations = uniform.x() + 1; + loop.increment = uniform.z(); } \ No newline at end of file