From 640a2b2c4104d957309ffe6d11de8b7e62e1cbb6 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 26 Feb 2025 01:47:07 +0100 Subject: [PATCH] Cache all GLSL shader compilation --- .../vulkan/shaders/vk_shadercache.cpp | 140 +++++++++++++++++- .../rendering/vulkan/shaders/vk_shadercache.h | 8 + src/common/rendering/vulkan/vk_levelmesh.cpp | 49 +----- src/common/rendering/vulkan/vk_levelmesh.h | 5 +- .../rendering/vulkan/vk_lightmapper.cpp | 85 +++-------- src/common/rendering/vulkan/vk_lightmapper.h | 5 +- 6 files changed, 177 insertions(+), 115 deletions(-) diff --git a/src/common/rendering/vulkan/shaders/vk_shadercache.cpp b/src/common/rendering/vulkan/shaders/vk_shadercache.cpp index 047b464e9..4c2cb2958 100644 --- a/src/common/rendering/vulkan/shaders/vk_shadercache.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shadercache.cpp @@ -5,10 +5,25 @@ #include "filesystem.h" #include "engineerrors.h" #include "cmdlib.h" +#include "i_specialpaths.h" #include +#include VkShaderCache::VkShaderCache(VulkanRenderDevice* fb) : fb(fb) { + FString path = M_GetCachePath(true); + CreatePath(path.GetChars()); + CacheFilename = path + "/shadercache.zdsc"; + + using namespace std::chrono; + LaunchTime = (uint64_t)(duration_cast(system_clock::now().time_since_epoch()).count()); + + Load(); +} + +VkShaderCache::~VkShaderCache() +{ + Save(); } std::vector VkShaderCache::Compile(ShaderType type, const TArrayView& sources, const std::function& includeFilter) @@ -23,7 +38,7 @@ std::vector VkShaderCache::Compile(ShaderType type, const TArrayViewsecond; + VkCachedCompile& cacheinfo = it->second; bool foundChanges = false; for (const VkCachedInclude& includeinfo : cacheinfo.Includes) @@ -37,7 +52,10 @@ std::vector VkShaderCache::Compile(ShaderType type, const TArrayView VkShaderCache::Compile(ShaderType type, const TArrayViewGetDevice()); + cachedCompile.LastUsed = LaunchTime; auto& c = CodeCache[checksum]; c = std::move(cachedCompile); @@ -155,6 +174,125 @@ FString VkShaderCache::LoadPrivateShaderLump(const char* lumpname) return GetStringFromLump(lump); } +static uint8_t readUInt8(FileReader& fr) { uint8_t v = 0; fr.Read(&v, 1); return v; } +static uint16_t readUInt16(FileReader& fr) { uint16_t v = 0; fr.Read(&v, 2); return v; } +static uint32_t readUInt32(FileReader& fr) { uint32_t v = 0; fr.Read(&v, 4); return v; } +static uint64_t readUInt64(FileReader& fr) { uint64_t v = 0; fr.Read(&v, 8); return v; } + +static FString readString(FileReader& fr, std::vector& strbuffer) +{ + size_t size = fr.ReadUInt32(); + if (strbuffer.size() < size + 1) + strbuffer.resize(size + 1); + if (fr.Read(strbuffer.data(), size) != (FileReader::Size)size) + return {}; + strbuffer[size] = 0; + return strbuffer.data(); +} + +static void writeUInt8(std::unique_ptr& fw, uint8_t v) { fw->Write(&v, 1); } +static void writeUInt16(std::unique_ptr& fw, uint16_t v) { fw->Write(&v, 2); } +static void writeUInt32(std::unique_ptr& fw, uint32_t v) { fw->Write(&v, 4); } +static void writeUInt64(std::unique_ptr& fw, uint64_t v) { fw->Write(&v, 8); } + +static void writeString(std::unique_ptr& fw, const FString& s) +{ + writeUInt32(fw, s.Len()); + fw->Write(s.GetChars(), s.Len()); +} + +void VkShaderCache::Load() +{ + try + { + FileReader fr; + if (fr.OpenFile(CacheFilename.GetChars())) + { + char magic[11] = {}; + fr.Read(magic, 11); + if (memcmp(magic, "shadercache", 11) != 0) + return; + + uint32_t version = readUInt32(fr); + if (version != 1) + return; + + std::vector strbuffer; + + uint32_t count = readUInt32(fr); + while (count > 0) + { + FString checksum = readString(fr, strbuffer); + + VkCachedCompile cachedCompile; + cachedCompile.LastUsed = readUInt64(fr); + cachedCompile.Code.resize(readUInt32(fr)); + if (fr.Read(cachedCompile.Code.data(), cachedCompile.Code.size() * sizeof(uint32_t)) != (FileReader::Size)cachedCompile.Code.size() * sizeof(uint32_t)) + return; + + uint32_t includecount = readUInt32(fr); + while (includecount > 0) + { + VkCachedInclude cachedInclude; + cachedInclude.LumpName = readString(fr, strbuffer); + cachedInclude.PrivateLump = readUInt8(fr) != 0; + cachedInclude.Checksum = readString(fr, strbuffer); + cachedCompile.Includes.push_back(std::move(cachedInclude)); + includecount--; + } + + // Drop entry if it hasn't been used for 30 days + if (cachedCompile.LastUsed + 30 * 24 * 60 * 60 > LaunchTime) + { + CodeCache[checksum] = std::move(cachedCompile); + } + count--; + } + } + } + catch (...) + { + } +} + +void VkShaderCache::Save() +{ + try + { + std::unique_ptr fw(FileWriter::Open(CacheFilename.GetChars())); + if (!fw) + return; + + fw->Write("shadercache", 11); + + uint32_t version = 1; + writeUInt32(fw, version); + + writeUInt32(fw, CodeCache.size()); + for (const auto& it : CodeCache) + { + const FString& checksum = it.first; + const VkCachedCompile& cachedCompile = it.second; + + writeString(fw, it.first); + writeUInt64(fw, cachedCompile.LastUsed); + writeUInt32(fw, (uint32_t)cachedCompile.Code.size()); + fw->Write(cachedCompile.Code.data(), cachedCompile.Code.size() * sizeof(uint32_t)); + + writeUInt32(fw, cachedCompile.Includes.size()); + for (const VkCachedInclude& cachedInclude : cachedCompile.Includes) + { + writeString(fw, cachedInclude.LumpName); + writeUInt8(fw, cachedInclude.PrivateLump ? 1 : 0); + writeString(fw, cachedInclude.Checksum); + } + } + } + catch (...) + { + } +} + ///////////////////////////////////////////////////////////////////////////// std::vector CachedGLSLCompiler::Compile(VulkanRenderDevice* fb) diff --git a/src/common/rendering/vulkan/shaders/vk_shadercache.h b/src/common/rendering/vulkan/shaders/vk_shadercache.h index 2be785424..f5e4b9a56 100644 --- a/src/common/rendering/vulkan/shaders/vk_shadercache.h +++ b/src/common/rendering/vulkan/shaders/vk_shadercache.h @@ -37,6 +37,7 @@ public: class VkCachedCompile { public: + uint64_t LastUsed = 0; std::vector Code; std::vector Includes; }; @@ -45,6 +46,7 @@ class VkShaderCache { public: VkShaderCache(VulkanRenderDevice* fb); + ~VkShaderCache(); std::vector Compile(ShaderType type, const TArrayView& sources, const std::function& includeFilter = {}); @@ -52,6 +54,9 @@ public: const VkCachedShaderLump& GetPrivateFile(const FString& lumpname); private: + void Load(); + void Save(); + FString CalcSha1(ShaderType type, const TArrayView& sources); FString CalcSha1(const FString& str); @@ -60,6 +65,9 @@ private: static FString LoadPublicShaderLump(const char* lumpname); static FString LoadPrivateShaderLump(const char* lumpname); + FString CacheFilename; + uint64_t LaunchTime = 0; + VulkanRenderDevice* fb = nullptr; std::map PublicFiles; diff --git a/src/common/rendering/vulkan/vk_levelmesh.cpp b/src/common/rendering/vulkan/vk_levelmesh.cpp index 223f4b4d2..b6012f981 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.cpp +++ b/src/common/rendering/vulkan/vk_levelmesh.cpp @@ -26,6 +26,7 @@ #include "vulkan/commands/vk_commandbuffer.h" #include "vulkan/descriptorsets/vk_descriptorset.h" #include "vulkan/pipelines/vk_renderpass.h" +#include "vulkan/shaders/vk_shadercache.h" #include "hw_levelmesh.h" #include "hw_material.h" #include "texturemanager.h" @@ -603,28 +604,21 @@ void VkLevelMesh::CreateViewerObjects() versionBlock += "#define USE_RAYQUERY\r\n"; } - auto onIncludeLocal = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, false); }; - auto onIncludeSystem = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, true); }; - Viewer.VertexShader = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Vertex) .AddSource("versionblock", versionBlock) .AddSource("vert_viewer.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_viewer.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("Viewer.VertexShader") .Create("vertex", fb->GetDevice()); Viewer.FragmentShader = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("versionblock", versionBlock) .AddSource("frag_viewer.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_viewer.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("Viewer.FragmentShader") .Create("vertex", fb->GetDevice()); @@ -657,41 +651,12 @@ void VkLevelMesh::CreateViewerObjects() FString VkLevelMesh::LoadPrivateShaderLump(const char* lumpname) { - int lump = fileSystem.CheckNumForFullName(lumpname, 0); - if (lump == -1) I_Error("Unable to load '%s'", lumpname); - return GetStringFromLump(lump); + return fb->GetShaderCache()->GetPrivateFile(lumpname).Code; } FString VkLevelMesh::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); -} - -ShaderIncludeResult VkLevelMesh::OnInclude(FString headerName, FString includerName, size_t depth, bool system) -{ - if (depth > 8) - I_Error("Too much include recursion!"); - - FString includeguardname; - includeguardname << "_HEADERGUARD_" << headerName.GetChars(); - includeguardname.ReplaceChars("/\\.", '_'); - - FString code; - code << "#ifndef " << includeguardname.GetChars() << "\n"; - code << "#define " << includeguardname.GetChars() << "\n"; - code << "#line 1\n"; - - if (system) - code << LoadPrivateShaderLump(headerName.GetChars()).GetChars() << "\n"; - else - code << LoadPublicShaderLump(headerName.GetChars()).GetChars() << "\n"; - - code << "#endif\n"; - - return ShaderIncludeResult(headerName.GetChars(), code.GetChars()); + return fb->GetShaderCache()->GetPublicFile(lumpname).Code; } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/common/rendering/vulkan/vk_levelmesh.h b/src/common/rendering/vulkan/vk_levelmesh.h index f55c4f62c..bf3d865f6 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.h +++ b/src/common/rendering/vulkan/vk_levelmesh.h @@ -118,9 +118,8 @@ private: void CreateViewerObjects(); - static FString LoadPrivateShaderLump(const char* lumpname); - static FString LoadPublicShaderLump(const char* lumpname); - static ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system); + FString LoadPrivateShaderLump(const char* lumpname); + FString LoadPublicShaderLump(const char* lumpname); VulkanRenderDevice* fb = nullptr; diff --git a/src/common/rendering/vulkan/vk_lightmapper.cpp b/src/common/rendering/vulkan/vk_lightmapper.cpp index 53d20ac8a..3ad559667 100644 --- a/src/common/rendering/vulkan/vk_lightmapper.cpp +++ b/src/common/rendering/vulkan/vk_lightmapper.cpp @@ -4,6 +4,7 @@ #include "vulkan/textures/vk_texture.h" #include "vulkan/commands/vk_commandbuffer.h" #include "vulkan/descriptorsets/vk_descriptorset.h" +#include "vulkan/shaders/vk_shadercache.h" #include "vk_levelmesh.h" #include "zvulkan/vulkanbuilders.h" #include "filesystem.h" @@ -501,39 +502,30 @@ void VkLightmapper::CreateShaders() traceprefix += "#define USE_RAYQUERY\r\n"; } - auto onIncludeLocal = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, false); }; - auto onIncludeSystem = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, true); }; - shaders.vertRaytrace = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Vertex) .AddSource("VersionBlock", prefix) .AddSource("vert_raytrace.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_raytrace.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.VertRaytrace") .Create("VkLightmapper.VertRaytrace", fb->GetDevice()); shaders.vertScreenquad = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Vertex) .AddSource("VersionBlock", prefix) .AddSource("vert_screenquad.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_screenquad.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.VertScreenquad") .Create("VkLightmapper.VertScreenquad", fb->GetDevice()); shaders.vertCopy = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Vertex) .AddSource("VersionBlock", prefix) .AddSource("vert_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_copy.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.VertCopy") .Create("VkLightmapper.VertCopy", fb->GetDevice()); @@ -550,58 +542,48 @@ void VkLightmapper::CreateShaders() defines += "#define USE_BOUNCE\n"; shaders.fragRaytrace[i] = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", defines) .AddSource("frag_raytrace.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_raytrace.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.FragRaytrace") .Create("VkLightmapper.FragRaytrace", fb->GetDevice()); } shaders.fragResolve = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix) .AddSource("frag_resolve.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_resolve.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.FragResolve") .Create("VkLightmapper.FragResolve", fb->GetDevice()); shaders.fragBlur[0] = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix + "#define BLUR_HORIZONTAL\r\n") .AddSource("frag_blur.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_blur.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.FragBlur") .Create("VkLightmapper.FragBlur", fb->GetDevice()); shaders.fragBlur[1] = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix + "#define BLUR_VERTICAL\r\n") .AddSource("frag_blur.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_blur.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.FragBlur") .Create("VkLightmapper.FragBlur", fb->GetDevice()); shaders.fragCopy = ShaderBuilder() - .Code(GLSLCompiler() + .Code(CachedGLSLCompiler() .Type(ShaderType::Fragment) .AddSource("VersionBlock", prefix) .AddSource("frag_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_copy.glsl").GetChars()) - .OnIncludeLocal(onIncludeLocal) - .OnIncludeSystem(onIncludeSystem) - .Compile(fb->GetDevice())) + .Compile(fb)) .DebugName("VkLightmapper.FragCopy") .Create("VkLightmapper.FragCopy", fb->GetDevice()); } @@ -628,41 +610,12 @@ int VkLightmapper::GetRaytracePipelineIndex() FString VkLightmapper::LoadPrivateShaderLump(const char* lumpname) { - int lump = fileSystem.CheckNumForFullName(lumpname, 0); - if (lump == -1) I_Error("Unable to load '%s'", lumpname); - return GetStringFromLump(lump); + return fb->GetShaderCache()->GetPrivateFile(lumpname).Code; } FString VkLightmapper::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); -} - -ShaderIncludeResult VkLightmapper::OnInclude(FString headerName, FString includerName, size_t depth, bool system) -{ - if (depth > 8) - I_Error("Too much include recursion!"); - - FString includeguardname; - includeguardname << "_HEADERGUARD_" << headerName.GetChars(); - includeguardname.ReplaceChars("/\\.", '_'); - - FString code; - code << "#ifndef " << includeguardname.GetChars() << "\n"; - code << "#define " << includeguardname.GetChars() << "\n"; - code << "#line 1\n"; - - if (system) - code << LoadPrivateShaderLump(headerName.GetChars()).GetChars() << "\n"; - else - code << LoadPublicShaderLump(headerName.GetChars()).GetChars() << "\n"; - - code << "#endif\n"; - - return ShaderIncludeResult(headerName.GetChars(), code.GetChars()); + return fb->GetShaderCache()->GetPublicFile(lumpname).Code; } void VkLightmapper::CreateRaytracePipeline() diff --git a/src/common/rendering/vulkan/vk_lightmapper.h b/src/common/rendering/vulkan/vk_lightmapper.h index 2cfd75502..3c9675825 100644 --- a/src/common/rendering/vulkan/vk_lightmapper.h +++ b/src/common/rendering/vulkan/vk_lightmapper.h @@ -135,9 +135,8 @@ private: int GetRaytracePipelineIndex(); - static FString LoadPrivateShaderLump(const char* lumpname); - static FString LoadPublicShaderLump(const char* lumpname); - static ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system); + FString LoadPrivateShaderLump(const char* lumpname); + FString LoadPublicShaderLump(const char* lumpname); FVector3 SwapYZ(const FVector3& v) { return FVector3(v.X, v.Z, v.Y); }