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