summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/renderer_opengl/gl_shader_decompiler.cpp')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp176
1 files changed, 64 insertions, 112 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index f7b77711a..3d3cd21f3 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -16,6 +16,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "video_core/engines/maxwell_3d.h"
+#include "video_core/engines/shader_type.h"
#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"
@@ -27,6 +28,7 @@ namespace OpenGL::GLShader {
namespace {
+using Tegra::Engines::ShaderType;
using Tegra::Shader::Attribute;
using Tegra::Shader::AttributeUse;
using Tegra::Shader::Header;
@@ -41,6 +43,9 @@ using namespace VideoCommon::Shader;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using Operation = const OperationNode&;
+class ASTDecompiler;
+class ExprDecompiler;
+
enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
struct TextureAoffi {};
@@ -224,7 +229,7 @@ private:
Type type{};
};
-constexpr const char* GetTypeString(Type type) {
+const char* GetTypeString(Type type) {
switch (type) {
case Type::Bool:
return "bool";
@@ -244,7 +249,7 @@ constexpr const char* GetTypeString(Type type) {
}
}
-constexpr const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
+const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
switch (image_type) {
case Tegra::Shader::ImageType::Texture1D:
return "1D";
@@ -332,16 +337,13 @@ std::string FlowStackTopName(MetaStackClass stack) {
return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack));
}
-constexpr bool IsVertexShader(ProgramType stage) {
- return stage == ProgramType::VertexA || stage == ProgramType::VertexB;
+[[deprecated]] constexpr bool IsVertexShader(ShaderType stage) {
+ return stage == ShaderType::Vertex;
}
-class ASTDecompiler;
-class ExprDecompiler;
-
class GLSLDecompiler final {
public:
- explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage,
+ explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderType stage,
std::string suffix)
: device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
@@ -428,7 +430,7 @@ private:
}
void DeclareGeometry() {
- if (stage != ProgramType::Geometry) {
+ if (stage != ShaderType::Geometry) {
return;
}
@@ -511,10 +513,14 @@ private:
}
void DeclareLocalMemory() {
- // TODO(Rodrigo): Unstub kernel local memory size and pass it from a register at
- // specialization time.
- const u64 local_memory_size =
- stage == ProgramType::Compute ? 0x400 : header.GetLocalMemorySize();
+ if (stage == ShaderType::Compute) {
+ code.AddLine("#ifdef LOCAL_MEMORY_SIZE");
+ code.AddLine("uint {}[LOCAL_MEMORY_SIZE];", GetLocalMemory());
+ code.AddLine("#endif");
+ return;
+ }
+
+ const u64 local_memory_size = header.GetLocalMemorySize();
if (local_memory_size == 0) {
return;
}
@@ -523,13 +529,6 @@ private:
code.AddNewLine();
}
- void DeclareSharedMemory() {
- if (stage != ProgramType::Compute) {
- return;
- }
- code.AddLine("shared uint {}[];", GetSharedMemory());
- }
-
void DeclareInternalFlags() {
for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
const auto flag_code = static_cast<InternalFlag>(flag);
@@ -579,12 +578,12 @@ private:
const u32 location{GetGenericAttributeIndex(index)};
std::string name{GetInputAttribute(index)};
- if (stage == ProgramType::Geometry) {
+ if (stage == ShaderType::Geometry) {
name = "gs_" + name + "[]";
}
std::string suffix;
- if (stage == ProgramType::Fragment) {
+ if (stage == ShaderType::Fragment) {
const auto input_mode{header.ps.GetAttributeUse(location)};
if (skip_unused && input_mode == AttributeUse::Unused) {
return;
@@ -596,7 +595,7 @@ private:
}
void DeclareOutputAttributes() {
- if (ir.HasPhysicalAttributes() && stage != ProgramType::Fragment) {
+ if (ir.HasPhysicalAttributes() && stage != ShaderType::Fragment) {
for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
DeclareOutputAttribute(ToGenericAttribute(i));
}
@@ -621,9 +620,9 @@ private:
}
void DeclareConstantBuffers() {
- for (const auto& entry : ir.GetConstantBuffers()) {
- const auto [index, size] = entry;
- code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index,
+ u32 binding = device.GetBaseBindings(stage).uniform_buffer;
+ for (const auto& [index, cbuf] : ir.GetConstantBuffers()) {
+ code.AddLine("layout (std140, binding = {}) uniform {} {{", binding++,
GetConstBufferBlock(index));
code.AddLine(" uvec4 {}[{}];", GetConstBuffer(index), MAX_CONSTBUFFER_ELEMENTS);
code.AddLine("}};");
@@ -632,9 +631,8 @@ private:
}
void DeclareGlobalMemory() {
- for (const auto& gmem : ir.GetGlobalMemory()) {
- const auto& [base, usage] = gmem;
-
+ u32 binding = device.GetBaseBindings(stage).shader_storage_buffer;
+ for (const auto& [base, usage] : ir.GetGlobalMemory()) {
// Since we don't know how the shader will use the shader, hint the driver to disable as
// much optimizations as possible
std::string qualifier = "coherent volatile";
@@ -644,8 +642,8 @@ private:
qualifier += " writeonly";
}
- code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{",
- base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base));
+ code.AddLine("layout (std430, binding = {}) {} buffer {} {{", binding++, qualifier,
+ GetGlobalMemoryBlock(base));
code.AddLine(" uint {}[];", GetGlobalMemory(base));
code.AddLine("}};");
code.AddNewLine();
@@ -653,15 +651,17 @@ private:
}
void DeclareSamplers() {
- const auto& samplers = ir.GetSamplers();
- for (const auto& sampler : samplers) {
- const std::string name{GetSampler(sampler)};
- const std::string description{"layout (binding = SAMPLER_BINDING_" +
- std::to_string(sampler.GetIndex()) + ") uniform"};
+ u32 binding = device.GetBaseBindings(stage).sampler;
+ for (const auto& sampler : ir.GetSamplers()) {
+ const std::string name = GetSampler(sampler);
+ const std::string description = fmt::format("layout (binding = {}) uniform", binding++);
+
std::string sampler_type = [&]() {
+ if (sampler.IsBuffer()) {
+ return "samplerBuffer";
+ }
switch (sampler.GetType()) {
case Tegra::Shader::TextureType::Texture1D:
- // Special cased, read below.
return "sampler1D";
case Tegra::Shader::TextureType::Texture2D:
return "sampler2D";
@@ -681,21 +681,9 @@ private:
sampler_type += "Shadow";
}
- if (sampler.GetType() == Tegra::Shader::TextureType::Texture1D) {
- // 1D textures can be aliased to texture buffers, hide the declarations behind a
- // preprocessor flag and use one or the other from the GPU state. This has to be
- // done because shaders don't have enough information to determine the texture type.
- EmitIfdefIsBuffer(sampler);
- code.AddLine("{} samplerBuffer {};", description, name);
- code.AddLine("#else");
- code.AddLine("{} {} {};", description, sampler_type, name);
- code.AddLine("#endif");
- } else {
- // The other texture types (2D, 3D and cubes) don't have this issue.
- code.AddLine("{} {} {};", description, sampler_type, name);
- }
+ code.AddLine("{} {} {};", description, sampler_type, name);
}
- if (!samplers.empty()) {
+ if (!ir.GetSamplers().empty()) {
code.AddNewLine();
}
}
@@ -718,7 +706,7 @@ private:
constexpr u32 element_stride = 4;
const u32 address{generic_base + index * generic_stride + element * element_stride};
- const bool declared = stage != ProgramType::Fragment ||
+ const bool declared = stage != ShaderType::Fragment ||
header.ps.GetAttributeUse(index) != AttributeUse::Unused;
const std::string value =
declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f";
@@ -735,8 +723,8 @@ private:
}
void DeclareImages() {
- const auto& images{ir.GetImages()};
- for (const auto& image : images) {
+ u32 binding = device.GetBaseBindings(stage).image;
+ for (const auto& image : ir.GetImages()) {
std::string qualifier = "coherent volatile";
if (image.IsRead() && !image.IsWritten()) {
qualifier += " readonly";
@@ -746,10 +734,10 @@ private:
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));
+ code.AddLine("layout ({}binding = {}) {} uniform uimage{} {};", format, binding++,
+ qualifier, type_declaration, GetImage(image));
}
- if (!images.empty()) {
+ if (!ir.GetImages().empty()) {
code.AddNewLine();
}
}
@@ -810,7 +798,7 @@ private:
}
if (const auto abuf = std::get_if<AbufNode>(&*node)) {
- UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ProgramType::Geometry,
+ UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderType::Geometry,
"Physical attributes in geometry shaders are not implemented");
if (abuf->IsPhysicalBuffer()) {
return {fmt::format("ReadPhysicalAttribute({})",
@@ -869,18 +857,13 @@ private:
}
if (const auto lmem = std::get_if<LmemNode>(&*node)) {
- if (stage == ProgramType::Compute) {
- LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
- }
return {
fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
Type::Uint};
}
if (const auto smem = std::get_if<SmemNode>(&*node)) {
- return {
- fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
- Type::Uint};
+ return {fmt::format("smem[{} >> 2]", Visit(smem->GetAddress()).AsUint()), Type::Uint};
}
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
@@ -910,7 +893,7 @@ private:
Expression ReadAttribute(Attribute::Index attribute, u32 element, const Node& buffer = {}) {
const auto GeometryPass = [&](std::string_view name) {
- if (stage == ProgramType::Geometry && buffer) {
+ if (stage == ShaderType::Geometry && buffer) {
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
// set an 0x80000000 index for those and the shader fails to build. Find out why
// this happens and what's its intent.
@@ -922,11 +905,11 @@ private:
switch (attribute) {
case Attribute::Index::Position:
switch (stage) {
- case ProgramType::Geometry:
+ case ShaderType::Geometry:
return {fmt::format("gl_in[{}].gl_Position{}", Visit(buffer).AsUint(),
GetSwizzle(element)),
Type::Float};
- case ProgramType::Fragment:
+ case ShaderType::Fragment:
return {element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)),
Type::Float};
default:
@@ -960,7 +943,7 @@ private:
return {"0", Type::Int};
case Attribute::Index::FrontFacing:
// TODO(Subv): Find out what the values are for the other elements.
- ASSERT(stage == ProgramType::Fragment);
+ ASSERT(stage == ShaderType::Fragment);
switch (element) {
case 3:
return {"(gl_FrontFacing ? -1 : 0)", Type::Int};
@@ -986,7 +969,7 @@ private:
// be found in fragment shaders, so we disable precise there. There are vertex shaders that
// also fail to build but nobody seems to care about those.
// Note: Only bugged drivers will skip precise.
- const bool disable_precise = device.HasPreciseBug() && stage == ProgramType::Fragment;
+ const bool disable_precise = device.HasPreciseBug() && stage == ShaderType::Fragment;
std::string temporary = code.GenerateTemporary();
code.AddLine("{}{} {} = {};", disable_precise ? "" : "precise ", GetTypeString(type),
@@ -1280,17 +1263,12 @@ private:
}
target = std::move(*output);
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
- if (stage == ProgramType::Compute) {
- LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
- }
target = {
fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
Type::Uint};
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
- ASSERT(stage == ProgramType::Compute);
- target = {
- fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
- Type::Uint};
+ ASSERT(stage == ShaderType::Compute);
+ target = {fmt::format("smem[{} >> 2]", Visit(smem->GetAddress()).AsUint()), Type::Uint};
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
@@ -1529,7 +1507,8 @@ private:
}
Expression HCastFloat(Operation operation) {
- return {fmt::format("vec2({})", VisitOperand(operation, 0).AsFloat()), Type::HalfFloat};
+ return {fmt::format("vec2({}, 0.0f)", VisitOperand(operation, 0).AsFloat()),
+ Type::HalfFloat};
}
Expression HUnpack(Operation operation) {
@@ -1787,27 +1766,14 @@ private:
expr += ", ";
}
- // Store a copy of the expression without the lod to be used with texture buffers
- std::string expr_buffer = expr;
-
- if (meta->lod) {
+ if (meta->lod && !meta->sampler.IsBuffer()) {
expr += ", ";
expr += Visit(meta->lod).AsInt();
}
expr += ')';
expr += GetSwizzle(meta->element);
- expr_buffer += ')';
- expr_buffer += GetSwizzle(meta->element);
-
- const std::string tmp{code.GenerateTemporary()};
- EmitIfdefIsBuffer(meta->sampler);
- code.AddLine("float {} = {};", tmp, expr_buffer);
- code.AddLine("#else");
- code.AddLine("float {} = {};", tmp, expr);
- code.AddLine("#endif");
-
- return {tmp, Type::Float};
+ return {std::move(expr), Type::Float};
}
Expression TextureGradient(Operation operation) {
@@ -1883,7 +1849,7 @@ private:
}
void PreExit() {
- if (stage != ProgramType::Fragment) {
+ if (stage != ShaderType::Fragment) {
return;
}
const auto& used_registers = ir.GetRegisters();
@@ -1936,27 +1902,21 @@ private:
}
Expression EmitVertex(Operation operation) {
- ASSERT_MSG(stage == ProgramType::Geometry,
+ ASSERT_MSG(stage == ShaderType::Geometry,
"EmitVertex is expected to be used in a geometry shader.");
-
- // If a geometry shader is attached, it will always flip (it's the last stage before
- // fragment). For more info about flipping, refer to gl_shader_gen.cpp.
- code.AddLine("gl_Position.xy *= viewport_flip.xy;");
code.AddLine("EmitVertex();");
return {};
}
Expression EndPrimitive(Operation operation) {
- ASSERT_MSG(stage == ProgramType::Geometry,
+ ASSERT_MSG(stage == ShaderType::Geometry,
"EndPrimitive is expected to be used in a geometry shader.");
-
code.AddLine("EndPrimitive();");
return {};
}
Expression YNegate(Operation operation) {
- // Config pack's third value is Y_NEGATE's state.
- return {"config_pack[2]", Type::Uint};
+ return {"y_direction", Type::Float};
}
template <u32 element>
@@ -2248,10 +2208,6 @@ private:
return "lmem_" + suffix;
}
- std::string GetSharedMemory() const {
- return fmt::format("smem_{}", suffix);
- }
-
std::string GetInternalFlag(InternalFlag flag) const {
constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
"overflow_flag"};
@@ -2269,10 +2225,6 @@ private:
return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image");
}
- void EmitIfdefIsBuffer(const Sampler& sampler) {
- code.AddLine("#ifdef SAMPLER_{}_IS_BUFFER", sampler.GetIndex());
- }
-
std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
return fmt::format("{}_{}_{}", name, index, suffix);
}
@@ -2291,7 +2243,7 @@ private:
const Device& device;
const ShaderIR& ir;
- const ProgramType stage;
+ const ShaderType stage;
const std::string suffix;
const Header header;
@@ -2546,7 +2498,7 @@ const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f );
)";
}
-std::string Decompile(const Device& device, const ShaderIR& ir, ProgramType stage,
+std::string Decompile(const Device& device, const ShaderIR& ir, ShaderType stage,
const std::string& suffix) {
GLSLDecompiler decompiler(device, ir, stage, suffix);
decompiler.Decompile();