Has anyone had chance to use the new r.UseShaderCache feature yet on Windows? I’ve noticed that it is turned on by default on Mac.
We’re hoping to use it but the documentation is a little bit vague so was hoping somebody could fill me in a bit on it’s usage and any gotchas or issues they’ve found.
I’d also be very keen to hear whether people find it makes a noticeable difference or not for their games.
Also, although the documentation and code do specify that it is only supported for OpenGL, the ShaderPlatformCanPrebindBoundShaderState(…) in ShaderCache.cpp returns true for SP_PCD3D_SM5 etc. Is D3D supported or planned to be supported at any point?
Thanks in advance,
Fantastic answer. Thanks for the (very thorough) info.
The ShaderCache is very much an in-progress feature that I added specifically to address shader hitching on Mac OS X’s OpenGL. It will work on Windows/Linux OpenGL 3, but the OpenGL 4 render path might not work properly as it hasn’t been as well tested. There’s no support for other RHIs yet, primarily because it hasn’t been as important for them so far. I still have plans to improve its scaling to games with very dynamic content and/or streaming and it will be extended to other platforms & RHIs in time.
The console variable r.UseShaderCaching is the primary switch to enable or disable shader caching, nothing will be done unless this is 1. If this is enabled then the game will try to load an existing ShaderCache.ushadercache file to populate the cache with previous execution data. It will prefer the local user’s version from their saved folder if it exists, but developers can ship an initial cache file in their game’s content directory. When enabled shaders will be submitted to the RHI as soon as they are deserialised from content rather than deferring it until first use. If they have been used in a previous execution the bound-shader-states that they belong to will be constructed once all other component shaders have been deserialised and compiled.
The console variable r.UseShaderDrawLog enables or disables tracking the RHI state used for each draw command which is required to take full advantage of shader caching. OpenGL implementations have to create new GPU-specific variants of shaders depending on the assigned OpenGL state for each draw call if they haven’t already cached the appropriate variant. Capturing the state adds noticeable overhead to several RHI calls, most importantly draw calls, which can reduce headline performance so when this is enabled can vary. For a primarily static, level based game where shaders and bound-shader-states can be captured within a single play-through of the game it may be better for developers to only enable this while capturing the initial cache for distribution but leave it disabled for end-users to improve performance. For games which dynamically load shaders, particularly those that stream shaders, it will be necessary to leave it enabled.
The console variable r.UseShaderPredraw takes the data captured with r.UseShaderDrawLog and issues a draw calls with appropriate state for each bound-shader-state to force the underlying driver to create the correct GPU shader variants internally. This occurs only at the end of frames so it doesn’t modify rendering state during the frame. The pre-draw can be very slow, so to amortise the cost over several frames you can specify a number of milliseconds to spend pre-drawing each frame using the console variable r.PredrawBatchTime. The default is -1 which will perform all the pre-draws in a single frame.
As the ShaderCache tracks shaders after compilation, using only their hashes to identify them it can’t know when the existing contents of the cache are redundant due to changes in the shaders. As a result you should call FShaderCache::SetGameVersion with a new version number whenever you make significant changes that will invalidate the existing cache contents. Ideally you would only repopulate the cache immediately prior to each such release to avoid redundant entries accumulating.
I hope that helps.