I just learned that some framerate spikes were being caused by shader variants being copied to the GPU only when they are used for the first time. The solution is to use Shader Variant Collections. The Unity docs are here: Unity - Manual: Shader variant collections I think this thread is part of the history.
In my case it was when calling SetActive() on a bunch of objects at the same time, I was getting a very long frame. Drilling down into the profiler, I saw that all the time in that long frame was spent in Shader.EditorLoadVariant. So after learning about Shader Variant Collections, I can do this to create a collection and preload it at boot, preventing the glitch runtime:
Run the game to the point where all the required shaders are being used.
Edit->Project Settings->Graphics
Scroll to the Shader preloading section at the bottom
Hit Clear β I learned through trial and error you need to do this. I think Unity adds to the Currently Tracked count everytime it sees a shader. So when I was doing a test with a new blank project, imported Effects package, it was saying it was tracking 37 variants, even with nothing in the scene.
Save to asset
Add that asset to the Preloaded shaders array
That all works fine. My questions are:
How is this managed in a production environment?
Does anyone automate the creation of the Shader Variant Collections? Or do you have to manually go into each scene or level, clear the tracked, then create a new asset? I can see that getting very tedious and be error prone as new shaders and materials are added to a scene.
What about subtle variations from one level to another? Rather than having a separate collection for each level/scene, they could be combined to create a collection that covers all cases.
Do shader variants ever get unloaded from GPU? So that when used again, do they get recompiled? It appears not from my testing.
Please share if you have experience with this issue.
Anybody come up with a good workflow for this? Manually creating the shaderVariantCollection has already been the source of a lot of bugs and confusion on our team. To make it worse it looks like the variants wonβt even get built to asset bundles unless they are bundled next to a shaderVariantCollection.
typeof(ShaderUtil).GetMethod("SaveCurrentShaderVariantCollection", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { string path });
Clear:
typeof(ShaderUtil).GetMethod("ClearCurrentShaderVariantCollection", BindingFlags.Static |
BindingFlags.NonPublic).Invoke(null, new object[0] );
Pipeline:
Techical Artist create and actualize folder with Materials, each representing shader variant we use ( add new materials with new used keywords, multi compile, shader feature etc, remove materials with no more used keywords list)
script create a bunch of Spheres with this materials infront of camera
Editor coroutine switch dynamic things to collect variants, for example day/night features, custom fog, custom lightmapping, Light shadows none/hard/soft
save main shader variant collection using reflection above
reverse engineer ShaderVariantCollection asset as text
split it to multiplie ShaderVariantCollection with same header, but with keywords per-shader-guid text-block
remove unidentified shaders
save each collection as separated file with same header
save addressables-shaders variants to collections also marked as addressables, to prevent duplication and not working prewarm caused by duplication
sort by variantCount
ShaderVariantCollection.Prewarm() in Coroutine at runtime
Later we plan to split shader variant collection per quality preset, and prewarm only selected quality, to make prewarm execute more lightweight at runtime