Merge pull request #333 from bunnei/const-buff-hints
shaders: Expose hints about used const buffers.
This commit is contained in:
		
						commit
						34264879b3
					
				@ -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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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() {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user