diff --git a/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp b/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp index 66b007209..3d88e86b2 100644 --- a/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp +++ b/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp @@ -610,7 +610,7 @@ std::unique_ptr VkRenderPassSetup::CreateWithStats(GraphicsPipel std::unique_ptr 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 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 VkRenderPassSetup::CreateVertexInputLibrary(int std::unique_ptr 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 VkRenderPassSetup::CreateVertexShaderLibrary(con std::unique_ptr 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(); builder->Cache(fb->GetRenderPassManager()->GetCache()); builder->Flags(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR); diff --git a/src/common/rendering/vulkan/shaders/vk_shader.cpp b/src/common/rendering/vulkan/shaders/vk_shader.cpp index 6d2d14aab..40b56f12f 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -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 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 VkShaderManager::CompileProgram(const VkShaderKey& key, bool isUberShader) +{ + auto program = std::make_unique(); 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 diff --git a/src/common/rendering/vulkan/shaders/vk_shader.h b/src/common/rendering/vulkan/shaders/vk_shader.h index 1abc1e905..cf34d6770 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.h +++ b/src/common/rendering/vulkan/shaders/vk_shader.h @@ -4,6 +4,7 @@ #include #include #include +#include #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& GetLightTilesShader() const { return LightTiles; } private: + VkShaderProgram* GetFromCache(const VkShaderKey& key, bool isUberShader); + VkShaderProgram* AddToCache(const VkShaderKey& key, bool isUberShader, std::unique_ptr program); + std::unique_ptr CompileProgram(const VkShaderKey& key, bool isUberShader); + std::vector 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 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 generic; - std::map specialized; + std::mutex mutex; + std::map> generic; + std::map> specialized; std::list PPShaders;