gl_rasterizer: Track texture buffer usage
This commit is contained in:
		| @@ -29,8 +29,10 @@ | ||||
| namespace OpenGL { | ||||
|  | ||||
| using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||||
| using PixelFormat = VideoCore::Surface::PixelFormat; | ||||
| using SurfaceType = VideoCore::Surface::SurfaceType; | ||||
|  | ||||
| using VideoCore::Surface::PixelFormat; | ||||
| using VideoCore::Surface::SurfaceTarget; | ||||
| using VideoCore::Surface::SurfaceType; | ||||
|  | ||||
| MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Format Setup", MP_RGB(128, 128, 192)); | ||||
| MICROPROFILE_DEFINE(OpenGL_VB, "OpenGL", "Vertex Buffer Setup", MP_RGB(128, 128, 192)); | ||||
| @@ -281,8 +283,14 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | ||||
|                                  static_cast<GLsizeiptr>(sizeof(ubo))); | ||||
|  | ||||
|         Shader shader{shader_cache.GetStageProgram(program)}; | ||||
|         const auto [program_handle, next_bindings] = | ||||
|             shader->GetProgramHandle(primitive_mode, base_bindings); | ||||
|  | ||||
|         const auto stage_enum{static_cast<Maxwell::ShaderStage>(stage)}; | ||||
|         SetupDrawConstBuffers(stage_enum, shader); | ||||
|         SetupGlobalRegions(stage_enum, shader); | ||||
|         const auto texture_buffer_usage{SetupTextures(stage_enum, shader, base_bindings)}; | ||||
|  | ||||
|         const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage}; | ||||
|         const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant); | ||||
|  | ||||
|         switch (program) { | ||||
|         case Maxwell::ShaderProgram::VertexA: | ||||
| @@ -300,11 +308,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | ||||
|                               shader_config.enable.Value(), shader_config.offset); | ||||
|         } | ||||
|  | ||||
|         const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage); | ||||
|         SetupDrawConstBuffers(stage_enum, shader); | ||||
|         SetupGlobalRegions(stage_enum, shader); | ||||
|         SetupTextures(stage_enum, shader, base_bindings); | ||||
|  | ||||
|         // Workaround for Intel drivers. | ||||
|         // When a clip distance is enabled but not set in the shader it crops parts of the screen | ||||
|         // (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the | ||||
| @@ -791,8 +794,8 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade | ||||
|     } | ||||
| } | ||||
|  | ||||
| void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader, | ||||
|                                      BaseBindings base_bindings) { | ||||
| TextureBufferUsage RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader, | ||||
|                                                    BaseBindings base_bindings) { | ||||
|     MICROPROFILE_SCOPE(OpenGL_Texture); | ||||
|     const auto& gpu = system.GPU(); | ||||
|     const auto& maxwell3d = gpu.Maxwell3D(); | ||||
| @@ -801,6 +804,8 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s | ||||
|     ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.texture_units), | ||||
|                "Exceeded the number of active textures."); | ||||
|  | ||||
|     TextureBufferUsage texture_buffer_usage{0}; | ||||
|  | ||||
|     for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | ||||
|         const auto& entry = entries[bindpoint]; | ||||
|         Tegra::Texture::FullTextureInfo texture; | ||||
| @@ -814,7 +819,8 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s | ||||
|         } | ||||
|         const u32 current_bindpoint = base_bindings.sampler + bindpoint; | ||||
|  | ||||
|         state.texture_units[current_bindpoint].sampler = sampler_cache.GetSampler(texture.tsc); | ||||
|         auto& unit{state.texture_units[current_bindpoint]}; | ||||
|         unit.sampler = sampler_cache.GetSampler(texture.tsc); | ||||
|  | ||||
|         if (const auto view{texture_cache.GetTextureSurface(texture, entry)}; view) { | ||||
|             view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source, | ||||
| @@ -822,9 +828,11 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s | ||||
|             state.texture_units[current_bindpoint].texture = view->GetTexture(); | ||||
|         } else { | ||||
|             // Can occur when texture addr is null or its memory is unmapped/invalid | ||||
|             state.texture_units[current_bindpoint].texture = 0; | ||||
|             unit.texture = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return texture_buffer_usage; | ||||
| } | ||||
|  | ||||
| void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { | ||||
|   | ||||
| @@ -126,9 +126,10 @@ private: | ||||
|     void SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, | ||||
|                             const Shader& shader); | ||||
|  | ||||
|     /// Configures the current textures to use for the draw command. | ||||
|     void SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader, | ||||
|                        BaseBindings base_bindings); | ||||
|     /// Configures the current textures to use for the draw command. Returns shaders texture buffer | ||||
|     /// usage. | ||||
|     TextureBufferUsage SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, | ||||
|                                      const Shader& shader, BaseBindings base_bindings); | ||||
|  | ||||
|     /// Syncs the viewport and depth range to match the guest state | ||||
|     void SyncViewport(OpenGLState& current_state); | ||||
|   | ||||
| @@ -168,8 +168,12 @@ GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgr | ||||
| } | ||||
|  | ||||
| CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries, | ||||
|                                Maxwell::ShaderProgram program_type, BaseBindings base_bindings, | ||||
|                                GLenum primitive_mode, bool hint_retrievable = false) { | ||||
|                                Maxwell::ShaderProgram program_type, const ProgramVariant& variant, | ||||
|                                bool hint_retrievable = false) { | ||||
|     auto base_bindings{variant.base_bindings}; | ||||
|     const auto primitive_mode{variant.primitive_mode}; | ||||
|     const auto texture_buffer_usage{variant.texture_buffer_usage}; | ||||
|  | ||||
|     std::string source = "#version 430 core\n" | ||||
|                          "#extension GL_ARB_separate_shader_objects : enable\n\n"; | ||||
|     source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); | ||||
| @@ -187,6 +191,14 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn | ||||
|                               base_bindings.sampler++); | ||||
|     } | ||||
|  | ||||
|     // Transform 1D textures to texture samplers by declaring its preprocessor macros. | ||||
|     for (std::size_t i = 0; i < texture_buffer_usage.size(); ++i) { | ||||
|         if (!texture_buffer_usage.test(i)) { | ||||
|             continue; | ||||
|         } | ||||
|         source += fmt::format("#define SAMPLER_{}_IS_BUFFER", i); | ||||
|     } | ||||
|  | ||||
|     if (program_type == Maxwell::ShaderProgram::Geometry) { | ||||
|         const auto [glsl_topology, debug_name, max_vertices] = | ||||
|             GetPrimitiveDescription(primitive_mode); | ||||
| @@ -261,20 +273,18 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, | ||||
|     shader_length = entries.shader_length; | ||||
| } | ||||
|  | ||||
| std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive_mode, | ||||
|                                                                 BaseBindings base_bindings) { | ||||
| std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) { | ||||
|     GLuint handle{}; | ||||
|     if (program_type == Maxwell::ShaderProgram::Geometry) { | ||||
|         handle = GetGeometryShader(primitive_mode, base_bindings); | ||||
|         handle = GetGeometryShader(variant); | ||||
|     } else { | ||||
|         const auto [entry, is_cache_miss] = programs.try_emplace(base_bindings); | ||||
|         const auto [entry, is_cache_miss] = programs.try_emplace(variant); | ||||
|         auto& program = entry->second; | ||||
|         if (is_cache_miss) { | ||||
|             program = TryLoadProgram(primitive_mode, base_bindings); | ||||
|             program = TryLoadProgram(variant); | ||||
|             if (!program) { | ||||
|                 program = | ||||
|                     SpecializeShader(code, entries, program_type, base_bindings, primitive_mode); | ||||
|                 disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); | ||||
|                 program = SpecializeShader(code, entries, program_type, variant); | ||||
|                 disk_cache.SaveUsage(GetUsage(variant)); | ||||
|             } | ||||
|  | ||||
|             LabelGLObject(GL_PROGRAM, program->handle, cpu_addr); | ||||
| @@ -283,6 +293,7 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive | ||||
|         handle = program->handle; | ||||
|     } | ||||
|  | ||||
|     auto base_bindings{variant.base_bindings}; | ||||
|     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()); | ||||
| @@ -290,43 +301,42 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive | ||||
|     return {handle, base_bindings}; | ||||
| } | ||||
|  | ||||
| GLuint CachedShader::GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings) { | ||||
|     const auto [entry, is_cache_miss] = geometry_programs.try_emplace(base_bindings); | ||||
| GLuint CachedShader::GetGeometryShader(const ProgramVariant& variant) { | ||||
|     const auto [entry, is_cache_miss] = geometry_programs.try_emplace(variant); | ||||
|     auto& programs = entry->second; | ||||
|  | ||||
|     switch (primitive_mode) { | ||||
|     switch (variant.primitive_mode) { | ||||
|     case GL_POINTS: | ||||
|         return LazyGeometryProgram(programs.points, base_bindings, primitive_mode); | ||||
|         return LazyGeometryProgram(programs.points, variant); | ||||
|     case GL_LINES: | ||||
|     case GL_LINE_STRIP: | ||||
|         return LazyGeometryProgram(programs.lines, base_bindings, primitive_mode); | ||||
|         return LazyGeometryProgram(programs.lines, variant); | ||||
|     case GL_LINES_ADJACENCY: | ||||
|     case GL_LINE_STRIP_ADJACENCY: | ||||
|         return LazyGeometryProgram(programs.lines_adjacency, base_bindings, primitive_mode); | ||||
|         return LazyGeometryProgram(programs.lines_adjacency, variant); | ||||
|     case GL_TRIANGLES: | ||||
|     case GL_TRIANGLE_STRIP: | ||||
|     case GL_TRIANGLE_FAN: | ||||
|         return LazyGeometryProgram(programs.triangles, base_bindings, primitive_mode); | ||||
|         return LazyGeometryProgram(programs.triangles, variant); | ||||
|     case GL_TRIANGLES_ADJACENCY: | ||||
|     case GL_TRIANGLE_STRIP_ADJACENCY: | ||||
|         return LazyGeometryProgram(programs.triangles_adjacency, base_bindings, primitive_mode); | ||||
|         return LazyGeometryProgram(programs.triangles_adjacency, variant); | ||||
|     default: | ||||
|         UNREACHABLE_MSG("Unknown primitive mode."); | ||||
|         return LazyGeometryProgram(programs.points, base_bindings, primitive_mode); | ||||
|         return LazyGeometryProgram(programs.points, variant); | ||||
|     } | ||||
| } | ||||
|  | ||||
| GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBindings base_bindings, | ||||
|                                          GLenum primitive_mode) { | ||||
| GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, | ||||
|                                          const ProgramVariant& variant) { | ||||
|     if (target_program) { | ||||
|         return target_program->handle; | ||||
|     } | ||||
|     const auto [glsl_name, debug_name, vertices] = GetPrimitiveDescription(primitive_mode); | ||||
|     target_program = TryLoadProgram(primitive_mode, base_bindings); | ||||
|     const auto [glsl_name, debug_name, vertices] = GetPrimitiveDescription(variant.primitive_mode); | ||||
|     target_program = TryLoadProgram(variant); | ||||
|     if (!target_program) { | ||||
|         target_program = | ||||
|             SpecializeShader(code, entries, program_type, base_bindings, primitive_mode); | ||||
|         disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); | ||||
|         target_program = SpecializeShader(code, entries, program_type, variant); | ||||
|         disk_cache.SaveUsage(GetUsage(variant)); | ||||
|     } | ||||
|  | ||||
|     LabelGLObject(GL_PROGRAM, target_program->handle, cpu_addr, debug_name); | ||||
| @@ -334,18 +344,19 @@ GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBind | ||||
|     return target_program->handle; | ||||
| }; | ||||
|  | ||||
| CachedProgram CachedShader::TryLoadProgram(GLenum primitive_mode, | ||||
|                                            BaseBindings base_bindings) const { | ||||
|     const auto found = precompiled_programs.find(GetUsage(primitive_mode, base_bindings)); | ||||
| CachedProgram CachedShader::TryLoadProgram(const ProgramVariant& variant) const { | ||||
|     const auto found = precompiled_programs.find(GetUsage(variant)); | ||||
|     if (found == precompiled_programs.end()) { | ||||
|         return {}; | ||||
|     } | ||||
|     return found->second; | ||||
| } | ||||
|  | ||||
| ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, | ||||
|                                             BaseBindings base_bindings) const { | ||||
|     return {unique_identifier, base_bindings, primitive_mode}; | ||||
| ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant) const { | ||||
|     ShaderDiskCacheUsage usage; | ||||
|     usage.unique_identifier = unique_identifier; | ||||
|     usage.variant = variant; | ||||
|     return usage; | ||||
| } | ||||
|  | ||||
| ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, | ||||
| @@ -411,8 +422,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | ||||
|             } | ||||
|             if (!shader) { | ||||
|                 shader = SpecializeShader(unspecialized.code, unspecialized.entries, | ||||
|                                           unspecialized.program_type, usage.bindings, | ||||
|                                           usage.primitive, true); | ||||
|                                           unspecialized.program_type, usage.variant, true); | ||||
|             } | ||||
|  | ||||
|             std::scoped_lock lock(mutex); | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| #include <array> | ||||
| #include <atomic> | ||||
| #include <bitset> | ||||
| #include <memory> | ||||
| #include <set> | ||||
| #include <tuple> | ||||
| @@ -67,8 +68,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     /// Gets the GL program handle for the shader | ||||
|     std::tuple<GLuint, BaseBindings> GetProgramHandle(GLenum primitive_mode, | ||||
|                                                       BaseBindings base_bindings); | ||||
|     std::tuple<GLuint, BaseBindings> GetProgramHandle(const ProgramVariant& variant); | ||||
|  | ||||
| private: | ||||
|     // Geometry programs. These are needed because GLSL needs an input topology but it's not | ||||
| @@ -82,15 +82,14 @@ private: | ||||
|         CachedProgram triangles_adjacency; | ||||
|     }; | ||||
|  | ||||
|     GLuint GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings); | ||||
|     GLuint GetGeometryShader(const ProgramVariant& variant); | ||||
|  | ||||
|     /// Generates a geometry shader or returns one that already exists. | ||||
|     GLuint LazyGeometryProgram(CachedProgram& target_program, BaseBindings base_bindings, | ||||
|                                GLenum primitive_mode); | ||||
|     GLuint LazyGeometryProgram(CachedProgram& target_program, const ProgramVariant& variant); | ||||
|  | ||||
|     CachedProgram TryLoadProgram(GLenum primitive_mode, BaseBindings base_bindings) const; | ||||
|     CachedProgram TryLoadProgram(const ProgramVariant& variant) const; | ||||
|  | ||||
|     ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const; | ||||
|     ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant) const; | ||||
|  | ||||
|     u8* host_ptr{}; | ||||
|     VAddr cpu_addr{}; | ||||
| @@ -104,8 +103,8 @@ private: | ||||
|  | ||||
|     std::string code; | ||||
|  | ||||
|     std::unordered_map<BaseBindings, CachedProgram> programs; | ||||
|     std::unordered_map<BaseBindings, GeometryPrograms> geometry_programs; | ||||
|     std::unordered_map<ProgramVariant, CachedProgram> programs; | ||||
|     std::unordered_map<ProgramVariant, GeometryPrograms> geometry_programs; | ||||
|  | ||||
|     std::unordered_map<u32, GLuint> cbuf_resource_cache; | ||||
|     std::unordered_map<u32, GLuint> gmem_resource_cache; | ||||
|   | ||||
| @@ -34,11 +34,11 @@ enum class PrecompiledEntryKind : u32 { | ||||
|     Dump, | ||||
| }; | ||||
|  | ||||
| constexpr u32 NativeVersion = 1; | ||||
| constexpr u32 NativeVersion = 2; | ||||
|  | ||||
| // Making sure sizes doesn't change by accident | ||||
| static_assert(sizeof(BaseBindings) == 12); | ||||
| static_assert(sizeof(ShaderDiskCacheUsage) == 24); | ||||
| static_assert(sizeof(ShaderDiskCacheUsage) == 32); | ||||
|  | ||||
| namespace { | ||||
|  | ||||
|   | ||||
| @@ -30,15 +30,17 @@ class IOFile; | ||||
|  | ||||
| namespace OpenGL { | ||||
|  | ||||
| using ProgramCode = std::vector<u64>; | ||||
| using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||||
|  | ||||
| struct ShaderDiskCacheUsage; | ||||
| struct ShaderDiskCacheDump; | ||||
|  | ||||
| using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>; | ||||
|  | ||||
| /// Allocated bindings used by an OpenGL shader program | ||||
| using ProgramCode = std::vector<u64>; | ||||
| using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||||
|  | ||||
| using TextureBufferUsage = std::bitset<64>; | ||||
|  | ||||
| /// Allocated bindings used by an OpenGL shader program. | ||||
| struct BaseBindings { | ||||
|     u32 cbuf{}; | ||||
|     u32 gmem{}; | ||||
| @@ -53,15 +55,29 @@ struct BaseBindings { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /// Describes how a shader is used | ||||
| /// Describes the different variants a single program can be compiled. | ||||
| struct ProgramVariant { | ||||
|     BaseBindings base_bindings; | ||||
|     GLenum primitive_mode{}; | ||||
|     TextureBufferUsage texture_buffer_usage{}; | ||||
|  | ||||
|     bool operator==(const ProgramVariant& rhs) const { | ||||
|         return std::tie(base_bindings, primitive_mode, texture_buffer_usage) == | ||||
|                std::tie(rhs.base_bindings, rhs.primitive_mode, rhs.texture_buffer_usage); | ||||
|     } | ||||
|  | ||||
|     bool operator!=(const ProgramVariant& rhs) const { | ||||
|         return !operator==(rhs); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /// Describes how a shader is used. | ||||
| struct ShaderDiskCacheUsage { | ||||
|     u64 unique_identifier{}; | ||||
|     BaseBindings bindings; | ||||
|     GLenum primitive{}; | ||||
|     ProgramVariant variant; | ||||
|  | ||||
|     bool operator==(const ShaderDiskCacheUsage& rhs) const { | ||||
|         return std::tie(unique_identifier, bindings, primitive) == | ||||
|                std::tie(rhs.unique_identifier, rhs.bindings, rhs.primitive); | ||||
|         return std::tie(unique_identifier, variant) == std::tie(rhs.unique_identifier, rhs.variant); | ||||
|     } | ||||
|  | ||||
|     bool operator!=(const ShaderDiskCacheUsage& rhs) const { | ||||
| @@ -80,11 +96,20 @@ struct hash<OpenGL::BaseBindings> { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <> | ||||
| struct hash<OpenGL::ProgramVariant> { | ||||
|     std::size_t operator()(const OpenGL::ProgramVariant& variant) const { | ||||
|         return std::hash<OpenGL::BaseBindings>()(variant.base_bindings) ^ | ||||
|                std::hash<OpenGL::TextureBufferUsage>()(variant.texture_buffer_usage) ^ | ||||
|                (static_cast<std::size_t>(variant.primitive_mode) << 6); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <> | ||||
| struct hash<OpenGL::ShaderDiskCacheUsage> { | ||||
|     std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept { | ||||
|         return static_cast<std::size_t>(usage.unique_identifier) ^ | ||||
|                std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16; | ||||
|                std::hash<OpenGL::ProgramVariant>()(usage.variant); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @@ -288,13 +313,15 @@ private: | ||||
|  | ||||
|     // Core system | ||||
|     Core::System& system; | ||||
|     // Stored transferable shaders | ||||
|     std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; | ||||
|     // Stores whole precompiled cache which will be read from/saved to the precompiled cache file | ||||
|     // Stores whole precompiled cache which will be read from or saved to the precompiled chache | ||||
|     // file | ||||
|     FileSys::VectorVfsFile precompiled_cache_virtual_file; | ||||
|     // Stores the current offset of the precompiled cache file for IO purposes | ||||
|     std::size_t precompiled_cache_virtual_file_offset = 0; | ||||
|  | ||||
|     // Stored transferable shaders | ||||
|     std::unordered_map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; | ||||
|  | ||||
|     // The cache has been loaded at boot | ||||
|     bool tried_to_load{}; | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 ReinUsesLisp
					ReinUsesLisp