You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
385 lines
14 KiB
385 lines
14 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using JetBrains.Annotations;
|
|
using UnityEditor.Build;
|
|
using UnityEditor.Build.Reporting;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace UnityEditor.Rendering
|
|
{
|
|
[Serializable]
|
|
class VariantCounter
|
|
{
|
|
public uint inputVariants;
|
|
public uint outputVariants;
|
|
public override string ToString() => $"Total={outputVariants}/{inputVariants}({outputVariants / (float)inputVariants * 100f:0.00}%)";
|
|
}
|
|
|
|
[Serializable]
|
|
class ShaderVariantInfo : VariantCounter
|
|
{
|
|
public string variantName;
|
|
public double stripTimeMs;
|
|
public override string ToString() => $"{variantName} - {base.ToString()} - Time={stripTimeMs}ms";
|
|
}
|
|
|
|
[Serializable]
|
|
class ShaderStrippingInfo : VariantCounter, ISerializationCallbackReceiver
|
|
{
|
|
public string name;
|
|
|
|
private Dictionary<string, (VariantCounter count, List<ShaderVariantInfo> variantInfos)> m_VariantsByPipeline = new();
|
|
|
|
public void AddVariant(string pipeline, ShaderVariantInfo variant)
|
|
{
|
|
if (!m_VariantsByPipeline.TryGetValue(pipeline, out var list))
|
|
{
|
|
list.count = new VariantCounter();
|
|
list.variantInfos = new List<ShaderVariantInfo>();
|
|
m_VariantsByPipeline.Add(pipeline, list);
|
|
}
|
|
|
|
list.count.inputVariants += variant.inputVariants;
|
|
list.count.outputVariants += variant.outputVariants;
|
|
|
|
inputVariants += variant.inputVariants;
|
|
outputVariants += variant.outputVariants;
|
|
|
|
list.variantInfos.Add(variant);
|
|
}
|
|
|
|
public override string ToString() => $"{name} - {base.ToString()}";
|
|
|
|
public void Log(ShaderVariantLogLevel logLevel)
|
|
{
|
|
IEnumerable<ShaderVariantInfo> variantsToLog = null;
|
|
switch (logLevel)
|
|
{
|
|
case ShaderVariantLogLevel.AllShaders:
|
|
{
|
|
variantsToLog = m_VariantsByPipeline.SelectMany(i => i.Value.variantInfos);
|
|
break;
|
|
}
|
|
case ShaderVariantLogLevel.OnlySRPShaders:
|
|
{
|
|
variantsToLog = m_VariantsByPipeline
|
|
.Where(i => !string.IsNullOrEmpty(i.Key))
|
|
.SelectMany(i => i.Value.variantInfos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (variantsToLog != null && variantsToLog.Any())
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.AppendLine($"{this}");
|
|
foreach (var variant in variantsToLog)
|
|
{
|
|
sb.AppendLine($"- {variant}");
|
|
}
|
|
|
|
Debug.Log(sb.ToString());
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
class PipelineVariants : VariantCounter
|
|
{
|
|
public string pipeline;
|
|
public ShaderVariantInfo[] variants;
|
|
}
|
|
|
|
[SerializeField] private PipelineVariants[] pipelines;
|
|
public void OnBeforeSerialize()
|
|
{
|
|
pipelines = m_VariantsByPipeline
|
|
.Select(pipeline => new PipelineVariants()
|
|
{
|
|
pipeline = pipeline.Key,
|
|
variants = pipeline.Value.variantInfos.ToArray(),
|
|
inputVariants = pipeline.Value.count.inputVariants,
|
|
outputVariants = pipeline.Value.count.outputVariants,
|
|
})
|
|
.ToArray();
|
|
}
|
|
|
|
public void OnAfterDeserialize()
|
|
{
|
|
pipelines = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class works as an scope of the <see cref="ShaderStrippingReport"/> hooking into the
|
|
/// <see cref="IPreprocessBuildWithReport"/> that are being called at the begin of the build and
|
|
/// to <see cref="IPostprocessBuildWithReport"/> that are the ones called after the build is finished
|
|
/// </summary>
|
|
class ShaderStrippingReportScope : IPostprocessBuildWithReport, IPreprocessBuildWithReport
|
|
{
|
|
public int callbackOrder => 0;
|
|
|
|
public void OnPreprocessBuild(BuildReport report)
|
|
{
|
|
bool isDevelopmentBuild = (report.summary.options & BuildOptions.Development) != 0;
|
|
ShaderStripping.ReportBegin(isDevelopmentBuild);
|
|
}
|
|
|
|
public void OnPostprocessBuild(BuildReport report)
|
|
{
|
|
ShaderStripping.ReportEnd();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class is instantiated as the reporter if the logging and exporting is disabled
|
|
/// this avoid tracking all the variants, allocating memory, and doing work that is not need
|
|
/// </summary>
|
|
class ShaderStrippingReportEmpty : IShaderStrippingReport
|
|
{
|
|
public void OnShaderProcessed<TShader, TShaderVariant>([DisallowNull] TShader shader, TShaderVariant shaderVariant, string pipeline, uint variantsIn, uint variantsOut, double stripTimeMs)
|
|
where TShader : UnityEngine.Object
|
|
{ }
|
|
public void DumpReport() { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class is instantiated as the reporter if the reporter is null because we are building asset bundles
|
|
/// </summary>
|
|
class ShaderStrippingReportLogger : IShaderStrippingReport
|
|
{
|
|
private bool m_IsLogEnabled = false;
|
|
|
|
public ShaderStrippingReportLogger()
|
|
{
|
|
// Check the current pipeline and check the shader variant settings
|
|
if (GraphicsSettings.TryGetRenderPipelineSettings<ShaderStrippingSetting>(out var shaderVariantSettings))
|
|
{
|
|
m_IsLogEnabled = shaderVariantSettings.shaderVariantLogLevel != ShaderVariantLogLevel.Disabled;
|
|
}
|
|
}
|
|
|
|
public void OnShaderProcessed<TShader, TShaderVariant>([DisallowNull] TShader shader, TShaderVariant shaderVariant, string pipeline, uint variantsIn, uint variantsOut, double stripTimeMs)
|
|
where TShader : UnityEngine.Object
|
|
{
|
|
if (!m_IsLogEnabled)
|
|
return;
|
|
|
|
if (!ShaderStrippingReport.TryGetVariantName(shader, shaderVariant, out string variantName))
|
|
return;
|
|
|
|
Debug.Log($"Shader={shader.name}{variantName} Pipeline={pipeline} Total={variantsIn}/{variantsOut}({variantsOut / (float)variantsIn * 100f:0.00}%) Time={stripTimeMs}ms");
|
|
}
|
|
|
|
public void DumpReport()
|
|
{
|
|
// Just logs variants into the console
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class to gather all the information about stripping in SRP packages
|
|
/// </summary>
|
|
class ShaderStrippingReport : IShaderStrippingReport
|
|
{
|
|
private readonly ShaderVariantLogLevel m_LogStrippedVariants;
|
|
private readonly bool m_ExportStrippedVariants;
|
|
|
|
// Shader
|
|
private readonly List<ShaderStrippingInfo> m_ShaderInfos = new();
|
|
private readonly VariantCounter m_ShaderVariantCounter = new();
|
|
|
|
// Compute Shader
|
|
private readonly List<ShaderStrippingInfo> m_ComputeShaderInfos = new();
|
|
private readonly VariantCounter m_ComputeShaderVariantCounter = new();
|
|
|
|
public ShaderStrippingReport(ShaderVariantLogLevel logLevel, bool export)
|
|
{
|
|
m_LogStrippedVariants = logLevel;
|
|
m_ExportStrippedVariants = export;
|
|
}
|
|
|
|
public void OnShaderProcessed<TShader, TShaderVariant>([DisallowNull] TShader shader, TShaderVariant shaderVariant, string pipeline, uint variantsIn, uint variantsOut, double stripTimeMs)
|
|
where TShader : UnityEngine.Object
|
|
{
|
|
if (!TryGetVariantName(shader, shaderVariant, out string variantName))
|
|
throw new NotImplementedException($"Report is not enabled for {typeof(TShader)} and {typeof(TShaderVariant)}");
|
|
|
|
var lastShaderStrippingInfo = FindLastShaderStrippingInfo(shader);
|
|
if (typeof(TShader) == typeof(Shader))
|
|
{
|
|
m_ShaderVariantCounter.inputVariants += variantsIn;
|
|
m_ShaderVariantCounter.outputVariants += variantsOut;
|
|
}
|
|
else if (typeof(TShader) == typeof(ComputeShader))
|
|
{
|
|
m_ComputeShaderVariantCounter.inputVariants += variantsIn;
|
|
m_ComputeShaderVariantCounter.outputVariants += variantsOut;
|
|
}
|
|
|
|
lastShaderStrippingInfo.AddVariant(pipeline, new ShaderVariantInfo()
|
|
{
|
|
inputVariants = variantsIn,
|
|
outputVariants = variantsOut,
|
|
stripTimeMs = stripTimeMs,
|
|
variantName = variantName
|
|
});
|
|
}
|
|
|
|
internal static string k_ShaderOutputPath = "Temp/shader-stripping.json";
|
|
internal static string k_ComputeShaderOutputPath = "Temp/compute-shader-stripping.json";
|
|
|
|
public void DumpReport()
|
|
{
|
|
if (m_LogStrippedVariants != ShaderVariantLogLevel.Disabled)
|
|
{
|
|
Debug.Log($"Shader Stripping - {m_ShaderVariantCounter}");
|
|
foreach (var info in m_ShaderInfos)
|
|
{
|
|
info.Log(m_LogStrippedVariants);
|
|
}
|
|
|
|
Debug.Log($"Compute Shader Stripping - {m_ComputeShaderVariantCounter}");
|
|
foreach (var info in m_ComputeShaderInfos)
|
|
{
|
|
info.Log(m_LogStrippedVariants);
|
|
}
|
|
}
|
|
|
|
if (m_ExportStrippedVariants)
|
|
{
|
|
ExportShaderStrippingInfo(k_ShaderOutputPath, m_ShaderVariantCounter, m_ShaderInfos);
|
|
ExportShaderStrippingInfo(k_ComputeShaderOutputPath, m_ComputeShaderVariantCounter, m_ComputeShaderInfos);
|
|
}
|
|
}
|
|
|
|
[CanBeNull] private ShaderStrippingInfo m_LastShaderStrippingInfo = null;
|
|
|
|
private ShaderStrippingInfo FindLastShaderStrippingInfo<TShader>([DisallowNull] TShader shader)
|
|
where TShader : UnityEngine.Object
|
|
{
|
|
if (m_LastShaderStrippingInfo != null && m_LastShaderStrippingInfo.name.Equals(shader.name))
|
|
return m_LastShaderStrippingInfo;
|
|
|
|
// We are reporting a new shader variant, need to create a new one
|
|
m_LastShaderStrippingInfo = new ShaderStrippingInfo()
|
|
{
|
|
name = shader.name
|
|
};
|
|
|
|
// The compiler will strip the branch that we are not using
|
|
if (typeof(TShader) == typeof(Shader))
|
|
{
|
|
m_ShaderInfos.Add(m_LastShaderStrippingInfo);
|
|
}
|
|
else if (typeof(TShader) == typeof(ComputeShader))
|
|
{
|
|
m_ComputeShaderInfos.Add(m_LastShaderStrippingInfo);
|
|
}
|
|
|
|
return m_LastShaderStrippingInfo;
|
|
}
|
|
|
|
[MustUseReturnValue]
|
|
internal static bool TryGetVariantName<TShader, TShaderVariant>([DisallowNull] TShader shader, TShaderVariant shaderVariant, out string variantName)
|
|
where TShader : UnityEngine.Object
|
|
{
|
|
variantName = string.Empty;
|
|
|
|
if (typeof(TShader) == typeof(Shader) && typeof(TShaderVariant) == typeof(ShaderSnippetData))
|
|
{
|
|
var snippetData = (ShaderSnippetData)Convert.ChangeType(shaderVariant, typeof(ShaderSnippetData));
|
|
string passName = string.IsNullOrEmpty(snippetData.passName) ? $"Pass {snippetData.pass.PassIndex}" : snippetData.passName;
|
|
variantName = $"{passName} ({snippetData.passType}) (SubShader: {snippetData.pass.SubshaderIndex}) (ShaderType: {snippetData.shaderType.ToString()})";
|
|
}
|
|
else if (typeof(TShader) == typeof(ComputeShader) && typeof(TShaderVariant) == typeof(string))
|
|
{
|
|
variantName = $"Kernel: {shaderVariant}";
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[Serializable]
|
|
class Export
|
|
{
|
|
public uint totalVariantsIn;
|
|
public uint totalVariantsOut;
|
|
public ShaderStrippingInfo[] shaders;
|
|
}
|
|
|
|
void ExportShaderStrippingInfo(string path, VariantCounter variantCounter, List<ShaderStrippingInfo> shaders)
|
|
{
|
|
try
|
|
{
|
|
var export = new Export()
|
|
{
|
|
totalVariantsIn = variantCounter.inputVariants,
|
|
totalVariantsOut = variantCounter.outputVariants,
|
|
shaders = shaders.ToArray()
|
|
};
|
|
|
|
File.WriteAllText(path, JsonUtility.ToJson(export, true));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
interface IShaderStrippingReport
|
|
{
|
|
void OnShaderProcessed<TShader, TShaderVariant>([DisallowNull] TShader shader,
|
|
TShaderVariant shaderVariant, string pipeline, uint variantsIn, uint variantsOut, double stripTimeMs)
|
|
where TShader : UnityEngine.Object;
|
|
|
|
void DumpReport();
|
|
}
|
|
|
|
internal static class ShaderStripping
|
|
{
|
|
public static bool s_DefaultExport = false;
|
|
|
|
static bool s_ShowWarningDebugShaders = false;
|
|
|
|
static IShaderStrippingReport m_Reporter;
|
|
public static IShaderStrippingReport reporter => m_Reporter ??= new ShaderStrippingReportLogger();
|
|
|
|
public static void ReportBegin(bool isDevelopmentBuild = false)
|
|
{
|
|
ShaderVariantLogLevel logStrippedVariants = ShaderVariantLogLevel.Disabled;
|
|
bool exportStrippedVariants = s_DefaultExport;
|
|
|
|
// Check the current pipeline and check the shader variant settings
|
|
if (GraphicsSettings.TryGetRenderPipelineSettings<ShaderStrippingSetting>(out var shaderVariantSettings))
|
|
{
|
|
logStrippedVariants = shaderVariantSettings.shaderVariantLogLevel;
|
|
exportStrippedVariants = shaderVariantSettings.exportShaderVariants;
|
|
s_ShowWarningDebugShaders = shaderVariantSettings.stripRuntimeDebugShaders && isDevelopmentBuild;
|
|
}
|
|
|
|
m_Reporter = (logStrippedVariants == ShaderVariantLogLevel.Disabled && exportStrippedVariants == false) ?
|
|
new ShaderStrippingReportEmpty() : new ShaderStrippingReport(logStrippedVariants, exportStrippedVariants);
|
|
}
|
|
|
|
public static void ReportEnd()
|
|
{
|
|
m_Reporter.DumpReport();
|
|
|
|
if (s_ShowWarningDebugShaders)
|
|
Debug.Log("Stripping Runtime Debug Shader Variants, you won't be able to use some features of Rendering Debugger in the Player Build.");
|
|
|
|
m_Reporter = null;
|
|
s_ShowWarningDebugShaders = false;
|
|
}
|
|
}
|
|
}
|