diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 363b941f36..a70ff79d0d 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(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 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(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(code_b.data()), + CalculateProgramSize(code_b)); + boost::hash_combine(seed, identifier_b); + return static_cast(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(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(); + program->Create(true, hint_retrievable, shader.handle); + return program; +} + +std::set GetSupportedFormats() { + std::set supported_formats; + + GLint num_formats{}; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats); + + std::vector formats(num_formats); + glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data()); + + for (const GLint format : formats) + supported_formats.insert(static_cast(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(code_size / sizeof(u64)), + static_cast(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 CachedShader::GetProgramHandle(GLenum primitive_mode, @@ -94,138 +261,195 @@ std::tuple 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(entries.const_buffers.size()) + 1; + base_bindings.cbuf += static_cast(entries.const_buffers.size()) + RESERVED_UBOS; base_bindings.gmem += static_cast(entries.global_memory_entries.size()); base_bindings.sampler += static_cast(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; - } - } - 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(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(setup.program.code_b.data()), setup.program.real_size_b); - boost::hash_combine(seed, identifier_b); - setup.program.unique_identifier = static_cast(seed); - } +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() {} +void ShaderCacheOpenGL::LoadDiskCache() { + std::vector raws; + std::vector usages; + if (!disk_cache.LoadTransferable(raws, usages)) { + return; + } + + std::vector 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 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); + } + } +} + +CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( + std::vector& precompiled, + const ShaderDiskCachePrecompiledEntry& precompiled_entry, + const std::set& 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(); + shader->handle = glCreateProgram(); + glProgramBinary(shader->handle, precompiled_entry.binary_format, + precompiled_entry.binary.data(), + static_cast(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; +} + +std::map ShaderCacheOpenGL::GenerateUnspecializedShaders( + const std::vector& raws) { + + std::map 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}); + + 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(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(program_addr, unique_identifier, program, disk_cache, + precompiled_programs, found->second); + } else { + shader = std::make_shared( + 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 18fb80bccd..763a47bce0 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 #include #include +#include #include #include @@ -23,13 +24,25 @@ namespace OpenGL { class CachedShader; class RasterizerOpenGL; +struct UnspecializedShader; using Shader = std::shared_ptr; +using CachedProgram = std::shared_ptr; using Maxwell = Tegra::Engines::Maxwell3D::Regs; +using PrecompiledPrograms = std::map; +using PrecompiledShaders = std::map; 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); - void CalculateProperties(); + CachedProgram TryLoadProgram(GLenum primitive_mode, BaseBindings base_bindings) const; + + ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const; + + 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 programs; + std::map programs; std::map geometry_programs; std::map cbuf_resource_cache; @@ -101,7 +116,19 @@ public: Shader GetStageProgram(Maxwell::ShaderProgram program); private: + std::map GenerateUnspecializedShaders( + const std::vector& raws); + + CachedProgram GeneratePrecompiledProgram( + std::vector& precompiled, + const ShaderDiskCachePrecompiledEntry& precompiled_entry, + const std::set& supported_formats); + std::array last_shaders; + + ShaderDiskCacheOpenGL disk_cache; + PrecompiledShaders precompiled_shaders; + PrecompiledPrograms precompiled_programs; }; } // namespace OpenGL