From da0ff9ab04377a6367ae3ca4ee45699c5bb5863b Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 11 Apr 2025 23:17:43 +0200 Subject: [PATCH] Run the GLSL compiler on the worker thread --- .../vulkan/pipelines/vk_renderpass.cpp | 32 +++++----- .../rendering/vulkan/shaders/vk_shader.cpp | 32 +++++----- .../rendering/vulkan/shaders/vk_shader.h | 11 +++- .../vulkan/shaders/vk_shadercache.cpp | 62 +++++++++++++------ .../rendering/vulkan/shaders/vk_shadercache.h | 11 ++-- src/common/rendering/vulkan/vk_levelmesh.cpp | 4 +- .../rendering/vulkan/vk_lightmapper.cpp | 4 +- 7 files changed, 98 insertions(+), 58 deletions(-) diff --git a/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp b/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp index 4d08f986d..66d08c363 100644 --- a/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp +++ b/src/common/rendering/vulkan/pipelines/vk_renderpass.cpp @@ -519,34 +519,38 @@ PipelineData* VkRenderPassSetup::GetSpecializedPipeline(const VkPipelineKey& key { // We haven't seen this before. Build it on the worker thread. + SpecializedPipelines[key] = {}; // Mark as seen + struct TaskData { - std::unique_ptr builder; + VkPipelineKey key; std::unique_ptr pipeline; + UniformStructHolder uniforms; }; auto data = std::make_shared(); - - // Compile the GLSL on the main thread (mainly due to the resource system) - UniformStructHolder uniforms; - data->builder = CreatePipeline(key, false, uniforms); - SpecializedPipelines[key].Uniforms = uniforms; + data->key = key; // Schedule the pipeline building on a worker thread VkPipelineKey k = key; auto passManager = fb->GetRenderPassManager(); passManager->RunOnWorkerThread([=]() { + auto builder = CreatePipeline(data->key, false, data->uniforms); + cycle_t ct; ct.ResetAndClock(); - data->pipeline = data->builder->Create(fb->GetDevice()); + data->pipeline = builder->Create(fb->GetDevice()); ct.Unclock(); const auto duration = ct.TimeMS(); passManager->RunOnMainThread([=]() { auto& slot = SpecializedPipelines[k]; if (!slot.pipeline) + { slot.pipeline = std::move(data->pipeline); + slot.Uniforms = std::move(data->uniforms); + } pipeline_time += duration; ++pipeline_count; @@ -605,7 +609,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()->GetGameShader(key.ShaderKey, isUberShader); Uniforms.Clear(); Uniforms = program->Uniforms; @@ -718,19 +722,19 @@ void VkRenderPassSetup::PrecompileFragmentShaderLibrary(const VkPipelineKey& key struct TaskData { - std::unique_ptr builder; + VkPipelineKey key; std::unique_ptr pipeline; }; auto data = std::make_shared(); - data->builder = CreateFragmentShaderLibrary(key, isUberShader); + data->key = key; auto passManager = fb->GetRenderPassManager(); passManager->RunOnWorkerThread([=]() { cycle_t ct; ct.ResetAndClock(); - data->pipeline = data->builder->Create(fb->GetDevice()); + data->pipeline = CreateFragmentShaderLibrary(data->key, isUberShader)->Create(fb->GetDevice()); ct.Unclock(); const auto duration = ct.TimeMS(); @@ -762,7 +766,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()->GetGameShader(key.ShaderKey, isUberShader); Uniforms.Clear(); Uniforms = program->Uniforms; @@ -792,7 +796,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()->GetGameShader(key.ShaderKey, isUberShader); GraphicsPipelineBuilder builder; builder.Cache(fb->GetRenderPassManager()->GetCache()); builder.Flags(VK_PIPELINE_CREATE_LIBRARY_BIT_KHR); @@ -807,7 +811,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()->GetGameShader(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 9e833bb21..cf8ccffe8 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -91,11 +91,15 @@ void VkShaderManager::Deinit() RemoveVkPPShader(PPShaders.back()); } -VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key, bool isUberShader) +VkShaderProgram* VkShaderManager::GetGameShader(const VkShaderKey& key, bool isUberShader) { - VkShaderProgram& program = isUberShader ? generic[key.GeneralizedShaderKey()] : specialized[key]; - if (program.frag) - return &program; + std::unique_lock lock(GameShaders.Mutex); + + std::unique_ptr& program = isUberShader ? GameShaders.Generic[key.GeneralizedShaderKey()] : GameShaders.Specialized[key]; + if (program) + return program.get(); + + program = std::make_unique(); const char* mainvp = "shaders/scene/vert_main.glsl"; const char* mainfp = "shaders/scene/frag_main.glsl"; @@ -127,9 +131,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 { @@ -167,9 +171,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 { @@ -177,14 +181,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.get(); } enum class FieldCondition @@ -630,12 +634,12 @@ FString VkShaderManager::GetVersionBlock() FString VkShaderManager::LoadPublicShaderLump(const char* lumpname) { - return fb->GetShaderCache()->GetPublicFile(lumpname).Code; + return fb->GetShaderCache()->GetPublicFile(lumpname)->Code; } FString VkShaderManager::LoadPrivateShaderLump(const char* lumpname) { - return fb->GetShaderCache()->GetPrivateFile(lumpname).Code; + return fb->GetShaderCache()->GetPrivateFile(lumpname)->Code; } VkPPShader* VkShaderManager::GetVkShader(PPShader* shader) diff --git a/src/common/rendering/vulkan/shaders/vk_shader.h b/src/common/rendering/vulkan/shaders/vk_shader.h index b50f2af4a..526d6aaf1 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* GetGameShader(const VkShaderKey& key, bool isUberShader); bool CompileNextShader() { return true; } @@ -194,8 +195,12 @@ private: VulkanRenderDevice* fb = nullptr; - std::map generic; - std::map specialized; + struct + { + std::mutex Mutex; + std::map> Generic; + std::map> Specialized; + } GameShaders; std::list PPShaders; diff --git a/src/common/rendering/vulkan/shaders/vk_shadercache.cpp b/src/common/rendering/vulkan/shaders/vk_shadercache.cpp index f28c49c87..d51e495e4 100644 --- a/src/common/rendering/vulkan/shaders/vk_shadercache.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shadercache.cpp @@ -30,6 +30,8 @@ std::vector VkShaderCache::Compile(ShaderType type, const TArrayView VkShaderCache::Compile(ShaderType type, const TArrayViewChecksum != includeinfo.Checksum) { foundChanges = true; break; @@ -63,6 +65,8 @@ std::vector VkShaderCache::Compile(ShaderType type, const TArrayView VkShaderCache::Compile(ShaderType type, const TArrayViewGetDevice()); cachedCompile.LastUsed = LaunchTime; + lock.lock(); + auto& c = CodeCache[checksum]; c = std::move(cachedCompile); return c.Code; @@ -88,12 +94,12 @@ ShaderIncludeResult VkShaderCache::OnInclude(VkCachedCompile& cachedCompile, FSt if (depth > 8) I_Error("Too much include recursion!"); - const VkCachedShaderLump& file = system ? GetPrivateFile(headerName) : GetPublicFile(headerName); + const VkCachedShaderLump* file = system ? GetPrivateFile(headerName) : GetPublicFile(headerName); VkCachedInclude cacheinfo; cacheinfo.LumpName = headerName; cacheinfo.PrivateLump = system; - cacheinfo.Checksum = file.Checksum; + cacheinfo.Checksum = file->Checksum; cachedCompile.Includes.push_back(std::move(cacheinfo)); FString includeguardname; @@ -104,7 +110,7 @@ ShaderIncludeResult VkShaderCache::OnInclude(VkCachedCompile& cachedCompile, FSt code << "#ifndef " << includeguardname.GetChars() << "\n"; code << "#define " << includeguardname.GetChars() << "\n"; code << "#line 1\n"; - code << includeFilter(file.Code.GetChars()) << "\n"; + code << includeFilter(file->Code.GetChars()) << "\n"; code << "#endif\n"; return ShaderIncludeResult(headerName.GetChars(), code.GetChars()); @@ -129,34 +135,52 @@ FString VkShaderCache::CalcSha1(const FString& str) return sha1.final(); } -const VkCachedShaderLump& VkShaderCache::GetPublicFile(const FString& lumpname) +const VkCachedShaderLump* VkShaderCache::GetPublicFile(const FString& lumpname) { + std::unique_lock lock(FileMutex); + auto it = PublicFiles.find(lumpname); if (it != PublicFiles.end()) - return it->second; + return it->second.get(); - VkCachedShaderLump cacheinfo; - cacheinfo.Code = LoadPublicShaderLump(lumpname.GetChars()); - cacheinfo.Checksum = CalcSha1(cacheinfo.Code); + auto cacheinfo = std::make_unique(); + cacheinfo->Code = LoadPublicShaderLump(lumpname.GetChars()); + cacheinfo->Checksum = CalcSha1(cacheinfo->Code); auto& result = PublicFiles[lumpname]; result = std::move(cacheinfo); - return result; + return result.get(); } -const VkCachedShaderLump& VkShaderCache::GetPrivateFile(const FString& lumpname) +const VkCachedShaderLump* VkShaderCache::GetPrivateFile(const FString& lumpname) { + std::unique_lock lock(FileMutex); + auto it = PrivateFiles.find(lumpname); if (it != PrivateFiles.end()) - return it->second; + return it->second.get(); - VkCachedShaderLump cacheinfo; - cacheinfo.Code = LoadPrivateShaderLump(lumpname.GetChars()); - cacheinfo.Checksum = CalcSha1(cacheinfo.Code); + auto cacheinfo = std::make_unique(); + cacheinfo->Code = LoadPrivateShaderLump(lumpname.GetChars()); + cacheinfo->Checksum = CalcSha1(cacheinfo->Code); auto& result = PrivateFiles[lumpname]; result = std::move(cacheinfo); - return result; + return result.get(); +} + +FString GetStringFromLumpThreadsafe(int lump) +{ + auto reader = fileSystem.OpenFileReader(lump, FileSys::READER_NEW, FileSys::READERFLAG_SEEKABLE); + if (reader.GetBuffer()) + { + return FString(reader.GetBuffer(), reader.GetLength()); + } + else + { + auto data = reader.Read(); + return FString(static_cast(data.data()), data.size()); + } } FString VkShaderCache::LoadPublicShaderLump(const char* lumpname) @@ -164,14 +188,14 @@ FString VkShaderCache::LoadPublicShaderLump(const char* lumpname) int lump = fileSystem.CheckNumForFullName(lumpname, 0); if (lump == -1) lump = fileSystem.CheckNumForFullName(lumpname); if (lump == -1) I_Error("Unable to load '%s'", lumpname); - return GetStringFromLump(lump); + return GetStringFromLumpThreadsafe(lump); } FString VkShaderCache::LoadPrivateShaderLump(const char* lumpname) { int lump = fileSystem.CheckNumForFullName(lumpname, 0); if (lump == -1) I_Error("Unable to load '%s'", lumpname); - return GetStringFromLump(lump); + return GetStringFromLumpThreadsafe(lump); } static uint8_t readUInt8(FileReader& fr) { uint8_t v = 0; fr.Read(&v, 1); return v; } diff --git a/src/common/rendering/vulkan/shaders/vk_shadercache.h b/src/common/rendering/vulkan/shaders/vk_shadercache.h index f5e4b9a56..e72c7652f 100644 --- a/src/common/rendering/vulkan/shaders/vk_shadercache.h +++ b/src/common/rendering/vulkan/shaders/vk_shadercache.h @@ -50,8 +50,8 @@ public: std::vector Compile(ShaderType type, const TArrayView& sources, const std::function& includeFilter = {}); - const VkCachedShaderLump& GetPublicFile(const FString& lumpname); - const VkCachedShaderLump& GetPrivateFile(const FString& lumpname); + const VkCachedShaderLump* GetPublicFile(const FString& lumpname); + const VkCachedShaderLump* GetPrivateFile(const FString& lumpname); private: void Load(); @@ -70,9 +70,12 @@ private: VulkanRenderDevice* fb = nullptr; - std::map PublicFiles; - std::map PrivateFiles; + std::mutex CacheMutex; std::map CodeCache; + + std::mutex FileMutex; + std::map> PublicFiles; + std::map> PrivateFiles; }; class CachedGLSLCompiler diff --git a/src/common/rendering/vulkan/vk_levelmesh.cpp b/src/common/rendering/vulkan/vk_levelmesh.cpp index b6012f981..2edcfedf8 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.cpp +++ b/src/common/rendering/vulkan/vk_levelmesh.cpp @@ -651,12 +651,12 @@ void VkLevelMesh::CreateViewerObjects() FString VkLevelMesh::LoadPrivateShaderLump(const char* lumpname) { - return fb->GetShaderCache()->GetPrivateFile(lumpname).Code; + return fb->GetShaderCache()->GetPrivateFile(lumpname)->Code; } FString VkLevelMesh::LoadPublicShaderLump(const char* lumpname) { - return fb->GetShaderCache()->GetPublicFile(lumpname).Code; + return fb->GetShaderCache()->GetPublicFile(lumpname)->Code; } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/common/rendering/vulkan/vk_lightmapper.cpp b/src/common/rendering/vulkan/vk_lightmapper.cpp index faf14a489..d201cfb78 100644 --- a/src/common/rendering/vulkan/vk_lightmapper.cpp +++ b/src/common/rendering/vulkan/vk_lightmapper.cpp @@ -610,12 +610,12 @@ int VkLightmapper::GetRaytracePipelineIndex() FString VkLightmapper::LoadPrivateShaderLump(const char* lumpname) { - return fb->GetShaderCache()->GetPrivateFile(lumpname).Code; + return fb->GetShaderCache()->GetPrivateFile(lumpname)->Code; } FString VkLightmapper::LoadPublicShaderLump(const char* lumpname) { - return fb->GetShaderCache()->GetPublicFile(lumpname).Code; + return fb->GetShaderCache()->GetPublicFile(lumpname)->Code; } void VkLightmapper::CreateRaytracePipeline()