diff options
Diffstat (limited to 'src/video_core/renderer_opengl')
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 271 |
1 files changed, 242 insertions, 29 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8fa9e6534..2955c6abf 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -20,6 +20,7 @@ #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/shader/node.h" +#include "video_core/shader/ast.h" #include "video_core/shader/shader_ir.h" namespace OpenGL::GLShader { @@ -334,43 +335,26 @@ constexpr bool IsVertexShader(ProgramType stage) { return stage == ProgramType::VertexA || stage == ProgramType::VertexB; } +class ASTDecompiler; +class ExprDecompiler; + class GLSLDecompiler final { public: explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage, std::string suffix) : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} - void Decompile() { - DeclareVertex(); - DeclareGeometry(); - DeclareRegisters(); - DeclarePredicates(); - DeclareLocalMemory(); - DeclareSharedMemory(); - DeclareInternalFlags(); - DeclareInputAttributes(); - DeclareOutputAttributes(); - DeclareConstantBuffers(); - DeclareGlobalMemory(); - DeclareSamplers(); - DeclarePhysicalAttributeReader(); - DeclareImages(); - - code.AddLine("void execute_{}() {{", suffix); - ++code.scope; - + void DecompileBranchMode() { // VM's program counter const auto first_address = ir.GetBasicBlocks().begin()->first; code.AddLine("uint jmp_to = {}U;", first_address); // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems // unlikely that shaders will use 20 nested SSYs and PBKs. - if (!ir.IsFlowStackDisabled()) { - constexpr u32 FLOW_STACK_SIZE = 20; - for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { - code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); - code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); - } + constexpr u32 FLOW_STACK_SIZE = 20; + for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { + code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); + code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); } code.AddLine("while (true) {{"); @@ -392,10 +376,37 @@ public: code.AddLine("default: return;"); code.AddLine("}}"); - for (std::size_t i = 0; i < 2; ++i) { - --code.scope; - code.AddLine("}}"); + --code.scope; + code.AddLine("}}"); + } + + void DecompileAST(); + + void Decompile() { + DeclareVertex(); + DeclareGeometry(); + DeclareRegisters(); + DeclarePredicates(); + DeclareLocalMemory(); + DeclareInternalFlags(); + DeclareInputAttributes(); + DeclareOutputAttributes(); + DeclareConstantBuffers(); + DeclareGlobalMemory(); + DeclareSamplers(); + DeclarePhysicalAttributeReader(); + + code.AddLine("void execute_{}() {{", suffix); + ++code.scope; + + if (ir.IsDecompiled()) { + DecompileAST(); + } else { + DecompileBranchMode(); } + + --code.scope; + code.AddLine("}}"); } std::string GetResult() { @@ -424,6 +435,9 @@ public: } private: + friend class ASTDecompiler; + friend class ExprDecompiler; + void DeclareVertex() { if (!IsVertexShader(stage)) return; @@ -1821,7 +1835,7 @@ private: return {}; } - Expression Exit(Operation operation) { + Expression WriteExit() { if (stage != ProgramType::Fragment) { code.AddLine("return;"); return {}; @@ -1861,6 +1875,10 @@ private: return {}; } + Expression Exit(Operation operation) { + return WriteExit(); + } + Expression Discard(Operation operation) { // Enclose "discard" in a conditional, so that GLSL compilation does not complain // about unexecuted instructions that may follow this. @@ -2253,6 +2271,201 @@ private: ShaderWriter code; }; +const std::string flow_var = "flow_var_"; + +class ExprDecompiler { +public: + ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} + + void operator()(VideoCommon::Shader::ExprAnd& expr) { + inner += "( "; + std::visit(*this, *expr.operand1); + inner += " && "; + std::visit(*this, *expr.operand2); + inner += ')'; + } + + void operator()(VideoCommon::Shader::ExprOr& expr) { + inner += "( "; + std::visit(*this, *expr.operand1); + inner += " || "; + std::visit(*this, *expr.operand2); + inner += ')'; + } + + void operator()(VideoCommon::Shader::ExprNot& expr) { + inner += '!'; + std::visit(*this, *expr.operand1); + } + + void operator()(VideoCommon::Shader::ExprPredicate& expr) { + auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate); + inner += decomp.GetPredicate(pred); + } + + void operator()(VideoCommon::Shader::ExprCondCode& expr) { + Node cc = decomp.ir.GetConditionCode(expr.cc); + std::string target; + + if (const auto pred = std::get_if<PredicateNode>(&*cc)) { + const auto index = pred->GetIndex(); + switch (index) { + case Tegra::Shader::Pred::NeverExecute: + target = "false"; + case Tegra::Shader::Pred::UnusedIndex: + target = "true"; + default: + target = decomp.GetPredicate(index); + } + } else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) { + target = decomp.GetInternalFlag(flag->GetFlag()); + } + inner += target; + } + + void operator()(VideoCommon::Shader::ExprVar& expr) { + inner += flow_var + std::to_string(expr.var_index); + } + + void operator()(VideoCommon::Shader::ExprBoolean& expr) { + inner += expr.value ? "true" : "false"; + } + + std::string& GetResult() { + return inner; + } + +private: + std::string inner{}; + GLSLDecompiler& decomp; +}; + +class ASTDecompiler { +public: + ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} + + void operator()(VideoCommon::Shader::ASTProgram& ast) { + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); + } + } + + void operator()(VideoCommon::Shader::ASTIfThen& ast) { + ExprDecompiler expr_parser{decomp}; + std::visit(expr_parser, *ast.condition); + decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); + decomp.code.scope++; + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); + } + decomp.code.scope--; + decomp.code.AddLine("}}"); + } + + void operator()(VideoCommon::Shader::ASTIfElse& ast) { + decomp.code.AddLine("else {{"); + decomp.code.scope++; + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); + } + decomp.code.scope--; + decomp.code.AddLine("}}"); + } + + void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) { + UNREACHABLE(); + } + + void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) { + decomp.VisitBlock(ast.nodes); + } + + void operator()(VideoCommon::Shader::ASTVarSet& ast) { + ExprDecompiler expr_parser{decomp}; + std::visit(expr_parser, *ast.condition); + decomp.code.AddLine("{}{} = {};", flow_var, ast.index, expr_parser.GetResult()); + } + + void operator()(VideoCommon::Shader::ASTLabel& ast) { + decomp.code.AddLine("// Label_{}:", ast.index); + } + + void operator()(VideoCommon::Shader::ASTGoto& ast) { + UNREACHABLE(); + } + + void operator()(VideoCommon::Shader::ASTDoWhile& ast) { + ExprDecompiler expr_parser{decomp}; + std::visit(expr_parser, *ast.condition); + decomp.code.AddLine("do {{"); + decomp.code.scope++; + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); + } + decomp.code.scope--; + decomp.code.AddLine("}} while({});", expr_parser.GetResult()); + } + + void operator()(VideoCommon::Shader::ASTReturn& ast) { + bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); + if (!is_true) { + ExprDecompiler expr_parser{decomp}; + std::visit(expr_parser, *ast.condition); + decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); + decomp.code.scope++; + } + if (ast.kills) { + decomp.code.AddLine("discard;"); + } else { + decomp.WriteExit(); + } + if (!is_true) { + decomp.code.scope--; + decomp.code.AddLine("}}"); + } + } + + void operator()(VideoCommon::Shader::ASTBreak& ast) { + bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); + if (!is_true) { + ExprDecompiler expr_parser{decomp}; + std::visit(expr_parser, *ast.condition); + decomp.code.AddLine("if ({}) {{", expr_parser.GetResult()); + decomp.code.scope++; + } + decomp.code.AddLine("break;"); + if (!is_true) { + decomp.code.scope--; + decomp.code.AddLine("}}"); + } + } + + void Visit(VideoCommon::Shader::ASTNode& node) { + std::visit(*this, *node->GetInnerData()); + } + +private: + GLSLDecompiler& decomp; +}; + +void GLSLDecompiler::DecompileAST() { + u32 num_flow_variables = ir.GetASTNumVariables(); + for (u32 i = 0; i < num_flow_variables; i++) { + code.AddLine("bool {}{} = false;", flow_var, i); + } + ASTDecompiler decompiler{*this}; + VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); + decompiler.Visit(program); +} + } // Anonymous namespace std::string GetCommonDeclarations() { |