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