1 // Copyright 2018 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <algorithm>
6 #include <thread>
7 #include <unordered_map>
8 #include <boost/functional/hash.hpp>
9 #include <boost/variant.hpp>
10 #include "core/core.h"
11 #include "video_core/renderer_opengl/gl_shader_disk_cache.h"
12 #include "video_core/renderer_opengl/gl_shader_manager.h"
13 #include "video_core/video_core.h"
14 
15 namespace OpenGL {
16 
GetUniqueIdentifier(const Pica::Regs & regs,const ProgramCode & code)17 static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) {
18     std::size_t hash = 0;
19     u64 regs_uid = Common::ComputeHash64(regs.reg_array.data(), Pica::Regs::NUM_REGS * sizeof(u32));
20     boost::hash_combine(hash, regs_uid);
21     if (code.size() > 0) {
22         u64 code_uid = Common::ComputeHash64(code.data(), code.size() * sizeof(u32));
23         boost::hash_combine(hash, code_uid);
24     }
25     return static_cast<u64>(hash);
26 }
27 
GeneratePrecompiledProgram(const ShaderDiskCacheDump & dump,const std::set<GLenum> & supported_formats)28 static OGLProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
29                                              const std::set<GLenum>& supported_formats) {
30 
31     if (supported_formats.find(dump.binary_format) == supported_formats.end()) {
32         LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing");
33         return {};
34     }
35 
36     auto shader = OGLProgram();
37     shader.handle = glCreateProgram();
38     glProgramParameteri(shader.handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
39     glProgramBinary(shader.handle, dump.binary_format, dump.binary.data(),
40                     static_cast<GLsizei>(dump.binary.size()));
41 
42     GLint link_status{};
43     glGetProgramiv(shader.handle, GL_LINK_STATUS, &link_status);
44     if (link_status == GL_FALSE) {
45         LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver - removing");
46         return {};
47     }
48 
49     return shader;
50 }
51 
GetSupportedFormats()52 static std::set<GLenum> GetSupportedFormats() {
53     std::set<GLenum> supported_formats;
54 
55     GLint num_formats{};
56     glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
57 
58     std::vector<GLint> formats(num_formats);
59     glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
60 
61     for (const GLint format : formats)
62         supported_formats.insert(static_cast<GLenum>(format));
63     return supported_formats;
64 }
65 
BuildVSConfigFromRaw(const ShaderDiskCacheRaw & raw)66 static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw(
67     const ShaderDiskCacheRaw& raw) {
68     Pica::Shader::ProgramCode program_code{};
69     Pica::Shader::SwizzleData swizzle_data{};
70     std::copy_n(raw.GetProgramCode().begin(), Pica::Shader::MAX_PROGRAM_CODE_LENGTH,
71                 program_code.begin());
72     std::copy_n(raw.GetProgramCode().begin() + Pica::Shader::MAX_PROGRAM_CODE_LENGTH,
73                 Pica::Shader::MAX_SWIZZLE_DATA_LENGTH, swizzle_data.begin());
74     Pica::Shader::ShaderSetup setup;
75     setup.program_code = program_code;
76     setup.swizzle_data = swizzle_data;
77     return {PicaVSConfig{raw.GetRawShaderConfig().vs, setup}, setup};
78 }
79 
SetShaderUniformBlockBinding(GLuint shader,const char * name,UniformBindings binding,std::size_t expected_size)80 static void SetShaderUniformBlockBinding(GLuint shader, const char* name, UniformBindings binding,
81                                          std::size_t expected_size) {
82     const GLuint ub_index = glGetUniformBlockIndex(shader, name);
83     if (ub_index == GL_INVALID_INDEX) {
84         return;
85     }
86     GLint ub_size = 0;
87     glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size);
88     ASSERT_MSG(ub_size == expected_size, "Uniform block size did not match! Got {}, expected {}",
89                static_cast<int>(ub_size), expected_size);
90     glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding));
91 }
92 
SetShaderUniformBlockBindings(GLuint shader)93 static void SetShaderUniformBlockBindings(GLuint shader) {
94     SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common,
95                                  sizeof(UniformData));
96     SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS, sizeof(VSUniformData));
97 }
98 
SetShaderSamplerBinding(GLuint shader,const char * name,TextureUnits::TextureUnit binding)99 static void SetShaderSamplerBinding(GLuint shader, const char* name,
100                                     TextureUnits::TextureUnit binding) {
101     GLint uniform_tex = glGetUniformLocation(shader, name);
102     if (uniform_tex != -1) {
103         glUniform1i(uniform_tex, binding.id);
104     }
105 }
106 
SetShaderImageBinding(GLuint shader,const char * name,GLuint binding)107 static void SetShaderImageBinding(GLuint shader, const char* name, GLuint binding) {
108     GLint uniform_tex = glGetUniformLocation(shader, name);
109     if (uniform_tex != -1) {
110         glUniform1i(uniform_tex, static_cast<GLint>(binding));
111     }
112 }
113 
SetShaderSamplerBindings(GLuint shader)114 static void SetShaderSamplerBindings(GLuint shader) {
115     OpenGLState cur_state = OpenGLState::GetCurState();
116     GLuint old_program = std::exchange(cur_state.draw.shader_program, shader);
117     cur_state.Apply();
118 
119     // Set the texture samplers to correspond to different texture units
120     SetShaderSamplerBinding(shader, "tex0", TextureUnits::PicaTexture(0));
121     SetShaderSamplerBinding(shader, "tex1", TextureUnits::PicaTexture(1));
122     SetShaderSamplerBinding(shader, "tex2", TextureUnits::PicaTexture(2));
123     SetShaderSamplerBinding(shader, "tex_cube", TextureUnits::TextureCube);
124 
125     // Set the texture samplers to correspond to different lookup table texture units
126     SetShaderSamplerBinding(shader, "texture_buffer_lut_lf", TextureUnits::TextureBufferLUT_LF);
127     SetShaderSamplerBinding(shader, "texture_buffer_lut_rg", TextureUnits::TextureBufferLUT_RG);
128     SetShaderSamplerBinding(shader, "texture_buffer_lut_rgba", TextureUnits::TextureBufferLUT_RGBA);
129 
130     SetShaderImageBinding(shader, "shadow_buffer", ImageUnits::ShadowBuffer);
131     SetShaderImageBinding(shader, "shadow_texture_px", ImageUnits::ShadowTexturePX);
132     SetShaderImageBinding(shader, "shadow_texture_nx", ImageUnits::ShadowTextureNX);
133     SetShaderImageBinding(shader, "shadow_texture_py", ImageUnits::ShadowTexturePY);
134     SetShaderImageBinding(shader, "shadow_texture_ny", ImageUnits::ShadowTextureNY);
135     SetShaderImageBinding(shader, "shadow_texture_pz", ImageUnits::ShadowTexturePZ);
136     SetShaderImageBinding(shader, "shadow_texture_nz", ImageUnits::ShadowTextureNZ);
137 
138     cur_state.draw.shader_program = old_program;
139     cur_state.Apply();
140 }
141 
SetFromRegs(const Pica::ShaderRegs & regs,const Pica::Shader::ShaderSetup & setup)142 void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs,
143                                    const Pica::Shader::ShaderSetup& setup) {
144     std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools),
145                    [](bool value) -> BoolAligned { return {value ? GL_TRUE : GL_FALSE}; });
146     std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i),
147                    [](const auto& value) -> GLuvec4 {
148                        return {value.x.Value(), value.y.Value(), value.z.Value(), value.w.Value()};
149                    });
150     std::transform(std::begin(setup.uniforms.f), std::end(setup.uniforms.f), std::begin(f),
151                    [](const auto& value) -> GLvec4 {
152                        return {value.x.ToFloat32(), value.y.ToFloat32(), value.z.ToFloat32(),
153                                value.w.ToFloat32()};
154                    });
155 }
156 
157 /**
158  * An object representing a shader program staging. It can be either a shader object or a program
159  * object, depending on whether separable program is used.
160  */
161 class OGLShaderStage {
162 public:
OGLShaderStage(bool separable)163     explicit OGLShaderStage(bool separable) {
164         if (separable) {
165             shader_or_program = OGLProgram();
166         } else {
167             shader_or_program = OGLShader();
168         }
169     }
170 
Create(const char * source,GLenum type)171     void Create(const char* source, GLenum type) {
172         if (shader_or_program.which() == 0) {
173             boost::get<OGLShader>(shader_or_program).Create(source, type);
174         } else {
175             OGLShader shader;
176             shader.Create(source, type);
177             OGLProgram& program = boost::get<OGLProgram>(shader_or_program);
178             program.Create(true, {shader.handle});
179             SetShaderUniformBlockBindings(program.handle);
180 
181             if (type == GL_FRAGMENT_SHADER) {
182                 SetShaderSamplerBindings(program.handle);
183             }
184         }
185     }
186 
GetHandle() const187     GLuint GetHandle() const {
188         if (shader_or_program.which() == 0) {
189             return boost::get<OGLShader>(shader_or_program).handle;
190         } else {
191             return boost::get<OGLProgram>(shader_or_program).handle;
192         }
193     }
194 
Inject(OGLProgram && program)195     void Inject(OGLProgram&& program) {
196         SetShaderUniformBlockBindings(program.handle);
197         SetShaderSamplerBindings(program.handle);
198         shader_or_program = std::move(program);
199     }
200 
201 private:
202     boost::variant<OGLShader, OGLProgram> shader_or_program;
203 };
204 
205 class TrivialVertexShader {
206 public:
TrivialVertexShader(bool separable)207     explicit TrivialVertexShader(bool separable) : program(separable) {
208         program.Create(GenerateTrivialVertexShader(separable).code.c_str(), GL_VERTEX_SHADER);
209     }
Get() const210     GLuint Get() const {
211         return program.GetHandle();
212     }
213 
214 private:
215     OGLShaderStage program;
216 };
217 
218 template <typename KeyConfigType,
219           ShaderDecompiler::ProgramResult (*CodeGenerator)(const KeyConfigType&, bool),
220           GLenum ShaderType>
221 class ShaderCache {
222 public:
ShaderCache(bool separable)223     explicit ShaderCache(bool separable) : separable(separable) {}
Get(const KeyConfigType & config)224     std::tuple<GLuint, std::optional<ShaderDecompiler::ProgramResult>> Get(
225         const KeyConfigType& config) {
226         auto [iter, new_shader] = shaders.emplace(config, OGLShaderStage{separable});
227         OGLShaderStage& cached_shader = iter->second;
228         std::optional<ShaderDecompiler::ProgramResult> result{};
229         if (new_shader) {
230             result = CodeGenerator(config, separable);
231             cached_shader.Create(result->code.c_str(), ShaderType);
232         }
233         return {cached_shader.GetHandle(), std::move(result)};
234     }
235 
Inject(const KeyConfigType & key,OGLProgram && program)236     void Inject(const KeyConfigType& key, OGLProgram&& program) {
237         OGLShaderStage stage{separable};
238         stage.Inject(std::move(program));
239         shaders.emplace(key, std::move(stage));
240     }
241 
242 private:
243     bool separable;
244     std::unordered_map<KeyConfigType, OGLShaderStage> shaders;
245 };
246 
247 // This is a cache designed for shaders translated from PICA shaders. The first cache matches the
248 // config structure like a normal cache does. On cache miss, the second cache matches the generated
249 // GLSL code. The configuration is like this because there might be leftover code in the PICA shader
250 // program buffer from the previous shader, which is hashed into the config, resulting several
251 // different config values from the same shader program.
252 template <typename KeyConfigType,
253           std::optional<ShaderDecompiler::ProgramResult> (*CodeGenerator)(
254               const Pica::Shader::ShaderSetup&, const KeyConfigType&, bool),
255           GLenum ShaderType>
256 class ShaderDoubleCache {
257 public:
ShaderDoubleCache(bool separable)258     explicit ShaderDoubleCache(bool separable) : separable(separable) {}
Get(const KeyConfigType & key,const Pica::Shader::ShaderSetup & setup)259     std::tuple<GLuint, std::optional<ShaderDecompiler::ProgramResult>> Get(
260         const KeyConfigType& key, const Pica::Shader::ShaderSetup& setup) {
261         std::optional<ShaderDecompiler::ProgramResult> result{};
262         auto map_it = shader_map.find(key);
263         if (map_it == shader_map.end()) {
264             auto program_opt = CodeGenerator(setup, key, separable);
265             if (!program_opt) {
266                 shader_map[key] = nullptr;
267                 return {0, std::nullopt};
268             }
269 
270             std::string& program = program_opt->code;
271             auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable});
272             OGLShaderStage& cached_shader = iter->second;
273             if (new_shader) {
274                 result.emplace();
275                 result->code = program;
276                 cached_shader.Create(program.c_str(), ShaderType);
277             }
278             shader_map[key] = &cached_shader;
279             return {cached_shader.GetHandle(), std::move(result)};
280         }
281 
282         if (map_it->second == nullptr) {
283             return {0, std::nullopt};
284         }
285 
286         return {map_it->second->GetHandle(), std::nullopt};
287     }
288 
Inject(const KeyConfigType & key,std::string decomp,OGLProgram && program)289     void Inject(const KeyConfigType& key, std::string decomp, OGLProgram&& program) {
290         OGLShaderStage stage{separable};
291         stage.Inject(std::move(program));
292         const auto iter = shader_cache.emplace(std::move(decomp), std::move(stage)).first;
293         OGLShaderStage& cached_shader = iter->second;
294         shader_map.insert_or_assign(key, &cached_shader);
295     }
296 
297 private:
298     bool separable;
299     std::unordered_map<KeyConfigType, OGLShaderStage*> shader_map;
300     std::unordered_map<std::string, OGLShaderStage> shader_cache;
301 };
302 
303 using ProgrammableVertexShaders =
304     ShaderDoubleCache<PicaVSConfig, &GenerateVertexShader, GL_VERTEX_SHADER>;
305 
306 using FixedGeometryShaders =
307     ShaderCache<PicaFixedGSConfig, &GenerateFixedGeometryShader, GL_GEOMETRY_SHADER>;
308 
309 using FragmentShaders = ShaderCache<PicaFSConfig, &GenerateFragmentShader, GL_FRAGMENT_SHADER>;
310 
311 class ShaderProgramManager::Impl {
312 public:
Impl(bool separable,bool is_amd)313     explicit Impl(bool separable, bool is_amd)
314         : is_amd(is_amd), separable(separable), programmable_vertex_shaders(separable),
315           trivial_vertex_shader(separable), fixed_geometry_shaders(separable),
316           fragment_shaders(separable), disk_cache(separable) {
317         if (separable)
318             pipeline.Create();
319     }
320 
321     struct ShaderTuple {
322         GLuint vs = 0;
323         GLuint gs = 0;
324         GLuint fs = 0;
325 
operator ==OpenGL::ShaderProgramManager::Impl::ShaderTuple326         bool operator==(const ShaderTuple& rhs) const {
327             return std::tie(vs, gs, fs) == std::tie(rhs.vs, rhs.gs, rhs.fs);
328         }
329 
operator !=OpenGL::ShaderProgramManager::Impl::ShaderTuple330         bool operator!=(const ShaderTuple& rhs) const {
331             return std::tie(vs, gs, fs) != std::tie(rhs.vs, rhs.gs, rhs.fs);
332         }
333 
334         struct Hash {
operator ()OpenGL::ShaderProgramManager::Impl::ShaderTuple::Hash335             std::size_t operator()(const ShaderTuple& tuple) const {
336                 std::size_t hash = 0;
337                 boost::hash_combine(hash, tuple.vs);
338                 boost::hash_combine(hash, tuple.gs);
339                 boost::hash_combine(hash, tuple.fs);
340                 return hash;
341             }
342         };
343     };
344 
345     bool is_amd;
346     bool separable;
347 
348     ShaderTuple current;
349 
350     ProgrammableVertexShaders programmable_vertex_shaders;
351     TrivialVertexShader trivial_vertex_shader;
352 
353     FixedGeometryShaders fixed_geometry_shaders;
354 
355     FragmentShaders fragment_shaders;
356     std::unordered_map<ShaderTuple, OGLProgram, ShaderTuple::Hash> program_cache;
357     OGLPipeline pipeline;
358     ShaderDiskCache disk_cache;
359 };
360 
ShaderProgramManager(bool separable,bool is_amd)361 ShaderProgramManager::ShaderProgramManager(bool separable, bool is_amd)
362     : impl(std::make_unique<Impl>(separable, is_amd)) {}
363 
364 ShaderProgramManager::~ShaderProgramManager() = default;
365 
UseProgrammableVertexShader(const Pica::Regs & regs,Pica::Shader::ShaderSetup & setup)366 bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::Regs& regs,
367                                                        Pica::Shader::ShaderSetup& setup) {
368     PicaVSConfig config{regs.vs, setup};
369     auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup);
370     if (handle == 0)
371         return false;
372     impl->current.vs = handle;
373     // Save VS to the disk cache if its a new shader
374     if (result) {
375         auto& disk_cache = impl->disk_cache;
376         ProgramCode program_code{setup.program_code.begin(), setup.program_code.end()};
377         program_code.insert(program_code.end(), setup.swizzle_data.begin(),
378                             setup.swizzle_data.end());
379         const u64 unique_identifier = GetUniqueIdentifier(regs, program_code);
380         const ShaderDiskCacheRaw raw{unique_identifier, ProgramType::VS, regs,
381                                      std::move(program_code)};
382         disk_cache.SaveRaw(raw);
383     }
384     return true;
385 }
386 
UseTrivialVertexShader()387 void ShaderProgramManager::UseTrivialVertexShader() {
388     impl->current.vs = impl->trivial_vertex_shader.Get();
389 }
390 
UseFixedGeometryShader(const Pica::Regs & regs)391 void ShaderProgramManager::UseFixedGeometryShader(const Pica::Regs& regs) {
392     PicaFixedGSConfig gs_config(regs);
393     auto [handle, _] = impl->fixed_geometry_shaders.Get(gs_config);
394     impl->current.gs = handle;
395 }
396 
UseTrivialGeometryShader()397 void ShaderProgramManager::UseTrivialGeometryShader() {
398     impl->current.gs = 0;
399 }
400 
UseFragmentShader(const Pica::Regs & regs)401 void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs) {
402     PicaFSConfig config = PicaFSConfig::BuildFromRegs(regs);
403     auto [handle, result] = impl->fragment_shaders.Get(config);
404     impl->current.fs = handle;
405     // Save FS to the disk cache if its a new shader
406     if (result) {
407         auto& disk_cache = impl->disk_cache;
408         u64 unique_identifier = GetUniqueIdentifier(regs, {});
409         ShaderDiskCacheRaw raw{unique_identifier, ProgramType::FS, regs, {}};
410         disk_cache.SaveRaw(raw);
411         disk_cache.SaveDecompiled(unique_identifier, *result, false);
412     }
413 }
414 
ApplyTo(OpenGLState & state)415 void ShaderProgramManager::ApplyTo(OpenGLState& state) {
416     if (impl->separable) {
417         if (impl->is_amd) {
418             // Without this reseting, AMD sometimes freezes when one stage is changed but not
419             // for the others. On the other hand, including this reset seems to introduce memory
420             // leak in Intel Graphics.
421             glUseProgramStages(
422                 impl->pipeline.handle,
423                 GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 0);
424         }
425 
426         glUseProgramStages(impl->pipeline.handle, GL_VERTEX_SHADER_BIT, impl->current.vs);
427         glUseProgramStages(impl->pipeline.handle, GL_GEOMETRY_SHADER_BIT, impl->current.gs);
428         glUseProgramStages(impl->pipeline.handle, GL_FRAGMENT_SHADER_BIT, impl->current.fs);
429         state.draw.shader_program = 0;
430         state.draw.program_pipeline = impl->pipeline.handle;
431     } else {
432         OGLProgram& cached_program = impl->program_cache[impl->current];
433         if (cached_program.handle == 0) {
434             cached_program.Create(false, {impl->current.vs, impl->current.gs, impl->current.fs});
435             SetShaderUniformBlockBindings(cached_program.handle);
436             SetShaderSamplerBindings(cached_program.handle);
437         }
438         state.draw.shader_program = cached_program.handle;
439     }
440 }
441 
LoadDiskCache(const std::atomic_bool & stop_loading,const VideoCore::DiskResourceLoadCallback & callback)442 void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
443                                          const VideoCore::DiskResourceLoadCallback& callback) {
444     if (!impl->separable) {
445         LOG_ERROR(Render_OpenGL,
446                   "Cannot load disk cache as separate shader programs are unsupported!");
447         return;
448     }
449     if (!GLAD_GL_ARB_get_program_binary) {
450         LOG_ERROR(Render_OpenGL,
451                   "Cannot load disk cache as ARB_get_program_binary is not supported!");
452         return;
453     }
454 
455     auto& disk_cache = impl->disk_cache;
456     const auto transferable = disk_cache.LoadTransferable();
457     if (!transferable) {
458         return;
459     }
460     const auto& raws = *transferable;
461 
462     auto [decompiled, dumps] = disk_cache.LoadPrecompiled();
463 
464     if (stop_loading) {
465         return;
466     }
467 
468     std::set<GLenum> supported_formats = GetSupportedFormats();
469 
470     // Track if precompiled cache was altered during loading to know if we have to serialize the
471     // virtual precompiled cache file back to the hard drive
472     bool precompiled_cache_altered = false;
473 
474     std::mutex mutex;
475     std::atomic_bool compilation_failed = false;
476     if (callback) {
477         callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size());
478     }
479     std::vector<std::size_t> load_raws_index;
480     // Loads both decompiled and precompiled shaders from the cache. If either one is missing for
481     const auto LoadPrecompiledWorker =
482         [&](std::size_t begin, std::size_t end, const std::vector<ShaderDiskCacheRaw>& raw_cache,
483             const ShaderDecompiledMap& decompiled_map, const ShaderDumpsMap& dump_map) {
484             for (std::size_t i = 0; i < end; ++i) {
485                 if (stop_loading || compilation_failed) {
486                     return;
487                 }
488                 const auto& raw{raw_cache[i]};
489                 const u64 unique_identifier{raw.GetUniqueIdentifier()};
490 
491                 const u64 calculated_hash =
492                     GetUniqueIdentifier(raw.GetRawShaderConfig(), raw.GetProgramCode());
493                 if (unique_identifier != calculated_hash) {
494                     LOG_ERROR(Render_OpenGL,
495                               "Invalid hash in entry={:016x} (obtained hash={:016x}) - removing "
496                               "shader cache",
497                               raw.GetUniqueIdentifier(), calculated_hash);
498                     disk_cache.InvalidateAll();
499                     return;
500                 }
501 
502                 const auto dump{dump_map.find(unique_identifier)};
503                 const auto decomp{decompiled_map.find(unique_identifier)};
504 
505                 OGLProgram shader;
506 
507                 if (dump != dump_map.end() && decomp != decompiled_map.end()) {
508                     // Only load this shader if its sanitize_mul setting matches
509                     if (raw.GetProgramType() == ProgramType::VS &&
510                         decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) {
511                         continue;
512                     }
513 
514                     // If the shader is dumped, attempt to load it
515                     shader = GeneratePrecompiledProgram(dump->second, supported_formats);
516                     if (shader.handle == 0) {
517                         // If any shader failed, stop trying to compile, delete the cache, and start
518                         // loading from raws
519                         compilation_failed = true;
520                         return;
521                     }
522                     // we have both the binary shader and the decompiled, so inject it into the
523                     // cache
524                     if (raw.GetProgramType() == ProgramType::VS) {
525                         auto [conf, setup] = BuildVSConfigFromRaw(raw);
526                         std::scoped_lock lock(mutex);
527 
528                         impl->programmable_vertex_shaders.Inject(conf, decomp->second.result.code,
529                                                                  std::move(shader));
530                     } else if (raw.GetProgramType() == ProgramType::FS) {
531                         PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig());
532                         std::scoped_lock lock(mutex);
533                         impl->fragment_shaders.Inject(conf, std::move(shader));
534                     } else {
535                         // Unsupported shader type got stored somehow so nuke the cache
536 
537                         LOG_CRITICAL(Frontend, "failed to load raw programtype {}",
538                                      raw.GetProgramType());
539                         compilation_failed = true;
540                         return;
541                     }
542                 } else {
543                     // Since precompiled didn't have the dump, we'll load them in the next phase
544                     std::scoped_lock lock(mutex);
545                     load_raws_index.push_back(i);
546                 }
547                 if (callback) {
548                     callback(VideoCore::LoadCallbackStage::Decompile, i, raw_cache.size());
549                 }
550             }
551         };
552 
553     LoadPrecompiledWorker(0, raws.size(), raws, decompiled, dumps);
554 
555     if (compilation_failed) {
556         // Invalidate the precompiled cache if a shader dumped shader was rejected
557         disk_cache.InvalidatePrecompiled();
558         dumps.clear();
559         precompiled_cache_altered = true;
560     }
561 
562     if (callback) {
563         callback(VideoCore::LoadCallbackStage::Build, 0, raws.size());
564     }
565 
566     compilation_failed = false;
567 
568     const auto LoadTransferable = [&](std::size_t begin, std::size_t end,
569                                       const std::vector<ShaderDiskCacheRaw>& raw_cache) {
570         for (std::size_t i = 0; i < end; ++i) {
571             if (stop_loading || compilation_failed) {
572                 return;
573             }
574             const auto& raw{raw_cache[i]};
575             const u64 unique_identifier{raw.GetUniqueIdentifier()};
576 
577             bool sanitize_mul = false;
578             GLuint handle{0};
579             std::optional<ShaderDecompiler::ProgramResult> result;
580             // Otherwise decompile and build the shader at boot and save the result to the
581             // precompiled file
582             if (raw.GetProgramType() == ProgramType::VS) {
583                 // TODO: This isn't the ideal place to lock, since we really only want to
584                 // lock access to the shared cache
585                 auto [conf, setup] = BuildVSConfigFromRaw(raw);
586                 std::scoped_lock lock(mutex);
587                 auto [h, r] = impl->programmable_vertex_shaders.Get(conf, setup);
588                 handle = h;
589                 result = std::move(r);
590                 sanitize_mul = conf.state.sanitize_mul;
591             } else if (raw.GetProgramType() == ProgramType::FS) {
592                 PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig());
593                 std::scoped_lock lock(mutex);
594                 auto [h, r] = impl->fragment_shaders.Get(conf);
595                 handle = h;
596                 result = std::move(r);
597             } else {
598                 // Unsupported shader type got stored somehow so nuke the cache
599                 LOG_ERROR(Frontend, "failed to load raw programtype {}", raw.GetProgramType());
600                 compilation_failed = true;
601                 return;
602             }
603             if (handle == 0) {
604                 LOG_ERROR(Frontend, "compilation from raw failed {:x} {:x}",
605                           raw.GetProgramCode().at(0), raw.GetProgramCode().at(1));
606                 compilation_failed = true;
607                 return;
608             }
609             // If this is a new shader, add it the precompiled cache
610             if (result) {
611                 disk_cache.SaveDecompiled(unique_identifier, *result, sanitize_mul);
612                 disk_cache.SaveDump(unique_identifier, handle);
613                 precompiled_cache_altered = true;
614             }
615 
616             if (callback) {
617                 callback(VideoCore::LoadCallbackStage::Build, i, raw_cache.size());
618             }
619         }
620     };
621 
622     LoadTransferable(0, raws.size(), raws);
623 
624     if (compilation_failed) {
625         disk_cache.InvalidateAll();
626     }
627 
628     if (precompiled_cache_altered) {
629         disk_cache.SaveVirtualPrecompiledFile();
630     }
631 }
632 
633 } // namespace OpenGL
634