Our implementation for this is :
using System;
using System.Collections.Generic;
using System.IO;
using Agens;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Callbacks;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
#if !AGENS_NO_SHADER_STRIPPING
class ShaderVariantsStripperProject : IPreprocessShaders
{
private ShaderStrippingSetting shaderStrippingSetting;
private static StreamWriter shaderStripLogFile;
public ShaderVariantsStripperProject()
{
var shaderStripSettings = Resources.LoadAll<ShaderStrippingSetting>("");
if (shaderStripSettings.Length == 0)
{
Debug.LogWarning("Did not find shader stripping settings asset , please create one. Compiling all shaders");
}
else
{
if (shaderStripSettings.Length > 1)
{
Debug.LogWarning("Warning More than one shader strip setting asset -- only using first found");
}
shaderStrippingSetting = shaderStripSettings[0];
}
}
public int callbackOrder { get { return (int)ShaderVariantsStripperOrder.Project; } }
public bool KeepVariant(Shader shader, ShaderSnippetData snippet, ShaderCompilerData shaderVariant)
{
bool resultKeepVariant = true;
if (shaderStripLogFile == null)
{
string timeStamp = String.Format("{0}d_{1}m__{2}h_{3}m",
DateTime.Now.Day, DateTime.Now.Month,
DateTime.Now.Hour, DateTime.Now.Minute);
shaderStripLogFile = new StreamWriter("Stripped_ShaderLog_" + timeStamp + ".txt");
}
var shaderKeywords = shaderVariant.shaderKeywordSet.GetShaderKeywords();
string[] keywords = new string[shaderKeywords.Length];
int i = 0;
foreach (var shaderKeyword in shaderVariant.shaderKeywordSet.GetShaderKeywords())
{
keywords[i]=shaderKeyword.GetKeywordName();
i++;
}
ShaderVariantCollection.ShaderVariant variant = new ShaderVariantCollection.ShaderVariant(shader,snippet.passType,keywords);
if (shaderStrippingSetting != null)
{
#if UNITY_IOS || UNITY_OSX
resultKeepVariant = shaderStrippingSetting.collectionOfShadersToKeepMetal.Contains(variant);
#elif UNITY_STANDALONE_WIN
resultKeepVariant = shaderStrippingSetting.collectionOfShadersToKeepPC.Contains(variant);
#endif
}
if (!resultKeepVariant)
{
string prefix = "not keepeing VARIANT: " + shader.name + " (";
if (snippet.passName.Length > 0)
prefix += snippet.passName + ", ";
prefix += snippet.shaderType.ToString() + ") ";
string log = prefix;
for (int labelIndex = 0; labelIndex < keywords.Length; ++labelIndex)
log += keywords[labelIndex] + " ";
shaderStripLogFile.Write(log + "\n");
}
return resultKeepVariant;
}
public void OnProcessShader(
Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> shaderVariants)
{
int inputShaderVariantCount = shaderVariants.Count;
for (int i = 0; i < shaderVariants.Count; ++i)
{
bool keepVariant = KeepVariant(shader, snippet, shaderVariants[i]);
if (!keepVariant)
{
shaderVariants.RemoveAt(i);
--i;
}
}
if (shaderStripLogFile != null)
{
float percentage = (float)shaderVariants.Count / (float)inputShaderVariantCount * 100f;
shaderStripLogFile.Write("STRIPPING(" + snippet.shaderType.ToString() + ") = Kept / Total = " + shaderVariants.Count + " / " + inputShaderVariantCount + " = " + percentage + "% of the generated shader variants remain in the player data\n");
}
}
[PostProcessBuild(1)]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) {
if (shaderStripLogFile != null)
{
shaderStripLogFile.Close();
shaderStripLogFile = null;
}
}
}
#endif
It seems to be working, we went from a memory footprint of 200 MB of shaderlab to 50 MB , which seems much more sane. However, the process of stripping the shaders seems to be very slow. 30 minutes added build time or so for metal.
The next thing I am going to try is to use the log file of what we are stripping out - because maybe there’s a configuration of the project that can be made, to not have Unity bundle that many variants to begin with.
Also, we are going to try to use the ShaderVariant collection to generate a more effecient strip file.
The hunch: it might be faster to use the shaderVariants file to generate a list of shaders&variants that probably should be stripped, and then directly strip based on that list - instead of calling shaderVariantCollection.Contains - which probably is the slow part of this mix.
I’ll report once I know more.
Update : We might be seeing similar caching based behaviour. Don’t really know what’s going on.
Update : also - we were returning Package in the shader stripping order, so that’s why it was so slow, I’ve updated the code to change it into Project