Search Unity

ShaderVariantCollection best practises?

Discussion in 'Shaders' started by PsyDev, Feb 8, 2017.

  1. PsyDev

    PsyDev

    Joined:
    Sep 30, 2015
    Posts:
    31
    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: https://docs.unity3d.com/Manual/OptimizingShaderLoadTime.html 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.
     
    futurlab_xbox and McDev02 like this.
  2. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Nice find I hit a major performance problem that seems to be caused by this!
     
  3. KarlKarl2000

    KarlKarl2000

    Joined:
    Jan 25, 2016
    Posts:
    606

    Yep I have the same questions as you.

    I found this. Seems to let us load \ unload shader variants on demand. So would be useful when certain levels need certain shaders.
    https://docs.unity3d.com/ScriptReference/ShaderVariantCollection.Add.html

    Just gotta figure out how to use it now lol.
     
  4. andysaia

    andysaia

    Joined:
    Nov 2, 2015
    Posts:
    21
    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.
     
    futurlab_xbox likes this.
  5. mitaywalle

    mitaywalle

    Joined:
    Jul 1, 2013
    Posts:
    253
    Save:
    Code (CSharp):
    1. typeof(ShaderUtil).GetMethod("SaveCurrentShaderVariantCollection", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { string path });
    Clear:
    Code (CSharp):
    1. typeof(ShaderUtil).GetMethod("ClearCurrentShaderVariantCollection", BindingFlags.Static |
    2. 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
    - find there Shader as guid reference
    - use this api to get real reference to Shader
    Code (CSharp):
    1.        string filePath = AssetDatabase.GUIDToAssetPath(guid);
    2.             Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(filePath);
    - 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
     
    Last edited: Mar 28, 2023