diff options
| author | Stenzek <stenzek@gmail.com> | 2018-03-01 19:21:06 +1000 |
|---|---|---|
| committer | Stenzek <stenzek@gmail.com> | 2018-03-10 16:11:19 +1000 |
| commit | e31cc1f679db82f6baca1bb3d934e2dd17542d55 (patch) | |
| tree | 0edb0e3947fd0f53c4e682ea102cec7297b5e8e0 /Source/Core/VideoCommon/ShaderCache.cpp | |
| parent | 9fa24700b69b98b2569f4bd1353482cbfb1ec2d7 (diff) | |
ShaderCache: Implement background shader compilation
This enables shaders to be compiled while the game is starting, instead
of blocking startup. If a shader is needed before it is compiled,
emulation will block.
Diffstat (limited to 'Source/Core/VideoCommon/ShaderCache.cpp')
| -rw-r--r-- | Source/Core/VideoCommon/ShaderCache.cpp | 131 |
1 files changed, 70 insertions, 61 deletions
diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 43a31dfd6b..512adbd12a 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -43,6 +43,8 @@ bool ShaderCache::Initialize() // Compile all known UIDs. CompileMissingPipelines(); + if (g_ActiveConfig.bWaitForShadersBeforeStarting) + WaitForAsyncCompiler(); // Switch to the runtime shader compiler thread configuration. m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); @@ -61,9 +63,7 @@ void ShaderCache::SetHostConfig(const ShaderHostConfig& host_config, u32 efb_mul void ShaderCache::Reload() { - m_async_shader_compiler->WaitUntilCompletion(); - m_async_shader_compiler->RetrieveWorkItems(); - + WaitForAsyncCompiler(); InvalidateCachedPipelines(); ClearShaderCaches(); @@ -77,6 +77,8 @@ void ShaderCache::Reload() // UIDs are still be in the map. Therefore, when these are rebuilt, the shaders will also // be recompiled. CompileMissingPipelines(); + if (g_ActiveConfig.bWaitForShadersBeforeStarting) + WaitForAsyncCompiler(); m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); } @@ -87,14 +89,14 @@ void ShaderCache::RetrieveAsyncShaders() void ShaderCache::Shutdown() { + // This may leave shaders uncommitted to the cache, but it's better than blocking shutdown + // until everything has finished compiling. m_async_shader_compiler->StopWorkerThreads(); - m_async_shader_compiler->RetrieveWorkItems(); - ClearShaderCaches(); ClearPipelineCaches(); } -const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineConfig& uid) +const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineUid& uid) { auto it = m_gx_pipeline_cache.find(uid); if (it != m_gx_pipeline_cache.end() && !it->second.second) @@ -109,8 +111,7 @@ const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineConfig& u return InsertGXPipeline(uid, std::move(pipeline)); } -std::optional<const AbstractPipeline*> -ShaderCache::GetPipelineForUidAsync(const GXPipelineConfig& uid) +std::optional<const AbstractPipeline*> ShaderCache::GetPipelineForUidAsync(const GXPipelineUid& uid) { auto it = m_gx_pipeline_cache.find(uid); if (it != m_gx_pipeline_cache.end()) @@ -159,7 +160,7 @@ ShaderCache::GetPipelineForUidAsync(const GXPipelineConfig& uid) return {}; } -const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineConfig& uid) +const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineUid& uid) { auto it = m_gx_uber_pipeline_cache.find(uid); if (it != m_gx_uber_pipeline_cache.end() && !it->second.second) @@ -172,12 +173,16 @@ const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineC return InsertGXUberPipeline(uid, std::move(pipeline)); } -void ShaderCache::WaitForAsyncCompiler(const std::string& msg) +void ShaderCache::WaitForAsyncCompiler() { - m_async_shader_compiler->WaitUntilCompletion([&msg](size_t completed, size_t total) { - Host_UpdateProgressDialog(msg.c_str(), static_cast<int>(completed), static_cast<int>(total)); - }); - m_async_shader_compiler->RetrieveWorkItems(); + while (m_async_shader_compiler->HasPendingWork()) + { + m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) { + Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), + static_cast<int>(completed), static_cast<int>(total)); + }); + m_async_shader_compiler->RetrieveWorkItems(); + } Host_UpdateProgressDialog("", -1, -1); } @@ -274,7 +279,7 @@ void ShaderCache::LoadPipelineUIDCache() CacheReader(ShaderCache* shader_cache_) : shader_cache(shader_cache_) {} void Read(const GXPipelineDiskCacheUid& key, const u8* data, u32 data_size) { - GXPipelineConfig config = {}; + GXPipelineUid config = {}; config.vertex_format = VertexLoaderManager::GetOrCreateMatchingFormat(key.vertex_decl); config.vs_uid = key.vs_uid; config.gs_uid = key.gs_uid; @@ -315,8 +320,6 @@ void ShaderCache::CompileMissingPipelines() if (!it.second.second) QueueUberPipelineCompile(it.first); } - - WaitForAsyncCompiler(GetStringT("Compiling shaders...")); } void ShaderCache::InvalidateCachedPipelines() @@ -508,8 +511,7 @@ AbstractPipelineConfig ShaderCache::GetGXPipelineConfig( return config; } -std::optional<AbstractPipelineConfig> -ShaderCache::GetGXPipelineConfig(const GXPipelineConfig& config) +std::optional<AbstractPipelineConfig> ShaderCache::GetGXPipelineConfig(const GXPipelineUid& config) { const AbstractShader* vs; auto vs_iter = m_vs_cache.shader_map.find(config.vs_uid); @@ -545,7 +547,7 @@ ShaderCache::GetGXPipelineConfig(const GXPipelineConfig& config) } std::optional<AbstractPipelineConfig> -ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineConfig& config) +ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineUid& config) { const AbstractShader* vs; auto vs_iter = m_uber_vs_cache.shader_map.find(config.vs_uid); @@ -580,7 +582,7 @@ ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineConfig& config) config.depth_state, config.blending_state); } -const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineConfig& config, +const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineUid& config, std::unique_ptr<AbstractPipeline> pipeline) { auto& entry = m_gx_pipeline_cache[config]; @@ -592,7 +594,7 @@ const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineConfig& co } const AbstractPipeline* -ShaderCache::InsertGXUberPipeline(const GXUberPipelineConfig& config, +ShaderCache::InsertGXUberPipeline(const GXUberPipelineUid& config, std::unique_ptr<AbstractPipeline> pipeline) { auto& entry = m_gx_uber_pipeline_cache[config]; @@ -603,7 +605,7 @@ ShaderCache::InsertGXUberPipeline(const GXUberPipelineConfig& config, return entry.first.get(); } -void ShaderCache::AppendGXPipelineUID(const GXPipelineConfig& config) +void ShaderCache::AppendGXPipelineUID(const GXPipelineUid& config) { // Convert to disk format. GXPipelineDiskCacheUid disk_uid = {}; @@ -633,7 +635,7 @@ void ShaderCache::QueueVertexShaderCompile(const VertexShaderUid& uid) return true; } - virtual void Retrieve() override { shader_cache->InsertVertexShader(uid, std::move(shader)); } + void Retrieve() override { shader_cache->InsertVertexShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr<AbstractShader> shader; @@ -661,11 +663,7 @@ void ShaderCache::QueueVertexUberShaderCompile(const UberShader::VertexShaderUid return true; } - virtual void Retrieve() override - { - shader_cache->InsertVertexUberShader(uid, std::move(shader)); - } - + void Retrieve() override { shader_cache->InsertVertexUberShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr<AbstractShader> shader; @@ -693,7 +691,7 @@ void ShaderCache::QueuePixelShaderCompile(const PixelShaderUid& uid) return true; } - virtual void Retrieve() override { shader_cache->InsertPixelShader(uid, std::move(shader)); } + void Retrieve() override { shader_cache->InsertPixelShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr<AbstractShader> shader; @@ -721,11 +719,7 @@ void ShaderCache::QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& return true; } - virtual void Retrieve() override - { - shader_cache->InsertPixelUberShader(uid, std::move(shader)); - } - + void Retrieve() override { shader_cache->InsertPixelUberShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr<AbstractShader> shader; @@ -737,12 +731,12 @@ void ShaderCache::QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& m_async_shader_compiler->QueueWorkItem(std::move(wi)); } -void ShaderCache::QueuePipelineCompile(const GXPipelineConfig& uid) +void ShaderCache::QueuePipelineCompile(const GXPipelineUid& uid) { class PipelineWorkItem final : public AsyncShaderCompiler::WorkItem { public: - PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineConfig& uid_, + PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineUid& uid_, const AbstractPipelineConfig& config_) : shader_cache(shader_cache_), uid(uid_), config(config_) { @@ -754,11 +748,11 @@ void ShaderCache::QueuePipelineCompile(const GXPipelineConfig& uid) return true; } - virtual void Retrieve() override { shader_cache->InsertGXPipeline(uid, std::move(pipeline)); } + void Retrieve() override { shader_cache->InsertGXPipeline(uid, std::move(pipeline)); } private: ShaderCache* shader_cache; std::unique_ptr<AbstractPipeline> pipeline; - GXPipelineConfig uid; + GXPipelineUid uid; AbstractPipelineConfig config; }; @@ -775,13 +769,15 @@ void ShaderCache::QueuePipelineCompile(const GXPipelineConfig& uid) m_gx_pipeline_cache[uid].second = true; } -void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineConfig& uid) +void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineUid& uid) { - class UberPipelineWorkItem final : public AsyncShaderCompiler::WorkItem + // Since the shaders may not be compiled at pipelines request time, we do this in two passes. + // This is necessary because we can't access the caches in the worker thread. + class UberPipelineCompilePass final : public AsyncShaderCompiler::WorkItem { public: - UberPipelineWorkItem(ShaderCache* shader_cache_, const GXUberPipelineConfig& uid_, - const AbstractPipelineConfig& config_) + UberPipelineCompilePass(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_, + const AbstractPipelineConfig& config_) : shader_cache(shader_cache_), uid(uid_), config(config_) { } @@ -792,27 +788,43 @@ void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineConfig& uid) return true; } - virtual void Retrieve() override - { - shader_cache->InsertGXUberPipeline(uid, std::move(pipeline)); - } - + void Retrieve() override { shader_cache->InsertGXUberPipeline(uid, std::move(pipeline)); } private: ShaderCache* shader_cache; std::unique_ptr<AbstractPipeline> pipeline; - GXUberPipelineConfig uid; + GXUberPipelineUid uid; AbstractPipelineConfig config; }; - - auto config = GetGXUberPipelineConfig(uid); - if (!config) + class UberPipelinePreparePass final : public AsyncShaderCompiler::WorkItem { - // One or more stages failed to compile. - InsertGXUberPipeline(uid, nullptr); - return; - } + public: + UberPipelinePreparePass(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override { return true; } + void Retrieve() override + { + auto config = shader_cache->GetGXUberPipelineConfig(uid); + if (!config) + { + // One or more stages failed to compile. + shader_cache->InsertGXUberPipeline(uid, nullptr); + return; + } + + auto wi = shader_cache->m_async_shader_compiler->CreateWorkItem<UberPipelineCompilePass>( + shader_cache, uid, *config); + shader_cache->m_async_shader_compiler->QueueWorkItem(std::move(wi)); + } - auto wi = m_async_shader_compiler->CreateWorkItem<UberPipelineWorkItem>(this, uid, *config); + private: + ShaderCache* shader_cache; + GXUberPipelineUid uid; + }; + + auto wi = m_async_shader_compiler->CreateWorkItem<UberPipelinePreparePass>(this, uid); m_async_shader_compiler->QueueWorkItem(std::move(wi)); m_gx_uber_pipeline_cache[uid].second = true; } @@ -841,9 +853,6 @@ void ShaderCache::PrecompileUberShaders() QueuePixelUberShaderCompile(puid); }); - // Wait for shaders to finish compiling. - WaitForAsyncCompiler(GetStringT("Compiling uber shaders...")); - // Create a dummy vertex format with no attributes. // All attributes will be enabled in GetUberVertexFormat. PortableVertexDeclaration dummy_vertex_decl = {}; @@ -856,7 +865,7 @@ void ShaderCache::PrecompileUberShaders() auto QueueDummyPipeline = [&](const UberShader::VertexShaderUid& vs_uid, const GeometryShaderUid& gs_uid, const UberShader::PixelShaderUid& ps_uid) { - GXUberPipelineConfig config; + GXUberPipelineUid config; config.vertex_format = dummy_vertex_format; config.vs_uid = vs_uid; config.gs_uid = gs_uid; |
