@@ -31,6 +31,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
3131
3232#include < algorithm>
3333#include < iomanip>
34+ #include < map>
35+ #include < mutex>
36+ #include < sstream>
3437
3538#ifndef VK_API_VERSION_MAJOR
3639# define VK_API_VERSION_MAJOR (version ) (((uint32_t )(version) >> 22 ) & 0x7FU )
@@ -129,6 +132,10 @@ bool ShaderCompiler::supported() const
129132}
130133
131134#if VSG_SUPPORTS_ShaderCompiler
135+ // SPIR-V compile cache: avoids repeated glslang invocations for identical shaders.
136+ static std::mutex s_spirvCacheMutex;
137+ static std::map<std::string, vsg::ShaderModule::SPIRV > s_spirvCache;
138+
132139bool ShaderCompiler::compile (ShaderStages& shaders, const std::vector<std::string>& defines, ref_ptr<const Options> options)
133140{
134141 // need to balance the inits.
@@ -138,6 +145,61 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector<std::strin
138145 _initialized = true ;
139146 }
140147
148+ // Build cache keys from shader stage, settings, and final source (with includes/defines).
149+ auto computeCacheKey = [&](const ref_ptr<ShaderStage>& vsg_shader) -> std::string {
150+ auto settings = vsg_shader->module ->hints ? vsg_shader->module ->hints : defaults;
151+ std::string source = vsg::insertIncludes (vsg_shader->module ->source , options);
152+ std::vector<std::string> allDefines (defines);
153+ if (settings)
154+ {
155+ for (const auto & define : settings->defines ) allDefines.push_back (define);
156+ }
157+ if (!allDefines.empty ()) source = combineSourceAndDefines (source, allDefines);
158+
159+ std::ostringstream k;
160+ k << static_cast <uint32_t >(vsg_shader->stage ) << ' ;' ;
161+ if (settings)
162+ {
163+ k << static_cast <int >(settings->language )
164+ << ' ;' << settings->vulkanVersion
165+ << ' ;' << settings->clientInputVersion
166+ << ' ;' << static_cast <int >(settings->target )
167+ << ' ;' << settings->defaultVersion
168+ << ' ;' << settings->forwardCompatible
169+ << ' ;' << settings->generateDebugInfo
170+ << ' ;' << settings->optimize << ' ;' ;
171+ }
172+ k << source;
173+ return k.str ();
174+ };
175+
176+ std::vector<std::string> cacheKeys;
177+ cacheKeys.reserve (shaders.size ());
178+ for (auto & vsg_shader : shaders)
179+ {
180+ if (vsg_shader && vsg_shader->module )
181+ cacheKeys.push_back (computeCacheKey (vsg_shader));
182+ else
183+ cacheKeys.emplace_back ();
184+ }
185+
186+ // Check if all shaders are already cached.
187+ {
188+ std::scoped_lock<std::mutex> lock (s_spirvCacheMutex);
189+ bool allCached = !shaders.empty ();
190+ for (size_t i = 0 ; i < shaders.size () && allCached; ++i)
191+ {
192+ if (!shaders[i] || !shaders[i]->module || s_spirvCache.find (cacheKeys[i]) == s_spirvCache.end ())
193+ allCached = false ;
194+ }
195+ if (allCached)
196+ {
197+ for (size_t i = 0 ; i < shaders.size (); ++i) shaders[i]->module ->code = s_spirvCache[cacheKeys[i]];
198+ return true ;
199+ }
200+ }
201+ // ---- end SPIR-V compile cache pre-check (results stored after a successful compile below) ---
202+
141203 auto getFriendlyNameForShader = [](const ref_ptr<ShaderStage>& vsg_shader) {
142204 switch (vsg_shader->stage )
143205 {
@@ -278,15 +340,15 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector<std::strin
278340 shader->setEnvClient (glslang::EShClientVulkan, targetClientVersion);
279341 shader->setEnvTarget (glslang::EShTargetSpv, targetLanguageVersion);
280342
281- std::string finalShaderSource = vsg::insertIncludes (vsg_shader->module ->source , options);
343+ std::string source = vsg::insertIncludes (vsg_shader->module ->source , options);
282344
283- std::vector<std::string> combinedDefines (defines);
284- for (const auto & define : settings->defines ) combinedDefines .push_back (define);
285- if (!combinedDefines .empty ()) finalShaderSource = combineSourceAndDefines (finalShaderSource, combinedDefines );
345+ std::vector<std::string> allDefines (defines);
346+ for (const auto & define : settings->defines ) allDefines .push_back (define);
347+ if (!allDefines .empty ()) source = combineSourceAndDefines (source, allDefines );
286348
287- vsg::debug (" ShaderCompiler::compile() combinedDefines = " , combinedDefines );
349+ vsg::debug (" ShaderCompiler::compile() allDefines = " , allDefines );
288350
289- const char * str = finalShaderSource .c_str ();
351+ const char * str = source .c_str ();
290352 shader->setStrings (&str, 1 );
291353
292354 EShMessages messages = EShMsgDefault;
@@ -298,7 +360,7 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector<std::strin
298360
299361 if (parseResult)
300362 {
301- debug (" Successful compile\n " , debugFormatShaderSource (finalShaderSource ), " \n " );
363+ debug (" Successful compile\n " , debugFormatShaderSource (source ), " \n " );
302364
303365 program->addShader (shader);
304366 stageShaderMap[envStage] = vsg_shader;
@@ -307,7 +369,7 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector<std::strin
307369 {
308370 // print error information
309371 warn (" \n ---- " , getFriendlyNameForShader (vsg_shader), " ---- \n " );
310- warn (debugFormatShaderSource (finalShaderSource ));
372+ warn (debugFormatShaderSource (source ));
311373 warn (" GLSL source failed to parse." );
312374 warn (" glslang info log:\n " , shader->getInfoLog ());
313375 info (" glslang debug info log: \n " , shader->getInfoDebugLog ());
@@ -380,6 +442,16 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector<std::strin
380442 }
381443 }
382444
445+ // Cache new compiled SPIR-V.
446+ {
447+ std::scoped_lock<std::mutex> lock (s_spirvCacheMutex);
448+ for (size_t i = 0 ; i < shaders.size (); ++i)
449+ {
450+ if (shaders[i] && shaders[i]->module && !shaders[i]->module ->code .empty ())
451+ s_spirvCache[cacheKeys[i]] = shaders[i]->module ->code ;
452+ }
453+ }
454+
383455 return true ;
384456}
385457#else
0 commit comments