diff --git a/src/vsg/utils/ShaderCompiler.cpp b/src/vsg/utils/ShaderCompiler.cpp index 9e79e395a..9f10ea67f 100644 --- a/src/vsg/utils/ShaderCompiler.cpp +++ b/src/vsg/utils/ShaderCompiler.cpp @@ -31,6 +31,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include +#include +#include +#include #ifndef VK_API_VERSION_MAJOR # define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU) @@ -129,6 +132,10 @@ bool ShaderCompiler::supported() const } #if VSG_SUPPORTS_ShaderCompiler +// SPIR-V compile cache: avoids repeated glslang invocations for identical shaders. +static std::mutex s_spirvCacheMutex; +static std::map s_spirvCache; + bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector& defines, ref_ptr options) { // need to balance the inits. @@ -138,6 +145,61 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector& vsg_shader) -> std::string { + auto settings = vsg_shader->module->hints ? vsg_shader->module->hints : defaults; + std::string source = vsg::insertIncludes(vsg_shader->module->source, options); + std::vector allDefines(defines); + if (settings) + { + for (const auto& define : settings->defines) allDefines.push_back(define); + } + if (!allDefines.empty()) source = combineSourceAndDefines(source, allDefines); + + std::ostringstream k; + k << static_cast(vsg_shader->stage) << ';'; + if (settings) + { + k << static_cast(settings->language) + << ';' << settings->vulkanVersion + << ';' << settings->clientInputVersion + << ';' << static_cast(settings->target) + << ';' << settings->defaultVersion + << ';' << settings->forwardCompatible + << ';' << settings->generateDebugInfo + << ';' << settings->optimize << ';'; + } + k << source; + return k.str(); + }; + + std::vector cacheKeys; + cacheKeys.reserve(shaders.size()); + for (auto& vsg_shader : shaders) + { + if (vsg_shader && vsg_shader->module) + cacheKeys.push_back(computeCacheKey(vsg_shader)); + else + cacheKeys.emplace_back(); + } + + // Check if all shaders are already cached. + { + std::scoped_lock lock(s_spirvCacheMutex); + bool allCached = !shaders.empty(); + for (size_t i = 0; i < shaders.size() && allCached; ++i) + { + if (!shaders[i] || !shaders[i]->module || s_spirvCache.find(cacheKeys[i]) == s_spirvCache.end()) + allCached = false; + } + if (allCached) + { + for (size_t i = 0; i < shaders.size(); ++i) shaders[i]->module->code = s_spirvCache[cacheKeys[i]]; + return true; + } + } + // ---- end SPIR-V compile cache pre-check (results stored after a successful compile below) --- + auto getFriendlyNameForShader = [](const ref_ptr& vsg_shader) { switch (vsg_shader->stage) { @@ -278,15 +340,15 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vectorsetEnvClient(glslang::EShClientVulkan, targetClientVersion); shader->setEnvTarget(glslang::EShTargetSpv, targetLanguageVersion); - std::string finalShaderSource = vsg::insertIncludes(vsg_shader->module->source, options); + std::string source = vsg::insertIncludes(vsg_shader->module->source, options); - std::vector combinedDefines(defines); - for (const auto& define : settings->defines) combinedDefines.push_back(define); - if (!combinedDefines.empty()) finalShaderSource = combineSourceAndDefines(finalShaderSource, combinedDefines); + std::vector allDefines(defines); + for (const auto& define : settings->defines) allDefines.push_back(define); + if (!allDefines.empty()) source = combineSourceAndDefines(source, allDefines); - vsg::debug("ShaderCompiler::compile() combinedDefines = ", combinedDefines); + vsg::debug("ShaderCompiler::compile() allDefines = ", allDefines); - const char* str = finalShaderSource.c_str(); + const char* str = source.c_str(); shader->setStrings(&str, 1); EShMessages messages = EShMsgDefault; @@ -298,7 +360,7 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vectoraddShader(shader); stageShaderMap[envStage] = vsg_shader; @@ -307,7 +369,7 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vectorgetInfoLog()); info("glslang debug info log: \n", shader->getInfoDebugLog()); @@ -380,6 +442,16 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector lock(s_spirvCacheMutex); + for (size_t i = 0; i < shaders.size(); ++i) + { + if (shaders[i] && shaders[i]->module && !shaders[i]->module->code.empty()) + s_spirvCache[cacheKeys[i]] = shaders[i]->module->code; + } + } + return true; } #else