Improvements to shader build time and runtime memory usage

Greetings from the Shaders team!

We would like to share the news on some immediate improvements coming up, which aim to resolve the current pain points of shader compilation time and memory usage. We also wish to share some best practices regarding shader variant stripping, which you may find useful.

Conditional shader features allow artists and developers to easily control the shader’s functionality at authoring or runtime. Static branching and shader variant compilation is traditionally used, which generally provides improved performance of shader execution compared to dynamic branching. As the feature set of the render pipelines grew, so did the amount of shader variants, often resulting in painfully long build times and high memory usage for shaders.

Improvements to shader compilation time and memory usage

To address these pain points, we are now working on immediate improvements to Unity’s shader variant stripping, as well as shader memory usage:

Variant Keyword Prefiltering is a new optimization to the engine’s built-in stripping, and will significantly reduce build times due to shader variant processing and compilation. Shader variant stripping greatly reduces the amount of compiled shader variants, however stripping is currently performed at the end of the shader processing stage. Processing a huge amount of variants can still take a long time, regardless of compilation.

This optimization introduces early exclusion of ‘multi_compile’’ keywords based on project settings to greatly reduce shader variants processing time. Variant Prefiltering landed in 2023.1.0a14 and was backported all the way back to 2021.3.15. Based on our internal tests, warm build times have been reduced by up to 90%!



Dynamic Shader Loading is an additional optimization which provides control over shader runtime memory usage. Shader variants actively utilized by the GPU in a frame are often a small portion of all variants actually included in the build. Prior to this optimization, all shader variants were front loaded to CPU memory on scene or resource load. As a result, shader memory usage was often much larger than what is actually needed at runtime.

This optimization enables streaming of shader data chunks into memory, and eviction of shader data that is no longer needed at runtime, based on a user controlled memory budget. This allows to reduce shader memory usage on platforms with limited memory budget.

The new Shader Variant Loading Settings are now accessible from the Editor’s Player Settings, and can be used to override the maximum number of shader chunks loaded and per-shader chunk size (MB):

You can also override the maximum amount of loaded shader chunks at runtime using the C# API, via Shader.maximumChunksOverride. This allows to override the shader memory budget based on factors such as the total available system and graphics memory queried at runtime.

Dynamic Shader Loading landed in 2023.1.0a11 and backported all the way back to 2021.3.12. In the case of URP’s BoatAttack, we observed a 78.8% reduction in runtime memory usage for shaders, from 315 MiB (default) to 66.8 MiB (dynamic loading). You can read more about this optimization in the official announcement .

We hope to share information about backports as soon as possible. In the longer term, we are also planning to improve Shader Variant Management as a whole, from better tools and workflows for tracking and stripping shader variants, to improved logging and control of build and runtime shader compilation, as well as documentation updates.

Build-time shader variant stripping

The engine’s build-time shader variant stripping aims to reduce the amount of compiled variant, the resulting build times and runtime memory usage for shaders. To maximize the benefits of Unity’s built-in variant stripping, it is recommended to follow the best practices outlined in the manual for Shader Variant Stripping.

Based on the render pipeline, you can also enable shader variant stripping settings in the URP Global Settings & HDRP Global Settings, and disable any unused features in the URP Assets & HDRP Assets used by the player quality settings.

When authoring your own conditional shader features using ‘multi_compile’ keywords, consider implementing editor scripts that perform custom build-time stripping via the OnProcessShaders callback. This way, you can explicitly remove variants that are not needed at runtime, based on your target platform or runtime settings.

Shader variant logging and runtime profiling

In 2020.2, the Editor.log was extended to include information (per shader/pass/stage) on the amount of shader variants stripped and compiled, as well as variants processing and compilation time. Before 2020.2, the same information can be logged by setting the “Shader Variant Log Level” in the Universal Render Pipeline Asset & HDRP Global Settings.

Player logging can be enabled via “Log Shader Compilation” in the Graphics Settings, and is useful for tracking shader variants that are requested by the GPU and compiled at runtime. In the Graphics Settings, it is also possible to track the runtime compiled shaders used by the Editor’s viewport, and save them to a Shader Variant Collection. (“save to asset” at the bottom of the menu)

Runtime memory usage of shaders can be profiled using the Memory Profiler package. Shaders which use up the most runtime memory are a good candidate for shader variant stripping.

When a requested shader variant is missing at runtime (due to stripping), the engine will fallback on the closest matching variant by default. in 2022.1.0a7*, Strict Shader Variant Matching was introduced and can be used to fallback on a pink debug shader instead, logging an error in the console detailing the missing variant and its keywords. This can be useful when performing shader variant stripping, in order to identify variants that should not be stripped.

We know the build time and memory situation has been a huge source of difficulty, and we appreciate all your feedback. You can now also submit requests directly to the Shader System Portal. Please let us know what you think, and share any improvements you would like to see in the future!

25 Likes

Thanks for info :slight_smile:

Waiting for editor tool to restrict some shader variant at authoring time and see it in edit, play mode and even in material inspector i.e. (this shader variant is disallowed by project settings)

5 Likes

Cool stuff. Glad this area of the engine is getting attention.

Do you have anything planned for the following issue:

We have our own custom uber-shader which can’t be included in “Always include”. Then for each level we have a scene that gets built as its own AssetBundle. Each of those AssetBundles will include its own copy of commonly used shaders which in our case results in a 300mb extra disk space.

For example for us the solution would be to be able to specify Shader Variant Collections as Always Include.

If Unity will make API that helps us treat each shader variant as a separate sub-asset or something like that → than we potentially can grab all shader variants and put them into separate bundle on build time

The existing way to achieve this is by using a shader variant collection list, though we definitely want to improve the API!

You can specify shader variant collection lists to be pre-loaded in the Graphics settings (and thus always included in the build). Though I agree that being able to ‘always include’ a shader variant collection list (regardless of preloading) will be a nice improvement, and we will definitely consider this.

1 Like

@dnach does the “This optimization is targeted for 23.1” means that 2021/2022 won’t receive this and still have the *128 (2^7, since 7 new keywords were added, and both are compiled as enabled/disabled permutation regardless of scene settings) increased shader compilation times for the shaders that are either based on Lit or that are created using SG?

Maybe I misunderstood so don’t get me wrong (frankly I don’t know who merged those changes to URP 12 without seeing the mayhem they will cause, so it’s hard for me to place any kind of blame), but if 2021 nor 2022 won’t get any reasonable fix for that, how are people actually expected to use URP in them (with no possibility to downgrade URP packages in newer Unity Editors, obviously) at all?

We had to write our own shader variant system that creates lots of sub shaders or else we would end up with GBs of loaded shader programs (with custom shader variant system it is less than 30 MB iirc).

I would like to see very aggressive shader loading at runtime. If only necessary shaders are loaded in, I wouldn’t expect more than a MB of loaded shader variants. Looking forward to this!

1 Like

@spamove We plan to backport both variant prefiltering and dynamic variant loading to 2022 and 2021.

14 Likes

This seems to be available from 2022.1 onwards?

Hey TJ! You are 100% correct and I will update the post to reflect.
Strict variant matching was added back in 2022.1.0a7.

1 Like

Dynamic shader variant loading is available in latest alpha!
More details here .

2 Likes

@dnach Can you elaborate on how Variant Keyword Prefiltering is different from OnProcessShader() where we can already skip specific keywords?
Is it just a fancy way of saying there’s going to be a UI to control keywords instead of going through coding with OnProcessShader()?

Hey!

Variant Prefiltering works by early filtering of unneeded ‘multi_compile’ shader keywords based on filtering rules, which are driven by Render Pipeline settings. By moving part of the shader variant processing to a point before variants enumeration, we significantly reduce the amount of time needed to process all the variants.

For example, if you disable Shadows in the Render Pipeline settings, we won’t generate any variants that enable shadows for enumeration.

The early prefiltering of keywords results in generating less shader variants to enumerate and process for potential scriptable stripping (by OnProcessShaders callback) and shader compilation.

2 Likes

Variant Keyword Prefiltering landed in 2023.1.0a14, and we are now working on backports. Based on our internal tests, we observed warm build times being reduced by up to 90%! (This will of course depend on the project)

6 Likes

Thanks sounds good. Waiting for the backport to 2021 LTS
Updated the project from 2019 lts to 2021 1.5 Weeks ago increasing the build time by xx% with shader variants in the millions.

Is there any ETA on how long these backports take? Having some incredibly bad issues lately with these exports, it’s pretty costly to what our studio is doing.

1 Like

Hard to say exactly, as they are quite complex and we want to test thoroughly. Probably in the range of several weeks.

3 Likes

Thanks for sharing this. Is there any news yet on when backports to 2021.3.x will arrive?

I’ll post here when we know in which version the backports will be available. Pull requests are prepared, we just need to land them :slight_smile:

8 Likes

Checking in on the status of the back ports since it is a critical issue for my team as well.

3 Likes