This commit is contained in:
2021-06-13 10:28:03 +02:00
parent eb70603c85
commit df2d24cbd3
7487 changed files with 943244 additions and 0 deletions

View File

@@ -0,0 +1,489 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.Universal
{
[Flags]
enum ShaderFeatures
{
MainLight = (1 << 0),
MainLightShadows = (1 << 1),
AdditionalLights = (1 << 2),
AdditionalLightShadows = (1 << 3),
VertexLighting = (1 << 4),
SoftShadows = (1 << 5),
MixedLighting = (1 << 6),
TerrainHoles = (1 << 7),
DeferredShading = (1 << 8), // DeferredRenderer is in the list of renderer
DeferredWithAccurateGbufferNormals = (1 << 9),
DeferredWithoutAccurateGbufferNormals = (1 << 10),
ScreenSpaceOcclusion = (1 << 11),
ScreenSpaceShadows = (1 << 12),
UseFastSRGBLinearConversion = (1 << 13)
}
internal class ShaderPreprocessor : IPreprocessShaders
{
public static readonly string kPassNameGBuffer = "GBuffer";
public static readonly string kTerrainShaderName = "Universal Render Pipeline/Terrain/Lit";
#if PROFILE_BUILD
private const string k_ProcessShaderTag = "OnProcessShader";
#endif
// Event callback to report shader stripping info. Form:
// ReportShaderStrippingData(Shader shader, ShaderSnippetData data, int currentVariantCount, double strippingTime)
internal static event Action<Shader, ShaderSnippetData, int, double> shaderPreprocessed;
private static readonly System.Diagnostics.Stopwatch m_stripTimer = new System.Diagnostics.Stopwatch();
ShaderKeyword m_MainLightShadows = new ShaderKeyword(ShaderKeywordStrings.MainLightShadows);
ShaderKeyword m_MainLightShadowsCascades = new ShaderKeyword(ShaderKeywordStrings.MainLightShadowCascades);
ShaderKeyword m_MainLightShadowsScreen = new ShaderKeyword(ShaderKeywordStrings.MainLightShadowScreen);
ShaderKeyword m_AdditionalLightsVertex = new ShaderKeyword(ShaderKeywordStrings.AdditionalLightsVertex);
ShaderKeyword m_AdditionalLightsPixel = new ShaderKeyword(ShaderKeywordStrings.AdditionalLightsPixel);
ShaderKeyword m_AdditionalLightShadows = new ShaderKeyword(ShaderKeywordStrings.AdditionalLightShadows);
ShaderKeyword m_DeferredAdditionalLightShadows = new ShaderKeyword(ShaderKeywordStrings._DEFERRED_ADDITIONAL_LIGHT_SHADOWS);
ShaderKeyword m_CastingPunctualLightShadow = new ShaderKeyword(ShaderKeywordStrings.CastingPunctualLightShadow);
ShaderKeyword m_SoftShadows = new ShaderKeyword(ShaderKeywordStrings.SoftShadows);
ShaderKeyword m_MixedLightingSubtractive = new ShaderKeyword(ShaderKeywordStrings.MixedLightingSubtractive);
ShaderKeyword m_LightmapShadowMixing = new ShaderKeyword(ShaderKeywordStrings.LightmapShadowMixing);
ShaderKeyword m_ShadowsShadowMask = new ShaderKeyword(ShaderKeywordStrings.ShadowsShadowMask);
ShaderKeyword m_Lightmap = new ShaderKeyword(ShaderKeywordStrings.LIGHTMAP_ON);
ShaderKeyword m_DirectionalLightmap = new ShaderKeyword(ShaderKeywordStrings.DIRLIGHTMAP_COMBINED);
ShaderKeyword m_AlphaTestOn = new ShaderKeyword(ShaderKeywordStrings._ALPHATEST_ON);
ShaderKeyword m_GbufferNormalsOct = new ShaderKeyword(ShaderKeywordStrings._GBUFFER_NORMALS_OCT);
ShaderKeyword m_UseDrawProcedural = new ShaderKeyword(ShaderKeywordStrings.UseDrawProcedural);
ShaderKeyword m_ScreenSpaceOcclusion = new ShaderKeyword(ShaderKeywordStrings.ScreenSpaceOcclusion);
ShaderKeyword m_UseFastSRGBLinearConversion = new ShaderKeyword(ShaderKeywordStrings.UseFastSRGBLinearConversion);
ShaderKeyword m_LocalDetailMulx2;
ShaderKeyword m_LocalDetailScaled;
ShaderKeyword m_LocalClearCoat;
ShaderKeyword m_LocalClearCoatMap;
int m_TotalVariantsInputCount;
int m_TotalVariantsOutputCount;
// Multiple callback may be implemented.
// The first one executed is the one where callbackOrder is returning the smallest number.
public int callbackOrder { get { return 0; } }
void InitializeLocalShaderKeywords(Shader shader)
{
m_LocalDetailMulx2 = new ShaderKeyword(shader, ShaderKeywordStrings._DETAIL_MULX2);
m_LocalDetailScaled = new ShaderKeyword(shader, ShaderKeywordStrings._DETAIL_SCALED);
m_LocalClearCoat = new ShaderKeyword(shader, ShaderKeywordStrings._CLEARCOAT);
m_LocalClearCoatMap = new ShaderKeyword(shader, ShaderKeywordStrings._CLEARCOATMAP);
}
bool IsFeatureEnabled(ShaderFeatures featureMask, ShaderFeatures feature)
{
return (featureMask & feature) != 0;
}
bool StripUnusedPass(ShaderFeatures features, ShaderSnippetData snippetData)
{
if (snippetData.passType == PassType.Meta)
return true;
if (snippetData.passType == PassType.ShadowCaster)
if (!IsFeatureEnabled(features, ShaderFeatures.MainLightShadows) && !IsFeatureEnabled(features, ShaderFeatures.AdditionalLightShadows))
return true;
return false;
}
bool StripUnusedFeatures(ShaderFeatures features, Shader shader, ShaderSnippetData snippetData, ShaderCompilerData compilerData)
{
// strip main light shadows, cascade and screen variants
if (!IsFeatureEnabled(features, ShaderFeatures.MainLightShadows))
{
if (compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadows))
return true;
if (compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadowsCascades))
return true;
if (compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadowsScreen))
return true;
if (snippetData.passType == PassType.ShadowCaster && !compilerData.shaderKeywordSet.IsEnabled(m_CastingPunctualLightShadow))
return true;
}
if (!IsFeatureEnabled(features, ShaderFeatures.SoftShadows) &&
compilerData.shaderKeywordSet.IsEnabled(m_SoftShadows))
return true;
// Left for backward compatibility
if (compilerData.shaderKeywordSet.IsEnabled(m_MixedLightingSubtractive) &&
!IsFeatureEnabled(features, ShaderFeatures.MixedLighting))
return true;
if (compilerData.shaderKeywordSet.IsEnabled(m_UseFastSRGBLinearConversion) &&
!IsFeatureEnabled(features, ShaderFeatures.UseFastSRGBLinearConversion))
return true;
// Strip here only if mixed lighting is disabled
// No need to check here if actually used by scenes as this taken care by builtin stripper
if ((compilerData.shaderKeywordSet.IsEnabled(m_LightmapShadowMixing) ||
compilerData.shaderKeywordSet.IsEnabled(m_ShadowsShadowMask)) &&
!IsFeatureEnabled(features, ShaderFeatures.MixedLighting))
return true;
// No additional light shadows
bool isAdditionalLightShadow = compilerData.shaderKeywordSet.IsEnabled(m_AdditionalLightShadows);
if (!IsFeatureEnabled(features, ShaderFeatures.AdditionalLightShadows) && isAdditionalLightShadow)
return true;
bool isPunctualLightShadowCasterPass = (snippetData.passType == PassType.ShadowCaster) && compilerData.shaderKeywordSet.IsEnabled(m_CastingPunctualLightShadow);
if (!IsFeatureEnabled(features, ShaderFeatures.AdditionalLightShadows) && isPunctualLightShadowCasterPass)
if (compilerData.shaderCompilerPlatform != ShaderCompilerPlatform.GLES3x) // [Work-around] We do not strip this variant on GLES3 because it could make some scenes crash current Unity GLES3 AndroidPlayer on OnePlus6T - TODO: remove this line once https://issuetracker.unity3d.com/product/unity/issues/guid/1293454/ is fixed
return true;
bool isDeferredAdditionalShadow = compilerData.shaderKeywordSet.IsEnabled(m_DeferredAdditionalLightShadows);
if (!IsFeatureEnabled(features, ShaderFeatures.AdditionalLightShadows) && isDeferredAdditionalShadow)
return true;
// Additional light are shaded per-vertex or per-pixel.
bool isFeaturePerPixelLightingEnabled = IsFeatureEnabled(features, ShaderFeatures.AdditionalLights);
bool isFeaturePerVertexLightingEnabled = IsFeatureEnabled(features, ShaderFeatures.VertexLighting);
bool isAdditionalLightPerPixel = compilerData.shaderKeywordSet.IsEnabled(m_AdditionalLightsPixel);
bool isAdditionalLightPerVertex = compilerData.shaderKeywordSet.IsEnabled(m_AdditionalLightsVertex);
// Strip if Per-Pixel lighting is NOT used in the project and the
// Per-Pixel (_ADDITIONAL_LIGHTS) or additional shadows (_ADDITIONAL_LIGHT_SHADOWS)
// variants are enabled in the shader.
if (!isFeaturePerPixelLightingEnabled && (isAdditionalLightPerPixel || isAdditionalLightShadow))
return true;
// Strip if Per-Vertex lighting is NOT used in the project and the
// Per-Vertex (_ADDITIONAL_LIGHTS_VERTEX) variant is enabled in the shader.
if (!isFeaturePerVertexLightingEnabled && isAdditionalLightPerVertex)
return true;
// Screen Space Shadows
if (!IsFeatureEnabled(features, ShaderFeatures.ScreenSpaceShadows) &&
compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadowsScreen))
return true;
// Screen Space Occlusion
if (!IsFeatureEnabled(features, ShaderFeatures.ScreenSpaceOcclusion) &&
compilerData.shaderKeywordSet.IsEnabled(m_ScreenSpaceOcclusion))
return true;
return false;
}
bool StripUnsupportedVariants(ShaderCompilerData compilerData)
{
// Dynamic GI is not supported so we can strip variants that have directional lightmap
// enabled but not baked lightmap.
if (compilerData.shaderKeywordSet.IsEnabled(m_DirectionalLightmap) &&
!compilerData.shaderKeywordSet.IsEnabled(m_Lightmap))
return true;
// As GLES2 has low amount of registers, we strip:
if (compilerData.shaderCompilerPlatform == ShaderCompilerPlatform.GLES20)
{
// VertexID - as GLES2 does not support VertexID that is required for full screen draw procedural pass;
if (compilerData.shaderKeywordSet.IsEnabled(m_UseDrawProcedural))
return true;
// Cascade shadows
if (compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadowsCascades))
return true;
// Screen space shadows
if (compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadowsScreen))
return true;
// Detail
if (compilerData.shaderKeywordSet.IsEnabled(m_LocalDetailMulx2) || compilerData.shaderKeywordSet.IsEnabled(m_LocalDetailScaled))
return true;
// Clear Coat
if (compilerData.shaderKeywordSet.IsEnabled(m_LocalClearCoat) || compilerData.shaderKeywordSet.IsEnabled(m_LocalClearCoatMap))
return true;
}
return false;
}
bool StripInvalidVariants(ShaderCompilerData compilerData)
{
bool isMainShadowNoCascades = compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadows);
bool isMainShadowCascades = compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadowsCascades);
bool isMainShadowScreen = compilerData.shaderKeywordSet.IsEnabled(m_MainLightShadowsScreen);
bool isMainShadow = isMainShadowNoCascades || isMainShadowCascades || isMainShadowScreen;
bool isAdditionalShadow = compilerData.shaderKeywordSet.IsEnabled(m_AdditionalLightShadows);
if (isAdditionalShadow && !compilerData.shaderKeywordSet.IsEnabled(m_AdditionalLightsPixel))
return true;
bool isDeferredAdditionalShadow = compilerData.shaderKeywordSet.IsEnabled(m_DeferredAdditionalLightShadows);
if (isDeferredAdditionalShadow && !compilerData.shaderKeywordSet.IsEnabled(m_AdditionalLightsPixel))
return true;
bool isShadowVariant = isMainShadow || isAdditionalShadow || isDeferredAdditionalShadow;
if (!isShadowVariant && compilerData.shaderKeywordSet.IsEnabled(m_SoftShadows))
return true;
return false;
}
bool StripUnused(ShaderFeatures features, Shader shader, ShaderSnippetData snippetData, ShaderCompilerData compilerData)
{
if (StripUnusedFeatures(features, shader, snippetData, compilerData))
return true;
if (StripInvalidVariants(compilerData))
return true;
if (StripUnsupportedVariants(compilerData))
return true;
if (StripUnusedPass(features, snippetData))
return true;
// Strip terrain holes
// TODO: checking for the string name here is expensive
// maybe we can rename alpha clip keyword name to be specific to terrain?
if (compilerData.shaderKeywordSet.IsEnabled(m_AlphaTestOn) &&
!IsFeatureEnabled(features, ShaderFeatures.TerrainHoles) &&
shader.name.Contains(kTerrainShaderName))
return true;
// TODO: Test against lightMode tag instead.
if (snippetData.passName == kPassNameGBuffer)
{
if (!IsFeatureEnabled(features, ShaderFeatures.DeferredShading))
return true;
if (IsFeatureEnabled(features, ShaderFeatures.DeferredWithAccurateGbufferNormals) && !compilerData.shaderKeywordSet.IsEnabled(m_GbufferNormalsOct))
return true;
if (IsFeatureEnabled(features, ShaderFeatures.DeferredWithoutAccurateGbufferNormals) && compilerData.shaderKeywordSet.IsEnabled(m_GbufferNormalsOct))
return true;
}
return false;
}
void LogShaderVariants(Shader shader, ShaderSnippetData snippetData, ShaderVariantLogLevel logLevel, int prevVariantsCount, int currVariantsCount)
{
if (logLevel == ShaderVariantLogLevel.AllShaders || shader.name.Contains("Universal Render Pipeline"))
{
float percentageCurrent = (float)currVariantsCount / (float)prevVariantsCount * 100f;
float percentageTotal = (float)m_TotalVariantsOutputCount / (float)m_TotalVariantsInputCount * 100f;
string result = string.Format("STRIPPING: {0} ({1} pass) ({2}) -" +
" Remaining shader variants = {3}/{4} = {5}% - Total = {6}/{7} = {8}%",
shader.name, snippetData.passName, snippetData.shaderType.ToString(), currVariantsCount,
prevVariantsCount, percentageCurrent, m_TotalVariantsOutputCount, m_TotalVariantsInputCount,
percentageTotal);
Debug.Log(result);
}
}
public void OnProcessShader(Shader shader, ShaderSnippetData snippetData, IList<ShaderCompilerData> compilerDataList)
{
#if PROFILE_BUILD
Profiler.BeginSample(k_ProcessShaderTag);
#endif
UniversalRenderPipelineAsset urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
if (urpAsset == null || compilerDataList == null || compilerDataList.Count == 0)
return;
// Local Keywords need to be initialized with the shader
InitializeLocalShaderKeywords(shader);
m_stripTimer.Start();
int prevVariantCount = compilerDataList.Count;
var inputShaderVariantCount = compilerDataList.Count;
for (int i = 0; i < inputShaderVariantCount;)
{
bool removeInput = StripUnused(ShaderBuildPreprocessor.supportedFeatures, shader, snippetData, compilerDataList[i]);
if (removeInput)
compilerDataList[i] = compilerDataList[--inputShaderVariantCount];
else
++i;
}
if (compilerDataList is List<ShaderCompilerData> inputDataList)
inputDataList.RemoveRange(inputShaderVariantCount, inputDataList.Count - inputShaderVariantCount);
else
{
for (int i = compilerDataList.Count - 1; i >= inputShaderVariantCount; --i)
compilerDataList.RemoveAt(i);
}
if (urpAsset.shaderVariantLogLevel != ShaderVariantLogLevel.Disabled)
{
m_TotalVariantsInputCount += prevVariantCount;
m_TotalVariantsOutputCount += compilerDataList.Count;
LogShaderVariants(shader, snippetData, urpAsset.shaderVariantLogLevel, prevVariantCount, compilerDataList.Count);
}
m_stripTimer.Stop();
double stripTimeMs = m_stripTimer.Elapsed.TotalMilliseconds;
m_stripTimer.Reset();
#if PROFILE_BUILD
Profiler.EndSample();
#endif
shaderPreprocessed?.Invoke(shader, snippetData, prevVariantCount, stripTimeMs);
}
}
class ShaderBuildPreprocessor : IPreprocessBuildWithReport
#if PROFILE_BUILD
, IPostprocessBuildWithReport
#endif
{
public static ShaderFeatures supportedFeatures
{
get
{
if (_supportedFeatures <= 0)
{
FetchAllSupportedFeatures();
}
return _supportedFeatures;
}
}
private static ShaderFeatures _supportedFeatures = 0;
public int callbackOrder { get { return 0; } }
#if PROFILE_BUILD
public void OnPostprocessBuild(BuildReport report)
{
Profiler.enabled = false;
}
#endif
public void OnPreprocessBuild(BuildReport report)
{
FetchAllSupportedFeatures();
#if PROFILE_BUILD
Profiler.enableBinaryLog = true;
Profiler.logFile = "profilerlog.raw";
Profiler.enabled = true;
#endif
}
private static void FetchAllSupportedFeatures()
{
List<UniversalRenderPipelineAsset> urps = new List<UniversalRenderPipelineAsset>();
urps.Add(GraphicsSettings.defaultRenderPipeline as UniversalRenderPipelineAsset);
for (int i = 0; i < QualitySettings.names.Length; i++)
{
urps.Add(QualitySettings.GetRenderPipelineAssetAt(i) as UniversalRenderPipelineAsset);
}
// Must reset flags.
_supportedFeatures = 0;
foreach (UniversalRenderPipelineAsset urp in urps)
{
if (urp != null)
{
_supportedFeatures |= GetSupportedShaderFeatures(urp);
}
}
}
private static ShaderFeatures GetSupportedShaderFeatures(UniversalRenderPipelineAsset pipelineAsset)
{
ShaderFeatures shaderFeatures;
shaderFeatures = ShaderFeatures.MainLight;
if (pipelineAsset.supportsMainLightShadows)
shaderFeatures |= ShaderFeatures.MainLightShadows;
if (pipelineAsset.additionalLightsRenderingMode == LightRenderingMode.PerVertex)
{
shaderFeatures |= ShaderFeatures.VertexLighting;
}
else if (pipelineAsset.additionalLightsRenderingMode == LightRenderingMode.PerPixel)
{
shaderFeatures |= ShaderFeatures.AdditionalLights;
if (pipelineAsset.supportsAdditionalLightShadows)
shaderFeatures |= ShaderFeatures.AdditionalLightShadows;
}
bool anyShadows = pipelineAsset.supportsMainLightShadows ||
(shaderFeatures & ShaderFeatures.AdditionalLightShadows) != 0;
if (pipelineAsset.supportsSoftShadows && anyShadows)
shaderFeatures |= ShaderFeatures.SoftShadows;
if (pipelineAsset.supportsMixedLighting)
shaderFeatures |= ShaderFeatures.MixedLighting;
if (pipelineAsset.supportsTerrainHoles)
shaderFeatures |= ShaderFeatures.TerrainHoles;
if (pipelineAsset.useFastSRGBLinearConversion)
shaderFeatures |= ShaderFeatures.UseFastSRGBLinearConversion;
bool hasScreenSpaceShadows = false;
bool hasScreenSpaceOcclusion = false;
bool hasDeferredRenderer = false;
bool withAccurateGbufferNormals = false;
bool withoutAccurateGbufferNormals = false;
int rendererCount = pipelineAsset.m_RendererDataList.Length;
for (int rendererIndex = 0; rendererIndex < rendererCount; ++rendererIndex)
{
ScriptableRenderer renderer = pipelineAsset.GetRenderer(rendererIndex);
if (renderer is ForwardRenderer)
{
ForwardRenderer forwardRenderer = (ForwardRenderer)renderer;
if (forwardRenderer.renderingMode == RenderingMode.Deferred)
{
hasDeferredRenderer |= true;
withAccurateGbufferNormals |= forwardRenderer.accurateGbufferNormals;
withoutAccurateGbufferNormals |= !forwardRenderer.accurateGbufferNormals;
}
}
// Check for Screen Space Ambient Occlusion Renderer Feature
ScriptableRendererData rendererData = pipelineAsset.m_RendererDataList[rendererIndex];
if (rendererData != null)
{
for (int rendererFeatureIndex = 0; rendererFeatureIndex < rendererData.rendererFeatures.Count; rendererFeatureIndex++)
{
ScriptableRendererFeature rendererFeature = rendererData.rendererFeatures[rendererFeatureIndex];
ScreenSpaceShadows ssshadows = rendererFeature as ScreenSpaceShadows;
hasScreenSpaceShadows |= ssshadows != null;
ScreenSpaceAmbientOcclusion ssao = rendererFeature as ScreenSpaceAmbientOcclusion;
hasScreenSpaceOcclusion |= ssao != null;
}
}
}
if (hasDeferredRenderer)
shaderFeatures |= ShaderFeatures.DeferredShading;
// We can only strip accurateGbufferNormals related variants if all DeferredRenderers use the same option.
if (withAccurateGbufferNormals && !withoutAccurateGbufferNormals)
shaderFeatures |= ShaderFeatures.DeferredWithAccurateGbufferNormals;
if (!withAccurateGbufferNormals && withoutAccurateGbufferNormals)
shaderFeatures |= ShaderFeatures.DeferredWithoutAccurateGbufferNormals;
if (hasScreenSpaceShadows)
shaderFeatures |= ShaderFeatures.ScreenSpaceShadows;
if (hasScreenSpaceOcclusion)
shaderFeatures |= ShaderFeatures.ScreenSpaceOcclusion;
return shaderFeatures;
}
}
}