Skip to content

Commit c4fedc6

Browse files
committed
Feature:
Add SPIR-V compile cache to ShaderCompiler Cache compiled shaders by source and settings so repeated glslang invocations for identical shaders are skipped.
1 parent 1d81ced commit c4fedc6

1 file changed

Lines changed: 80 additions & 8 deletions

File tree

src/vsg/utils/ShaderCompiler.cpp

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
132139
bool 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

Comments
 (0)