diff options
Diffstat (limited to '')
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 90 |
1 files changed, 79 insertions, 11 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index ac6ccfec7..aeb908744 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -26,6 +26,7 @@ using Tegra::Shader::Sampler; using Tegra::Shader::SubOp; constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; +constexpr u32 PROGRAM_HEADER_SIZE = 0x50; class DecompileFail : public std::runtime_error { public: @@ -621,6 +622,23 @@ public: } private: + // Shader program header for a Fragment Shader. + struct FragmentHeader { + INSERT_PADDING_WORDS(5); + INSERT_PADDING_WORDS(13); + u32 enabled_color_outputs; + union { + BitField<0, 1, u32> writes_samplemask; + BitField<1, 1, u32> writes_depth; + }; + + bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { + u32 bit = render_target * 4 + component; + return enabled_color_outputs & (1 << bit); + } + }; + static_assert(sizeof(FragmentHeader) == PROGRAM_HEADER_SIZE, "FragmentHeader size is wrong"); + /// Gets the Subroutine object corresponding to the specified address. const Subroutine& GetSubroutine(u32 begin, u32 end) const { auto iter = subroutines.find(Subroutine{begin, end, suffix}); @@ -894,6 +912,36 @@ private: shader.AddLine('}'); } + /// Writes the output values from a fragment shader to the corresponding GLSL output variables. + void EmitFragmentOutputsWrite() { + ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); + FragmentHeader header; + std::memcpy(&header, program_code.data(), PROGRAM_HEADER_SIZE); + + ASSERT_MSG(header.writes_samplemask == 0, "Samplemask write is unimplemented"); + + // Write the color outputs using the data in the shader registers, disabled + // rendertargets/components are skipped in the register assignment. + u32 current_reg = 0; + for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; + ++render_target) { + // TODO(Subv): Figure out how dual-source blending is configured in the Switch. + for (u32 component = 0; component < 4; ++component) { + if (header.IsColorComponentOutputEnabled(render_target, component)) { + shader.AddLine(fmt::format("color[{}][{}] = {};", render_target, component, + regs.GetRegisterAsFloat(current_reg))); + ++current_reg; + } + } + } + + if (header.writes_depth) { + // The depth output is always 2 registers after the last color output, and current_reg + // already contains one past the last color register. + shader.AddLine("gl_FragDepth = " + regs.GetRegisterAsFloat(current_reg + 1) + ';'); + } + } + /** * Compiles a single instruction from Tegra to GLSL. * @param offset the offset of the Tegra shader instruction. @@ -1514,7 +1562,7 @@ private: switch (instr.tex.texture_type) { case Tegra::Shader::TextureType::Texture2D: { std::string x = regs.GetRegisterAsFloat(instr.gpr8); - std::string y = regs.GetRegisterAsFloat(instr.gpr20); + std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); coord = "vec2 coords = vec2(" + x + ", " + y + ");"; break; } @@ -1525,8 +1573,18 @@ private: coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; break; } + case Tegra::Shader::TextureType::TextureCube: { + std::string x = regs.GetRegisterAsFloat(instr.gpr8); + std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); + ASSERT(instr.gpr20.Value() == Register::ZeroIndex); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + break; + } default: - UNIMPLEMENTED(); + LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", + static_cast<u32>(instr.tex.texture_type.Value())); + UNREACHABLE(); } const std::string sampler = @@ -1568,6 +1626,13 @@ private: } break; } + case Tegra::Shader::TextureType::Texture3D: { + std::string x = regs.GetRegisterAsFloat(instr.gpr8); + std::string y = regs.GetRegisterAsFloat(instr.gpr20); + std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + break; + } case Tegra::Shader::TextureType::TextureCube: { std::string x = regs.GetRegisterAsFloat(instr.gpr8); std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); @@ -1576,7 +1641,9 @@ private: break; } default: - UNIMPLEMENTED(); + LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", + static_cast<u32>(instr.texs.GetTextureType())); + UNREACHABLE(); } const std::string sampler = GetSampler(instr.sampler, instr.texs.GetTextureType(), instr.texs.IsArrayTexture()); @@ -1593,7 +1660,8 @@ private: switch (instr.tlds.GetTextureType()) { case Tegra::Shader::TextureType::Texture2D: { if (instr.tlds.IsArrayTexture()) { - UNIMPLEMENTED(); + LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); + UNREACHABLE(); } else { std::string x = regs.GetRegisterAsInteger(instr.gpr8); std::string y = regs.GetRegisterAsInteger(instr.gpr20); @@ -1602,7 +1670,9 @@ private: break; } default: - UNIMPLEMENTED(); + LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", + static_cast<u32>(instr.tlds.GetTextureType())); + UNREACHABLE(); } const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(), instr.tlds.IsArrayTexture()); @@ -1623,7 +1693,9 @@ private: break; } default: - UNIMPLEMENTED(); + LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", + static_cast<u32>(instr.tld4.texture_type.Value())); + UNREACHABLE(); } const std::string sampler = @@ -1960,12 +2032,8 @@ private: default: { switch (opcode->GetId()) { case OpCode::Id::EXIT: { - // Final color output is currently hardcoded to GPR0-3 for fragment shaders if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { - shader.AddLine("color.r = " + regs.GetRegisterAsFloat(0) + ';'); - shader.AddLine("color.g = " + regs.GetRegisterAsFloat(1) + ';'); - shader.AddLine("color.b = " + regs.GetRegisterAsFloat(2) + ';'); - shader.AddLine("color.a = " + regs.GetRegisterAsFloat(3) + ';'); + EmitFragmentOutputsWrite(); } switch (instr.flow.cond) { |