Feature: Cache compiled SPIR-V to avoid recompiling identical shaders - #1723
Feature: Cache compiled SPIR-V to avoid recompiling identical shaders#1723mallonoce wants to merge 1 commit into
Conversation
|
I think there is likely a better solution at a higher level than grafting some statics into ShaderCompiler.cpp that can't be managed externally. The vsg::ShaderSet class has variants that provides pre-compiled shader support. Calling Viewer::compile() when changing settings is also using a sledge hammer to tighten a screw. The vsg::Object base class has a compare(..) method to help with finding matches, which ShaderStage etc. implement so might be useful. However, as the details of the issue that this PR addresses are so high level it's impossible to say exactly what the best solution would be. I think it would be worth investigating what is actual going on in your usage case then figure out what the best changes would be, either in the VSG codebase, or in your application usage, or a combo of both. |
|
@mallonoce I responded back in June but haven't had any reply. The submission isn't suitable for merging as is as I believe there are better ways for tackling it. To resolve the best way I need to understand your usage case that it causing multiple compiles of GLSL to SPIRV then figure out the most efficient way to tackle it. As a general note putting a global cache alognside an ShaderCompiler object rather storing variables within that ShaderCompiler object is not good design/implementation. All cache variables should go directly into the ShaderCompiler class. If one ones to reuse shaders then reusing the ShaderCompiler object is the right way to go. |
Cache compiled shaders by source and settings so repeated glslang invocations for identical shaders are skipped.
Description
This adds a cache of compiled SPIR-V inside ShaderCompiler::compile so that an
identical shader is sent through glslang at most once per run.
We ran into this in a proprietary application that changes scene graph nodes very
often. Every time the user changes the layout we rebuild part of the scene and
call Viewer::compile again. We first noticed it after updating VSG from 1.1.10 to
1.1.15: on 1.1.15 each change spent almost all of its time recompiling shaders,
around 220 ms per change, while on 1.1.10 the same application did not.
The cause is that ShaderCompiler::compile runs glslang whenever a ShaderModule has
no code yet. When the scene is rebuilt with fresh modules that have the same source
and settings as before, the same shaders are compiled from scratch every time.
The cache is keyed by the shader stage, the compile settings, and the final source
after includes and defines are applied. On a hit it assigns the cached SPIR-V to
module->code and skips glslang. On a miss it stores the result after a successful
compile. Access is guarded by a mutex, so each unique shader is compiled once and
reused after that.
The cache is a static map with no eviction. The set of unique shaders is small in
practice, so it settles at a few entries, but I am happy to make it optional or add
a size limit if you prefer.
No new dependencies.
Type of change
How Has This Been Tested?
We profiled a RelWithDebInfo build with the Windows Performance Toolkit and added
timing around the phases of our redraw.
To reproduce: build an application that rebuilds its scene and calls Viewer::compile
repeatedly (so ShaderModules are recreated with the same source and settings), and
time Viewer::compile with and without the patch.
Test Configuration:
Checklist: