summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp446
1 files changed, 308 insertions, 138 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 74cb59bc1..6a610a3bc 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -19,6 +19,8 @@
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
+#include "video_core/shader/ast.h"
+#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
namespace OpenGL::GLShader {
@@ -241,6 +243,26 @@ constexpr const char* GetTypeString(Type type) {
}
}
+constexpr const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
+ switch (image_type) {
+ case Tegra::Shader::ImageType::Texture1D:
+ return "1D";
+ case Tegra::Shader::ImageType::TextureBuffer:
+ return "Buffer";
+ case Tegra::Shader::ImageType::Texture1DArray:
+ return "1DArray";
+ case Tegra::Shader::ImageType::Texture2D:
+ return "2D";
+ case Tegra::Shader::ImageType::Texture2DArray:
+ return "2DArray";
+ case Tegra::Shader::ImageType::Texture3D:
+ return "3D";
+ default:
+ UNREACHABLE();
+ return "1D";
+ }
+}
+
/// Generates code to use for a swizzle operation.
constexpr const char* GetSwizzle(u32 element) {
constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
@@ -313,39 +335,24 @@ 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.
+ constexpr u32 FLOW_STACK_SIZE = 20;
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));
@@ -371,10 +378,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() {
@@ -398,13 +432,14 @@ public:
usage.is_read, usage.is_written);
}
entries.clip_distances = ir.GetClipDistances();
- entries.shader_viewport_layer_array =
- IsVertexShader(stage) && (ir.UsesLayer() || ir.UsesViewportIndex());
entries.shader_length = ir.GetLength();
return entries;
}
private:
+ friend class ASTDecompiler;
+ friend class ExprDecompiler;
+
void DeclareVertex() {
if (!IsVertexShader(stage))
return;
@@ -722,42 +757,6 @@ private:
void DeclareImages() {
const auto& images{ir.GetImages()};
for (const auto& [offset, image] : images) {
- const char* image_type = [&] {
- switch (image.GetType()) {
- case Tegra::Shader::ImageType::Texture1D:
- return "image1D";
- case Tegra::Shader::ImageType::TextureBuffer:
- return "imageBuffer";
- case Tegra::Shader::ImageType::Texture1DArray:
- return "image1DArray";
- case Tegra::Shader::ImageType::Texture2D:
- return "image2D";
- case Tegra::Shader::ImageType::Texture2DArray:
- return "image2DArray";
- case Tegra::Shader::ImageType::Texture3D:
- return "image3D";
- default:
- UNREACHABLE();
- return "image1D";
- }
- }();
-
- const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
- if (!image.IsSizeKnown()) {
- return {"", ""};
- }
- switch (image.GetSize()) {
- case Tegra::Shader::ImageAtomicSize::U32:
- return {"u", "r32ui, "};
- case Tegra::Shader::ImageAtomicSize::S32:
- return {"i", "r32i, "};
- default:
- UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
- static_cast<u32>(image.GetSize()));
- return {"", ""};
- }
- }();
-
std::string qualifier = "coherent volatile";
if (image.IsRead() && !image.IsWritten()) {
qualifier += " readonly";
@@ -765,9 +764,10 @@ private:
qualifier += " writeonly";
}
- code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform "
- "{} {};",
- image.GetIndex(), qualifier, image_type, GetImage(image));
+ const char* format = image.IsAtomic() ? "r32ui, " : "";
+ const char* type_declaration = GetImageTypeDeclaration(image.GetType());
+ code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format,
+ image.GetIndex(), qualifier, type_declaration, GetImage(image));
}
if (!images.empty()) {
code.AddNewLine();
@@ -1234,28 +1234,13 @@ private:
}
std::string BuildImageValues(Operation operation) {
+ constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"};
const auto meta{std::get<MetaImage>(operation.GetMeta())};
- const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
- constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
- if (!meta.image.IsSizeKnown()) {
- return {float_constructors, Type::Float};
- }
- switch (meta.image.GetSize()) {
- case Tegra::Shader::ImageAtomicSize::U32:
- return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
- case Tegra::Shader::ImageAtomicSize::S32:
- return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
- default:
- UNIMPLEMENTED_MSG("Unimplemented image size={}",
- static_cast<u32>(meta.image.GetSize()));
- return {float_constructors, Type::Float};
- }
- }();
const std::size_t values_count{meta.values.size()};
std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
for (std::size_t i = 0; i < values_count; ++i) {
- expr += Visit(meta.values.at(i)).As(type);
+ expr += Visit(meta.values.at(i)).AsUint();
if (i + 1 < values_count) {
expr += ", ";
}
@@ -1264,29 +1249,6 @@ private:
return expr;
}
- Expression AtomicImage(Operation operation, const char* opname) {
- constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
- const auto meta{std::get<MetaImage>(operation.GetMeta())};
- ASSERT(meta.values.size() == 1);
- ASSERT(meta.image.IsSizeKnown());
-
- const auto type = [&]() {
- switch (const auto size = meta.image.GetSize()) {
- case Tegra::Shader::ImageAtomicSize::U32:
- return Type::Uint;
- case Tegra::Shader::ImageAtomicSize::S32:
- return Type::Int;
- default:
- UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
- return Type::Uint;
- }
- }();
-
- return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
- BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
- type};
- }
-
Expression Assign(Operation operation) {
const Node& dest = operation[0];
const Node& src = operation[1];
@@ -1545,6 +1507,8 @@ private:
case Tegra::Shader::HalfType::H1_H1:
return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat};
}
+ UNREACHABLE();
+ return {"0", Type::Int};
}
Expression HMergeF32(Operation operation) {
@@ -1809,6 +1773,19 @@ private:
return {tmp, Type::Float};
}
+ Expression ImageLoad(Operation operation) {
+ if (!device.HasImageLoadFormatted()) {
+ LOG_ERROR(Render_OpenGL,
+ "Device lacks GL_EXT_shader_image_load_formatted, stubbing image load");
+ return {"0", Type::Int};
+ }
+
+ const auto meta{std::get<MetaImage>(operation.GetMeta())};
+ return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image),
+ BuildIntegerCoordinates(operation), GetSwizzle(meta.element)),
+ Type::Uint};
+ }
+
Expression ImageStore(Operation operation) {
const auto meta{std::get<MetaImage>(operation.GetMeta())};
code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
@@ -1816,31 +1793,14 @@ private:
return {};
}
- Expression AtomicImageAdd(Operation operation) {
- return AtomicImage(operation, "imageAtomicAdd");
- }
-
- Expression AtomicImageMin(Operation operation) {
- return AtomicImage(operation, "imageAtomicMin");
- }
-
- Expression AtomicImageMax(Operation operation) {
- return AtomicImage(operation, "imageAtomicMax");
- }
- Expression AtomicImageAnd(Operation operation) {
- return AtomicImage(operation, "imageAtomicAnd");
- }
-
- Expression AtomicImageOr(Operation operation) {
- return AtomicImage(operation, "imageAtomicOr");
- }
-
- Expression AtomicImageXor(Operation operation) {
- return AtomicImage(operation, "imageAtomicXor");
- }
+ template <const std::string_view& opname>
+ Expression AtomicImage(Operation operation) {
+ const auto meta{std::get<MetaImage>(operation.GetMeta())};
+ ASSERT(meta.values.size() == 1);
- Expression AtomicImageExchange(Operation operation) {
- return AtomicImage(operation, "imageAtomicExchange");
+ return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image),
+ BuildIntegerCoordinates(operation), Visit(meta.values[0]).AsUint()),
+ Type::Uint};
}
Expression Branch(Operation operation) {
@@ -1877,10 +1837,9 @@ private:
return {};
}
- Expression Exit(Operation operation) {
+ void PreExit() {
if (stage != ProgramType::Fragment) {
- code.AddLine("return;");
- return {};
+ return;
}
const auto& used_registers = ir.GetRegisters();
const auto SafeGetRegister = [&](u32 reg) -> Expression {
@@ -1912,7 +1871,10 @@ private:
// already contains one past the last color register.
code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat());
}
+ }
+ Expression Exit(Operation operation) {
+ PreExit();
code.AddLine("return;");
return {};
}
@@ -2035,6 +1997,12 @@ private:
Func() = delete;
~Func() = delete;
+ static constexpr std::string_view Add = "Add";
+ static constexpr std::string_view And = "And";
+ static constexpr std::string_view Or = "Or";
+ static constexpr std::string_view Xor = "Xor";
+ static constexpr std::string_view Exchange = "Exchange";
+
static constexpr std::string_view ShuffleIndexed = "shuffleNV";
static constexpr std::string_view ShuffleUp = "shuffleUpNV";
static constexpr std::string_view ShuffleDown = "shuffleDownNV";
@@ -2172,14 +2140,14 @@ private:
&GLSLDecompiler::TextureQueryLod,
&GLSLDecompiler::TexelFetch,
+ &GLSLDecompiler::ImageLoad,
&GLSLDecompiler::ImageStore,
- &GLSLDecompiler::AtomicImageAdd,
- &GLSLDecompiler::AtomicImageMin,
- &GLSLDecompiler::AtomicImageMax,
- &GLSLDecompiler::AtomicImageAnd,
- &GLSLDecompiler::AtomicImageOr,
- &GLSLDecompiler::AtomicImageXor,
- &GLSLDecompiler::AtomicImageExchange,
+
+ &GLSLDecompiler::AtomicImage<Func::Add>,
+ &GLSLDecompiler::AtomicImage<Func::And>,
+ &GLSLDecompiler::AtomicImage<Func::Or>,
+ &GLSLDecompiler::AtomicImage<Func::Xor>,
+ &GLSLDecompiler::AtomicImage<Func::Exchange>,
&GLSLDecompiler::Branch,
&GLSLDecompiler::BranchIndirect,
@@ -2303,6 +2271,208 @@ private:
ShaderWriter code;
};
+static constexpr std::string_view flow_var = "flow_var_";
+
+std::string GetFlowVariable(u32 i) {
+ return fmt::format("{}{}", flow_var, i);
+}
+
+class ExprDecompiler {
+public:
+ explicit 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) {
+ const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
+ inner += decomp.GetPredicate(pred);
+ }
+
+ void operator()(VideoCommon::Shader::ExprCondCode& expr) {
+ const 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());
+ } else {
+ UNREACHABLE();
+ }
+ inner += target;
+ }
+
+ void operator()(VideoCommon::Shader::ExprVar& expr) {
+ inner += GetFlowVariable(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:
+ explicit 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("{} = {};", GetFlowVariable(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) {
+ const 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.PreExit();
+ decomp.code.AddLine("return;");
+ }
+ if (!is_true) {
+ decomp.code.scope--;
+ decomp.code.AddLine("}}");
+ }
+ }
+
+ void operator()(VideoCommon::Shader::ASTBreak& ast) {
+ const 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() {
+ const u32 num_flow_variables = ir.GetASTNumVariables();
+ for (u32 i = 0; i < num_flow_variables; i++) {
+ code.AddLine("bool {} = false;", GetFlowVariable(i));
+ }
+ ASTDecompiler decompiler{*this};
+ VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
+ decompiler.Visit(program);
+}
+
} // Anonymous namespace
std::string GetCommonDeclarations() {