UnityGame/Library/PackageCache/com.unity.render-pipelines.universal/Editor/ShaderBuildPreprocessor.cs
2024-10-27 10:53:47 +03:00

1030 lines
50 KiB
C#

using System;
using System.Collections.Generic;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;
#if XR_MANAGEMENT_4_0_1_OR_NEWER
using UnityEditor.XR.Management;
using UnityEngine.XR.Management;
#endif
using ShaderPrefilteringData = UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset.ShaderPrefilteringData;
using PrefilteringMode = UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset.PrefilteringMode;
using PrefilteringModeMainLightShadows = UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset.PrefilteringModeMainLightShadows;
using PrefilteringModeAdditionalLights = UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset.PrefilteringModeAdditionalLights;
namespace UnityEditor.Rendering.Universal
{
[Flags]
enum ShaderFeatures : long
{
None = 0,
MainLight = (1L << 0),
MainLightShadows = (1L << 1),
AdditionalLightsPixel = (1L << 2),
AdditionalLightShadows = (1L << 3),
AdditionalLightsVertex = (1L << 4),
SoftShadows = (1L << 5),
MixedLighting = (1L << 6),
TerrainHoles = (1L << 7),
DeferredShading = (1L << 8), // DeferredRenderer is in the list of renderer
AccurateGbufferNormals = (1L << 9),
ScreenSpaceOcclusion = (1L << 10),
ScreenSpaceShadows = (1L << 11),
UseFastSRGBLinearConversion = (1L << 12),
LightLayers = (1L << 13),
ReflectionProbeBlending = (1L << 14),
ReflectionProbeBoxProjection = (1L << 15),
DBufferMRT1 = (1L << 16),
DBufferMRT2 = (1L << 17),
DBufferMRT3 = (1L << 18),
DecalScreenSpace = (1L << 19),
DecalGBuffer = (1L << 20),
DecalNormalBlendLow = (1L << 21),
DecalNormalBlendMedium = (1L << 22),
DecalNormalBlendHigh = (1L << 23),
ForwardPlus = (1L << 24),
RenderPassEnabled = (1L << 25),
MainLightShadowsCascade = (1L << 26),
DrawProcedural = (1L << 27),
ScreenSpaceOcclusionAfterOpaque = (1L << 28),
AdditionalLightsKeepOffVariants = (1L << 29),
ShadowsKeepOffVariants = (1L << 30),
UseLegacyLightmaps = (1L << 31),
DecalLayers = (1L << 32),
OpaqueWriteRenderingLayers = (1L << 33),
GBufferWriteRenderingLayers = (1L << 34),
DepthNormalPassRenderingLayers = (1L << 35),
LightCookies = (1L << 36),
LODCrossFade = (1L << 37),
ProbeVolumeL1 = (1L << 38),
ProbeVolumeL2 = (1L << 39),
HdrGrading = (1L << 40),
AutoSHMode = (1L << 41),
AutoSHModePerVertex = (1L << 42),
ExplicitSHMode = (1L << 43),
DataDrivenLensFlare = (1L << 44),
ScreenSpaceLensFlare = (1L << 45),
SoftShadowsLow = (1L << 46),
SoftShadowsMedium = (1L << 47),
SoftShadowsHigh = (1L << 48),
AlphaOutput = (1L << 49),
}
[Flags]
enum VolumeFeatures
{
None = 0,
Calculated = (1 << 0),
LensDistortion = (1 << 1),
//2: Unused for now
ChromaticAberration = (1 << 3),
ToneMapping = (1 << 4),
FilmGrain = (1 << 5),
DepthOfField = (1 << 6),
CameraMotionBlur = (1 << 7),
PaniniProjection = (1 << 8),
BloomLQ = (1 << 9),
BloomLQDirt = (1 << 10),
BloomHQ = (1 << 11),
BloomHQDirt = (1 << 12),
}
/// <summary>
/// This class is used solely to make sure Shader Prefiltering data inside the
/// URP Assets get updated before anything (Like Asset Bundles) are built.
/// </summary>
class UpdateShaderPrefilteringDataBeforeBuild : IPreprocessShaders
{
public int callbackOrder => -100;
public UpdateShaderPrefilteringDataBeforeBuild()
{
ShaderBuildPreprocessor.GatherShaderFeatures(Debug.isDebugBuild);
}
public void OnProcessShader(Shader shader, ShaderSnippetData snippetData, IList<ShaderCompilerData> compilerDataList){}
}
/// <summary>
/// Preprocess Build class used to determine the shader features used in the project.
/// Also called when building Asset Bundles.
/// </summary>
class ShaderBuildPreprocessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
// Public
public int callbackOrder => 0;
public static bool s_StripUnusedVariants;
public static bool s_StripDebugDisplayShaders;
public static bool s_StripUnusedPostProcessingVariants;
public static bool s_StripScreenCoordOverrideVariants;
public static bool s_Strip2DPasses;
public static bool s_UseSoftShadowQualityLevelKeywords;
public static bool s_StripXRVariants;
public static List<ShaderFeatures> supportedFeaturesList
{
get
{
// This can happen for example when building AssetBundles.
if (s_SupportedFeaturesList.Count == 0)
GatherShaderFeatures(Debug.isDebugBuild);
return s_SupportedFeaturesList;
}
}
public static VolumeFeatures volumeFeatures
{
get
{
// This can happen for example when building AssetBundles.
if (s_VolumeFeatures == VolumeFeatures.None)
GetSupportedFeaturesFromVolumes();
return s_VolumeFeatures;
}
}
// Private
private static bool s_KeepOffVariantForAdditionalLights;
private static bool s_UseSHPerVertexForSHAuto;
private static VolumeFeatures s_VolumeFeatures;
private static List<ShaderFeatures> s_SupportedFeaturesList = new();
// Helper class to detect XR build targets at build time.
internal sealed class PlatformBuildTimeDetect
{
private static PlatformBuildTimeDetect s_PlatformInfo;
internal bool isStandaloneXR { get; private set; }
internal bool isHololens { get; private set; }
internal bool isQuest { get; private set; }
internal bool isSwitch { get; private set; }
private PlatformBuildTimeDetect()
{
BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
isSwitch = buildTargetGroup == BuildTargetGroup.Switch;
#if XR_MANAGEMENT_4_0_1_OR_NEWER
var buildTargetSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup);
if (buildTargetSettings != null && buildTargetSettings.AssignedSettings != null && buildTargetSettings.AssignedSettings.activeLoaders.Count > 0)
{
isStandaloneXR = buildTargetGroup == BuildTargetGroup.Standalone;
isHololens = buildTargetGroup == BuildTargetGroup.WSA;
isQuest = buildTargetGroup == BuildTargetGroup.Android;
}
#endif
}
internal static PlatformBuildTimeDetect GetInstance()
{
if (s_PlatformInfo == null)
s_PlatformInfo = new PlatformBuildTimeDetect();
return s_PlatformInfo;
}
internal static void ClearInstance()
{
s_PlatformInfo = null;
}
}
internal struct RendererRequirements
{
public int msaaSampleCount;
public bool isUniversalRenderer;
public bool needsUnusedVariants;
public bool needsProcedural;
public bool needsMainLightShadows;
public bool needsAdditionalLightShadows;
public bool needsSoftShadows;
public bool needsSoftShadowsQualityLevels;
public bool needsShadowsOff;
public bool needsAdditionalLightsOff;
public bool needsGBufferRenderingLayers;
public bool needsGBufferAccurateNormals;
public bool needsRenderPass;
public bool needsReflectionProbeBlending;
public bool needsReflectionProbeBoxProjection;
public bool needsSHVertexForSHAuto;
public RenderingMode renderingMode;
}
// Called before the build is started...
public void OnPreprocessBuild(BuildReport report)
{
#if PROFILE_BUILD
Profiler.enableBinaryLog = true;
Profiler.logFile = "profilerlog.raw";
Profiler.enabled = true;
#endif
bool isDevelopmentBuild = (report.summary.options & BuildOptions.Development) != 0;
GatherShaderFeatures(isDevelopmentBuild);
}
// Called after the build has finished...
public void OnPostprocessBuild(BuildReport report)
{
PlatformBuildTimeDetect.ClearInstance();
#if PROFILE_BUILD
Profiler.enabled = false;
#endif
}
// Gathers all the shader features and updates the prefiltering
// settings for all URP Assets in the quality settings
internal static void GatherShaderFeatures(bool isDevelopmentBuild)
{
GetGlobalAndPlatformSettings(isDevelopmentBuild);
GetSupportedFeaturesFromVolumes();
s_Strip2DPasses = true;
s_SupportedFeaturesList.Clear();
using (ListPool<UniversalRenderPipelineAsset>.Get(out List<UniversalRenderPipelineAsset> urpAssets))
{
bool buildingForURP = EditorUserBuildSettings.activeBuildTarget.TryGetRenderPipelineAssets(urpAssets);
if (buildingForURP)
{
// Get Supported features & update data used for Shader Prefiltering and Scriptable Stripping
GetSupportedShaderFeaturesFromAssets(ref urpAssets, ref s_SupportedFeaturesList, s_StripUnusedVariants);
}
}
}
// Retrieves the global and platform settings used in the project...
private static void GetGlobalAndPlatformSettings(bool isDevelopmentBuild)
{
if (GraphicsSettings.TryGetRenderPipelineSettings<ShaderStrippingSetting>(out var shaderStrippingSettings))
s_StripDebugDisplayShaders = !isDevelopmentBuild || shaderStrippingSettings.stripRuntimeDebugShaders;
else
s_StripDebugDisplayShaders = true;
if (GraphicsSettings.TryGetRenderPipelineSettings<URPShaderStrippingSetting>(out var urpShaderStrippingSettings))
{
s_StripUnusedPostProcessingVariants = urpShaderStrippingSettings.stripUnusedPostProcessingVariants;
s_StripUnusedVariants = urpShaderStrippingSettings.stripUnusedVariants;
s_StripScreenCoordOverrideVariants = urpShaderStrippingSettings.stripScreenCoordOverrideVariants;
}
PlatformBuildTimeDetect platformBuildTimeDetect = PlatformBuildTimeDetect.GetInstance();
bool isShaderAPIMobileDefined = GraphicsSettings.HasShaderDefine(BuiltinShaderDefine.SHADER_API_MOBILE);
if (platformBuildTimeDetect.isSwitch || isShaderAPIMobileDefined)
s_UseSHPerVertexForSHAuto = true;
// XR Stripping
#if XR_MANAGEMENT_4_0_1_OR_NEWER
BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
XRGeneralSettings generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup);
s_StripXRVariants = generalSettings == null || generalSettings.Manager == null || generalSettings.Manager.activeLoaders.Count <= 0;
if (platformBuildTimeDetect.isStandaloneXR)
s_StripDebugDisplayShaders = true;
if (platformBuildTimeDetect.isHololens || platformBuildTimeDetect.isQuest)
{
s_KeepOffVariantForAdditionalLights = true;
s_UseSoftShadowQualityLevelKeywords = true;
s_UseSHPerVertexForSHAuto = true;
}
#else
s_UseSoftShadowQualityLevelKeywords = false;
s_StripXRVariants = true;
#endif
}
// Checks each Volume Profile Assets for used features...
private static void GetSupportedFeaturesFromVolumes()
{
if (!s_StripUnusedPostProcessingVariants)
return;
s_VolumeFeatures = VolumeFeatures.Calculated;
string[] guids = AssetDatabase.FindAssets("t:VolumeProfile");
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
// We only care what is in assets folder
if (!path.StartsWith("Assets"))
continue;
VolumeProfile asset = AssetDatabase.LoadAssetAtPath<VolumeProfile>(path);
if (asset == null)
continue;
if (asset.Has<LensDistortion>())
s_VolumeFeatures |= VolumeFeatures.LensDistortion;
Bloom bloom;
if (asset.TryGet<Bloom>(out bloom))
{
//strip unused bloom variants. #pragma multi_compile_local_fragment _ _BLOOM_LQ _BLOOM_HQ _BLOOM_LQ_DIRT _BLOOM_HQ_DIRT
if (bloom.highQualityFiltering.value)
{
if (bloom.dirtIntensity.value > 0f && bloom.dirtTexture.value != null)
s_VolumeFeatures |= VolumeFeatures.BloomHQDirt;
else
s_VolumeFeatures |= VolumeFeatures.BloomHQ;
}
else
{
if (bloom.dirtIntensity.value > 0f && bloom.dirtTexture.value != null)
s_VolumeFeatures |= VolumeFeatures.BloomLQDirt;
else
s_VolumeFeatures |= VolumeFeatures.BloomLQ;
}
}
if (asset.Has<Tonemapping>())
s_VolumeFeatures |= VolumeFeatures.ToneMapping;
if (asset.Has<FilmGrain>())
s_VolumeFeatures |= VolumeFeatures.FilmGrain;
if (asset.Has<DepthOfField>())
s_VolumeFeatures |= VolumeFeatures.DepthOfField;
if (asset.Has<MotionBlur>())
s_VolumeFeatures |= VolumeFeatures.CameraMotionBlur;
if (asset.Has<PaniniProjection>())
s_VolumeFeatures |= VolumeFeatures.PaniniProjection;
if (asset.Has<ChromaticAberration>())
s_VolumeFeatures |= VolumeFeatures.ChromaticAberration;
}
}
// Checks each Universal Render Pipeline Asset for features used...
internal static void GetSupportedShaderFeaturesFromAssets(ref List<UniversalRenderPipelineAsset> urpAssets, ref List<ShaderFeatures> rendererFeaturesList, bool stripUnusedVariants)
{
List<ScreenSpaceAmbientOcclusionSettings> ssaoRendererFeatures = new List<ScreenSpaceAmbientOcclusionSettings>(16);
for (int urpAssetIndex = 0; urpAssetIndex < urpAssets.Count; urpAssetIndex++)
{
// Get the asset and check if it's valid
UniversalRenderPipelineAsset urpAsset = urpAssets[urpAssetIndex];
if (urpAsset == null)
continue;
// Check the asset for supported features
ShaderFeatures urpAssetShaderFeatures = GetSupportedShaderFeaturesFromAsset(
ref urpAsset,
ref rendererFeaturesList,
ref ssaoRendererFeatures,
stripUnusedVariants,
out bool containsForwardRenderer,
out bool everyRendererHasSSAO
);
// Creates a struct containing all the prefiltering settings for this asset
ShaderPrefilteringData spd = CreatePrefilteringSettings(
ref urpAssetShaderFeatures,
containsForwardRenderer,
everyRendererHasSSAO,
s_StripXRVariants,
!PlayerSettings.allowHDRDisplaySupport || !urpAsset.supportsHDR,
s_StripDebugDisplayShaders,
s_StripScreenCoordOverrideVariants,
s_StripUnusedVariants,
ref ssaoRendererFeatures
);
// Update the Prefiltering settings for this URP asset
urpAsset.UpdateShaderKeywordPrefiltering(ref spd);
// Mark the asset dirty so it can be serialized once the build is finished
EditorUtility.SetDirty(urpAsset);
// Clean up
ssaoRendererFeatures.Clear();
}
}
// Checks the assigned Universal Pipeline Asset for features used...
internal static ShaderFeatures GetSupportedShaderFeaturesFromAsset(
ref UniversalRenderPipelineAsset urpAsset,
ref List<ShaderFeatures> rendererFeaturesList,
ref List<ScreenSpaceAmbientOcclusionSettings> ssaoRendererFeatures,
bool stripUnusedVariants,
out bool containsForwardRenderer,
out bool everyRendererHasSSAO)
{
ShaderFeatures urpAssetShaderFeatures = ShaderFeatures.MainLight;
// Additional Lights and Shadows...
switch (urpAsset.additionalLightsRenderingMode)
{
case LightRenderingMode.PerVertex:
urpAssetShaderFeatures |= ShaderFeatures.AdditionalLightsVertex;
break;
case LightRenderingMode.PerPixel:
urpAssetShaderFeatures |= ShaderFeatures.AdditionalLightsPixel;
break;
case LightRenderingMode.Disabled:
break;
default:
throw new ArgumentOutOfRangeException();
}
if (urpAsset.lightProbeSystem == LightProbeSystem.ProbeVolumes)
{
if (urpAsset.probeVolumeSHBands == ProbeVolumeSHBands.SphericalHarmonicsL1)
urpAssetShaderFeatures |= ShaderFeatures.ProbeVolumeL1;
if (urpAsset.probeVolumeSHBands == ProbeVolumeSHBands.SphericalHarmonicsL2)
urpAssetShaderFeatures |= ShaderFeatures.ProbeVolumeL2;
}
if (urpAsset.supportsMixedLighting)
urpAssetShaderFeatures |= ShaderFeatures.MixedLighting;
if (urpAsset.supportsTerrainHoles)
urpAssetShaderFeatures |= ShaderFeatures.TerrainHoles;
if (urpAsset.useFastSRGBLinearConversion)
urpAssetShaderFeatures |= ShaderFeatures.UseFastSRGBLinearConversion;
if (urpAsset.useRenderingLayers)
urpAssetShaderFeatures |= ShaderFeatures.LightLayers;
if (urpAsset.supportsLightCookies)
urpAssetShaderFeatures |= ShaderFeatures.LightCookies;
bool hasHDROutput = PlayerSettings.allowHDRDisplaySupport && urpAsset.supportsHDR;
if (urpAsset.colorGradingMode == ColorGradingMode.HighDynamicRange || hasHDROutput)
urpAssetShaderFeatures |= ShaderFeatures.HdrGrading;
if (urpAsset.enableLODCrossFade)
urpAssetShaderFeatures |= ShaderFeatures.LODCrossFade;
if (urpAsset.shEvalMode == ShEvalMode.Auto)
urpAssetShaderFeatures |= ShaderFeatures.AutoSHMode;
if (urpAsset.supportScreenSpaceLensFlare)
urpAssetShaderFeatures |= ShaderFeatures.ScreenSpaceLensFlare;
if (urpAsset.supportDataDrivenLensFlare)
urpAssetShaderFeatures |= ShaderFeatures.DataDrivenLensFlare;
if (urpAsset.gpuResidentDrawerMode != GPUResidentDrawerMode.Disabled)
urpAssetShaderFeatures |= ShaderFeatures.UseLegacyLightmaps;
// URP post-processing and alpha output follows the back-buffer color format requested in the asset.
// Back-buffer alpha format is required. Or a render texture with alpha formats.
// Without any external option we would need to keep all shaders and assume potential alpha output for all projects.
// Therefore we strip the shader based on the asset enabling the alpha output for post-processing.
// Alpha backbuffer is supported for:
// SDR 32-bit, RGBA8, (!urpAsset.supportsHDR)
// HDR 64-bit, RGBA16Float, (urpAsset.supportsHDR && urpAsset.hdrColorBufferPrecision == HDRColorBufferPrecision._64Bits)
if(urpAsset.allowPostProcessAlphaOutput)
urpAssetShaderFeatures |= ShaderFeatures.AlphaOutput;
// Check each renderer & renderer feature
urpAssetShaderFeatures = GetSupportedShaderFeaturesFromRenderers(
ref urpAsset,
ref rendererFeaturesList,
urpAssetShaderFeatures,
ref ssaoRendererFeatures,
stripUnusedVariants,
out containsForwardRenderer,
out everyRendererHasSSAO);
return urpAssetShaderFeatures;
}
// Checks each Universal Renderer in the assigned URP Asset for features used...
internal static ShaderFeatures GetSupportedShaderFeaturesFromRenderers(
ref UniversalRenderPipelineAsset urpAsset,
ref List<ShaderFeatures> rendererFeaturesList,
ShaderFeatures urpAssetShaderFeatures,
ref List<ScreenSpaceAmbientOcclusionSettings> ssaoRendererFeatures,
bool stripUnusedVariants,
out bool containsForwardRenderer,
out bool everyRendererHasSSAO)
{
// Sanity check
if (rendererFeaturesList == null)
rendererFeaturesList = new List<ShaderFeatures>();
// The combined URP Asset features used for Prefiltering
// We start with None instead of URP Asset features as they can change
// when iterating over the renderers, such as when Forward Plus is in use.
ShaderFeatures combinedURPAssetShaderFeatures = ShaderFeatures.None;
containsForwardRenderer = false;
everyRendererHasSSAO = true;
ScriptableRendererData[] rendererDataArray = urpAsset.m_RendererDataList;
for (int rendererIndex = 0; rendererIndex < rendererDataArray.Length; ++rendererIndex)
{
// Get feature requirements from the renderer
ScriptableRenderer renderer = urpAsset.GetRenderer(rendererIndex);
ScriptableRendererData rendererData = rendererDataArray[rendererIndex];
RendererRequirements rendererRequirements = GetRendererRequirements(ref urpAsset, ref renderer, ref rendererData, stripUnusedVariants);
// Get & add Supported features from renderers used for Scriptable Stripping and prefiltering.
ShaderFeatures rendererShaderFeatures = GetSupportedShaderFeaturesFromRenderer(ref rendererRequirements, ref rendererData, ref ssaoRendererFeatures, ref containsForwardRenderer, urpAssetShaderFeatures);
rendererFeaturesList.Add(rendererShaderFeatures);
// Check to see if it's possible to remove the OFF variant for SSAO
everyRendererHasSSAO &= IsFeatureEnabled(rendererShaderFeatures, ShaderFeatures.ScreenSpaceOcclusion);
// Check for completely removing 2D passes
s_Strip2DPasses &= renderer is not Renderer2D;
// Add the features from the renderer to the combined feature set for this URP Asset
combinedURPAssetShaderFeatures |= rendererShaderFeatures;
}
return combinedURPAssetShaderFeatures;
}
internal static bool NeedsProceduralKeyword(ref RendererRequirements rendererRequirements)
{
#if ENABLE_VR && ENABLE_XR_MODULE
var xrResourcesAreValid = GraphicsSettings.GetRenderPipelineSettings<UniversalRenderPipelineRuntimeXRResources>()?.valid ?? false;
return rendererRequirements.isUniversalRenderer && xrResourcesAreValid;
#else
return false;
#endif
}
internal static RendererRequirements GetRendererRequirements(ref UniversalRenderPipelineAsset urpAsset, ref ScriptableRenderer renderer, ref ScriptableRendererData rendererData, bool stripUnusedVariants)
{
UniversalRenderer universalRenderer = renderer as UniversalRenderer;
UniversalRendererData universalRendererData = rendererData as UniversalRendererData;
RendererRequirements rsd = new();
rsd.needsUnusedVariants = !stripUnusedVariants;
rsd.isUniversalRenderer = universalRendererData != null && universalRenderer != null;
rsd.msaaSampleCount = urpAsset.msaaSampleCount;
rsd.renderingMode = rsd.isUniversalRenderer ? universalRendererData.renderingMode : RenderingMode.Forward;
rsd.needsMainLightShadows = urpAsset.supportsMainLightShadows && urpAsset.mainLightRenderingMode == LightRenderingMode.PerPixel;
rsd.needsAdditionalLightShadows = urpAsset.supportsAdditionalLightShadows && (urpAsset.additionalLightsRenderingMode == LightRenderingMode.PerPixel || rsd.renderingMode == RenderingMode.ForwardPlus);
rsd.needsSoftShadows = urpAsset.supportsSoftShadows && (rsd.needsMainLightShadows || rsd.needsAdditionalLightShadows);
rsd.needsSoftShadowsQualityLevels = rsd.needsSoftShadows && s_UseSoftShadowQualityLevelKeywords;
rsd.needsShadowsOff = !renderer.stripShadowsOffVariants;
rsd.needsAdditionalLightsOff = s_KeepOffVariantForAdditionalLights || !renderer.stripAdditionalLightOffVariants;
rsd.needsGBufferRenderingLayers = (rsd.isUniversalRenderer && rsd.renderingMode == RenderingMode.Deferred && urpAsset.useRenderingLayers);
rsd.needsGBufferAccurateNormals = (rsd.isUniversalRenderer && rsd.renderingMode == RenderingMode.Deferred && universalRenderer.accurateGbufferNormals);
rsd.needsRenderPass = (rsd.isUniversalRenderer && rsd.renderingMode == RenderingMode.Deferred);
rsd.needsReflectionProbeBlending = urpAsset.reflectionProbeBlending;
rsd.needsReflectionProbeBoxProjection = urpAsset.reflectionProbeBoxProjection;
rsd.needsProcedural = NeedsProceduralKeyword(ref rsd);
rsd.needsSHVertexForSHAuto = s_UseSHPerVertexForSHAuto;
return rsd;
}
// Checks the assigned Universal renderer for features used...
internal static ShaderFeatures GetSupportedShaderFeaturesFromRenderer(ref RendererRequirements rendererRequirements, ref ScriptableRendererData rendererData, ref List<ScreenSpaceAmbientOcclusionSettings> ssaoRendererFeatures, ref bool containsForwardRenderer, ShaderFeatures urpAssetShaderFeatures)
{
ShaderFeatures shaderFeatures = urpAssetShaderFeatures;
// Procedural...
if (rendererRequirements.needsProcedural)
shaderFeatures |= ShaderFeatures.DrawProcedural;
// Rendering Modes...
switch (rendererRequirements.renderingMode)
{
case RenderingMode.ForwardPlus:
shaderFeatures |= ShaderFeatures.ForwardPlus;
break;
case RenderingMode.Deferred:
shaderFeatures |= ShaderFeatures.DeferredShading;
break;
case RenderingMode.Forward:
default:
containsForwardRenderer = true;
break;
}
// Renderer features...
if (rendererData != null)
{
List<ScriptableRendererFeature> rendererFeatures = rendererData.rendererFeatures;
shaderFeatures |= GetSupportedShaderFeaturesFromRendererFeatures(ref rendererRequirements, ref rendererFeatures, ref ssaoRendererFeatures);
}
// The Off variant for Additional Lights
if (rendererRequirements.needsAdditionalLightsOff)
shaderFeatures |= ShaderFeatures.AdditionalLightsKeepOffVariants;
// Forward+
if (rendererRequirements.renderingMode == RenderingMode.ForwardPlus)
{
shaderFeatures |= ShaderFeatures.AdditionalLightsKeepOffVariants;
shaderFeatures |= ShaderFeatures.ForwardPlus;
shaderFeatures &= ~(ShaderFeatures.AdditionalLightsPixel | ShaderFeatures.AdditionalLightsVertex);
}
// Main & Additional Light Shadows
// Keeps the Off variant for Main and Additional Light shadows
if (rendererRequirements.needsShadowsOff)
shaderFeatures |= ShaderFeatures.ShadowsKeepOffVariants;
if (rendererRequirements.needsMainLightShadows)
{
// Cascade count can be changed at runtime, so include both of them
shaderFeatures |= ShaderFeatures.MainLightShadows;
shaderFeatures |= ShaderFeatures.MainLightShadowsCascade;
}
// Additional Light Shadows
if (rendererRequirements.needsAdditionalLightShadows)
shaderFeatures |= ShaderFeatures.AdditionalLightShadows;
// Soft shadows for Main and Additional Lights
if (rendererRequirements.needsSoftShadows && !rendererRequirements.needsSoftShadowsQualityLevels)
shaderFeatures |= ShaderFeatures.SoftShadows;
if (rendererRequirements.needsSoftShadowsQualityLevels)
{
if (UniversalRenderPipeline.asset?.softShadowQuality == SoftShadowQuality.Low)
shaderFeatures |= ShaderFeatures.SoftShadowsLow;
if (UniversalRenderPipeline.asset?.softShadowQuality == SoftShadowQuality.Medium)
shaderFeatures |= ShaderFeatures.SoftShadowsMedium;
if (UniversalRenderPipeline.asset?.softShadowQuality == SoftShadowQuality.High)
shaderFeatures |= ShaderFeatures.SoftShadowsHigh;
}
// Deferred GBuffer Rendering Layers
if (rendererRequirements.needsGBufferRenderingLayers)
shaderFeatures |= ShaderFeatures.GBufferWriteRenderingLayers;
// Deferred GBuffer Accurate Normals
if (rendererRequirements.needsGBufferAccurateNormals)
shaderFeatures |= ShaderFeatures.AccurateGbufferNormals;
// Deferred GBuffer Native Render Pass
if (rendererRequirements.needsRenderPass)
shaderFeatures |= ShaderFeatures.RenderPassEnabled;
// Reflection Probe Blending
if (rendererRequirements.needsReflectionProbeBlending)
shaderFeatures |= ShaderFeatures.ReflectionProbeBlending;
// Reflection Probe Box Projection
if (rendererRequirements.needsReflectionProbeBoxProjection)
shaderFeatures |= ShaderFeatures.ReflectionProbeBoxProjection;
if (rendererRequirements.needsSHVertexForSHAuto)
shaderFeatures |= ShaderFeatures.AutoSHModePerVertex;
return shaderFeatures;
}
// Checks each Universal Renderer Feature in the assigned renderer...
internal static ShaderFeatures GetSupportedShaderFeaturesFromRendererFeatures(ref RendererRequirements rendererRequirements, ref List<ScriptableRendererFeature> rendererFeatures, ref List<ScreenSpaceAmbientOcclusionSettings> ssaoRendererFeatures)
{
ShaderFeatures shaderFeatures = ShaderFeatures.None;
bool usesRenderingLayers = false;
RenderingLayerUtils.Event renderingLayersEvent = RenderingLayerUtils.Event.Opaque;
bool isDeferredRenderer = (rendererRequirements.renderingMode == RenderingMode.Deferred);
for (int rendererFeatureIndex = 0; rendererFeatureIndex < rendererFeatures.Count; rendererFeatureIndex++)
{
ScriptableRendererFeature rendererFeature = rendererFeatures[rendererFeatureIndex];
// Make sure the renderer feature isn't missing
if (rendererFeature == null)
continue;
// We don't add disabled renderer features if "Strip Unused Variants" is enabled.
if (!rendererRequirements.needsUnusedVariants && !rendererFeature.isActive)
continue;
// Rendering Layers...
if (rendererRequirements.isUniversalRenderer &&
RenderingLayerUtils.RequireRenderingLayers(rendererFeatures,
rendererRequirements.renderingMode,
rendererRequirements.needsGBufferAccurateNormals,
rendererRequirements.msaaSampleCount, out RenderingLayerUtils.Event rendererEvent, out _))
{
usesRenderingLayers = true;
RenderingLayerUtils.CombineRendererEvents(isDeferredRenderer, rendererRequirements.msaaSampleCount, rendererEvent, ref renderingLayersEvent);
}
// Screen Space Shadows...
ScreenSpaceShadows sssFeature = rendererFeature as ScreenSpaceShadows;
if (sssFeature != null)
{
// Add it if it's enabled or if unused variants should not be stripped...
if (sssFeature.isActive || rendererRequirements.needsUnusedVariants)
shaderFeatures |= ShaderFeatures.ScreenSpaceShadows;
continue;
}
// Screen Space Ambient Occlusion (SSAO)...
// Removing the OFF variant requires every renderer to use SSAO. That is checked later.
ScreenSpaceAmbientOcclusion ssaoFeature = rendererFeature as ScreenSpaceAmbientOcclusion;
if (ssaoFeature != null)
{
ScreenSpaceAmbientOcclusionSettings ssaoSettings = ssaoFeature.settings;
ssaoRendererFeatures.Add(ssaoSettings);
// Keep _SCREEN_SPACE_OCCLUSION and the Off variant when stripping of unused variants is disabled
if (rendererRequirements.needsUnusedVariants)
{
shaderFeatures |= ShaderFeatures.ScreenSpaceOcclusion;
shaderFeatures |= ShaderFeatures.ScreenSpaceOcclusionAfterOpaque;
}
// The feature is active (Tested a few lines above) so check for AfterOpaque
else
{
if (ssaoSettings.AfterOpaque)
shaderFeatures |= ShaderFeatures.ScreenSpaceOcclusionAfterOpaque;
else
shaderFeatures |= ShaderFeatures.ScreenSpaceOcclusion;
}
// Otherwise the keyword will not be used
continue;
}
// Decals...
DecalRendererFeature decal = rendererFeature as DecalRendererFeature;
if (decal != null && rendererRequirements.isUniversalRenderer)
{
// Keep all Decals variants when stripping of unused variants is disabled
if (rendererRequirements.needsUnusedVariants)
{
shaderFeatures |= ShaderFeatures.DBufferMRT1;
shaderFeatures |= ShaderFeatures.DBufferMRT2;
shaderFeatures |= ShaderFeatures.DBufferMRT3;
shaderFeatures |= ShaderFeatures.DecalScreenSpace;
shaderFeatures |= ShaderFeatures.DecalNormalBlendLow;
shaderFeatures |= ShaderFeatures.DecalNormalBlendMedium;
shaderFeatures |= ShaderFeatures.DecalNormalBlendHigh;
shaderFeatures |= ShaderFeatures.DecalGBuffer;
shaderFeatures |= ShaderFeatures.DecalLayers;
}
else
{
DecalTechnique technique = decal.GetTechnique(isDeferredRenderer, rendererRequirements.needsGBufferAccurateNormals, false);
switch (technique)
{
case DecalTechnique.DBuffer:
shaderFeatures |= GetFromDecalSurfaceData(decal.GetDBufferSettings().surfaceData);
break;
case DecalTechnique.ScreenSpace:
shaderFeatures |= GetFromNormalBlend(decal.GetScreenSpaceSettings().normalBlend);
shaderFeatures |= ShaderFeatures.DecalScreenSpace;
break;
case DecalTechnique.GBuffer:
shaderFeatures |= GetFromNormalBlend(decal.GetScreenSpaceSettings().normalBlend);
shaderFeatures |= ShaderFeatures.DecalGBuffer;
//data.shaderFeatures |= ShaderFeatures.DecalScreenSpace; // In case deferred is not supported it will fallback to forward
break;
}
if (decal.requiresDecalLayers)
shaderFeatures |= ShaderFeatures.DecalLayers;
}
}
}
// If using rendering layers, enable the appropriate feature
if (usesRenderingLayers)
{
if (rendererRequirements.needsUnusedVariants)
{
shaderFeatures |= ShaderFeatures.GBufferWriteRenderingLayers;
shaderFeatures |= ShaderFeatures.OpaqueWriteRenderingLayers;
shaderFeatures |= ShaderFeatures.DepthNormalPassRenderingLayers;
}
else if (isDeferredRenderer)
{
// Rendering layers in both Depth Normal and GBuffer passes are needed
// as some object might be rendered in forward and others in deferred.
shaderFeatures |= ShaderFeatures.DepthNormalPassRenderingLayers;
shaderFeatures |= ShaderFeatures.GBufferWriteRenderingLayers;
}
else
{
// Check if other passes need the keyword
switch (renderingLayersEvent)
{
case RenderingLayerUtils.Event.DepthNormalPrePass:
shaderFeatures |= ShaderFeatures.DepthNormalPassRenderingLayers;
break;
case RenderingLayerUtils.Event.Opaque:
shaderFeatures |= ShaderFeatures.OpaqueWriteRenderingLayers;
break;
default:
throw new NotImplementedException();
}
}
}
return shaderFeatures;
}
// Retrieves the correct feature used from the Decal Surface Data Settings...
private static ShaderFeatures GetFromDecalSurfaceData(DecalSurfaceData surfaceData)
{
ShaderFeatures shaderFeatures = ShaderFeatures.None;
switch (surfaceData)
{
case DecalSurfaceData.Albedo:
shaderFeatures |= ShaderFeatures.DBufferMRT1;
break;
case DecalSurfaceData.AlbedoNormal:
shaderFeatures |= ShaderFeatures.DBufferMRT2;
break;
case DecalSurfaceData.AlbedoNormalMAOS:
shaderFeatures |= ShaderFeatures.DBufferMRT3;
break;
}
return shaderFeatures;
}
// Retrieves the correct feature used from the Decal Normal Blend Settings...
private static ShaderFeatures GetFromNormalBlend(DecalNormalBlend normalBlend)
{
ShaderFeatures shaderFeatures = ShaderFeatures.None;
switch (normalBlend)
{
case DecalNormalBlend.Low:
shaderFeatures |= ShaderFeatures.DecalNormalBlendLow;
break;
case DecalNormalBlend.Medium:
shaderFeatures |= ShaderFeatures.DecalNormalBlendMedium;
break;
case DecalNormalBlend.High:
shaderFeatures |= ShaderFeatures.DecalNormalBlendHigh;
break;
}
return shaderFeatures;
}
// Creates a struct containing all the prefiltering settings for the asset sent as a parameter
internal static ShaderPrefilteringData CreatePrefilteringSettings(
ref ShaderFeatures shaderFeatures,
bool isAssetUsingForward,
bool everyRendererHasSSAO,
bool stripXR,
bool stripHDR,
bool stripDebug,
bool stripScreenCoord,
bool stripUnusedVariants,
ref List<ScreenSpaceAmbientOcclusionSettings> ssaoRendererFeatures
)
{
bool isAssetUsingForwardPlus = IsFeatureEnabled(shaderFeatures, ShaderFeatures.ForwardPlus);
bool isAssetUsingDeferred = IsFeatureEnabled(shaderFeatures, ShaderFeatures.DeferredShading);
ShaderPrefilteringData spd = new();
spd.stripXRKeywords = stripXR;
spd.stripSoftShadowsQualityLow = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.SoftShadowsLow);
spd.stripSoftShadowsQualityMedium = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.SoftShadowsMedium);
spd.stripSoftShadowsQualityHigh = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.SoftShadowsHigh);
spd.stripHDRKeywords = stripHDR;
spd.stripAlphaOutputKeywords = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.AlphaOutput);
spd.stripDebugDisplay = stripDebug;
spd.stripScreenCoordOverride = stripScreenCoord;
// Rendering Modes
// Check if only Deferred is being used
spd.deferredPrefilteringMode = PrefilteringMode.Remove;
if (isAssetUsingDeferred)
{
// Only Deferred being used...
if (!isAssetUsingForward && !isAssetUsingForwardPlus)
spd.deferredPrefilteringMode = PrefilteringMode.SelectOnly;
else
spd.deferredPrefilteringMode = PrefilteringMode.Select;
}
// Check if only Forward+ is being used
spd.forwardPlusPrefilteringMode = PrefilteringMode.Remove;
if (isAssetUsingForwardPlus)
{
// Only Forward Plus being used...
if (!isAssetUsingForward && !isAssetUsingDeferred)
spd.forwardPlusPrefilteringMode = PrefilteringMode.SelectOnly;
else
spd.forwardPlusPrefilteringMode = PrefilteringMode.Select;
}
// Additional Lights...
spd.additionalLightsPrefilteringMode = PrefilteringModeAdditionalLights.Remove;
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.AdditionalLightsVertex))
{
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.AdditionalLightsKeepOffVariants))
spd.additionalLightsPrefilteringMode = PrefilteringModeAdditionalLights.SelectVertexAndOff;
else
spd.additionalLightsPrefilteringMode = PrefilteringModeAdditionalLights.SelectVertex;
}
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.AdditionalLightsPixel))
{
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.AdditionalLightsKeepOffVariants))
spd.additionalLightsPrefilteringMode = PrefilteringModeAdditionalLights.SelectPixelAndOff;
else
spd.additionalLightsPrefilteringMode = PrefilteringModeAdditionalLights.SelectPixel;
}
// Shadows...
// Main Light Shadows...
spd.mainLightShadowsPrefilteringMode = PrefilteringModeMainLightShadows.Remove;
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.MainLightShadows))
{
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.MainLightShadowsCascade))
{
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.ShadowsKeepOffVariants))
spd.mainLightShadowsPrefilteringMode = PrefilteringModeMainLightShadows.SelectAll;
else
spd.mainLightShadowsPrefilteringMode = PrefilteringModeMainLightShadows.SelectMainLightAndCascades;
}
else
{
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.ShadowsKeepOffVariants))
spd.mainLightShadowsPrefilteringMode = PrefilteringModeMainLightShadows.SelectMainLightAndOff;
else
spd.mainLightShadowsPrefilteringMode = PrefilteringModeMainLightShadows.SelectMainLight;
}
}
// Additional Light Shadows...
spd.additionalLightsShadowsPrefilteringMode = PrefilteringMode.Remove;
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.AdditionalLightShadows))
{
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.ShadowsKeepOffVariants))
spd.additionalLightsShadowsPrefilteringMode = PrefilteringMode.Select;
else
spd.additionalLightsShadowsPrefilteringMode = PrefilteringMode.SelectOnly;
}
// Decals' MRT keywords
spd.stripDBufferMRT1 = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.DBufferMRT1);
spd.stripDBufferMRT2 = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.DBufferMRT2);
spd.stripDBufferMRT3 = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.DBufferMRT3);
// Native Render Pass
spd.stripNativeRenderPass = !IsFeatureEnabled(shaderFeatures, ShaderFeatures.RenderPassEnabled);
// Rendering Layers
spd.stripWriteRenderingLayers =
!IsFeatureEnabled(shaderFeatures, ShaderFeatures.DepthNormalPassRenderingLayers)
&& !IsFeatureEnabled(shaderFeatures, ShaderFeatures.GBufferWriteRenderingLayers)
&& !IsFeatureEnabled(shaderFeatures, ShaderFeatures.OpaqueWriteRenderingLayers);
// Disable lightmap texture arrays (GPU resident drawer)
spd.useLegacyLightmaps = IsFeatureEnabled(shaderFeatures, ShaderFeatures.UseLegacyLightmaps);
// Screen Space Ambient Occlusion
spd.screenSpaceOcclusionPrefilteringMode = PrefilteringMode.Remove;
if (IsFeatureEnabled(shaderFeatures, ShaderFeatures.ScreenSpaceOcclusion))
{
// Remove the SSAO's OFF variant if Global Settings allow it and every renderer uses it.
if (stripUnusedVariants && everyRendererHasSSAO)
spd.screenSpaceOcclusionPrefilteringMode = PrefilteringMode.SelectOnly;
// Otherwise we keep both
else
spd.screenSpaceOcclusionPrefilteringMode = PrefilteringMode.Select;
}
// SSAO shader keywords
spd.stripSSAODepthNormals = true;
spd.stripSSAOSourceDepthLow = true;
spd.stripSSAOSourceDepthMedium = true;
spd.stripSSAOSourceDepthHigh = true;
spd.stripSSAOBlueNoise = true;
spd.stripSSAOInterleaved = true;
spd.stripSSAOSampleCountLow = true;
spd.stripSSAOSampleCountMedium = true;
spd.stripSSAOSampleCountHigh = true;
for (int i = 0; i < ssaoRendererFeatures.Count; i++)
{
ScreenSpaceAmbientOcclusionSettings ssaoSettings = ssaoRendererFeatures[i];
bool isUsingDepthNormals = ssaoSettings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
spd.stripSSAODepthNormals &= !isUsingDepthNormals;
spd.stripSSAOSourceDepthLow &= isUsingDepthNormals || ssaoSettings.NormalSamples != ScreenSpaceAmbientOcclusionSettings.NormalQuality.Low;
spd.stripSSAOSourceDepthMedium &= isUsingDepthNormals || ssaoSettings.NormalSamples != ScreenSpaceAmbientOcclusionSettings.NormalQuality.Medium;
spd.stripSSAOSourceDepthHigh &= isUsingDepthNormals || ssaoSettings.NormalSamples != ScreenSpaceAmbientOcclusionSettings.NormalQuality.High;
spd.stripSSAOBlueNoise &= ssaoSettings.AOMethod != ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise;
spd.stripSSAOInterleaved &= ssaoSettings.AOMethod != ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.InterleavedGradient;
spd.stripSSAOSampleCountLow &= ssaoSettings.Samples != ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Low;
spd.stripSSAOSampleCountMedium &= ssaoSettings.Samples != ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Medium;
spd.stripSSAOSampleCountHigh &= ssaoSettings.Samples != ScreenSpaceAmbientOcclusionSettings.AOSampleOption.High;
}
return spd;
}
// Checks whether a ShaderFeature is enabled or not
internal static bool IsFeatureEnabled(ShaderFeatures featureMask, ShaderFeatures feature)
{
return (featureMask & feature) != 0;
}
}
}