From 73d9c494ea6ae0a8076f679506ff07e2fe014826 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sun, 15 Apr 2018 03:32:12 -0400
Subject: [PATCH] shaders: Expose hints about used const buffers.

---
 .../renderer_opengl/gl_shader_decompiler.cpp  | 40 +++++++++++---
 .../renderer_opengl/gl_shader_decompiler.h    |  4 +-
 .../renderer_opengl/gl_shader_gen.cpp         | 54 ++++++++++++++++---
 .../renderer_opengl/gl_shader_gen.h           | 38 ++++++++++++-
 .../renderer_opengl/gl_shader_manager.h       | 41 +++++++++-----
 5 files changed, 146 insertions(+), 31 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 1290fa4cd5..9cf2c6a0cd 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -140,6 +140,11 @@ public:
         return declarations.GetResult() + shader.GetResult();
     }
 
+    /// Returns entries in the shader that are useful for external functions
+    ShaderEntries GetEntries() const {
+        return {GetConstBuffersDeclarations()};
+    }
+
 private:
     /// Gets the Subroutine object corresponding to the specified address.
     const Subroutine& GetSubroutine(u32 begin, u32 end) const {
@@ -186,10 +191,9 @@ private:
     }
 
     /// Generates code representing a uniform (C buffer) register.
-    std::string GetUniform(const Uniform& reg) const {
-        std::string index = std::to_string(reg.index);
-        return "uniform_" + index + "[" + std::to_string(reg.offset >> 2) + "][" +
-               std::to_string(reg.offset & 3) + "]";
+    std::string GetUniform(const Uniform& reg) {
+        declr_const_buffers[reg.index].MarkAsUsed(reg.index, reg.offset);
+        return 'c' + std::to_string(reg.index) + '[' + std::to_string(reg.offset) + ']';
     }
 
     /**
@@ -439,6 +443,14 @@ private:
         GenerateDeclarations();
     }
 
+    /// Returns a list of constant buffer declarations
+    std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
+        std::vector<ConstBufferEntry> result;
+        std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
+                     std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
+        return result;
+    }
+
     /// Add declarations for registers
     void GenerateDeclarations() {
         for (const auto& reg : declr_register) {
@@ -463,6 +475,17 @@ private:
                                  ") out vec4 " + GetOutputAttribute(index) + ";");
         }
         declarations.AddLine("");
+
+        unsigned const_buffer_layout = 0;
+        for (const auto& entry : GetConstBuffersDeclarations()) {
+            declarations.AddLine("layout(std430, binding = " + std::to_string(const_buffer_layout) +
+                                 ") buffer c" + std::to_string(entry.GetIndex()) + "_buffer");
+            declarations.AddLine("{");
+            declarations.AddLine("    float c" + std::to_string(entry.GetIndex()) + "[];");
+            declarations.AddLine("};");
+            declarations.AddLine("");
+            ++const_buffer_layout;
+        }
     }
 
 private:
@@ -478,18 +501,19 @@ private:
     std::set<std::string> declr_register;
     std::set<Attribute::Index> declr_input_attribute;
     std::set<Attribute::Index> declr_output_attribute;
-}; // namespace Decompiler
+    std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;
+};
 
 std::string GetCommonDeclarations() {
     return "bool exec_shader();";
 }
 
-boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
-                                              Maxwell3D::Regs::ShaderStage stage) {
+boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
+                                                Maxwell3D::Regs::ShaderStage stage) {
     try {
         auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines();
         GLSLGenerator generator(subroutines, program_code, main_offset, stage);
-        return generator.GetShaderCode();
+        return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
     } catch (const DecompileFail& exception) {
         LOG_ERROR(HW_GPU, "Shader decompilation failed: %s", exception.what());
     }
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 2f4047d87b..9f6e0ef58a 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -17,8 +17,8 @@ using Tegra::Engines::Maxwell3D;
 
 std::string GetCommonDeclarations();
 
-boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
-                                              Maxwell3D::Regs::ShaderStage stage);
+boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
+                                                Maxwell3D::Regs::ShaderStage stage);
 
 } // namespace Decompiler
 } // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 524c2cfb5d..aeea1c8051 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -3,18 +3,60 @@
 // Refer to the license.txt file included.
 
 #include "common/assert.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/renderer_opengl/gl_shader_decompiler.h"
 #include "video_core/renderer_opengl/gl_shader_gen.h"
 
 namespace GLShader {
 
-std::string GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config) {
-    UNREACHABLE();
-    return {};
+using Tegra::Engines::Maxwell3D;
+
+static constexpr u32 PROGRAM_OFFSET{10};
+
+ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config) {
+    std::string out = "#version 430 core\n";
+    out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
+    out += Decompiler::GetCommonDeclarations();
+
+    ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET,
+                                                         Maxwell3D::Regs::ShaderStage::Vertex)
+                                .get_value_or({});
+    out += R"(
+
+out gl_PerVertex {
+    vec4 gl_Position;
+};
+
+void main() {
+    exec_shader();
 }
 
-std::string GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config) {
-    UNREACHABLE();
-    return {};
+)";
+    out += program.first;
+    return {out, program.second};
+}
+
+ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config) {
+    std::string out = "#version 430 core\n";
+    out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
+    out += Decompiler::GetCommonDeclarations();
+
+    ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET,
+                                                         Maxwell3D::Regs::ShaderStage::Fragment)
+                                .get_value_or({});
+    out += R"(
+
+out vec4 color;
+
+uniform sampler2D tex[32];
+
+void main() {
+    exec_shader();
+}
+
+)";
+    out += program.first;
+    return {out, program.second};
 }
 
 } // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 925e66ee43..3d9aead74f 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -7,6 +7,8 @@
 #include <array>
 #include <string>
 #include <type_traits>
+#include <utility>
+#include <vector>
 #include "common/common_types.h"
 #include "common/hash.h"
 
@@ -16,6 +18,38 @@ constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x1000};
 
 using ProgramCode = std::array<u64, MAX_PROGRAM_CODE_LENGTH>;
 
+class ConstBufferEntry {
+public:
+    void MarkAsUsed(unsigned index, unsigned offset) {
+        is_used = true;
+        this->index = index;
+        max_offset = std::max(max_offset, offset);
+    }
+
+    bool IsUsed() const {
+        return is_used;
+    }
+
+    unsigned GetIndex() const {
+        return index;
+    }
+
+    unsigned GetSize() const {
+        return max_offset + 1;
+    }
+
+private:
+    bool is_used{};
+    unsigned index{};
+    unsigned max_offset{};
+};
+
+struct ShaderEntries {
+    std::vector<ConstBufferEntry> const_buffer_entries;
+};
+
+using ProgramResult = std::pair<std::string, ShaderEntries>;
+
 struct ShaderSetup {
     ShaderSetup(ProgramCode&& program_code) : program_code(std::move(program_code)) {}
 
@@ -58,13 +92,13 @@ struct MaxwellFSConfig : Common::HashableStruct<MaxwellShaderConfigCommon> {
  * Generates the GLSL vertex shader program source code for the given VS program
  * @returns String of the shader source code
  */
-std::string GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config);
+ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config);
 
 /**
  * Generates the GLSL fragment shader program source code for the given FS program
  * @returns String of the shader source code
  */
-std::string GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config);
+ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config);
 
 } // namespace GLShader
 
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index f003ce5323..ecc92d9863 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -41,19 +41,25 @@ class OGLShaderStage {
 public:
     OGLShaderStage() = default;
 
-    void Create(const char* source, GLenum type) {
+    void Create(const ProgramResult& program_result, GLenum type) {
         OGLShader shader;
-        shader.Create(source, type);
+        shader.Create(program_result.first.c_str(), type);
         program.Create(true, shader.handle);
         Impl::SetShaderUniformBlockBindings(program.handle);
         Impl::SetShaderSamplerBindings(program.handle);
+        entries = program_result.second;
     }
     GLuint GetHandle() const {
         return program.handle;
     }
 
+    ShaderEntries GetEntries() const {
+        return entries;
+    }
+
 private:
     OGLProgram program;
+    ShaderEntries entries;
 };
 
 // TODO(wwylele): beautify this doc
@@ -61,25 +67,28 @@ private:
 // The double cache is needed because diffent KeyConfigType, which includes a hash of the code
 // region (including its leftover unused code) can generate the same GLSL code.
 template <typename KeyConfigType,
-          std::string (*CodeGenerator)(const ShaderSetup&, const KeyConfigType&), GLenum ShaderType>
+          ProgramResult (*CodeGenerator)(const ShaderSetup&, const KeyConfigType&),
+          GLenum ShaderType>
 class ShaderCache {
 public:
     ShaderCache() = default;
 
-    GLuint Get(const KeyConfigType& key, const ShaderSetup& setup) {
+    using Result = std::pair<GLuint, ShaderEntries>;
+
+    Result Get(const KeyConfigType& key, const ShaderSetup& setup) {
         auto map_it = shader_map.find(key);
         if (map_it == shader_map.end()) {
-            std::string program = CodeGenerator(setup, key);
+            ProgramResult program = CodeGenerator(setup, key);
 
-            auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{});
+            auto [iter, new_shader] = shader_cache.emplace(program.first, OGLShaderStage{});
             OGLShaderStage& cached_shader = iter->second;
             if (new_shader) {
-                cached_shader.Create(program.c_str(), ShaderType);
+                cached_shader.Create(program, ShaderType);
             }
             shader_map[key] = &cached_shader;
-            return cached_shader.GetHandle();
+            return {cached_shader.GetHandle(), program.second};
         } else {
-            return map_it->second->GetHandle();
+            return {map_it->second->GetHandle(), map_it->second->GetEntries()};
         }
     }
 
@@ -98,12 +107,18 @@ public:
         pipeline.Create();
     }
 
-    void UseProgrammableVertexShader(const MaxwellVSConfig& config, const ShaderSetup setup) {
-        current.vs = vertex_shaders.Get(config, setup);
+    ShaderEntries UseProgrammableVertexShader(const MaxwellVSConfig& config,
+                                              const ShaderSetup setup) {
+        ShaderEntries result;
+        std::tie(current.vs, result) = vertex_shaders.Get(config, setup);
+        return result;
     }
 
-    void UseProgrammableFragmentShader(const MaxwellFSConfig& config, const ShaderSetup setup) {
-        current.fs = fragment_shaders.Get(config, setup);
+    ShaderEntries UseProgrammableFragmentShader(const MaxwellFSConfig& config,
+                                                const ShaderSetup setup) {
+        ShaderEntries result;
+        std::tie(current.fs, result) = fragment_shaders.Get(config, setup);
+        return result;
     }
 
     void UseTrivialGeometryShader() {