Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug Addressable and shader variants

Discussion in 'Addressables' started by KospY, May 22, 2021.

  1. KospY


    May 12, 2014
    When building addressable bundle, I discovered that the graphic tab and stripping options are completely ignored. I'm not sure if it's "by design" or not, but the lack of control about shader variants is a pain.

    Currently the only way I found to force Unity to bundle some variants is to create fake materials with the different keywords, so when we enable/disable keywords in-game it work. I also add to write a custom post processor to strip some keywords that are being included for no reason.

    Do I miss something? Does there is any plan to make shader variants less of pain with addressable? Also why addressable ignore shader variant collection?
  2. TreyK-47


    Unity Technologies

    Oct 22, 2019
    I'll flag this with the team for some insight.
    futurlab_peterh likes this.
  3. pillakirsten


    Unity Technologies

    May 22, 2019
    Hi as far as I know, shader variants should be supported by Addressables. It is hard to determine the issue without knowing the exact project setup.

    For example if the scene containing the assets is not addressable (is only in player data) but the assets are addressable, then the shader variants won't work for assets in the scene "by design". If both are addressable, then I would expect the shader variants to work.

    If that's not the case then I would recommend submitting a bug report with your project if possible.
    futurlab_peterh likes this.
  4. KospY


    May 12, 2014

    Our issue is we are changing shader keywords at runtime, to enable/disable some shader features.
    By default the materials that are part of scenes and prefabs in addressable are without the keyword, so they get stripped by addressable.

    Before addressable we could address that with a shader variant collection (generated by the editor why we play) or by referencing the shader in graphics tab. But it seem that it don't work anymore as addressable completely ignore the graphics tab. (even the "strip all" option don't have any effect)

    What is the correct procedure to force include shader variants with addressable now?
    Last edited: May 30, 2021
    futurlab_peterh likes this.
  5. Ryanc_unity


    Unity Technologies

    Jul 22, 2015
    This is still supported in addressables. Just save the generated shader variant collection to somewhere in your assets folder, then mark it as addressable, and then rebuild addressables. In doing so, this provides the addressable build system with knowledge about the additional variants (provided by the svc) that you want to support at runtime.
  6. unity_QGZXqB4cNGVnaw


    Sep 13, 2018
    Hi @Ryanc_unity!

    We are trying to load a scene (marked as 'Addressable') with some assets inside which are not marked as 'Addressables' and used materials are not marked as well. Those assets use URP/Lit shader with enabled Emission and EmissionMap. When this scene is loaded from addressables bundles, the emission map doesn't work (but is assigned in the inspector). The only way we solved it was by marking used materials as 'Addressable'. It is quite inconvenient since we need to track all the materials and make sure all of them are marked as addressable (we also pack scenes separately in bundles so we needed to create a special group for those materials). I think it happens due to shader stripping, is there a better way to solve this issue?
    futurlab_peterh and wang-li like this.
  7. sacb0y


    May 9, 2016
    Could this also fix the issue of Shafer variants getting out of control with the URP lit shader when using addressable?
  8. AdionN


    Nov 6, 2019
    I have also problems with URP lit and addressables. In my case even if I added SVC to addressables, while building it, it stills builds every single URP lit shader (188M) and when I start game it loads everything to memory, so URP lit shader is over 200 mb...

    What I was trying to do is using OnProcessShader to strip every shader that is not in SVC, reducing size of shaders to 5 mb or so. But then I still have problems with rendering, as some shaders were still missing. Most likely because getting shaders data from SVC is close to impossible, because you get GUID for most of shaders, but some like UI/Default dont have GUID, so I cant get their data from SVC and cant get its name for checking. For example UI/Default is ```
    first: {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}``` in SVC....
    dan_soqqle and FamishedMammal like this.
  9. dan_soqqle


    Feb 2, 2022
    Do you have a solution about the loading of all the shaders into memory? because as soon as the lit shader is loaded it goes nuts, and crashes the app right away on mobile.

    The most annoying thing is that its super hard to reset after loading the 'offending' scene. Even after deleting it from the groups and cleaning+ rebuilding, the shader is still stuck. Wasted tons of time on this ..
    Last edited: Nov 26, 2022
  10. sacb0y


    May 9, 2016
    In my case this was more of a problem on vulkan than opengl.

    But my solution was switching most of my shaders to amplify shader.
    dan_soqqle likes this.
  11. dan_soqqle


    Feb 2, 2022
    thank you will check it out. For now, I upgraded to 2022 where it seems there is some variable shader loading feature and also it seems i dont have a stuck shader RAM problem. Even after deleting the shader cache in the library. Still testing this . . .
  12. AdionN


    Nov 6, 2019

    So there several things about it. Addressables forums states, that if you create ShaderCollection (Playersettings-> Graphics -> save collection) and then make it into adrressables, it will smart enough to load only shaders that is in that list (have in mind you will need to clear it and run all scenes again to collect all used shaders cleanly). However it did not work for me, especially with Webgl, as memory limit is super tight. What I ended doing is stripping shaders. which looks something like that:

    Here ShadersInUse is shaders that I gather from shaders collection mentioned before. In short I include all shaders that are default. then if it not default or not needed for me I check if they are in the list with correct variant words. If they are, I skip them and let them to be compiled, if not, then I remove them from compiling stack.
    This is not my fully idea, but I forgot whom code I ended up using, I am sorry Hero of the internet!
    you will need to adapt the code but this is hacky method, at least for now.

    Code (CSharp):
    2.     public class ShaderStrippingController : UnityEditor.Editor, IPreprocessShaders {
    3.         private int counter = 0;
    5.         [SerializeField] private ShaderScriptingDataSo ShadersInUse;
    6.         [SerializeField] private bool stripShaders = false;
    7.         public int callbackOrder { get; }
    8.         ShaderKeyword m_KeywordDebug;
    10.         public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data) {
    11.             if (ShadersInUse != null) stripShaders = ShadersInUse.StripShaders;
    12.             if (!stripShaders) return;
    13.             string shaderName =;
    14.             if ((shaderName.StartsWith("Hidden/") || !shaderName.Contains("Terrain") ) || shaderName.StartsWith("Unlit/") || shaderName.StartsWith("Legacy Shaders/") || shaderName.StartsWith("Particles/")
    15.                 || shaderName.StartsWith("UI/Default")) {
    17.                 Debug.Log("AddedToBuild" + shaderName);
    18.                 return;
    19.             }
    21.          for( int i = data.Count - 1; i >= 0; --i )
    22.             {
    23.                 bool shouldSkipShaderVariant = true;
    24.                 var shaderInQuestion = ShadersInUse.ShadersInfo.Where(shaderList => shaderList.shaderName == shaderName);
    25.                 //Workaround for shaders that does not have GUID in Shader variants collection. If shader not find, skip stripping and add shader variants.
    26.                 if (shaderInQuestion.Count() == 0) {
    27.                     return;
    28.                 }
    30.                 foreach (var possibleShader in shaderInQuestion) {
    31.                     var compilingShaderKeyWords = data[i].shaderKeywordSet.GetShaderKeywords().ToList();
    32.                     if (compilingShaderKeyWords.Count == 0) {
    33.                         shouldSkipShaderVariant = false;
    34.                     } else {
    35.                         List<string> tempKeyWords = new List<string>();
    36.                         foreach (var VARIABLE in data[i].shaderKeywordSet.GetShaderKeywords()) {
    37.                             tempKeyWords.Add(;
    38.                         }
    39.                         try {
    40.                             ShaderVariantCollection.ShaderVariant tempShader =
    41.                                 new (shader,snippet.passType,tempKeyWords.ToArray());
    43.                             foreach (var possibleShaderVariant in possibleShader.variants) {
    44.                                 if (ShaderVariantsEqual(tempShader, possibleShaderVariant)) {
    45.                                     Debug.Log($"AddedToBuild {} {tempShader.keywords.Count()}");
    46.                                     shouldSkipShaderVariant = false;
    47.                                     break;
    48.                                 }
    49.                             }
    50.                         } catch (Exception e) {
    51.                             shouldSkipShaderVariant = false;
    52.                         }
    53.                         if (!shouldSkipShaderVariant) break;
    54.                     }
    55.                 }
    57.                 if (!shouldSkipShaderVariant) {
    58.                     counter += 1;
    59.                 }
    60.                 if(shouldSkipShaderVariant)
    61.                 {
    62.                     data.RemoveAt( i );
    63.                 }
    64.    List<string> _tempCompareShaderVariants = new List<string>();
    65.         bool ShaderVariantsEqual(ShaderVariantCollection.ShaderVariant a, ShaderVariantCollection.ShaderVariant b) {
    66.             if (a.shader != b.shader || a.passType != b.passType) return false;
    67.             if ((a.keywords == null) != (b.keywords == null)) return false;
    68.             if (a.keywords.Length != b.keywords.Length) return false;
    69.             _tempCompareShaderVariants.Clear();
    70.             _tempCompareShaderVariants.AddRange(a.keywords);
    71.             for (int i = 0; i < b.keywords.Length; ++i) {
    72.                 if (!_tempCompareShaderVariants.Contains(b.keywords[i])) {
    73.                     return false;
    74.                 }
    75.             }
    76.             return true;
    77.         }
    78.         private string ListToText(List<ShaderKeyword> list) {
    79.             string result = "";
    80.             foreach(var listMember in list) {
    81.                 result += + "  ";
    82.             }
    83.             return result;
    84.         }
    85. }
    NOTE: this shouldnt be a problem if you have your addressables in correct folders and etc. I am now putting all objects in correct addressables groups and dont trust automatic solving of analysis tools. At least that way I hope to later remove this hacky solution out of my way.