How-To: Show Shader Precompile Progress (optional)

Goal: hold the loading screen while Unreal precompiles the bundled PSO cache after a game or driver update, and show an "Optimizing shaders 42%" progress bar while it happens.

Prerequisites
You can reach the manager (How-To: Get the Building Blocks step 1) and have a loading screen widget (Quick Start). This feature is optional - with bEnableShaderPrecompileGate off, nothing below changes existing behaviour.

It also needs the project to actually ship a recorded .upipelinecache. Step 1 covers that; without it the gate correctly does nothing.

1. Enable the engine's PSO systems

Turn on Share Material Shader Code in Project Settings -> Packaging, then add this to DefaultEngine.ini:

ini
[/Script/Engine.RendererSettings]
r.ShaderPipelineCache.Enabled=1
r.ShaderPipelineCache.StartupMode=0
r.ShaderPipelineCache.ExcludePrecachePSO=1
r.PSOPrecaching=1
r.PSOPrecache.Components=1
[DevOptions.Shaders]
NeedsShaderStableKeys=true
StartupMode must be 0
The engine opens the pipeline cache during engine init, before plugin modules load. With the default StartupMode=1 the precompile can begin before this plugin exists and the batch total is lost, leaving the progress bar estimated. The plugin logs a warning to LogCrimsonLoadingScreen if it sees a non-zero value.

Then record a cache: run a packaged build with -logPSO, play through representative content at each scalability level, expand the recording with the ShaderPipelineCacheTools commandlet, and place the result in Build/<Platform>/PipelineCaches/ before cooking. See the plugin README for the full command.

2. Choose a gate mode

Project Settings -> Crimson -> Loading Screen -> Shader Precompilation.

ModeBehaviour
BlockUntilComplete (default)Hold the loading screen until the batch finishes.
BackgroundThenBlockOnPlayLet shaders trickle behind an interactive menu; hold only when the game asks (step 4).
NeverBlockNever hold. Progress is still reported if you want your own corner indicator.

3. Show progress in the widget

Reveal your shader panel only when the state is Precompiling. Warming means work is outstanding but still inside the grace period - most boots never leave it, and the player should see nothing.

Screenshot pendingImages/CrimsonLoadingScreen/howto-shader-progress-bp.png
In the loading screen widget's Tick: Get Game Instance Subsystem (CrimsonLoadingScreenManager) -> Get Shader Precompile State -> Equal (Enum) against Precompiling -> Branch. True branch: Set Visibility (Visible) on the shader panel, and Get Shader Precompile Progress -> Set Percent on a Progress Bar.

Prefer events over polling? Bind On Shader Precompile Progress (Progress, Remaining, Total) and On Shader Precompile Complete on the manager - both are Blueprint-assignable, with C++ multicast mirrors.

4. Block on Play (BackgroundThenBlockOnPlay only)

With that mode the screen is not held automatically. Call this from your Play button so shaders compile behind the menu but are guaranteed complete before gameplay. It is a no-op once the precompile has finished.

Screenshot pendingImages/CrimsonLoadingScreen/howto-shader-blockonplay-bp.png
Play button OnClicked -> Get Game Instance Subsystem (CrimsonLoadingScreenManager) -> Request Block Until Shader Precompile Complete -> then your Open Level / travel node.

5. Test it without packaging

PIE always reports zero work: PSO precaching is compiled out under WITH_EDITOR and the editor never opens a bundled cache. Drive the UI with a synthetic batch instead:

text
CrimsonLoadingScreen.ShaderPrecompile.SimulateSecs 20
Verify
The loading screen stays up. For the first 2 seconds nothing changes; then the shader panel reveals and the bar animates to 100% before the screen releases. Set SimulateSecs to 1 instead and the panel must never appear - that is the grace period doing its job. The CVar clears itself on completion.

For a real end-to-end test you need a packaged build launched with -clearPSODriverCache and r.PSOPrecache.Validation=2, then stat PSOPrecache to check Missed and Too late counts.

-clearPSODriverCache can be silently ignored
NVIDIA drivers from around September 2025 changed the driver cache format. If a supposedly cold run finishes suspiciously fast, disable Shader Cache in the NVIDIA Control Panel instead.

6. Optional: drain runtime precache before a level shows

Separate from the boot-time batch, bHoldForRuntimePSOPrecache delays the end of a level load until outstanding runtime precache requests drain, reducing first-frame hitching and material pop-in.

Keep the priority filter
Leave RuntimePSOPrecacheMinPriority at High and above. Low-priority requests are issued continuously as components register, so waiting on all of them tends never to converge. MaxRuntimePSOPrecacheWaitSecs caps the wait regardless - do not set it to 0.

See also: Concept: PSO Precaching & the Grace Period for why the plugin measures instead of predicting, and Concept: Lifecycle & Show Conditions for where these two checks sit among the other conditions.