mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
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.
This commit is contained in:
@ -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
|
||||
void Retrieve() override { shader_cache->InsertGXUberPipeline(uid, std::move(pipeline)); }
|
||||
private:
|
||||
ShaderCache* shader_cache;
|
||||
std::unique_ptr<AbstractPipeline> pipeline;
|
||||
GXUberPipelineUid uid;
|
||||
AbstractPipelineConfig config;
|
||||
};
|
||||
class UberPipelinePreparePass final : public AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
UberPipelinePreparePass(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_)
|
||||
: shader_cache(shader_cache_), uid(uid_)
|
||||
{
|
||||
shader_cache->InsertGXUberPipeline(uid, std::move(pipeline));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private:
|
||||
ShaderCache* shader_cache;
|
||||
std::unique_ptr<AbstractPipeline> pipeline;
|
||||
GXUberPipelineConfig uid;
|
||||
AbstractPipelineConfig config;
|
||||
GXUberPipelineUid uid;
|
||||
};
|
||||
|
||||
auto config = GetGXUberPipelineConfig(uid);
|
||||
if (!config)
|
||||
{
|
||||
// One or more stages failed to compile.
|
||||
InsertGXUberPipeline(uid, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto wi = m_async_shader_compiler->CreateWorkItem<UberPipelineWorkItem>(this, uid, *config);
|
||||
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;
|
||||
|
Reference in New Issue
Block a user