Browse Source

Merge pull request #399 from bunnei/shader-ints

Shader decompiler prep for integer instructions
bunnei 8 năm trước cách đây
mục cha
commit
bc43946140

+ 9 - 1
src/video_core/engines/shader_bytecode.h

@@ -19,7 +19,10 @@ namespace Tegra {
 namespace Shader {
 
 struct Register {
-    // Register 255 is special cased to always be 0
+    /// Number of registers
+    static constexpr size_t NumRegisters = 256;
+
+    /// Register 255 is special cased to always be 0
     static constexpr size_t ZeroIndex = 255;
 
     constexpr Register() = default;
@@ -48,6 +51,11 @@ struct Register {
         return ~value;
     }
 
+    u64 GetSwizzledIndex(u64 elem) const {
+        elem = (value + elem) & 3;
+        return (value & ~3) + elem;
+    }
+
 private:
     u64 value{};
 };

+ 111 - 8
src/video_core/renderer_opengl/gl_shader_decompiler.cpp

@@ -146,6 +146,90 @@ private:
     std::string shader_source;
 };
 
+/**
+ * Represents an emulated shader register, used to track the state of that register for emulation
+ * with GLSL. At this time, a register can be used as a float or an integer. This class is used for
+ * bookkeeping within the GLSL program.
+ */
+class GLSLRegister {
+public:
+    GLSLRegister(size_t index, ShaderWriter& shader)
+        : index{index}, shader{shader}, float_str{"freg_" + std::to_string(index)},
+          integer_str{"ireg_" + std::to_string(index)} {}
+
+    /// Returns a GLSL string representing the current state of the register
+    const std::string& GetActiveString() {
+        declr_type.insert(active_type);
+
+        switch (active_type) {
+        case Type::Float:
+            return float_str;
+        case Type::Integer:
+            return integer_str;
+        }
+
+        UNREACHABLE();
+        return float_str;
+    }
+
+    /// Returns a GLSL string representing the register as a float
+    const std::string& GetFloatString() const {
+        ASSERT(IsFloatUsed());
+        return float_str;
+    }
+
+    /// Returns a GLSL string representing the register as an integer
+    const std::string& GetIntegerString() const {
+        ASSERT(IsIntegerUsed());
+        return integer_str;
+    }
+
+    /// Convert the current register state from float to integer
+    void FloatToInteger() {
+        ASSERT(active_type == Type::Float);
+
+        const std::string src = GetActiveString();
+        active_type = Type::Integer;
+        const std::string dest = GetActiveString();
+
+        shader.AddLine(dest + " = floatBitsToInt(" + src + ");");
+    }
+
+    /// Convert the current register state from integer to float
+    void IntegerToFloat() {
+        ASSERT(active_type == Type::Integer);
+
+        const std::string src = GetActiveString();
+        active_type = Type::Float;
+        const std::string dest = GetActiveString();
+
+        shader.AddLine(dest + " = intBitsToFloat(" + src + ");");
+    }
+
+    /// Returns true if the register was ever used as a float, used for register declarations
+    bool IsFloatUsed() const {
+        return declr_type.find(Type::Float) != declr_type.end();
+    }
+
+    /// Returns true if the register was ever used as an integer, used for register declarations
+    bool IsIntegerUsed() const {
+        return declr_type.find(Type::Integer) != declr_type.end();
+    }
+
+private:
+    enum class Type {
+        Float,
+        Integer,
+    };
+
+    const size_t index;
+    const std::string float_str;
+    const std::string integer_str;
+    ShaderWriter& shader;
+    Type active_type{Type::Float};
+    std::set<Type> declr_type;
+};
+
 class GLSLGenerator {
 public:
     GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
@@ -153,6 +237,7 @@ public:
         : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
           stage(stage) {
 
+        BuildRegisterList();
         Generate();
     }
 
@@ -166,6 +251,13 @@ public:
     }
 
 private:
+    /// Build the GLSL register list
+    void BuildRegisterList() {
+        for (size_t index = 0; index < Register::NumRegisters; ++index) {
+            regs.emplace_back(index, shader);
+        }
+    }
+
     /// Gets the Subroutine object corresponding to the specified address.
     const Subroutine& GetSubroutine(u32 begin, u32 end) const {
         auto iter = subroutines.find(Subroutine{begin, end});
@@ -221,14 +313,11 @@ private:
 
     /// Generates code representing a temporary (GPR) register.
     std::string GetRegister(const Register& reg, unsigned elem = 0) {
-        if (reg == Register::ZeroIndex)
+        if (reg == Register::ZeroIndex) {
             return "0";
-        if (stage == Maxwell3D::Regs::ShaderStage::Fragment && reg < 4) {
-            // GPRs 0-3 are output color for the fragment shader
-            return std::string{"color."} + "rgba"[(reg + elem) & 3];
         }
 
-        return *declr_register.insert("register_" + std::to_string(reg + elem)).first;
+        return regs[reg.GetSwizzledIndex(elem)].GetActiveString();
     }
 
     /// Generates code representing a uniform (C buffer) register.
@@ -628,6 +717,15 @@ private:
             case OpCode::Id::EXIT: {
                 ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex),
                            "Predicated exits not implemented");
+
+                // Final color output is currently hardcoded to GPR0-3 for fragment shaders
+                if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
+                    shader.AddLine("color.r = " + GetRegister(0) + ";");
+                    shader.AddLine("color.g = " + GetRegister(1) + ";");
+                    shader.AddLine("color.b = " + GetRegister(2) + ";");
+                    shader.AddLine("color.a = " + GetRegister(3) + ";");
+                }
+
                 shader.AddLine("return true;");
                 offset = PROGRAM_END - 1;
                 break;
@@ -755,8 +853,13 @@ private:
 
     /// Add declarations for registers
     void GenerateDeclarations() {
-        for (const auto& reg : declr_register) {
-            declarations.AddLine("float " + reg + " = 0.0;");
+        for (const auto& reg : regs) {
+            if (reg.IsFloatUsed()) {
+                declarations.AddLine("float " + reg.GetFloatString() + " = 0.0;");
+            }
+            if (reg.IsIntegerUsed()) {
+                declarations.AddLine("int " + reg.GetIntegerString() + " = 0;");
+            }
         }
         declarations.AddNewLine();
 
@@ -803,9 +906,9 @@ private:
 
     ShaderWriter shader;
     ShaderWriter declarations;
+    std::vector<GLSLRegister> regs;
 
     // Declarations
-    std::set<std::string> declr_register;
     std::set<std::string> declr_predicates;
     std::set<Attribute::Index> declr_input_attribute;
     std::set<Attribute::Index> declr_output_attribute;