Shader stripping stripping out shaders that are neccesary

Good day to you all.

I have this weird issue where Unity strips out used and necessary shaders when I do a build with either the strip all or strip unused options on shader variant stripping. I’m using Shader variant collections to make sure that the engine knows what shaders are neccesary and it still seems to strip out shaders that are needed and I’m kind of out of options. Builds with keep all takes days, which is not very sustainable to say the least but that’s the only way it seems to work.

When I enter a level of the build, the whole thing is reduced to basically 1 frame every 5 or so seconds, half the level is missing and after about a minute it crashes. The funny thing is that it doesn’t affect the entire computer, it’s just the Unity application that is unresponsive. I tried adding URP/Lit and other variants to the Always Included Shaders list but that resulted in the same thing.

I welcome any tips on reducing shader variants as well, what is a good way of going about shaders and stripping out manually that might not be needed, especially store bought assets as they blow up the shader variant count.

Unity logs that I deem important (I’d love to upload the whole thing but it’s 10 mb in size and just spams these two entries in seemingly random sequence, I don’t use DOTS anywhere in my project as far as I’m aware but there might be some internal Unity thing that uses it for rendering):

Shader Universal Render Pipeline/Lit, subshader 0, pass 3, stage vertex: variant DOTS_INSTANCING_ON not found.
0x00007ff9fd57616e (UnityPlayer) UnityMain
0x00007ff9fd3704a2 (UnityPlayer) UnityMain
0x00007ff9fd56b3ce (UnityPlayer) UnityMain
0x00007ff9fe1ef701 (UnityPlayer) UnityMain
0x00007ff9fcf4d7aa (UnityPlayer) 
0x00007ff9fcf58220 (UnityPlayer) 
0x00007ff9fcf56034 (UnityPlayer) 
0x00007ff9fcf44b30 (UnityPlayer) 
0x00007ff9fcea2698 (UnityPlayer) 
0x00007ff9fcc5262f (UnityPlayer) 
0x00007ff9fcc61f10 (UnityPlayer) 
0x00007ff9fcc622cd (UnityPlayer) 
0x00007ff9fd556019 (UnityPlayer) UnityMain
0x00007ff9fccbe3ca (UnityPlayer) 
0x00007ff9fccbcd0b (UnityPlayer) 
0x00007ff9fccc09e9 (UnityPlayer) 
0x00007ff9fcfc445b (UnityPlayer) 
0x00007ffacf6de8d7 (KERNEL32) BaseThreadInitThunk
0x00007ffad122c53c (ntdll) RtlUserThreadStart

Shader Universal Render Pipeline/Lit, subshader 0, pass 1, stage vertex: variant DOTS_INSTANCING_ON _CASTING_PUNCTUAL_LIGHT_SHADOW not found.
0x00007ff9fd57616e (UnityPlayer) UnityMain
0x00007ff9fd3704a2 (UnityPlayer) UnityMain
0x00007ff9fd56b3ce (UnityPlayer) UnityMain
0x00007ff9fe1ef701 (UnityPlayer) UnityMain
0x00007ff9fcf4d7aa (UnityPlayer) 
0x00007ff9fcf58220 (UnityPlayer) 
0x00007ff9fcf56034 (UnityPlayer) 
0x00007ff9fcf44b30 (UnityPlayer) 
0x00007ff9fcea2698 (UnityPlayer) 
0x00007ff9fcc5262f (UnityPlayer) 
0x00007ff9fcc529bc (UnityPlayer) 
0x00007ff9fcc4e130 (UnityPlayer) 
0x00007ff9fcc4e6cc (UnityPlayer) 
0x00007ff9fd556019 (UnityPlayer) UnityMain
0x00007ff9fccbe3ca (UnityPlayer) 
0x00007ff9fccbcd0b (UnityPlayer) 
0x00007ff9fccc09e9 (UnityPlayer) 
0x00007ff9fcfc445b (UnityPlayer) 
0x00007ffacf6de8d7 (KERNEL32) BaseThreadInitThunk
0x00007ffad122c53c (ntdll) RtlUserThreadStart

Other details that might be important to the issue:

  • I’m using URP with Forward+ rendering and the GPU Resident Drawer enabled
  • I have both shadows and Additional Lights enabled
  • There is Post-Processing present with Shadow Midtones Highlights and Color Adjustments enabled
  • The project has been basically dragged kicking and screaming all the way from 2019 to 6.2f2 (everything else works perfectly but this might affect build times)
  • I’m using two store bought assets that blow up the shader variant count: INab Studio’s Dissolve Shader and the All in 1 VFX toolkit, other than these two, there are small shaders from Synty asset packs where the only one that has a significant amount of shader variants is from their nature pack, and it’s their tree wind shaders but even that is a fraction of the shader variants that are being compiled for basic things like URP/LitTerain
  • I’m using realtime lighting and reflections, so nothing is baked aside from a single reflection probe that is used to fake environmental reflection on shiny surfaces
  • I’m using linear fog and no other types of fog
  • I only have one Quality preset

I can’t really point to anything that will help you with your overall issue, but i can say for sure, that some time ago, i had a project, which used gpu instanced shaders, and the variant count / build time, was ab.so.lutely abnoxious, lol.

Might be worth looking into how many, if any, are instanced.

I have successfully solved the issue. It was actually a lot more simple than I thought. Literally just uncheck the Strict Variant Matching option in the player settings.

The rest of this reply will be a bit of a tangent but related to my other issue of exploding shader variants and multiple days long build times. I will try to include as many key phrases as I can so that other people looking up the issue on very long shader variant building can maybe find this solution useful themselves.

For years, I have used this one project. It was so long in fact, that Unity’s built-in rendering pipeline went from the recommended one, to viable but not recommended to entirely being deprecated over the time I have been using the project. Over the years the project was littered with a ton of random asset store stuff which included countless shaders I never even looked at and building was never taking longer than 40 minutes. Over time tough, something changed. Around the time I migrated to Unity 6, my build times ballooned including the shader variant counts. They went from hundreds to hundred thousands. The only thing I saw that changed was Unity’s version and since other people said the same thing, I just assumed Unity 6 might just not be a viable engine anymore for this project.

There were also a lot of (frankly smug) people online saying that you should NEVER EVER use store bought shaders because they are unoptimised garbage anyways and just make building a pain.

There was this nagging feeling tough. I have not added any asset store shaders, and yet back then the build times were shorter and now they are longer. There is no way they cause all this headache. I don’t know why I never really thought of this, but I did a build, checked one specific asset store shader that had about 200k variants and took days just to go trough that one shader. Then I made an entirely new project, where only that asset was present. Sure enough, the shader variant count was 912. Turns out, there are a few options you can tick or untick in the Graphics tab, tucked way under post-processing stuff so you never really look there.

These highlighted options are the culprits. No idea when this happened but at one point I must have unticked these thinking “I don’t want shader issues, why would I tick these”. These are ticked on by default and for some reason they overwrite the Keep all/Strip all/Strip unused option at the top of the graphics tab.

Sure enough, with these ticked on, my build time went from a week to just 2 hours (never thought I’d be thankful for a 2 hour long build time lol) and shader variant counts stayed under a 1000. I hope this is useful for someone in the future and if there is anyone out there reading this who knows more about the engine to explain these options I’d be very happy to hear an explanation to what could have happened here. Either way, I’m just happy both of my issues were solved, from me, by me lol. I really don’t want others to go trough all the pain I had to, to arrive here.

When you uncheck this setting to stop the engine from reporting errors, you may get incorrect rendering. The errors are there for a reason :slight_smile:

So all it does is it suppresses the error message if there is no strict shader variant matching?

No. It also silently chooses the closest variant to the one that is needed - this is the part that may lead to incorrect rendering.
When this option is enabled, it uses the error shader instead and prints the information about the variant it couldn’t find.

I see, I will be looking out for possible rendering issues then. Thank you!

It’s not “possible” it’s “definitely.” You now have rendering issues if you enabled this and it “solved” your problems. You basically should never use it. Find the reason for your missing shaders and resolve it.

Will there be a fix for this?
We literally can’t build our games in Unity 6.

A fix for what, specifically? Which render pipeline are you using? How is it configured?

URP (17.2.0),
Unity 6000.2.15f

Build variants stripping does not include shader variants, that were collected with “Preload Shaders” in Project Settings → Graphics.

Objects that correctly render in Editor Play Mode (and are collected in Shader Variant Collection) draw magenta in build and spam that the variant is not found.

A fix for what, specifically?

Same overall issue though as reported in this thread, and my own post about this from 7 January. When shader variant stripping is enabled, Unity 6 and 6.3 seems to be removing shader variants that player builds are requesting at runtime, even if those shader variants are included in a ShaderVariantCollection assigned to the Preloaded Shaders list in Project Settings → Graphics, and/or if the shader is included in the Always Included Shaders list.

When strict shader variant matching is enabled, the player log is filled with entries about one or more missing variants, and effects either draw with the magenta error shader or not at all. In my own testing, and the bug report project I’ll mention in a moment, this seems to affect both the standard URP Lit shader, as well as custom shaders made with ShaderGraph. When strict shader variant matching is disabled, effects and objects will render as the player tries to use the closest matching variant that’s available in the build, however there are instances, such as in my bug report project, where the closest match is not close enough and effects render incorrectly.

Which render pipeline are you using? How is it configured?

I filed a bug report with a reproduction project and replication steps on 8 January. It’s not been triaged by a QA engineer yet, so I only have an incident ID if you want to check it out; IN-130027.

The bug report was made using editor version 6000.3.2f1, using URP, but I was able to observe similar behaviour with the same project files on editor version 6000.0.64f1. The project settings should be included with the bug report, but if they’re not reply there with what’s missing and I can send you those file(s). I did have GPU Resident Drawer enabled, I was using the Forward+ rendering path, and Instancing Variants was set to Keep All. However despite this, as the log snippet in my other post demonstrates, at least one shader was missing a variant where the only keyword requested was DOTS_INSTANCING_ON. There was seemingly nothing I could do, bar disabling shader stripping entirely, to get that shader variant included. I’ve not re-tested with 6000.3.3f1 or later as I’ve not had the time, but there’s nothing I can quickly see in the release notes for the later releases that suggests a fix has been made.

I’m happy to answer any other questions, either here or on the bug report.

Editor may use different variants from the built application. When you see those errors, please add the missing variants manually to the shader variant collection you created in the Editor.
If you have the correct variants in the Shader Variant Collection that is added in the “Preload Shaders” list, and they are still not found, please report a bug.

This is the correct behaviour for the “Preloaded Shaders”, but not for “Always Included Shaders”. We’ll check this when we get to your bug report.

@SideSwipe9th already reported one exactly on this issue. Necessary variants are in the SVC. SVC is in “Preloaded Shaders” list. Some necessary variants are not in the build and development build spams “not found” exactly for those variants. There is nothing i can “add”, because it’s already “added”.
This happens both for Shader Graph shaders and for HLSL shaders (even with tag “UniversalPipeline”).

Wait, what? In your reply to Lynxed you just said that adding the correct variants to a Shader Variant Collection, and assigning it to the Preloaded Shaders list should be enough to ensure that those variants are included in a build. But in your reply to me you seem to be saying the opposite. Which is it?

Just to make clear, in my bug report, I’m assigning a Shader Variant Collection, with the shaders identified in the player log as missing when strict shader variant matching is enabled included in the collection, to the Preloaded Shaders list. Despite this, in both Unity 6 and 6.3 those shader variants are still being stripped from a build when shader variant stripping is enabled.

Could you please clarify what exactly is the process to ensure that shader variants that a player build requests are included in a build, especially if it is different from my own understanding? Thanks.

Shader Variant Collections provide data the same way as materials do. They still go through the stripping process, so it’s possible that the needed variants get stripped by OnProcessShaders. Without looking at the particular project, it’s hard to say, what exactly is happening, unfortunately.

Is that wise? If I’m understanding you correctly, aside from disabling shader stripping entirely, there is no way to guarantee that a specific shader variant that a player build requests is included in a build. You can hint at it through Shader Variant Collections, but the build process can still ignore or override that based on some other criteria that are outside of developer control.

If that is the case, then strict shader variant matching would never be guaranteed to work while shader stripping of unused variants is enabled. There is no mechanism for me as a developer to guarantee specific variants are include, particularly if the editor and player are disagreeing as to what variants they need, short of disabling shader variant stripping entirely and dealing with builds that can take days or weeks, or writing my own custom shader variant stripping code on a per-project basis.

Is there a reason why Shader Variant Collections, when properly populated and assigned, are not considered a canonical source for variants that must always be included?

No worries, I’m fully cognisant of the fact that I may well be doing something wrong in my project and bug report, and I don’t want to pre-empt that process. But sticking with the generalities we’re discussing here and if I’m following you correctly, it sounds like there is no mechanism to guarantee that any specific shader variant is included in a build.

It’s how they have been designed initially. Changing this behaviour would introduce regressions for people that use it accordingly.

You’re right, there’s no way for the user to say “I want this specific variant to be included”. That said, shader stripping should also be done correctly. If it’s stripping something that it shouldn’t, this needs to be fixed.

I think, that “Preload Shaders” should mean exactly that: everything that is in the list gets compiled and included. Otherwise this is a pointless and fake function.

My downgrade to 2022.3 got me to the point, where that i still have 2 variants that are not added no matter what i do. My custom shader is pretty much an HLSL-version of ShaderGraph’s Lit shader, so there is no way i can just add it to “always add” list: millions of variations lead to build failing with “too many variants”.

I also had to say goodbye to Enum Keywords in my ShaderGraph Shader. Enum keywords are not included in the build. Had to make a hierarchy of boolean keywords, and those got added to the build.

I’ve written an editor script, that parses the player log output and adds shader variants into SVC. So i’m absolutely sure the shader variants that are stripped are in the SVC. But because of the “we strip it anyway” It does not mean anything.

I realise this may be an unanswerable question, as regressions can be difficult to predict, but regressions in what sense? If their initial behaviour was to ensure that specific variants were included, but this was changed at some point after they were introduced to only be a hint that can be ignored, what problem was that change attempting to solve that reverting to the older behaviour, but not necessarily the older implementation, would reintroduce?

As an aside, what about making this an opt-in change? Add a checkbox to where you assign shader variant collections in the Project Settings → Graphics window, where if it’s ticked variants that are in the collections are always included. Having looked briefly at the code that performs the stripping for URP, you could then check if the shader variant the build process is currently processing is in that collection, before you preform the other checks that currently strip it, and if it is in a properly assigned collection don’t strip it and skip over the rest of the stripping code for that variant. That way, if the checkbox isn’t set, you get the current behaviour, and if it is set, developers like myself now have a mechanism to guarantee that specific variants are always included?

I realise that would increase the code complexity of the variant stripping process, and it doesn’t fix the underlying issue, but when you consider what I’m about to say below, I’m not sure this can be fixed in any other way, and this would at least provide a solid workaround that would enable us to use strict shader variant matching as intended.

I agree, but can that even be fixed though? For as long as I’ve been aware of the setting, the editor and player have frequently disagreed on what shader variants are used during gameplay. Part of that seems to be by design, as there are (or were) editor only shader keywords that cannot be included in a build. But even with the current implementation and leaving the stripping issue aside, if I use the editor tracking system in Project Settings → Graphics to prepopulate a shader variant collection, the collection it generates can be incomplete versus what a player build actually requests at runtime. There are seemingly always some number of variants that a build requests that are simply never used in the editor, and I have to add manually to the collection.

Why does a player build request shader variants that are not used by the editor, and therefore unable to be automatically captured by the editor shader variant tracking system? Unless and until both editor and player use exactly the same variants, at exactly the same time, for the same circumstances in a scene, I’m not sure you can solve this problem. If the build process is making assumptions on what shader variants are needed based on editor settings, that process will always run the risk of stripping variants the player actually needs, if the player has its own separate criteria for selecting shader variants at runtime.