1 // Copyright 2018 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "VideoCommon/ShaderCache.h"
6
7 #include "Common/Assert.h"
8 #include "Common/FileUtil.h"
9 #include "Common/MsgHandler.h"
10 #include "Core/ConfigManager.h"
11
12 #include "VideoCommon/FramebufferManager.h"
13 #include "VideoCommon/FramebufferShaderGen.h"
14 #include "VideoCommon/RenderBase.h"
15 #include "VideoCommon/Statistics.h"
16 #include "VideoCommon/VertexLoaderManager.h"
17 #include "VideoCommon/VertexManagerBase.h"
18 #include "VideoCommon/VideoCommon.h"
19 #include "VideoCommon/VideoConfig.h"
20
21 #include <imgui.h>
22
23 std::unique_ptr<VideoCommon::ShaderCache> g_shader_cache;
24
25 namespace VideoCommon
26 {
ShaderCache()27 ShaderCache::ShaderCache() : m_api_type{APIType::Nothing}
28 {
29 }
30
~ShaderCache()31 ShaderCache::~ShaderCache()
32 {
33 ClearCaches();
34 }
35
Initialize()36 bool ShaderCache::Initialize()
37 {
38 m_api_type = g_ActiveConfig.backend_info.api_type;
39 m_host_config = ShaderHostConfig::GetCurrent();
40
41 if (!CompileSharedPipelines())
42 return false;
43
44 m_async_shader_compiler = g_renderer->CreateAsyncShaderCompiler();
45 return true;
46 }
47
InitializeShaderCache()48 void ShaderCache::InitializeShaderCache()
49 {
50 m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads());
51
52 // Load shader and UID caches.
53 if (g_ActiveConfig.bShaderCache && m_api_type != APIType::Nothing)
54 {
55 LoadCaches();
56 LoadPipelineUIDCache();
57 }
58
59 // Queue ubershader precompiling if required.
60 if (g_ActiveConfig.UsingUberShaders())
61 QueueUberShaderPipelines();
62
63 // Compile all known UIDs.
64 CompileMissingPipelines();
65 if (g_ActiveConfig.bWaitForShadersBeforeStarting)
66 WaitForAsyncCompiler();
67
68 // Switch to the runtime shader compiler thread configuration.
69 m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
70 }
71
Reload()72 void ShaderCache::Reload()
73 {
74 WaitForAsyncCompiler();
75 ClosePipelineUIDCache();
76 ClearCaches();
77
78 if (!CompileSharedPipelines())
79 PanicAlert("Failed to compile shared pipelines after reload.");
80
81 if (g_ActiveConfig.bShaderCache)
82 LoadCaches();
83
84 // Switch to the precompiling shader configuration while we rebuild.
85 m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads());
86
87 // We don't need to explicitly recompile the individual ubershaders here, as the pipelines
88 // UIDs are still be in the map. Therefore, when these are rebuilt, the shaders will also
89 // be recompiled.
90 CompileMissingPipelines();
91 if (g_ActiveConfig.bWaitForShadersBeforeStarting)
92 WaitForAsyncCompiler();
93 m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
94 }
95
RetrieveAsyncShaders()96 void ShaderCache::RetrieveAsyncShaders()
97 {
98 m_async_shader_compiler->RetrieveWorkItems();
99 }
100
Shutdown()101 void ShaderCache::Shutdown()
102 {
103 // This may leave shaders uncommitted to the cache, but it's better than blocking shutdown
104 // until everything has finished compiling.
105 if (m_async_shader_compiler)
106 m_async_shader_compiler->StopWorkerThreads();
107
108 ClosePipelineUIDCache();
109 }
110
GetPipelineForUid(const GXPipelineUid & uid)111 const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineUid& uid)
112 {
113 auto it = m_gx_pipeline_cache.find(uid);
114 if (it != m_gx_pipeline_cache.end() && !it->second.second)
115 return it->second.first.get();
116
117 const bool exists_in_cache = it != m_gx_pipeline_cache.end();
118 std::unique_ptr<AbstractPipeline> pipeline;
119 std::optional<AbstractPipelineConfig> pipeline_config = GetGXPipelineConfig(uid);
120 if (pipeline_config)
121 pipeline = g_renderer->CreatePipeline(*pipeline_config);
122 if (g_ActiveConfig.bShaderCache && !exists_in_cache)
123 AppendGXPipelineUID(uid);
124 return InsertGXPipeline(uid, std::move(pipeline));
125 }
126
GetPipelineForUidAsync(const GXPipelineUid & uid)127 std::optional<const AbstractPipeline*> ShaderCache::GetPipelineForUidAsync(const GXPipelineUid& uid)
128 {
129 auto it = m_gx_pipeline_cache.find(uid);
130 if (it != m_gx_pipeline_cache.end())
131 {
132 // .second is the pending flag, i.e. compiling in the background.
133 if (!it->second.second)
134 return it->second.first.get();
135 else
136 return {};
137 }
138
139 AppendGXPipelineUID(uid);
140 QueuePipelineCompile(uid, COMPILE_PRIORITY_ONDEMAND_PIPELINE);
141 return {};
142 }
143
GetUberPipelineForUid(const GXUberPipelineUid & uid)144 const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineUid& uid)
145 {
146 auto it = m_gx_uber_pipeline_cache.find(uid);
147 if (it != m_gx_uber_pipeline_cache.end() && !it->second.second)
148 return it->second.first.get();
149
150 std::unique_ptr<AbstractPipeline> pipeline;
151 std::optional<AbstractPipelineConfig> pipeline_config = GetGXPipelineConfig(uid);
152 if (pipeline_config)
153 pipeline = g_renderer->CreatePipeline(*pipeline_config);
154 return InsertGXUberPipeline(uid, std::move(pipeline));
155 }
156
WaitForAsyncCompiler()157 void ShaderCache::WaitForAsyncCompiler()
158 {
159 while (m_async_shader_compiler->HasPendingWork() || m_async_shader_compiler->HasCompletedWork())
160 {
161 m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) {
162 g_renderer->BeginUIFrame();
163
164 const float scale = ImGui::GetIO().DisplayFramebufferScale.x;
165
166 ImGui::SetNextWindowSize(ImVec2(400.0f * scale, 50.0f * scale), ImGuiCond_Always);
167 ImGui::SetNextWindowPosCenter(ImGuiCond_Always);
168 if (ImGui::Begin(Common::GetStringT("Compiling Shaders").c_str(), nullptr,
169 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
170 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
171 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
172 ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
173 {
174 ImGui::Text("Compiling shaders: %zu/%zu", completed, total);
175 ImGui::ProgressBar(static_cast<float>(completed) /
176 static_cast<float>(std::max(total, static_cast<size_t>(1))),
177 ImVec2(-1.0f, 0.0f), "");
178 }
179 ImGui::End();
180
181 g_renderer->EndUIFrame();
182 });
183 m_async_shader_compiler->RetrieveWorkItems();
184 }
185 }
186
187 template <typename SerializedUidType, typename UidType>
SerializePipelineUid(const UidType & uid,SerializedUidType & serialized_uid)188 static void SerializePipelineUid(const UidType& uid, SerializedUidType& serialized_uid)
189 {
190 // Convert to disk format. Ensure all padding bytes are zero.
191 std::memset(&serialized_uid, 0, sizeof(serialized_uid));
192 serialized_uid.vertex_decl = uid.vertex_format->GetVertexDeclaration();
193 serialized_uid.vs_uid = uid.vs_uid;
194 serialized_uid.gs_uid = uid.gs_uid;
195 serialized_uid.ps_uid = uid.ps_uid;
196 serialized_uid.rasterization_state_bits = uid.rasterization_state.hex;
197 serialized_uid.depth_state_bits = uid.depth_state.hex;
198 serialized_uid.blending_state_bits = uid.blending_state.hex;
199 }
200
201 template <typename UidType, typename SerializedUidType>
UnserializePipelineUid(const SerializedUidType & uid,UidType & real_uid)202 static void UnserializePipelineUid(const SerializedUidType& uid, UidType& real_uid)
203 {
204 real_uid.vertex_format = VertexLoaderManager::GetOrCreateMatchingFormat(uid.vertex_decl);
205 real_uid.vs_uid = uid.vs_uid;
206 real_uid.gs_uid = uid.gs_uid;
207 real_uid.ps_uid = uid.ps_uid;
208 real_uid.rasterization_state.hex = uid.rasterization_state_bits;
209 real_uid.depth_state.hex = uid.depth_state_bits;
210 real_uid.blending_state.hex = uid.blending_state_bits;
211 }
212
213 template <ShaderStage stage, typename K, typename T>
LoadShaderCache(T & cache,APIType api_type,const char * type,bool include_gameid)214 void ShaderCache::LoadShaderCache(T& cache, APIType api_type, const char* type, bool include_gameid)
215 {
216 class CacheReader : public LinearDiskCacheReader<K, u8>
217 {
218 public:
219 CacheReader(T& cache_) : cache(cache_) {}
220 void Read(const K& key, const u8* value, u32 value_size)
221 {
222 auto shader = g_renderer->CreateShaderFromBinary(stage, value, value_size);
223 if (shader)
224 {
225 auto& entry = cache.shader_map[key];
226 entry.shader = std::move(shader);
227 entry.pending = false;
228
229 switch (stage)
230 {
231 case ShaderStage::Vertex:
232 INCSTAT(g_stats.num_vertex_shaders_created);
233 INCSTAT(g_stats.num_vertex_shaders_alive);
234 break;
235 case ShaderStage::Pixel:
236 INCSTAT(g_stats.num_pixel_shaders_created);
237 INCSTAT(g_stats.num_pixel_shaders_alive);
238 break;
239 default:
240 break;
241 }
242 }
243 }
244
245 private:
246 T& cache;
247 };
248
249 std::string filename = GetDiskShaderCacheFileName(api_type, type, include_gameid, true);
250 CacheReader reader(cache);
251 u32 count = cache.disk_cache.OpenAndRead(filename, reader);
252 INFO_LOG(VIDEO, "Loaded %u cached shaders from %s", count, filename.c_str());
253 }
254
255 template <typename T>
ClearShaderCache(T & cache)256 void ShaderCache::ClearShaderCache(T& cache)
257 {
258 cache.disk_cache.Sync();
259 cache.disk_cache.Close();
260 cache.shader_map.clear();
261 }
262
263 template <typename KeyType, typename DiskKeyType, typename T>
LoadPipelineCache(T & cache,LinearDiskCache<DiskKeyType,u8> & disk_cache,APIType api_type,const char * type,bool include_gameid)264 void ShaderCache::LoadPipelineCache(T& cache, LinearDiskCache<DiskKeyType, u8>& disk_cache,
265 APIType api_type, const char* type, bool include_gameid)
266 {
267 class CacheReader : public LinearDiskCacheReader<DiskKeyType, u8>
268 {
269 public:
270 CacheReader(ShaderCache* this_ptr_, T& cache_) : this_ptr(this_ptr_), cache(cache_) {}
271 bool AnyFailed() const { return failed; }
272 void Read(const DiskKeyType& key, const u8* value, u32 value_size)
273 {
274 KeyType real_uid;
275 UnserializePipelineUid(key, real_uid);
276
277 // Skip those which are already compiled.
278 if (failed || cache.find(real_uid) != cache.end())
279 return;
280
281 auto config = this_ptr->GetGXPipelineConfig(real_uid);
282 if (!config)
283 return;
284
285 auto pipeline = g_renderer->CreatePipeline(*config, value, value_size);
286 if (!pipeline)
287 {
288 // If any of the pipelines fail to create, consider the cache stale.
289 failed = true;
290 return;
291 }
292
293 auto& entry = cache[real_uid];
294 entry.first = std::move(pipeline);
295 entry.second = false;
296 }
297
298 private:
299 ShaderCache* this_ptr;
300 T& cache;
301 bool failed = false;
302 };
303
304 std::string filename = GetDiskShaderCacheFileName(api_type, type, include_gameid, true);
305 CacheReader reader(this, cache);
306 u32 count = disk_cache.OpenAndRead(filename, reader);
307 INFO_LOG(VIDEO, "Loaded %u cached pipelines from %s", count, filename.c_str());
308
309 // If any of the pipelines in the cache failed to create, it's likely because of a change of
310 // driver version, or system configuration. In this case, when the UID cache picks up the pipeline
311 // later on, we'll write a duplicate entry to the pipeline cache. There's also no point in keeping
312 // the old cache data around, so discard and recreate the disk cache.
313 if (reader.AnyFailed())
314 {
315 WARN_LOG(VIDEO, "Failed to load one or more pipelines from cache '%s'. Discarding.",
316 filename.c_str());
317 disk_cache.Close();
318 File::Delete(filename);
319 disk_cache.OpenAndRead(filename, reader);
320 }
321 }
322
323 template <typename T, typename Y>
ClearPipelineCache(T & cache,Y & disk_cache)324 void ShaderCache::ClearPipelineCache(T& cache, Y& disk_cache)
325 {
326 disk_cache.Sync();
327 disk_cache.Close();
328
329 // Set the pending flag to false, and destroy the pipeline.
330 for (auto& it : cache)
331 {
332 it.second.first.reset();
333 it.second.second = false;
334 }
335 }
336
LoadCaches()337 void ShaderCache::LoadCaches()
338 {
339 // Ubershader caches, if present.
340 if (g_ActiveConfig.backend_info.bSupportsShaderBinaries)
341 {
342 LoadShaderCache<ShaderStage::Vertex, UberShader::VertexShaderUid>(m_uber_vs_cache, m_api_type,
343 "uber-vs", false);
344 LoadShaderCache<ShaderStage::Pixel, UberShader::PixelShaderUid>(m_uber_ps_cache, m_api_type,
345 "uber-ps", false);
346
347 // We also share geometry shaders, as there aren't many variants.
348 if (m_host_config.backend_geometry_shaders)
349 LoadShaderCache<ShaderStage::Geometry, GeometryShaderUid>(m_gs_cache, m_api_type, "gs",
350 false);
351
352 // Specialized shaders, gameid-specific.
353 LoadShaderCache<ShaderStage::Vertex, VertexShaderUid>(m_vs_cache, m_api_type, "specialized-vs",
354 true);
355 LoadShaderCache<ShaderStage::Pixel, PixelShaderUid>(m_ps_cache, m_api_type, "specialized-ps",
356 true);
357 }
358
359 if (g_ActiveConfig.backend_info.bSupportsPipelineCacheData)
360 {
361 LoadPipelineCache<GXPipelineUid, SerializedGXPipelineUid>(
362 m_gx_pipeline_cache, m_gx_pipeline_disk_cache, m_api_type, "specialized-pipeline", true);
363 LoadPipelineCache<GXUberPipelineUid, SerializedGXUberPipelineUid>(
364 m_gx_uber_pipeline_cache, m_gx_uber_pipeline_disk_cache, m_api_type, "uber-pipeline",
365 false);
366 }
367 }
368
ClearCaches()369 void ShaderCache::ClearCaches()
370 {
371 ClearPipelineCache(m_gx_pipeline_cache, m_gx_pipeline_disk_cache);
372 ClearShaderCache(m_vs_cache);
373 ClearShaderCache(m_gs_cache);
374 ClearShaderCache(m_ps_cache);
375
376 ClearPipelineCache(m_gx_uber_pipeline_cache, m_gx_uber_pipeline_disk_cache);
377 ClearShaderCache(m_uber_vs_cache);
378 ClearShaderCache(m_uber_ps_cache);
379
380 m_screen_quad_vertex_shader.reset();
381 m_texture_copy_vertex_shader.reset();
382 m_efb_copy_vertex_shader.reset();
383 m_texcoord_geometry_shader.reset();
384 m_color_geometry_shader.reset();
385 m_texture_copy_pixel_shader.reset();
386 m_color_pixel_shader.reset();
387
388 m_efb_copy_to_vram_pipelines.clear();
389 m_efb_copy_to_ram_pipelines.clear();
390 m_copy_rgba8_pipeline.reset();
391 m_rgba8_stereo_copy_pipeline.reset();
392 for (auto& pipeline : m_palette_conversion_pipelines)
393 pipeline.reset();
394 m_texture_reinterpret_pipelines.clear();
395 m_texture_decoding_shaders.clear();
396
397 SETSTAT(g_stats.num_pixel_shaders_created, 0);
398 SETSTAT(g_stats.num_pixel_shaders_alive, 0);
399 SETSTAT(g_stats.num_vertex_shaders_created, 0);
400 SETSTAT(g_stats.num_vertex_shaders_alive, 0);
401 }
402
CompileMissingPipelines()403 void ShaderCache::CompileMissingPipelines()
404 {
405 // Queue all uids with a null pipeline for compilation.
406 for (auto& it : m_gx_pipeline_cache)
407 {
408 if (!it.second.first)
409 QueuePipelineCompile(it.first, COMPILE_PRIORITY_SHADERCACHE_PIPELINE);
410 }
411 for (auto& it : m_gx_uber_pipeline_cache)
412 {
413 if (!it.second.first)
414 QueueUberPipelineCompile(it.first, COMPILE_PRIORITY_UBERSHADER_PIPELINE);
415 }
416 }
417
CompileVertexShader(const VertexShaderUid & uid) const418 std::unique_ptr<AbstractShader> ShaderCache::CompileVertexShader(const VertexShaderUid& uid) const
419 {
420 const ShaderCode source_code =
421 GenerateVertexShaderCode(m_api_type, m_host_config, uid.GetUidData());
422 return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer());
423 }
424
425 std::unique_ptr<AbstractShader>
CompileVertexUberShader(const UberShader::VertexShaderUid & uid) const426 ShaderCache::CompileVertexUberShader(const UberShader::VertexShaderUid& uid) const
427 {
428 const ShaderCode source_code =
429 UberShader::GenVertexShader(m_api_type, m_host_config, uid.GetUidData());
430 return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer());
431 }
432
CompilePixelShader(const PixelShaderUid & uid) const433 std::unique_ptr<AbstractShader> ShaderCache::CompilePixelShader(const PixelShaderUid& uid) const
434 {
435 const ShaderCode source_code =
436 GeneratePixelShaderCode(m_api_type, m_host_config, uid.GetUidData());
437 return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer());
438 }
439
440 std::unique_ptr<AbstractShader>
CompilePixelUberShader(const UberShader::PixelShaderUid & uid) const441 ShaderCache::CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const
442 {
443 const ShaderCode source_code =
444 UberShader::GenPixelShader(m_api_type, m_host_config, uid.GetUidData());
445 return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer());
446 }
447
InsertVertexShader(const VertexShaderUid & uid,std::unique_ptr<AbstractShader> shader)448 const AbstractShader* ShaderCache::InsertVertexShader(const VertexShaderUid& uid,
449 std::unique_ptr<AbstractShader> shader)
450 {
451 auto& entry = m_vs_cache.shader_map[uid];
452 entry.pending = false;
453
454 if (shader && !entry.shader)
455 {
456 if (g_ActiveConfig.bShaderCache && g_ActiveConfig.backend_info.bSupportsShaderBinaries)
457 {
458 auto binary = shader->GetBinary();
459 if (!binary.empty())
460 m_vs_cache.disk_cache.Append(uid, binary.data(), static_cast<u32>(binary.size()));
461 }
462 INCSTAT(g_stats.num_vertex_shaders_created);
463 INCSTAT(g_stats.num_vertex_shaders_alive);
464 entry.shader = std::move(shader);
465 }
466
467 return entry.shader.get();
468 }
469
InsertVertexUberShader(const UberShader::VertexShaderUid & uid,std::unique_ptr<AbstractShader> shader)470 const AbstractShader* ShaderCache::InsertVertexUberShader(const UberShader::VertexShaderUid& uid,
471 std::unique_ptr<AbstractShader> shader)
472 {
473 auto& entry = m_uber_vs_cache.shader_map[uid];
474 entry.pending = false;
475
476 if (shader && !entry.shader)
477 {
478 if (g_ActiveConfig.bShaderCache && g_ActiveConfig.backend_info.bSupportsShaderBinaries)
479 {
480 auto binary = shader->GetBinary();
481 if (!binary.empty())
482 m_uber_vs_cache.disk_cache.Append(uid, binary.data(), static_cast<u32>(binary.size()));
483 }
484 INCSTAT(g_stats.num_vertex_shaders_created);
485 INCSTAT(g_stats.num_vertex_shaders_alive);
486 entry.shader = std::move(shader);
487 }
488
489 return entry.shader.get();
490 }
491
InsertPixelShader(const PixelShaderUid & uid,std::unique_ptr<AbstractShader> shader)492 const AbstractShader* ShaderCache::InsertPixelShader(const PixelShaderUid& uid,
493 std::unique_ptr<AbstractShader> shader)
494 {
495 auto& entry = m_ps_cache.shader_map[uid];
496 entry.pending = false;
497
498 if (shader && !entry.shader)
499 {
500 if (g_ActiveConfig.bShaderCache && g_ActiveConfig.backend_info.bSupportsShaderBinaries)
501 {
502 auto binary = shader->GetBinary();
503 if (!binary.empty())
504 m_ps_cache.disk_cache.Append(uid, binary.data(), static_cast<u32>(binary.size()));
505 }
506 INCSTAT(g_stats.num_pixel_shaders_created);
507 INCSTAT(g_stats.num_pixel_shaders_alive);
508 entry.shader = std::move(shader);
509 }
510
511 return entry.shader.get();
512 }
513
InsertPixelUberShader(const UberShader::PixelShaderUid & uid,std::unique_ptr<AbstractShader> shader)514 const AbstractShader* ShaderCache::InsertPixelUberShader(const UberShader::PixelShaderUid& uid,
515 std::unique_ptr<AbstractShader> shader)
516 {
517 auto& entry = m_uber_ps_cache.shader_map[uid];
518 entry.pending = false;
519
520 if (shader && !entry.shader)
521 {
522 if (g_ActiveConfig.bShaderCache && g_ActiveConfig.backend_info.bSupportsShaderBinaries)
523 {
524 auto binary = shader->GetBinary();
525 if (!binary.empty())
526 m_uber_ps_cache.disk_cache.Append(uid, binary.data(), static_cast<u32>(binary.size()));
527 }
528 INCSTAT(g_stats.num_pixel_shaders_created);
529 INCSTAT(g_stats.num_pixel_shaders_alive);
530 entry.shader = std::move(shader);
531 }
532
533 return entry.shader.get();
534 }
535
CreateGeometryShader(const GeometryShaderUid & uid)536 const AbstractShader* ShaderCache::CreateGeometryShader(const GeometryShaderUid& uid)
537 {
538 const ShaderCode source_code =
539 GenerateGeometryShaderCode(m_api_type, m_host_config, uid.GetUidData());
540 std::unique_ptr<AbstractShader> shader =
541 g_renderer->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer());
542
543 auto& entry = m_gs_cache.shader_map[uid];
544 entry.pending = false;
545
546 if (shader && !entry.shader)
547 {
548 if (g_ActiveConfig.bShaderCache && g_ActiveConfig.backend_info.bSupportsShaderBinaries)
549 {
550 auto binary = shader->GetBinary();
551 if (!binary.empty())
552 m_gs_cache.disk_cache.Append(uid, binary.data(), static_cast<u32>(binary.size()));
553 }
554 entry.shader = std::move(shader);
555 }
556
557 return entry.shader.get();
558 }
559
NeedsGeometryShader(const GeometryShaderUid & uid) const560 bool ShaderCache::NeedsGeometryShader(const GeometryShaderUid& uid) const
561 {
562 return m_host_config.backend_geometry_shaders && !uid.GetUidData()->IsPassthrough();
563 }
564
UseGeometryShaderForEFBCopies() const565 bool ShaderCache::UseGeometryShaderForEFBCopies() const
566 {
567 return m_host_config.backend_geometry_shaders && m_host_config.stereo;
568 }
569
GetGXPipelineConfig(const NativeVertexFormat * vertex_format,const AbstractShader * vertex_shader,const AbstractShader * geometry_shader,const AbstractShader * pixel_shader,const RasterizationState & rasterization_state,const DepthState & depth_state,const BlendingState & blending_state)570 AbstractPipelineConfig ShaderCache::GetGXPipelineConfig(
571 const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader,
572 const AbstractShader* geometry_shader, const AbstractShader* pixel_shader,
573 const RasterizationState& rasterization_state, const DepthState& depth_state,
574 const BlendingState& blending_state)
575 {
576 AbstractPipelineConfig config = {};
577 config.usage = AbstractPipelineUsage::GX;
578 config.vertex_format = vertex_format;
579 config.vertex_shader = vertex_shader;
580 config.geometry_shader = geometry_shader;
581 config.pixel_shader = pixel_shader;
582 config.rasterization_state = rasterization_state;
583 config.depth_state = depth_state;
584 config.blending_state = blending_state;
585 config.framebuffer_state = g_framebuffer_manager->GetEFBFramebufferState();
586
587 if (config.blending_state.logicopenable && !g_ActiveConfig.backend_info.bSupportsLogicOp)
588 {
589 WARN_LOG(VIDEO, "Approximating logic op with blending, this will produce incorrect rendering.");
590 config.blending_state.ApproximateLogicOpWithBlending();
591 }
592
593 return config;
594 }
595
GetGXPipelineConfig(const GXPipelineUid & config)596 std::optional<AbstractPipelineConfig> ShaderCache::GetGXPipelineConfig(const GXPipelineUid& config)
597 {
598 const AbstractShader* vs;
599 auto vs_iter = m_vs_cache.shader_map.find(config.vs_uid);
600 if (vs_iter != m_vs_cache.shader_map.end() && !vs_iter->second.pending)
601 vs = vs_iter->second.shader.get();
602 else
603 vs = InsertVertexShader(config.vs_uid, CompileVertexShader(config.vs_uid));
604
605 PixelShaderUid ps_uid = config.ps_uid;
606 ClearUnusedPixelShaderUidBits(m_api_type, m_host_config, &ps_uid);
607
608 const AbstractShader* ps;
609 auto ps_iter = m_ps_cache.shader_map.find(ps_uid);
610 if (ps_iter != m_ps_cache.shader_map.end() && !ps_iter->second.pending)
611 ps = ps_iter->second.shader.get();
612 else
613 ps = InsertPixelShader(ps_uid, CompilePixelShader(ps_uid));
614
615 if (!vs || !ps)
616 return {};
617
618 const AbstractShader* gs = nullptr;
619 if (NeedsGeometryShader(config.gs_uid))
620 {
621 auto gs_iter = m_gs_cache.shader_map.find(config.gs_uid);
622 if (gs_iter != m_gs_cache.shader_map.end() && !gs_iter->second.pending)
623 gs = gs_iter->second.shader.get();
624 else
625 gs = CreateGeometryShader(config.gs_uid);
626 if (!gs)
627 return {};
628 }
629
630 return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state,
631 config.depth_state, config.blending_state);
632 }
633
634 std::optional<AbstractPipelineConfig>
GetGXPipelineConfig(const GXUberPipelineUid & config)635 ShaderCache::GetGXPipelineConfig(const GXUberPipelineUid& config)
636 {
637 const AbstractShader* vs;
638 auto vs_iter = m_uber_vs_cache.shader_map.find(config.vs_uid);
639 if (vs_iter != m_uber_vs_cache.shader_map.end() && !vs_iter->second.pending)
640 vs = vs_iter->second.shader.get();
641 else
642 vs = InsertVertexUberShader(config.vs_uid, CompileVertexUberShader(config.vs_uid));
643
644 UberShader::PixelShaderUid ps_uid = config.ps_uid;
645 UberShader::ClearUnusedPixelShaderUidBits(m_api_type, m_host_config, &ps_uid);
646
647 const AbstractShader* ps;
648 auto ps_iter = m_uber_ps_cache.shader_map.find(ps_uid);
649 if (ps_iter != m_uber_ps_cache.shader_map.end() && !ps_iter->second.pending)
650 ps = ps_iter->second.shader.get();
651 else
652 ps = InsertPixelUberShader(ps_uid, CompilePixelUberShader(ps_uid));
653
654 if (!vs || !ps)
655 return {};
656
657 const AbstractShader* gs = nullptr;
658 if (NeedsGeometryShader(config.gs_uid))
659 {
660 auto gs_iter = m_gs_cache.shader_map.find(config.gs_uid);
661 if (gs_iter != m_gs_cache.shader_map.end() && !gs_iter->second.pending)
662 gs = gs_iter->second.shader.get();
663 else
664 gs = CreateGeometryShader(config.gs_uid);
665 if (!gs)
666 return {};
667 }
668
669 return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state,
670 config.depth_state, config.blending_state);
671 }
672
InsertGXPipeline(const GXPipelineUid & config,std::unique_ptr<AbstractPipeline> pipeline)673 const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineUid& config,
674 std::unique_ptr<AbstractPipeline> pipeline)
675 {
676 auto& entry = m_gx_pipeline_cache[config];
677 entry.second = false;
678 if (!entry.first && pipeline)
679 {
680 entry.first = std::move(pipeline);
681
682 if (g_ActiveConfig.bShaderCache)
683 {
684 auto cache_data = entry.first->GetCacheData();
685 if (!cache_data.empty())
686 {
687 SerializedGXPipelineUid disk_uid;
688 SerializePipelineUid(config, disk_uid);
689 m_gx_pipeline_disk_cache.Append(disk_uid, cache_data.data(),
690 static_cast<u32>(cache_data.size()));
691 }
692 }
693 }
694
695 return entry.first.get();
696 }
697
698 const AbstractPipeline*
InsertGXUberPipeline(const GXUberPipelineUid & config,std::unique_ptr<AbstractPipeline> pipeline)699 ShaderCache::InsertGXUberPipeline(const GXUberPipelineUid& config,
700 std::unique_ptr<AbstractPipeline> pipeline)
701 {
702 auto& entry = m_gx_uber_pipeline_cache[config];
703 entry.second = false;
704 if (!entry.first && pipeline)
705 {
706 entry.first = std::move(pipeline);
707
708 if (g_ActiveConfig.bShaderCache)
709 {
710 auto cache_data = entry.first->GetCacheData();
711 if (!cache_data.empty())
712 {
713 SerializedGXUberPipelineUid disk_uid;
714 SerializePipelineUid(config, disk_uid);
715 m_gx_uber_pipeline_disk_cache.Append(disk_uid, cache_data.data(),
716 static_cast<u32>(cache_data.size()));
717 }
718 }
719 }
720
721 return entry.first.get();
722 }
723
LoadPipelineUIDCache()724 void ShaderCache::LoadPipelineUIDCache()
725 {
726 constexpr u32 CACHE_FILE_MAGIC = 0x44495550; // PUID
727 constexpr size_t CACHE_HEADER_SIZE = sizeof(u32) + sizeof(u32);
728 std::string filename =
729 File::GetUserPath(D_CACHE_IDX) + SConfig::GetInstance().GetGameID() + ".uidcache";
730 if (m_gx_pipeline_uid_cache_file.Open(filename, "rb+"))
731 {
732 // If an existing case exists, validate the version before reading entries.
733 u32 existing_magic;
734 u32 existing_version;
735 bool uid_file_valid = false;
736 if (m_gx_pipeline_uid_cache_file.ReadBytes(&existing_magic, sizeof(existing_magic)) &&
737 m_gx_pipeline_uid_cache_file.ReadBytes(&existing_version, sizeof(existing_version)) &&
738 existing_magic == CACHE_FILE_MAGIC && existing_version == GX_PIPELINE_UID_VERSION)
739 {
740 // Ensure the expected size matches the actual size of the file. If it doesn't, it means
741 // the cache file may be corrupted, and we should not proceed with loading potentially
742 // garbage or invalid UIDs.
743 const u64 file_size = m_gx_pipeline_uid_cache_file.GetSize();
744 const size_t uid_count =
745 static_cast<size_t>(file_size - CACHE_HEADER_SIZE) / sizeof(SerializedGXPipelineUid);
746 const size_t expected_size = uid_count * sizeof(SerializedGXPipelineUid) + CACHE_HEADER_SIZE;
747 uid_file_valid = file_size == expected_size;
748 if (uid_file_valid)
749 {
750 for (size_t i = 0; i < uid_count; i++)
751 {
752 SerializedGXPipelineUid serialized_uid;
753 if (m_gx_pipeline_uid_cache_file.ReadBytes(&serialized_uid, sizeof(serialized_uid)))
754 {
755 // This just adds the pipeline to the map, it is compiled later.
756 AddSerializedGXPipelineUID(serialized_uid);
757 }
758 else
759 {
760 uid_file_valid = false;
761 break;
762 }
763 }
764 }
765
766 // We open the file for reading and writing, so we must seek to the end before writing.
767 if (uid_file_valid)
768 uid_file_valid = m_gx_pipeline_uid_cache_file.Seek(expected_size, SEEK_SET);
769 }
770
771 // If the file is invalid, close it. We re-open and truncate it below.
772 if (!uid_file_valid)
773 m_gx_pipeline_uid_cache_file.Close();
774 }
775
776 // If the file is not open, it means it was either corrupted or didn't exist.
777 if (!m_gx_pipeline_uid_cache_file.IsOpen())
778 {
779 if (m_gx_pipeline_uid_cache_file.Open(filename, "wb"))
780 {
781 // Write the version identifier.
782 m_gx_pipeline_uid_cache_file.WriteBytes(&CACHE_FILE_MAGIC, sizeof(GX_PIPELINE_UID_VERSION));
783 m_gx_pipeline_uid_cache_file.WriteBytes(&GX_PIPELINE_UID_VERSION,
784 sizeof(GX_PIPELINE_UID_VERSION));
785
786 // Write any current UIDs out to the file.
787 // This way, if we load a UID cache where the data was incomplete (e.g. Dolphin crashed),
788 // we don't lose the existing UIDs which were previously at the beginning.
789 for (const auto& it : m_gx_pipeline_cache)
790 AppendGXPipelineUID(it.first);
791 }
792 }
793
794 INFO_LOG(VIDEO, "Read %u pipeline UIDs from %s",
795 static_cast<unsigned>(m_gx_pipeline_cache.size()), filename.c_str());
796 }
797
ClosePipelineUIDCache()798 void ShaderCache::ClosePipelineUIDCache()
799 {
800 // This is left as a method in case we need to append extra data to the file in the future.
801 m_gx_pipeline_uid_cache_file.Close();
802 }
803
AddSerializedGXPipelineUID(const SerializedGXPipelineUid & uid)804 void ShaderCache::AddSerializedGXPipelineUID(const SerializedGXPipelineUid& uid)
805 {
806 GXPipelineUid real_uid;
807 UnserializePipelineUid(uid, real_uid);
808
809 auto iter = m_gx_pipeline_cache.find(real_uid);
810 if (iter != m_gx_pipeline_cache.end())
811 return;
812
813 // Flag it as empty with a null pipeline object, for later compilation.
814 auto& entry = m_gx_pipeline_cache[real_uid];
815 entry.second = false;
816 }
817
AppendGXPipelineUID(const GXPipelineUid & config)818 void ShaderCache::AppendGXPipelineUID(const GXPipelineUid& config)
819 {
820 if (!m_gx_pipeline_uid_cache_file.IsOpen())
821 return;
822
823 SerializedGXPipelineUid disk_uid;
824 SerializePipelineUid(config, disk_uid);
825 if (!m_gx_pipeline_uid_cache_file.WriteBytes(&disk_uid, sizeof(disk_uid)))
826 {
827 WARN_LOG(VIDEO, "Writing pipeline UID to cache failed, closing file.");
828 m_gx_pipeline_uid_cache_file.Close();
829 }
830 }
831
QueueVertexShaderCompile(const VertexShaderUid & uid,u32 priority)832 void ShaderCache::QueueVertexShaderCompile(const VertexShaderUid& uid, u32 priority)
833 {
834 class VertexShaderWorkItem final : public AsyncShaderCompiler::WorkItem
835 {
836 public:
837 VertexShaderWorkItem(ShaderCache* shader_cache_, const VertexShaderUid& uid_)
838 : shader_cache(shader_cache_), uid(uid_)
839 {
840 }
841
842 bool Compile() override
843 {
844 shader = shader_cache->CompileVertexShader(uid);
845 return true;
846 }
847
848 void Retrieve() override { shader_cache->InsertVertexShader(uid, std::move(shader)); }
849
850 private:
851 ShaderCache* shader_cache;
852 std::unique_ptr<AbstractShader> shader;
853 VertexShaderUid uid;
854 };
855
856 m_vs_cache.shader_map[uid].pending = true;
857 auto wi = m_async_shader_compiler->CreateWorkItem<VertexShaderWorkItem>(this, uid);
858 m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
859 }
860
QueueVertexUberShaderCompile(const UberShader::VertexShaderUid & uid,u32 priority)861 void ShaderCache::QueueVertexUberShaderCompile(const UberShader::VertexShaderUid& uid, u32 priority)
862 {
863 class VertexUberShaderWorkItem final : public AsyncShaderCompiler::WorkItem
864 {
865 public:
866 VertexUberShaderWorkItem(ShaderCache* shader_cache_, const UberShader::VertexShaderUid& uid_)
867 : shader_cache(shader_cache_), uid(uid_)
868 {
869 }
870
871 bool Compile() override
872 {
873 shader = shader_cache->CompileVertexUberShader(uid);
874 return true;
875 }
876
877 void Retrieve() override { shader_cache->InsertVertexUberShader(uid, std::move(shader)); }
878
879 private:
880 ShaderCache* shader_cache;
881 std::unique_ptr<AbstractShader> shader;
882 UberShader::VertexShaderUid uid;
883 };
884
885 m_uber_vs_cache.shader_map[uid].pending = true;
886 auto wi = m_async_shader_compiler->CreateWorkItem<VertexUberShaderWorkItem>(this, uid);
887 m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
888 }
889
QueuePixelShaderCompile(const PixelShaderUid & uid,u32 priority)890 void ShaderCache::QueuePixelShaderCompile(const PixelShaderUid& uid, u32 priority)
891 {
892 class PixelShaderWorkItem final : public AsyncShaderCompiler::WorkItem
893 {
894 public:
895 PixelShaderWorkItem(ShaderCache* shader_cache_, const PixelShaderUid& uid_)
896 : shader_cache(shader_cache_), uid(uid_)
897 {
898 }
899
900 bool Compile() override
901 {
902 shader = shader_cache->CompilePixelShader(uid);
903 return true;
904 }
905
906 void Retrieve() override { shader_cache->InsertPixelShader(uid, std::move(shader)); }
907
908 private:
909 ShaderCache* shader_cache;
910 std::unique_ptr<AbstractShader> shader;
911 PixelShaderUid uid;
912 };
913
914 m_ps_cache.shader_map[uid].pending = true;
915 auto wi = m_async_shader_compiler->CreateWorkItem<PixelShaderWorkItem>(this, uid);
916 m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
917 }
918
QueuePixelUberShaderCompile(const UberShader::PixelShaderUid & uid,u32 priority)919 void ShaderCache::QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& uid, u32 priority)
920 {
921 class PixelUberShaderWorkItem final : public AsyncShaderCompiler::WorkItem
922 {
923 public:
924 PixelUberShaderWorkItem(ShaderCache* shader_cache_, const UberShader::PixelShaderUid& uid_)
925 : shader_cache(shader_cache_), uid(uid_)
926 {
927 }
928
929 bool Compile() override
930 {
931 shader = shader_cache->CompilePixelUberShader(uid);
932 return true;
933 }
934
935 void Retrieve() override { shader_cache->InsertPixelUberShader(uid, std::move(shader)); }
936
937 private:
938 ShaderCache* shader_cache;
939 std::unique_ptr<AbstractShader> shader;
940 UberShader::PixelShaderUid uid;
941 };
942
943 m_uber_ps_cache.shader_map[uid].pending = true;
944 auto wi = m_async_shader_compiler->CreateWorkItem<PixelUberShaderWorkItem>(this, uid);
945 m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
946 }
947
QueuePipelineCompile(const GXPipelineUid & uid,u32 priority)948 void ShaderCache::QueuePipelineCompile(const GXPipelineUid& uid, u32 priority)
949 {
950 class PipelineWorkItem final : public AsyncShaderCompiler::WorkItem
951 {
952 public:
953 PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineUid& uid_, u32 priority_)
954 : shader_cache(shader_cache_), uid(uid_), priority(priority_)
955 {
956 // Check if all the stages required for this pipeline have been compiled.
957 // If not, this work item becomes a no-op, and re-queues the pipeline for the next frame.
958 if (SetStagesReady())
959 config = shader_cache->GetGXPipelineConfig(uid);
960 }
961
962 bool SetStagesReady()
963 {
964 stages_ready = true;
965
966 auto vs_it = shader_cache->m_vs_cache.shader_map.find(uid.vs_uid);
967 stages_ready &= vs_it != shader_cache->m_vs_cache.shader_map.end() && !vs_it->second.pending;
968 if (vs_it == shader_cache->m_vs_cache.shader_map.end())
969 shader_cache->QueueVertexShaderCompile(uid.vs_uid, priority);
970
971 PixelShaderUid ps_uid = uid.ps_uid;
972 ClearUnusedPixelShaderUidBits(shader_cache->m_api_type, shader_cache->m_host_config, &ps_uid);
973
974 auto ps_it = shader_cache->m_ps_cache.shader_map.find(ps_uid);
975 stages_ready &= ps_it != shader_cache->m_ps_cache.shader_map.end() && !ps_it->second.pending;
976 if (ps_it == shader_cache->m_ps_cache.shader_map.end())
977 shader_cache->QueuePixelShaderCompile(ps_uid, priority);
978
979 return stages_ready;
980 }
981
982 bool Compile() override
983 {
984 if (config)
985 pipeline = g_renderer->CreatePipeline(*config);
986 return true;
987 }
988
989 void Retrieve() override
990 {
991 if (stages_ready)
992 {
993 shader_cache->InsertGXPipeline(uid, std::move(pipeline));
994 }
995 else
996 {
997 // Re-queue for next frame.
998 auto wi = shader_cache->m_async_shader_compiler->CreateWorkItem<PipelineWorkItem>(
999 shader_cache, uid, priority);
1000 shader_cache->m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
1001 }
1002 }
1003
1004 private:
1005 ShaderCache* shader_cache;
1006 std::unique_ptr<AbstractPipeline> pipeline;
1007 GXPipelineUid uid;
1008 u32 priority;
1009 std::optional<AbstractPipelineConfig> config;
1010 bool stages_ready;
1011 };
1012
1013 auto wi = m_async_shader_compiler->CreateWorkItem<PipelineWorkItem>(this, uid, priority);
1014 m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
1015 m_gx_pipeline_cache[uid].second = true;
1016 }
1017
QueueUberPipelineCompile(const GXUberPipelineUid & uid,u32 priority)1018 void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineUid& uid, u32 priority)
1019 {
1020 class UberPipelineWorkItem final : public AsyncShaderCompiler::WorkItem
1021 {
1022 public:
1023 UberPipelineWorkItem(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_, u32 priority_)
1024 : shader_cache(shader_cache_), uid(uid_), priority(priority_)
1025 {
1026 // Check if all the stages required for this UberPipeline have been compiled.
1027 // If not, this work item becomes a no-op, and re-queues the UberPipeline for the next frame.
1028 if (SetStagesReady())
1029 config = shader_cache->GetGXPipelineConfig(uid);
1030 }
1031
1032 bool SetStagesReady()
1033 {
1034 stages_ready = true;
1035
1036 auto vs_it = shader_cache->m_uber_vs_cache.shader_map.find(uid.vs_uid);
1037 stages_ready &=
1038 vs_it != shader_cache->m_uber_vs_cache.shader_map.end() && !vs_it->second.pending;
1039 if (vs_it == shader_cache->m_uber_vs_cache.shader_map.end())
1040 shader_cache->QueueVertexUberShaderCompile(uid.vs_uid, priority);
1041
1042 UberShader::PixelShaderUid ps_uid = uid.ps_uid;
1043 UberShader::ClearUnusedPixelShaderUidBits(shader_cache->m_api_type,
1044 shader_cache->m_host_config, &ps_uid);
1045
1046 auto ps_it = shader_cache->m_uber_ps_cache.shader_map.find(ps_uid);
1047 stages_ready &=
1048 ps_it != shader_cache->m_uber_ps_cache.shader_map.end() && !ps_it->second.pending;
1049 if (ps_it == shader_cache->m_uber_ps_cache.shader_map.end())
1050 shader_cache->QueuePixelUberShaderCompile(ps_uid, priority);
1051
1052 return stages_ready;
1053 }
1054
1055 bool Compile() override
1056 {
1057 if (config)
1058 UberPipeline = g_renderer->CreatePipeline(*config);
1059 return true;
1060 }
1061
1062 void Retrieve() override
1063 {
1064 if (stages_ready)
1065 {
1066 shader_cache->InsertGXUberPipeline(uid, std::move(UberPipeline));
1067 }
1068 else
1069 {
1070 // Re-queue for next frame.
1071 auto wi = shader_cache->m_async_shader_compiler->CreateWorkItem<UberPipelineWorkItem>(
1072 shader_cache, uid, priority);
1073 shader_cache->m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
1074 }
1075 }
1076
1077 private:
1078 ShaderCache* shader_cache;
1079 std::unique_ptr<AbstractPipeline> UberPipeline;
1080 GXUberPipelineUid uid;
1081 u32 priority;
1082 std::optional<AbstractPipelineConfig> config;
1083 bool stages_ready;
1084 };
1085
1086 auto wi = m_async_shader_compiler->CreateWorkItem<UberPipelineWorkItem>(this, uid, priority);
1087 m_async_shader_compiler->QueueWorkItem(std::move(wi), priority);
1088 m_gx_uber_pipeline_cache[uid].second = true;
1089 }
1090
QueueUberShaderPipelines()1091 void ShaderCache::QueueUberShaderPipelines()
1092 {
1093 // Create a dummy vertex format with no attributes.
1094 // All attributes will be enabled in GetUberVertexFormat.
1095 PortableVertexDeclaration dummy_vertex_decl = {};
1096 dummy_vertex_decl.position.components = 4;
1097 dummy_vertex_decl.position.type = VAR_FLOAT;
1098 dummy_vertex_decl.position.enable = true;
1099 dummy_vertex_decl.stride = sizeof(float) * 4;
1100 NativeVertexFormat* dummy_vertex_format =
1101 VertexLoaderManager::GetUberVertexFormat(dummy_vertex_decl);
1102 auto QueueDummyPipeline = [&](const UberShader::VertexShaderUid& vs_uid,
1103 const GeometryShaderUid& gs_uid,
1104 const UberShader::PixelShaderUid& ps_uid) {
1105 GXUberPipelineUid config;
1106 config.vertex_format = dummy_vertex_format;
1107 config.vs_uid = vs_uid;
1108 config.gs_uid = gs_uid;
1109 config.ps_uid = ps_uid;
1110 config.rasterization_state = RenderState::GetCullBackFaceRasterizationState(
1111 static_cast<PrimitiveType>(gs_uid.GetUidData()->primitive_type));
1112 config.depth_state = RenderState::GetNoDepthTestingDepthState();
1113 config.blending_state = RenderState::GetNoBlendingBlendState();
1114 if (ps_uid.GetUidData()->uint_output)
1115 {
1116 // uint_output is only ever enabled when logic ops are enabled.
1117 config.blending_state.logicopenable = true;
1118 config.blending_state.logicmode = BlendMode::AND;
1119 }
1120
1121 auto iter = m_gx_uber_pipeline_cache.find(config);
1122 if (iter != m_gx_uber_pipeline_cache.end())
1123 return;
1124
1125 auto& entry = m_gx_uber_pipeline_cache[config];
1126 entry.second = false;
1127 };
1128
1129 // Populate the pipeline configs with empty entries, these will be compiled afterwards.
1130 UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) {
1131 UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) {
1132 // UIDs must have compatible texgens, a mismatching combination will never be queried.
1133 if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens)
1134 return;
1135
1136 UberShader::PixelShaderUid cleared_puid = puid;
1137 UberShader::ClearUnusedPixelShaderUidBits(m_api_type, m_host_config, &cleared_puid);
1138 EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) {
1139 if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens ||
1140 (!guid.GetUidData()->IsPassthrough() && !m_host_config.backend_geometry_shaders))
1141 {
1142 return;
1143 }
1144 QueueDummyPipeline(vuid, guid, cleared_puid);
1145 });
1146 });
1147 });
1148 }
1149
1150 const AbstractPipeline*
GetEFBCopyToVRAMPipeline(const TextureConversionShaderGen::TCShaderUid & uid)1151 ShaderCache::GetEFBCopyToVRAMPipeline(const TextureConversionShaderGen::TCShaderUid& uid)
1152 {
1153 auto iter = m_efb_copy_to_vram_pipelines.find(uid);
1154 if (iter != m_efb_copy_to_vram_pipelines.end())
1155 return iter->second.get();
1156
1157 auto shader_code = TextureConversionShaderGen::GeneratePixelShader(m_api_type, uid.GetUidData());
1158 auto shader = g_renderer->CreateShaderFromSource(ShaderStage::Pixel, shader_code.GetBuffer());
1159 if (!shader)
1160 {
1161 m_efb_copy_to_vram_pipelines.emplace(uid, nullptr);
1162 return nullptr;
1163 }
1164
1165 AbstractPipelineConfig config = {};
1166 config.vertex_format = nullptr;
1167 config.vertex_shader = m_efb_copy_vertex_shader.get();
1168 config.geometry_shader =
1169 UseGeometryShaderForEFBCopies() ? m_texcoord_geometry_shader.get() : nullptr;
1170 config.pixel_shader = shader.get();
1171 config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
1172 config.depth_state = RenderState::GetNoDepthTestingDepthState();
1173 config.blending_state = RenderState::GetNoBlendingBlendState();
1174 config.framebuffer_state = RenderState::GetRGBA8FramebufferState();
1175 config.usage = AbstractPipelineUsage::Utility;
1176 auto iiter = m_efb_copy_to_vram_pipelines.emplace(uid, g_renderer->CreatePipeline(config));
1177 return iiter.first->second.get();
1178 }
1179
GetEFBCopyToRAMPipeline(const EFBCopyParams & uid)1180 const AbstractPipeline* ShaderCache::GetEFBCopyToRAMPipeline(const EFBCopyParams& uid)
1181 {
1182 auto iter = m_efb_copy_to_ram_pipelines.find(uid);
1183 if (iter != m_efb_copy_to_ram_pipelines.end())
1184 return iter->second.get();
1185
1186 const std::string shader_code =
1187 TextureConversionShaderTiled::GenerateEncodingShader(uid, m_api_type);
1188 const auto shader = g_renderer->CreateShaderFromSource(ShaderStage::Pixel, shader_code);
1189 if (!shader)
1190 {
1191 m_efb_copy_to_ram_pipelines.emplace(uid, nullptr);
1192 return nullptr;
1193 }
1194
1195 AbstractPipelineConfig config = {};
1196 config.vertex_shader = m_screen_quad_vertex_shader.get();
1197 config.pixel_shader = shader.get();
1198 config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
1199 config.depth_state = RenderState::GetNoDepthTestingDepthState();
1200 config.blending_state = RenderState::GetNoBlendingBlendState();
1201 config.framebuffer_state = RenderState::GetColorFramebufferState(AbstractTextureFormat::BGRA8);
1202 config.usage = AbstractPipelineUsage::Utility;
1203 auto iiter = m_efb_copy_to_ram_pipelines.emplace(uid, g_renderer->CreatePipeline(config));
1204 return iiter.first->second.get();
1205 }
1206
CompileSharedPipelines()1207 bool ShaderCache::CompileSharedPipelines()
1208 {
1209 m_screen_quad_vertex_shader = g_renderer->CreateShaderFromSource(
1210 ShaderStage::Vertex, FramebufferShaderGen::GenerateScreenQuadVertexShader());
1211 m_texture_copy_vertex_shader = g_renderer->CreateShaderFromSource(
1212 ShaderStage::Vertex, FramebufferShaderGen::GenerateTextureCopyVertexShader());
1213 m_efb_copy_vertex_shader = g_renderer->CreateShaderFromSource(
1214 ShaderStage::Vertex,
1215 TextureConversionShaderGen::GenerateVertexShader(m_api_type).GetBuffer());
1216 if (!m_screen_quad_vertex_shader || !m_texture_copy_vertex_shader || !m_efb_copy_vertex_shader)
1217 return false;
1218
1219 if (UseGeometryShaderForEFBCopies())
1220 {
1221 m_texcoord_geometry_shader = g_renderer->CreateShaderFromSource(
1222 ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 0));
1223 m_color_geometry_shader = g_renderer->CreateShaderFromSource(
1224 ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(0, 1));
1225 if (!m_texcoord_geometry_shader || !m_color_geometry_shader)
1226 return false;
1227 }
1228
1229 m_texture_copy_pixel_shader = g_renderer->CreateShaderFromSource(
1230 ShaderStage::Pixel, FramebufferShaderGen::GenerateTextureCopyPixelShader());
1231 m_color_pixel_shader = g_renderer->CreateShaderFromSource(
1232 ShaderStage::Pixel, FramebufferShaderGen::GenerateColorPixelShader());
1233 if (!m_texture_copy_pixel_shader || !m_color_pixel_shader)
1234 return false;
1235
1236 AbstractPipelineConfig config;
1237 config.vertex_format = nullptr;
1238 config.vertex_shader = m_texture_copy_vertex_shader.get();
1239 config.geometry_shader = nullptr;
1240 config.pixel_shader = m_texture_copy_pixel_shader.get();
1241 config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
1242 config.depth_state = RenderState::GetNoDepthTestingDepthState();
1243 config.blending_state = RenderState::GetNoBlendingBlendState();
1244 config.framebuffer_state = RenderState::GetRGBA8FramebufferState();
1245 config.usage = AbstractPipelineUsage::Utility;
1246 m_copy_rgba8_pipeline = g_renderer->CreatePipeline(config);
1247 if (!m_copy_rgba8_pipeline)
1248 return false;
1249
1250 if (UseGeometryShaderForEFBCopies())
1251 {
1252 config.geometry_shader = m_texcoord_geometry_shader.get();
1253 m_rgba8_stereo_copy_pipeline = g_renderer->CreatePipeline(config);
1254 if (!m_rgba8_stereo_copy_pipeline)
1255 return false;
1256 }
1257
1258 if (m_host_config.backend_palette_conversion)
1259 {
1260 config.vertex_shader = m_screen_quad_vertex_shader.get();
1261 config.geometry_shader = nullptr;
1262
1263 for (size_t i = 0; i < NUM_PALETTE_CONVERSION_SHADERS; i++)
1264 {
1265 auto shader = g_renderer->CreateShaderFromSource(
1266 ShaderStage::Pixel, TextureConversionShaderTiled::GeneratePaletteConversionShader(
1267 static_cast<TLUTFormat>(i), m_api_type));
1268 if (!shader)
1269 return false;
1270
1271 config.pixel_shader = shader.get();
1272 m_palette_conversion_pipelines[i] = g_renderer->CreatePipeline(config);
1273 if (!m_palette_conversion_pipelines[i])
1274 return false;
1275 }
1276 }
1277
1278 return true;
1279 }
1280
GetPaletteConversionPipeline(TLUTFormat format)1281 const AbstractPipeline* ShaderCache::GetPaletteConversionPipeline(TLUTFormat format)
1282 {
1283 ASSERT(static_cast<size_t>(format) < NUM_PALETTE_CONVERSION_SHADERS);
1284 return m_palette_conversion_pipelines[static_cast<size_t>(format)].get();
1285 }
1286
GetTextureReinterpretPipeline(TextureFormat from_format,TextureFormat to_format)1287 const AbstractPipeline* ShaderCache::GetTextureReinterpretPipeline(TextureFormat from_format,
1288 TextureFormat to_format)
1289 {
1290 const auto key = std::make_pair(from_format, to_format);
1291 auto iter = m_texture_reinterpret_pipelines.find(key);
1292 if (iter != m_texture_reinterpret_pipelines.end())
1293 return iter->second.get();
1294
1295 std::string shader_source =
1296 FramebufferShaderGen::GenerateTextureReinterpretShader(from_format, to_format);
1297 if (shader_source.empty())
1298 {
1299 m_texture_reinterpret_pipelines.emplace(key, nullptr);
1300 return nullptr;
1301 }
1302
1303 std::unique_ptr<AbstractShader> shader =
1304 g_renderer->CreateShaderFromSource(ShaderStage::Pixel, shader_source);
1305 if (!shader)
1306 {
1307 m_texture_reinterpret_pipelines.emplace(key, nullptr);
1308 return nullptr;
1309 }
1310
1311 AbstractPipelineConfig config;
1312 config.vertex_format = nullptr;
1313 config.vertex_shader = m_screen_quad_vertex_shader.get();
1314 config.geometry_shader = nullptr;
1315 config.pixel_shader = shader.get();
1316 config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
1317 config.depth_state = RenderState::GetNoDepthTestingDepthState();
1318 config.blending_state = RenderState::GetNoBlendingBlendState();
1319 config.framebuffer_state = RenderState::GetRGBA8FramebufferState();
1320 config.usage = AbstractPipelineUsage::Utility;
1321 auto iiter = m_texture_reinterpret_pipelines.emplace(key, g_renderer->CreatePipeline(config));
1322 return iiter.first->second.get();
1323 }
1324
GetTextureDecodingShader(TextureFormat format,TLUTFormat palette_format)1325 const AbstractShader* ShaderCache::GetTextureDecodingShader(TextureFormat format,
1326 TLUTFormat palette_format)
1327 {
1328 const auto key = std::make_pair(static_cast<u32>(format), static_cast<u32>(palette_format));
1329 auto iter = m_texture_decoding_shaders.find(key);
1330 if (iter != m_texture_decoding_shaders.end())
1331 return iter->second.get();
1332
1333 std::string shader_source =
1334 TextureConversionShaderTiled::GenerateDecodingShader(format, palette_format, APIType::OpenGL);
1335 if (shader_source.empty())
1336 {
1337 m_texture_decoding_shaders.emplace(key, nullptr);
1338 return nullptr;
1339 }
1340
1341 std::unique_ptr<AbstractShader> shader =
1342 g_renderer->CreateShaderFromSource(ShaderStage::Compute, shader_source);
1343 if (!shader)
1344 {
1345 m_texture_decoding_shaders.emplace(key, nullptr);
1346 return nullptr;
1347 }
1348
1349 auto iiter = m_texture_decoding_shaders.emplace(key, std::move(shader));
1350 return iiter.first->second.get();
1351 }
1352 } // namespace VideoCommon
1353