diff options
| author | ReinUsesLisp <reinuseslisp@airmail.cc> | 2019-01-14 00:58:15 -0300 | 
|---|---|---|
| committer | ReinUsesLisp <reinuseslisp@airmail.cc> | 2019-02-06 22:20:57 -0300 | 
| commit | a3703f5767332dfc5f7e8d37a1f715d8ccb76fcf (patch) | |
| tree | 84ffe77dabc36e77dc0fb543d96d8774483c67a0 | |
| parent | 403908622670d11531e132e5bbab13a5f4cf05ce (diff) | |
gl_shader_cache: Refactor to support disk shader cache
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_cache.cpp | 450 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_cache.h | 59 | 
2 files changed, 388 insertions, 121 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 363b941f3..a70ff79d0 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -11,6 +11,7 @@  #include "video_core/renderer_opengl/gl_rasterizer.h"  #include "video_core/renderer_opengl/gl_shader_cache.h"  #include "video_core/renderer_opengl/gl_shader_decompiler.h" +#include "video_core/renderer_opengl/gl_shader_disk_cache.h"  #include "video_core/renderer_opengl/gl_shader_manager.h"  #include "video_core/renderer_opengl/utils.h"  #include "video_core/shader/shader_ir.h" @@ -19,8 +20,19 @@ namespace OpenGL {  using VideoCommon::Shader::ProgramCode; +// One UBO is always reserved for emulation values +constexpr u32 RESERVED_UBOS = 1; + +struct UnspecializedShader { +    std::string code; +    GLShader::ShaderEntries entries; +    Maxwell::ShaderProgram program_type; +}; + +namespace { +  /// Gets the address for the specified shader stage program -static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { +VAddr GetShaderAddress(Maxwell::ShaderProgram program) {      const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();      const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)];      const auto address = gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + @@ -30,7 +42,7 @@ static VAddr GetShaderAddress(Maxwell::ShaderProgram program) {  }  /// Gets the shader program code from memory for the specified address -static ProgramCode GetShaderCode(VAddr addr) { +ProgramCode GetShaderCode(VAddr addr) {      ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);      Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64));      return program_code; @@ -51,38 +63,193 @@ constexpr GLenum GetShaderType(Maxwell::ShaderProgram program_type) {      }  } -CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) -    : addr{addr}, program_type{program_type}, setup{GetShaderCode(addr)} { +/// Gets if the current instruction offset is a scheduler instruction +constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) { +    // Sched instructions appear once every 4 instructions. +    constexpr std::size_t SchedPeriod = 4; +    const std::size_t absolute_offset = offset - main_offset; +    return (absolute_offset % SchedPeriod) == 0; +} -    GLShader::ProgramResult program_result; +/// Describes primitive behavior on geometry shaders +constexpr std::tuple<const char*, const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) { +    switch (primitive_mode) { +    case GL_POINTS: +        return {"points", "Points", 1}; +    case GL_LINES: +    case GL_LINE_STRIP: +        return {"lines", "Lines", 2}; +    case GL_LINES_ADJACENCY: +    case GL_LINE_STRIP_ADJACENCY: +        return {"lines_adjacency", "LinesAdj", 4}; +    case GL_TRIANGLES: +    case GL_TRIANGLE_STRIP: +    case GL_TRIANGLE_FAN: +        return {"triangles", "Triangles", 3}; +    case GL_TRIANGLES_ADJACENCY: +    case GL_TRIANGLE_STRIP_ADJACENCY: +        return {"triangles_adjacency", "TrianglesAdj", 6}; +    default: +        return {"points", "Invalid", 1}; +    } +} -    switch (program_type) { -    case Maxwell::ShaderProgram::VertexA: +/// Calculates the size of a program stream +std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) { +    constexpr std::size_t start_offset = 10; +    std::size_t offset = start_offset; +    std::size_t size = start_offset * sizeof(u64); +    while (offset < program.size()) { +        const u64 instruction = program[offset]; +        if (!IsSchedInstruction(offset, start_offset)) { +            if (instruction == 0 || (instruction >> 52) == 0x50b) { +                // End on Maxwell's "nop" instruction +                break; +            } +        } +        size += sizeof(u64); +        offset++; +    } +    // The last instruction is included in the program size +    return std::min(size + sizeof(u64), program.size() * sizeof(u64)); +} + +/// Hashes one (or two) program streams +u64 GetUniqueIdentifier(Maxwell::ShaderProgram program_type, const ProgramCode& code, +                        const ProgramCode& code_b) { +    u64 unique_identifier = +        Common::CityHash64(reinterpret_cast<const char*>(code.data()), CalculateProgramSize(code)); +    if (program_type != Maxwell::ShaderProgram::VertexA) { +        return unique_identifier; +    } +    // VertexA programs include two programs + +    std::size_t seed = 0; +    boost::hash_combine(seed, unique_identifier); + +    const u64 identifier_b = Common::CityHash64(reinterpret_cast<const char*>(code_b.data()), +                                                CalculateProgramSize(code_b)); +    boost::hash_combine(seed, identifier_b); +    return static_cast<u64>(seed); +} + +/// Creates an unspecialized program from code streams +GLShader::ProgramResult CreateProgram(Maxwell::ShaderProgram program_type, ProgramCode program_code, +                                      ProgramCode program_code_b) { +    GLShader::ShaderSetup setup(std::move(program_code)); +    if (program_type == Maxwell::ShaderProgram::VertexA) {          // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders.          // Conventional HW does not support this, so we combine VertexA and VertexB into one          // stage here. -        setup.SetProgramB(GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); +        setup.SetProgramB(std::move(program_code_b)); +    } + +    switch (program_type) { +    case Maxwell::ShaderProgram::VertexA:      case Maxwell::ShaderProgram::VertexB: -        CalculateProperties(); -        program_result = GLShader::GenerateVertexShader(setup); -        break; +        return GLShader::GenerateVertexShader(setup);      case Maxwell::ShaderProgram::Geometry: -        CalculateProperties(); -        program_result = GLShader::GenerateGeometryShader(setup); -        break; +        return GLShader::GenerateGeometryShader(setup);      case Maxwell::ShaderProgram::Fragment: -        CalculateProperties(); -        program_result = GLShader::GenerateFragmentShader(setup); -        break; +        return GLShader::GenerateFragmentShader(setup);      default:          LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type));          UNREACHABLE(); +        return {}; +    } +} + +CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries, +                               Maxwell::ShaderProgram program_type, BaseBindings base_bindings, +                               GLenum primitive_mode, bool hint_retrievable = false) { +    std::string source = "#version 430 core\n"; +    source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); + +    for (const auto& cbuf : entries.const_buffers) { +        source += +            fmt::format("#define CBUF_BINDING_{} {}\n", cbuf.GetIndex(), base_bindings.cbuf++); +    } +    for (const auto& gmem : entries.global_memory_entries) { +        source += fmt::format("#define GMEM_BINDING_{}_{} {}\n", gmem.GetCbufIndex(), +                              gmem.GetCbufOffset(), base_bindings.gmem++); +    } +    for (const auto& sampler : entries.samplers) { +        source += fmt::format("#define SAMPLER_BINDING_{} {}\n", sampler.GetIndex(), +                              base_bindings.sampler++); +    } + +    if (program_type == Maxwell::ShaderProgram::Geometry) { +        const auto [glsl_topology, _, max_vertices] = GetPrimitiveDescription(primitive_mode); + +        source += "layout (" + std::string(glsl_topology) + ") in;\n"; +        source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n'; +    } + +    source += code; + +    OGLShader shader; +    shader.Create(source.c_str(), GetShaderType(program_type)); + +    auto program = std::make_shared<OGLProgram>(); +    program->Create(true, hint_retrievable, shader.handle); +    return program; +} + +std::set<GLenum> GetSupportedFormats() { +    std::set<GLenum> supported_formats; + +    GLint num_formats{}; +    glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats); + +    std::vector<GLint> formats(num_formats); +    glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data()); + +    for (const GLint format : formats) +        supported_formats.insert(static_cast<GLenum>(format)); +    return supported_formats; +} + +} // namespace + +CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, +                           ShaderDiskCacheOpenGL& disk_cache, +                           const PrecompiledPrograms& precompiled_programs, +                           ProgramCode&& program_code, ProgramCode&& program_code_b) +    : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, +      disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { + +    const std::size_t code_size = CalculateProgramSize(program_code); +    const std::size_t code_size_b = +        program_code_b.empty() ? 0 : CalculateProgramSize(program_code_b); + +    GLShader::ProgramResult program_result = +        CreateProgram(program_type, program_code, program_code_b); +    if (program_result.first.empty()) { +        // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now          return;      }      code = program_result.first;      entries = program_result.second;      shader_length = entries.shader_length; + +    const ShaderDiskCacheRaw raw(unique_identifier, program_type, +                                 static_cast<u32>(code_size / sizeof(u64)), +                                 static_cast<u32>(code_size_b / sizeof(u64)), +                                 std::move(program_code), std::move(program_code_b)); +    disk_cache.SaveRaw(raw); +} + +CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, +                           ShaderDiskCacheOpenGL& disk_cache, +                           const PrecompiledPrograms& precompiled_programs, +                           GLShader::ProgramResult result) +    : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, +      disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { + +    code = std::move(result.first); +    entries = result.second; +    shader_length = entries.shader_length;  }  std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive_mode, @@ -94,138 +261,195 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive          const auto [entry, is_cache_miss] = programs.try_emplace(base_bindings);          auto& program = entry->second;          if (is_cache_miss) { -            std::string source = AllocateBindings(base_bindings); -            source += code; +            program = TryLoadProgram(primitive_mode, base_bindings); +            if (!program) { +                program = +                    SpecializeShader(code, entries, program_type, base_bindings, primitive_mode); +                disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); +            } -            OGLShader shader; -            shader.Create(source.c_str(), GetShaderType(program_type)); -            program.Create(true, shader.handle); -            LabelGLObject(GL_PROGRAM, program.handle, addr); +            LabelGLObject(GL_PROGRAM, program->handle, addr);          } -        handle = program.handle; +        handle = program->handle;      } -    // Add const buffer and samplers offset reserved by this shader. One UBO binding is reserved for -    // emulation values -    base_bindings.cbuf += static_cast<u32>(entries.const_buffers.size()) + 1; +    base_bindings.cbuf += static_cast<u32>(entries.const_buffers.size()) + RESERVED_UBOS;      base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size());      base_bindings.sampler += static_cast<u32>(entries.samplers.size());      return {handle, base_bindings};  } -std::string CachedShader::AllocateBindings(BaseBindings base_bindings) { -    std::string code = "#version 430 core\n"; -    code += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); - -    for (const auto& cbuf : entries.const_buffers) { -        code += fmt::format("#define CBUF_BINDING_{} {}\n", cbuf.GetIndex(), base_bindings.cbuf++); -    } - -    for (const auto& gmem : entries.global_memory_entries) { -        code += fmt::format("#define GMEM_BINDING_{}_{} {}\n", gmem.GetCbufIndex(), -                            gmem.GetCbufOffset(), base_bindings.gmem++); -    } - -    for (const auto& sampler : entries.samplers) { -        code += fmt::format("#define SAMPLER_BINDING_{} {}\n", sampler.GetIndex(), -                            base_bindings.sampler++); -    } - -    return code; -} -  GLuint CachedShader::GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings) {      const auto [entry, is_cache_miss] = geometry_programs.try_emplace(base_bindings);      auto& programs = entry->second;      switch (primitive_mode) {      case GL_POINTS: -        return LazyGeometryProgram(programs.points, base_bindings, "points", 1, "ShaderPoints"); +        return LazyGeometryProgram(programs.points, base_bindings, primitive_mode);      case GL_LINES:      case GL_LINE_STRIP: -        return LazyGeometryProgram(programs.lines, base_bindings, "lines", 2, "ShaderLines"); +        return LazyGeometryProgram(programs.lines, base_bindings, primitive_mode);      case GL_LINES_ADJACENCY:      case GL_LINE_STRIP_ADJACENCY: -        return LazyGeometryProgram(programs.lines_adjacency, base_bindings, "lines_adjacency", 4, -                                   "ShaderLinesAdjacency"); +        return LazyGeometryProgram(programs.lines_adjacency, base_bindings, primitive_mode);      case GL_TRIANGLES:      case GL_TRIANGLE_STRIP:      case GL_TRIANGLE_FAN: -        return LazyGeometryProgram(programs.triangles, base_bindings, "triangles", 3, -                                   "ShaderTriangles"); +        return LazyGeometryProgram(programs.triangles, base_bindings, primitive_mode);      case GL_TRIANGLES_ADJACENCY:      case GL_TRIANGLE_STRIP_ADJACENCY: -        return LazyGeometryProgram(programs.triangles_adjacency, base_bindings, -                                   "triangles_adjacency", 6, "ShaderTrianglesAdjacency"); +        return LazyGeometryProgram(programs.triangles_adjacency, base_bindings, primitive_mode);      default:          UNREACHABLE_MSG("Unknown primitive mode."); -        return LazyGeometryProgram(programs.points, base_bindings, "points", 1, "ShaderPoints"); +        return LazyGeometryProgram(programs.points, base_bindings, primitive_mode);      }  } -GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, BaseBindings base_bindings, -                                         const std::string& glsl_topology, u32 max_vertices, -                                         const std::string& debug_name) { -    if (target_program.handle != 0) { -        return target_program.handle; +GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBindings base_bindings, +                                         GLenum primitive_mode) { +    if (target_program) { +        return target_program->handle; +    } +    const auto [_, debug_name, __] = GetPrimitiveDescription(primitive_mode); +    target_program = TryLoadProgram(primitive_mode, base_bindings); +    if (!target_program) { +        target_program = +            SpecializeShader(code, entries, program_type, base_bindings, primitive_mode); +        disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings));      } -    std::string source = AllocateBindings(base_bindings); -    source += "layout (" + glsl_topology + ") in;\n"; -    source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n'; -    source += code; -    OGLShader shader; -    shader.Create(source.c_str(), GL_GEOMETRY_SHADER); -    target_program.Create(true, shader.handle); -    LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name); -    return target_program.handle; +    LabelGLObject(GL_PROGRAM, target_program->handle, addr, debug_name); + +    return target_program->handle;  }; -static bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) { -    // sched instructions appear once every 4 instructions. -    static constexpr std::size_t SchedPeriod = 4; -    const std::size_t absolute_offset = offset - main_offset; -    return (absolute_offset % SchedPeriod) == 0; +CachedProgram CachedShader::TryLoadProgram(GLenum primitive_mode, +                                           BaseBindings base_bindings) const { +    const auto found = precompiled_programs.find(GetUsage(primitive_mode, base_bindings)); +    if (found == precompiled_programs.end()) { +        return {}; +    } +    return found->second;  } -static std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) { -    constexpr std::size_t start_offset = 10; -    std::size_t offset = start_offset; -    std::size_t size = start_offset * sizeof(u64); -    while (offset < program.size()) { -        const u64 inst = program[offset]; -        if (!IsSchedInstruction(offset, start_offset)) { -            if (inst == 0 || (inst >> 52) == 0x50b) { -                break; -            } +ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, +                                            BaseBindings base_bindings) const { +    return {unique_identifier, base_bindings, primitive_mode}; +} + +ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} + +void ShaderCacheOpenGL::LoadDiskCache() { +    std::vector<ShaderDiskCacheRaw> raws; +    std::vector<ShaderDiskCacheUsage> usages; +    if (!disk_cache.LoadTransferable(raws, usages)) { +        return; +    } + +    std::vector<ShaderDiskCachePrecompiledEntry> precompiled = disk_cache.LoadPrecompiled(); +    const auto SearchPrecompiled = [&precompiled](const ShaderDiskCacheUsage& usage) { +        return std::find_if( +            precompiled.begin(), precompiled.end(), +            [&usage](const auto& precompiled_entry) { return precompiled_entry.usage == usage; }); +    }; + +    const std::set<GLenum> supported_formats{GetSupportedFormats()}; +    const auto unspecialized{GenerateUnspecializedShaders(raws)}; + +    // Build shaders +    for (std::size_t i = 0; i < usages.size(); ++i) { +        const auto& usage{usages[i]}; +        LOG_INFO(Render_OpenGL, "Building shader {:016x} ({} of {})", usage.unique_identifier, +                 i + 1, usages.size()); + +        const auto& unspec{unspecialized.at(usage.unique_identifier)}; + +        const auto precompiled_it = SearchPrecompiled(usage); +        const bool is_precompiled = precompiled_it != precompiled.end(); + +        CachedProgram shader; +        if (is_precompiled) { +            shader = GeneratePrecompiledProgram(precompiled, *precompiled_it, supported_formats); +        } +        if (!shader) { +            shader = SpecializeShader(unspec.code, unspec.entries, unspec.program_type, +                                      usage.bindings, usage.primitive, true); +        } +        precompiled_programs.insert({usage, std::move(shader)}); +    } + +    // TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw before +    // precompiling them + +    for (std::size_t i = 0; i < usages.size(); ++i) { +        const auto& usage{usages[i]}; +        if (SearchPrecompiled(usage) == precompiled.end()) { +            const auto& program = precompiled_programs.at(usage); +            disk_cache.SavePrecompiled(usage, program->handle);          } -        size += sizeof(inst); -        offset++;      } -    return size;  } -void CachedShader::CalculateProperties() { -    setup.program.real_size = CalculateProgramSize(setup.program.code); -    setup.program.real_size_b = 0; -    setup.program.unique_identifier = Common::CityHash64( -        reinterpret_cast<const char*>(setup.program.code.data()), setup.program.real_size); -    if (program_type == Maxwell::ShaderProgram::VertexA) { -        std::size_t seed = 0; -        boost::hash_combine(seed, setup.program.unique_identifier); -        setup.program.real_size_b = CalculateProgramSize(setup.program.code_b); -        const u64 identifier_b = Common::CityHash64( -            reinterpret_cast<const char*>(setup.program.code_b.data()), setup.program.real_size_b); -        boost::hash_combine(seed, identifier_b); -        setup.program.unique_identifier = static_cast<u64>(seed); +CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( +    std::vector<ShaderDiskCachePrecompiledEntry>& precompiled, +    const ShaderDiskCachePrecompiledEntry& precompiled_entry, +    const std::set<GLenum>& supported_formats) { + +    if (supported_formats.find(precompiled_entry.binary_format) == supported_formats.end()) { +        LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing"); +        disk_cache.InvalidatePrecompiled(); +        precompiled.clear(); +        return {};      } + +    CachedProgram shader = std::make_shared<OGLProgram>(); +    shader->handle = glCreateProgram(); +    glProgramBinary(shader->handle, precompiled_entry.binary_format, +                    precompiled_entry.binary.data(), +                    static_cast<GLsizei>(precompiled_entry.binary.size())); + +    GLint link_status{}; +    glGetProgramiv(shader->handle, GL_LINK_STATUS, &link_status); +    if (link_status == GL_FALSE) { +        LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver - removing"); +        disk_cache.InvalidatePrecompiled(); +        precompiled.clear(); + +        shader.reset(); +    } + +    return shader;  } -ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} +std::map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecializedShaders( +    const std::vector<ShaderDiskCacheRaw>& raws) { + +    std::map<u64, UnspecializedShader> unspecialized; +    for (const auto& raw : raws) { +        const u64 calculated_hash = +            GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); +        if (raw.GetUniqueIdentifier() != calculated_hash) { +            LOG_ERROR( +                Render_OpenGL, +                "Invalid hash in entry={:016x} (obtained hash={:016x}) - removing shader cache", +                raw.GetUniqueIdentifier(), calculated_hash); +            disk_cache.InvalidateTransferable(); +            return {}; +        } + +        auto result = +            CreateProgram(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); + +        precompiled_shaders.insert({raw.GetUniqueIdentifier(), result}); -void ShaderCacheOpenGL::LoadDiskCache() {} +        unspecialized.insert( +            {raw.GetUniqueIdentifier(), +             {std::move(result.first), std::move(result.second), raw.GetProgramType()}}); +    } +    return unspecialized; +}  Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {      if (!Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.shaders) { @@ -239,7 +463,23 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {      if (!shader) {          // No shader found - create a new one -        shader = std::make_shared<CachedShader>(program_addr, program); +        ProgramCode program_code = GetShaderCode(program_addr); +        ProgramCode program_code_b; +        if (program == Maxwell::ShaderProgram::VertexA) { +            program_code_b = GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB)); +        } +        const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); + +        const auto found = precompiled_shaders.find(unique_identifier); +        if (found != precompiled_shaders.end()) { +            shader = +                std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache, +                                               precompiled_programs, found->second); +        } else { +            shader = std::make_shared<CachedShader>( +                program_addr, unique_identifier, program, disk_cache, precompiled_programs, +                std::move(program_code), std::move(program_code_b)); +        }          Register(shader);      } diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 18fb80bcc..763a47bce 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -7,6 +7,7 @@  #include <array>  #include <map>  #include <memory> +#include <set>  #include <tuple>  #include <glad/glad.h> @@ -23,13 +24,25 @@ namespace OpenGL {  class CachedShader;  class RasterizerOpenGL; +struct UnspecializedShader;  using Shader = std::shared_ptr<CachedShader>; +using CachedProgram = std::shared_ptr<OGLProgram>;  using Maxwell = Tegra::Engines::Maxwell3D::Regs; +using PrecompiledPrograms = std::map<ShaderDiskCacheUsage, CachedProgram>; +using PrecompiledShaders = std::map<u64, GLShader::ProgramResult>;  class CachedShader final : public RasterizerCacheObject {  public: -    CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); +    explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, +                          ShaderDiskCacheOpenGL& disk_cache, +                          const PrecompiledPrograms& precompiled_programs, +                          ProgramCode&& program_code, ProgramCode&& program_code_b); + +    explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, +                          ShaderDiskCacheOpenGL& disk_cache, +                          const PrecompiledPrograms& precompiled_programs, +                          GLShader::ProgramResult result);      VAddr GetAddr() const override {          return addr; @@ -56,33 +69,35 @@ private:      // declared by the hardware. Workaround this issue by generating a different shader per input      // topology class.      struct GeometryPrograms { -        OGLProgram points; -        OGLProgram lines; -        OGLProgram lines_adjacency; -        OGLProgram triangles; -        OGLProgram triangles_adjacency; +        CachedProgram points; +        CachedProgram lines; +        CachedProgram lines_adjacency; +        CachedProgram triangles; +        CachedProgram triangles_adjacency;      }; -    std::string AllocateBindings(BaseBindings base_bindings); -      GLuint GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings);      /// Generates a geometry shader or returns one that already exists. -    GLuint LazyGeometryProgram(OGLProgram& target_program, BaseBindings base_bindings, -                               const std::string& glsl_topology, u32 max_vertices, -                               const std::string& debug_name); +    GLuint LazyGeometryProgram(CachedProgram& target_program, BaseBindings base_bindings, +                               GLenum primitive_mode); + +    CachedProgram TryLoadProgram(GLenum primitive_mode, BaseBindings base_bindings) const; + +    ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const; -    void CalculateProperties(); +    const VAddr addr; +    const u64 unique_identifier; +    const Maxwell::ShaderProgram program_type; +    ShaderDiskCacheOpenGL& disk_cache; +    const PrecompiledPrograms& precompiled_programs; -    VAddr addr{};      std::size_t shader_length{}; -    Maxwell::ShaderProgram program_type{}; -    GLShader::ShaderSetup setup;      GLShader::ShaderEntries entries;      std::string code; -    std::map<BaseBindings, OGLProgram> programs; +    std::map<BaseBindings, CachedProgram> programs;      std::map<BaseBindings, GeometryPrograms> geometry_programs;      std::map<u32, GLuint> cbuf_resource_cache; @@ -101,7 +116,19 @@ public:      Shader GetStageProgram(Maxwell::ShaderProgram program);  private: +    std::map<u64, UnspecializedShader> GenerateUnspecializedShaders( +        const std::vector<ShaderDiskCacheRaw>& raws); + +    CachedProgram GeneratePrecompiledProgram( +        std::vector<ShaderDiskCachePrecompiledEntry>& precompiled, +        const ShaderDiskCachePrecompiledEntry& precompiled_entry, +        const std::set<GLenum>& supported_formats); +      std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; + +    ShaderDiskCacheOpenGL disk_cache; +    PrecompiledShaders precompiled_shaders; +    PrecompiledPrograms precompiled_programs;  };  } // namespace OpenGL  | 
