On mobile devices, it is essential to verify that all fragment shaders are mobile friendly. When you use built-in shaders, you should use the Mobile or Unlit version of the shader. Avoid excessive use of multi-pass shaders (for example, legacy specular) and excessive shader passes (that is, more than 2 passes).
|Pass||High-End Mobile [ms]||Low-End Mobile [ms]|
⋆ Time for multi-pass shaders (such as bloom) depends heavily on the screen resolution. Combining them into fewer passes leads to better performance.
Where appropriate, you should use the most basic shaders. Make use of the inexpensive Mobile > Unlit (Supports Lightmap) shader to lightmap your Scenes.
You can remove every shader that you don’t use from the Always included list of shaders in the Graphics Settings (Edit > ProjectSettings > Graphics). Additionally, you can add shaders to the list which always includes them for the lifetime of the application. Tip: If you want finer control over load times, use shader variant collections instead; this lets you take the performance impact of loading at a time you choose during run time, rather than increasing your initial load time. See the section on Shader Preloading for more details.
Some Unity Shaders are always included in the build by default, such as the Splash Screen, pink error Shader, and the clear screen. These Shaders account for a dozen kilobytes in total, but not in the range of megabytes. To see which shaders Unity includes in your build, read the build log.
After the build you can find data for large shaders in the Editor.log, which includes shader timing and size and looks similar to the following log:
Compiled shader 'TEST Standard (Specular setup)' **in** 31.23s d3d9 (total **internal** programs: 482, unique: 474) d3d11 (total **internal** programs: 482, unique: 466) metal (total **internal** programs: 482, unique: 480) glcore (total **internal** programs: 482, unique: 454) Compressed shader 'TEST Standard (Specular setup)' on d3d9 from 1.04MB to 0.14MB Compressed shader 'TEST Standard (Specular setup)' on d3d11 from 1.39MB to 0.12MB Compressed shader 'TEST Standard (Specular setup)' on metal from 2.56MB to 0.20MB Compressed shader 'TEST Standard (Specular setup)' on glcore from 2.04MB to 0.15MB
This report tells you a couple of things about the Test shader:
The shader expands into 482 variants due to #pragma multi_compile and shader_feature.
Unity compresses the shader included in the game data to roughly the sum of the compressed sizes: 0.14+0.12+0.20+0.15 = 0.61MB
At run time, Unity keeps the compressed data in memory (0.61MB), while the data for your currently used graphics API (for example, Metal) is uncompressed, which in the above example would account for 2.56MB.
Inspecting the log file shows the compressed disk size for single Shaders. To determine the size of Shaders at run time, you can perform a detailed memory capture with the Unity Profiler. If you complete a deep memory profile, you can inspect Shaderlab, which includes everything associated with Shaders under the Shaderlab root, including buffers, source code, and other allocations related to the compilation of shaders. Shaders themselves have their own object root, and the Profiler lists them under Shaders.
Shader keywords are global. Currently, you can only use 196 keywords, because Unity itself uses 60 internally.
When you build Shaders, you can use underscore _ for disabling/enabling purpose functionality to avoid occupying global keywords (for instance, when using #pragma multi_compile _SUPER_FEATURE).
Tip: use shader_feature over multi_compile as it saves memory by stripping unneeded keywords.
Shaders often include a multitude of variants which increase build size and which might not be necessary.
If you use the following defines in a Shader, Unity generates variants with both A and C defined:
#if 1 #pragma multi_compile A B #else #pragma multi_compile C D #endif
Unity runs the code for parsing #pragmas for variants before the pre-process step. Avoid using #defines in shader code. To read more about shader variants, see documentation on Making multiple shader program variants.
Tip: Disable Shader settings (such as linear fog) in the Graphics Settings if you don’t need them. This removes variants to handle those settings from all Shaders when making a build.
Unity can preload shader variant collection during application load time, or you can load them via scripts. If you load them via script, you gain control over the loading process. See documentation on Optimizing Shader Load Time for more information.
Note: If you add a Shader and a variant collection which refers to it, Unity loads all sub-shaders (LODs) of the Shader when you warm up the variant collection.
Unity can preload Shaders and keep them in memory for the lifetime of the application, which grants control over how much memory Shaders occupy. Additionally, preloading Shaders reduces Scene load time issues as you control the time when Unity loads the Shaders.
Built-in Shaders on mobile are generalized for a specific use-case; for example, Unity made the UI/Default shader specifically for UI elements. You should remove any Shaders from the Always Included Shader list that you do not use.
Note: Unity can strip shaders from a build when you remove a Graphics API. Go to the Player Settings (menu: Edit > Project Settings > Player) window, scroll down to the Other Settings section and remove any Graphics API you don’t need. Unity still ships with the same binary as before, but does not use the Graphics API anymore. The benefit is that disabling an unused Graphics API strips all Shaders specific to it from the built-in Resources and saves disk space.