|
|
@@ -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() {
|