Add thread safety to VkShaderManager::GetProgram

This commit is contained in:
dpjudas 2025-04-19 08:57:43 +02:00
commit 74e2e39e35
3 changed files with 60 additions and 19 deletions

View file

@ -610,7 +610,7 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreateWithStats(GraphicsPipel
std::unique_ptr<GraphicsPipelineBuilder> VkRenderPassSetup::CreatePipeline(const VkPipelineKey& key, bool isUberShader, UniformStructHolder& Uniforms)
{
VkShaderProgram* program = fb->GetShaderManager()->Get(key.ShaderKey, isUberShader);
VkShaderProgram* program = fb->GetShaderManager()->GetProgram(key.ShaderKey, isUberShader);
Uniforms.Clear();
Uniforms = program->Uniforms;
@ -767,7 +767,7 @@ VulkanPipeline* VkRenderPassSetup::GetFragmentShaderLibrary(const VkPipelineKey&
std::unique_ptr<VulkanPipeline> VkRenderPassSetup::LinkPipeline(const VkPipelineKey& key, bool isUberShader, UniformStructHolder& Uniforms)
{
VkShaderProgram* program = fb->GetShaderManager()->Get(key.ShaderKey, isUberShader);
VkShaderProgram* program = fb->GetShaderManager()->GetProgram(key.ShaderKey, isUberShader);
Uniforms.Clear();
Uniforms = program->Uniforms;
@ -797,7 +797,7 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreateVertexInputLibrary(int
std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreateVertexShaderLibrary(const VkPipelineKey& key, bool isUberShader)
{
VkShaderProgram* program = fb->GetShaderManager()->Get(key.ShaderKey, isUberShader);
VkShaderProgram* program = fb->GetShaderManager()->GetProgram(key.ShaderKey, isUberShader);
GraphicsPipelineBuilder builder;
builder.Cache(fb->GetRenderPassManager()->GetCache());
builder.Flags(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
@ -812,7 +812,7 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreateVertexShaderLibrary(con
std::unique_ptr<GraphicsPipelineBuilder> VkRenderPassSetup::CreateFragmentShaderLibrary(const VkPipelineKey& key, bool isUberShader)
{
VkShaderProgram* program = fb->GetShaderManager()->Get(key.ShaderKey, isUberShader);
VkShaderProgram* program = fb->GetShaderManager()->GetProgram(key.ShaderKey, isUberShader);
auto builder = std::make_unique<GraphicsPipelineBuilder>();
builder->Cache(fb->GetRenderPassManager()->GetCache());
builder->Flags(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);

View file

@ -76,11 +76,46 @@ void VkShaderManager::Deinit()
RemoveVkPPShader(PPShaders.back());
}
VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key, bool isUberShader)
VkShaderProgram* VkShaderManager::GetProgram(const VkShaderKey& key, bool isUberShader)
{
VkShaderProgram& program = isUberShader ? generic[key.GeneralizedShaderKey()] : specialized[key];
if (!program.frag.empty())
return &program;
VkShaderProgram* program = GetFromCache(key, isUberShader);
if (program)
return program;
return AddToCache(key, isUberShader, CompileProgram(key, isUberShader));
}
VkShaderProgram* VkShaderManager::GetFromCache(const VkShaderKey& key, bool isUberShader)
{
std::unique_lock lock(mutex);
if (isUberShader)
{
auto it = generic.find(key.GeneralizedShaderKey());
if (it != generic.end())
return it->second.get();
}
else
{
auto it = specialized.find(key);
if (it != specialized.end())
return it->second.get();
}
return nullptr;
}
VkShaderProgram* VkShaderManager::AddToCache(const VkShaderKey& key, bool isUberShader, std::unique_ptr<VkShaderProgram> program)
{
// Multiple threads may be compiling the program (we don't block the cache).
// If someone beat us to it, use their copy instead.
std::unique_lock lock(mutex);
auto& entry = isUberShader ? generic[key.GeneralizedShaderKey()] : specialized[key];
if (!entry)
entry = std::move(program);
return entry.get();
}
std::unique_ptr<VkShaderProgram> VkShaderManager::CompileProgram(const VkShaderKey& key, bool isUberShader)
{
auto program = std::make_unique<VkShaderProgram>();
const char* mainvp = "shaders/scene/vert_main.glsl";
const char* mainfp = "shaders/scene/frag_main.glsl";
@ -112,9 +147,9 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key, bool isUberShader)
customKey.Layout.AlphaTest = false;
const auto& desc = effectshaders[key.SpecialEffect];
program.vert = LoadVertShader(desc.ShaderName, mainvp, nullptr, desc.defines, key, nullptr, isUberShader);
program->vert = LoadVertShader(desc.ShaderName, mainvp, nullptr, desc.defines, key, nullptr, isUberShader);
if (!key.NoFragmentShader)
program.frag = LoadFragShader(desc.ShaderName, desc.fp1, desc.fp2, desc.fp3, desc.fp4, desc.fp5, desc.defines, key, nullptr, isUberShader);
program->frag = LoadFragShader(desc.ShaderName, desc.fp1, desc.fp2, desc.fp3, desc.fp4, desc.fp5, desc.defines, key, nullptr, isUberShader);
}
else
{
@ -152,9 +187,9 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key, bool isUberShader)
if (key.EffectState < FIRST_USER_SHADER)
{
const auto& desc = defaultshaders[key.EffectState];
program.vert = LoadVertShader(desc.ShaderName, mainvp, nullptr, desc.Defines, key, nullptr, isUberShader);
program->vert = LoadVertShader(desc.ShaderName, mainvp, nullptr, desc.Defines, key, nullptr, isUberShader);
if (!key.NoFragmentShader)
program.frag = LoadFragShader(desc.ShaderName, mainfp, desc.material_lump, desc.mateffect_lump, desc.lightmodel_lump_shared, desc.lightmodel_lump, desc.Defines, key, nullptr, isUberShader);
program->frag = LoadFragShader(desc.ShaderName, mainfp, desc.material_lump, desc.mateffect_lump, desc.lightmodel_lump_shared, desc.lightmodel_lump, desc.Defines, key, nullptr, isUberShader);
}
else
{
@ -162,14 +197,14 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key, bool isUberShader)
const FString& name = ExtractFileBase(desc.shader.GetChars());
FString defines = defaultshaders[desc.shaderType].Defines + desc.defines;
program.vert = LoadVertShader(name, mainvp, desc.vertshader.IsEmpty() ? nullptr : desc.vertshader.GetChars(), defines.GetChars(), key, &desc, isUberShader);
program->vert = LoadVertShader(name, mainvp, desc.vertshader.IsEmpty() ? nullptr : desc.vertshader.GetChars(), defines.GetChars(), key, &desc, isUberShader);
if (!key.NoFragmentShader)
program.frag = LoadFragShader(name, mainfp, desc.shader.GetChars(), defaultshaders[desc.shaderType].mateffect_lump, defaultshaders[desc.shaderType].lightmodel_lump_shared, defaultshaders[desc.shaderType].lightmodel_lump, defines.GetChars(), key, &desc, isUberShader);
program->frag = LoadFragShader(name, mainfp, desc.shader.GetChars(), defaultshaders[desc.shaderType].mateffect_lump, defaultshaders[desc.shaderType].lightmodel_lump_shared, defaultshaders[desc.shaderType].lightmodel_lump, defines.GetChars(), key, &desc, isUberShader);
desc.Uniforms.WriteUniforms(program.Uniforms);
desc.Uniforms.WriteUniforms(program->Uniforms);
}
}
return &program;
return program;
}
enum class FieldCondition

View file

@ -4,6 +4,7 @@
#include <memory>
#include <list>
#include <map>
#include <mutex>
#include "vectors.h"
#include "matrix.h"
#include "name.h"
@ -168,7 +169,7 @@ public:
void Deinit();
VkShaderProgram* Get(const VkShaderKey& key, bool isUberShader);
VkShaderProgram* GetProgram(const VkShaderKey& key, bool isUberShader);
bool CompileNextShader() { return true; }
@ -182,6 +183,10 @@ public:
const std::vector<uint32_t>& GetLightTilesShader() const { return LightTiles; }
private:
VkShaderProgram* GetFromCache(const VkShaderKey& key, bool isUberShader);
VkShaderProgram* AddToCache(const VkShaderKey& key, bool isUberShader, std::unique_ptr<VkShaderProgram> program);
std::unique_ptr<VkShaderProgram> CompileProgram(const VkShaderKey& key, bool isUberShader);
std::vector<uint32_t> LoadVertShader(FString shadername, const char *vert_lump, const char *vert_lump_custom, const char *defines, const VkShaderKey& key, const UserShaderDesc *shader, bool isUberShader);
std::vector<uint32_t> LoadFragShader(FString shadername, const char *frag_lump, const char *material_lump, const char* mateffect_lump, const char *light_lump_shared, const char *lightmodel_lump, const char *defines, const VkShaderKey& key, const UserShaderDesc *shader, bool isUberShader);
@ -194,8 +199,9 @@ private:
VulkanRenderDevice* fb = nullptr;
std::map<uint64_t, VkShaderProgram> generic;
std::map<VkShaderKey, VkShaderProgram> specialized;
std::mutex mutex;
std::map<uint64_t, std::unique_ptr<VkShaderProgram>> generic;
std::map<VkShaderKey, std::unique_ptr<VkShaderProgram>> specialized;
std::list<VkPPShader*> PPShaders;