using System; using Unity.Collections; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; using UnityEditor.Rendering.Universal; #endif using UnityEngine.Scripting.APIUpdating; using Lightmapping = UnityEngine.Experimental.GlobalIllumination.Lightmapping; using UnityEngine.Experimental.Rendering; using UnityEngine.Profiling; namespace UnityEngine.Rendering.Universal { public sealed partial class UniversalRenderPipeline : RenderPipeline { public const string k_ShaderTagName = "UniversalPipeline"; private static class Profiling { private static Dictionary s_HashSamplerCache = new Dictionary(); public static readonly ProfilingSampler unknownSampler = new ProfilingSampler("Unknown"); // Specialization for camera loop to avoid allocations. public static ProfilingSampler TryGetOrAddCameraSampler(Camera camera) { #if UNIVERSAL_PROFILING_NO_ALLOC return unknownSampler; #else ProfilingSampler ps = null; int cameraId = camera.GetHashCode(); bool exists = s_HashSamplerCache.TryGetValue(cameraId, out ps); if (!exists) { // NOTE: camera.name allocates! ps = new ProfilingSampler($"{nameof(UniversalRenderPipeline)}.{nameof(RenderSingleCamera)}: {camera.name}"); s_HashSamplerCache.Add(cameraId, ps); } return ps; #endif } public static class Pipeline { // TODO: Would be better to add Profiling name hooks into RenderPipeline.cs, requires changes outside of Universal. #if UNITY_2021_1_OR_NEWER public static readonly ProfilingSampler beginContextRendering = new ProfilingSampler($"{nameof(RenderPipeline)}.{nameof(BeginContextRendering)}"); public static readonly ProfilingSampler endContextRendering = new ProfilingSampler($"{nameof(RenderPipeline)}.{nameof(EndContextRendering)}"); #else public static readonly ProfilingSampler beginFrameRendering = new ProfilingSampler($"{nameof(RenderPipeline)}.{nameof(BeginFrameRendering)}"); public static readonly ProfilingSampler endFrameRendering = new ProfilingSampler($"{nameof(RenderPipeline)}.{nameof(EndFrameRendering)}"); #endif public static readonly ProfilingSampler beginCameraRendering = new ProfilingSampler($"{nameof(RenderPipeline)}.{nameof(BeginCameraRendering)}"); public static readonly ProfilingSampler endCameraRendering = new ProfilingSampler($"{nameof(RenderPipeline)}.{nameof(EndCameraRendering)}"); const string k_Name = nameof(UniversalRenderPipeline); public static readonly ProfilingSampler initializeCameraData = new ProfilingSampler($"{k_Name}.{nameof(InitializeCameraData)}"); public static readonly ProfilingSampler initializeStackedCameraData = new ProfilingSampler($"{k_Name}.{nameof(InitializeStackedCameraData)}"); public static readonly ProfilingSampler initializeAdditionalCameraData = new ProfilingSampler($"{k_Name}.{nameof(InitializeAdditionalCameraData)}"); public static readonly ProfilingSampler initializeRenderingData = new ProfilingSampler($"{k_Name}.{nameof(InitializeRenderingData)}"); public static readonly ProfilingSampler initializeShadowData = new ProfilingSampler($"{k_Name}.{nameof(InitializeShadowData)}"); public static readonly ProfilingSampler initializeLightData = new ProfilingSampler($"{k_Name}.{nameof(InitializeLightData)}"); public static readonly ProfilingSampler getPerObjectLightFlags = new ProfilingSampler($"{k_Name}.{nameof(GetPerObjectLightFlags)}"); public static readonly ProfilingSampler getMainLightIndex = new ProfilingSampler($"{k_Name}.{nameof(GetMainLightIndex)}"); public static readonly ProfilingSampler setupPerFrameShaderConstants = new ProfilingSampler($"{k_Name}.{nameof(SetupPerFrameShaderConstants)}"); public static class Renderer { const string k_Name = nameof(ScriptableRenderer); public static readonly ProfilingSampler setupCullingParameters = new ProfilingSampler($"{k_Name}.{nameof(ScriptableRenderer.SetupCullingParameters)}"); public static readonly ProfilingSampler setup = new ProfilingSampler($"{k_Name}.{nameof(ScriptableRenderer.Setup)}"); }; public static class Context { const string k_Name = nameof(ScriptableRenderContext); public static readonly ProfilingSampler submit = new ProfilingSampler($"{k_Name}.{nameof(ScriptableRenderContext.Submit)}"); }; public static class XR { public static readonly ProfilingSampler mirrorView = new ProfilingSampler("XR Mirror View"); }; }; } #if ENABLE_VR && ENABLE_XR_MODULE internal static XRSystem m_XRSystem = new XRSystem(); #endif public static float maxShadowBias { get => 10.0f; } public static float minRenderScale { get => 0.1f; } public static float maxRenderScale { get => 2.0f; } // Amount of Lights that can be shaded per object (in the for loop in the shader) public static int maxPerObjectLights { // No support to bitfield mask and int[] in gles2. Can't index fast more than 4 lights. // Check Lighting.hlsl for more details. get => (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2) ? 4 : 8; } // These limits have to match same limits in Input.hlsl internal const int k_MaxVisibleAdditionalLightsMobileShaderLevelLessThan45 = 16; internal const int k_MaxVisibleAdditionalLightsMobile = 32; internal const int k_MaxVisibleAdditionalLightsNonMobile = 256; public static int maxVisibleAdditionalLights { get { // Must match: Input.hlsl, MAX_VISIBLE_LIGHTS bool isMobile = GraphicsSettings.HasShaderDefine(BuiltinShaderDefine.SHADER_API_MOBILE); if (isMobile && (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2 || (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3 && Graphics.minOpenGLESVersion <= OpenGLESVersion.OpenGLES30))) return k_MaxVisibleAdditionalLightsMobileShaderLevelLessThan45; // GLES can be selected as platform on Windows (not a mobile platform) but uniform buffer size so we must use a low light count. return (isMobile || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3) ? k_MaxVisibleAdditionalLightsMobile : k_MaxVisibleAdditionalLightsNonMobile; } } // Match with values in Input.hlsl internal static int lightsPerTile => ((maxVisibleAdditionalLights + 31) / 32) * 32; internal static int maxZBins => 1024 * 4; internal static int maxTileVec4s => 4096; internal const int k_DefaultRenderingLayerMask = 0x00000001; private readonly DebugDisplaySettingsUI m_DebugDisplaySettingsUI = new DebugDisplaySettingsUI(); private UniversalRenderPipelineGlobalSettings m_GlobalSettings; public override RenderPipelineGlobalSettings defaultSettings => m_GlobalSettings; // Reference to the asset associated with the pipeline. // When a pipeline asset is switched in `GraphicsSettings`, the `UniversalRenderPipelineCore.asset` member // becomes unreliable for the purpose of pipeline and renderer clean-up in the `Dispose` call from // `RenderPipelineManager.CleanupRenderPipeline`. // This field provides the correct reference for the purpose of cleaning up the renderers on this pipeline // asset. private readonly UniversalRenderPipelineAsset pipelineAsset; /// public override string ToString() => pipelineAsset?.ToString(); public UniversalRenderPipeline(UniversalRenderPipelineAsset asset) { pipelineAsset = asset; #if UNITY_EDITOR m_GlobalSettings = UniversalRenderPipelineGlobalSettings.Ensure(); #else m_GlobalSettings = UniversalRenderPipelineGlobalSettings.instance; #endif SetSupportedRenderingFeatures(); // In QualitySettings.antiAliasing disabled state uses value 0, where in URP 1 int qualitySettingsMsaaSampleCount = QualitySettings.antiAliasing > 0 ? QualitySettings.antiAliasing : 1; bool msaaSampleCountNeedsUpdate = qualitySettingsMsaaSampleCount != asset.msaaSampleCount; // Let engine know we have MSAA on for cases where we support MSAA backbuffer if (msaaSampleCountNeedsUpdate) { QualitySettings.antiAliasing = asset.msaaSampleCount; #if ENABLE_VR && ENABLE_XR_MODULE XRSystem.UpdateMSAALevel(asset.msaaSampleCount); #endif } #if ENABLE_VR && ENABLE_XR_MODULE XRSystem.UpdateRenderScale(asset.renderScale); #endif Shader.globalRenderPipeline = "UniversalPipeline"; Lightmapping.SetDelegate(lightsDelegate); CameraCaptureBridge.enabled = true; RenderingUtils.ClearSystemInfoCache(); DecalProjector.defaultMaterial = asset.decalMaterial; DebugManager.instance.RefreshEditor(); m_DebugDisplaySettingsUI.RegisterDebug(DebugDisplaySettings.Instance); } protected override void Dispose(bool disposing) { m_DebugDisplaySettingsUI.UnregisterDebug(); base.Dispose(disposing); pipelineAsset.DestroyRenderers(); Shader.globalRenderPipeline = ""; SupportedRenderingFeatures.active = new SupportedRenderingFeatures(); ShaderData.instance.Dispose(); DeferredShaderData.instance.Dispose(); #if ENABLE_VR && ENABLE_XR_MODULE m_XRSystem?.Dispose(); #endif #if UNITY_EDITOR SceneViewDrawMode.ResetDrawMode(); #endif Lightmapping.ResetDelegate(); CameraCaptureBridge.enabled = false; } #if UNITY_2021_1_OR_NEWER protected override void Render(ScriptableRenderContext renderContext, Camera[] cameras) { Render(renderContext, new List(cameras)); } #endif #if UNITY_2021_1_OR_NEWER protected override void Render(ScriptableRenderContext renderContext, List cameras) #else protected override void Render(ScriptableRenderContext renderContext, Camera[] cameras) #endif { // TODO: Would be better to add Profiling name hooks into RenderPipelineManager. // C#8 feature, only in >= 2020.2 using var profScope = new ProfilingScope(null, ProfilingSampler.Get(URPProfileId.UniversalRenderTotal)); #if UNITY_2021_1_OR_NEWER using (new ProfilingScope(null, Profiling.Pipeline.beginContextRendering)) { BeginContextRendering(renderContext, cameras); } #else using (new ProfilingScope(null, Profiling.Pipeline.beginFrameRendering)) { BeginFrameRendering(renderContext, cameras); } #endif GraphicsSettings.lightsUseLinearIntensity = (QualitySettings.activeColorSpace == ColorSpace.Linear); GraphicsSettings.lightsUseColorTemperature = true; GraphicsSettings.useScriptableRenderPipelineBatching = asset.useSRPBatcher; GraphicsSettings.defaultRenderingLayerMask = k_DefaultRenderingLayerMask; SetupPerFrameShaderConstants(); #if ENABLE_VR && ENABLE_XR_MODULE // Update XR MSAA level per frame. XRSystem.UpdateMSAALevel(asset.msaaSampleCount); #endif #if UNITY_EDITOR // We do not want to start rendering if URP global settings are not ready (m_globalSettings is null) // or been deleted/moved (m_globalSettings is not necessarily null) if (m_GlobalSettings == null || UniversalRenderPipelineGlobalSettings.instance == null) { m_GlobalSettings = UniversalRenderPipelineGlobalSettings.Ensure(); if(m_GlobalSettings == null) return; } #endif SortCameras(cameras); #if UNITY_2021_1_OR_NEWER for (int i = 0; i < cameras.Count; ++i) #else for (int i = 0; i < cameras.Length; ++i) #endif { var camera = cameras[i]; if (IsGameCamera(camera)) { RenderCameraStack(renderContext, camera); } else { using (new ProfilingScope(null, Profiling.Pipeline.beginCameraRendering)) { BeginCameraRendering(renderContext, camera); } #if VISUAL_EFFECT_GRAPH_0_0_1_OR_NEWER //It should be called before culling to prepare material. When there isn't any VisualEffect component, this method has no effect. VFX.VFXManager.PrepareCamera(camera); #endif UpdateVolumeFramework(camera, null); RenderSingleCamera(renderContext, camera); using (new ProfilingScope(null, Profiling.Pipeline.endCameraRendering)) { EndCameraRendering(renderContext, camera); } } } #if UNITY_2021_1_OR_NEWER using (new ProfilingScope(null, Profiling.Pipeline.endContextRendering)) { EndContextRendering(renderContext, cameras); } #else using (new ProfilingScope(null, Profiling.Pipeline.endFrameRendering)) { EndFrameRendering(renderContext, cameras); } #endif } /// /// Standalone camera rendering. Use this to render procedural cameras. /// This method doesn't call BeginCameraRendering and EndCameraRendering callbacks. /// /// Render context used to record commands during execution. /// Camera to render. /// public static void RenderSingleCamera(ScriptableRenderContext context, Camera camera) { UniversalAdditionalCameraData additionalCameraData = null; if (IsGameCamera(camera)) camera.gameObject.TryGetComponent(out additionalCameraData); if (additionalCameraData != null && additionalCameraData.renderType != CameraRenderType.Base) { Debug.LogWarning("Only Base cameras can be rendered with standalone RenderSingleCamera. Camera will be skipped."); return; } InitializeCameraData(camera, additionalCameraData, true, out var cameraData); #if ADAPTIVE_PERFORMANCE_2_0_0_OR_NEWER if (asset.useAdaptivePerformance) ApplyAdaptivePerformance(ref cameraData); #endif RenderSingleCamera(context, cameraData, cameraData.postProcessEnabled); } static bool TryGetCullingParameters(CameraData cameraData, out ScriptableCullingParameters cullingParams) { #if ENABLE_VR && ENABLE_XR_MODULE if (cameraData.xr.enabled) { cullingParams = cameraData.xr.cullingParams; // Sync the FOV on the camera to match the projection from the XR device if (!cameraData.camera.usePhysicalProperties && !XRGraphicsAutomatedTests.enabled) cameraData.camera.fieldOfView = Mathf.Rad2Deg * Mathf.Atan(1.0f / cullingParams.stereoProjectionMatrix.m11) * 2.0f; return true; } #endif return cameraData.camera.TryGetCullingParameters(false, out cullingParams); } /// /// Renders a single camera. This method will do culling, setup and execution of the renderer. /// /// Render context used to record commands during execution. /// Camera rendering data. This might contain data inherited from a base camera. /// True if at least one camera has post-processing enabled in the stack, false otherwise. static void RenderSingleCamera(ScriptableRenderContext context, CameraData cameraData, bool anyPostProcessingEnabled) { Camera camera = cameraData.camera; var renderer = cameraData.renderer; if (renderer == null) { Debug.LogWarning(string.Format("Trying to render {0} with an invalid renderer. Camera rendering will be skipped.", camera.name)); return; } if (!TryGetCullingParameters(cameraData, out var cullingParameters)) return; ScriptableRenderer.current = renderer; bool isSceneViewCamera = cameraData.isSceneViewCamera; // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name"). // Currently there's an issue which results in mismatched markers. // The named CommandBuffer will close its "profiling scope" on execution. // That will orphan ProfilingScope markers as the named CommandBuffer markers are their parents. // Resulting in following pattern: // exec(cmd.start, scope.start, cmd.end) and exec(cmd.start, scope.end, cmd.end) CommandBuffer cmd = CommandBufferPool.Get(); // TODO: move skybox code from C++ to URP in order to remove the call to context.Submit() inside DrawSkyboxPass // Until then, we can't use nested profiling scopes with XR multipass CommandBuffer cmdScope = cameraData.xr.enabled ? null : cmd; ProfilingSampler sampler = Profiling.TryGetOrAddCameraSampler(camera); using (new ProfilingScope(cmdScope, sampler)) // Enqueues a "BeginSample" command into the CommandBuffer cmd { renderer.Clear(cameraData.renderType); using (new ProfilingScope(null, Profiling.Pipeline.Renderer.setupCullingParameters)) { renderer.OnPreCullRenderPasses(in cameraData); renderer.SetupCullingParameters(ref cullingParameters, ref cameraData); } context.ExecuteCommandBuffer(cmd); // Send all the commands enqueued so far in the CommandBuffer cmd, to the ScriptableRenderContext context cmd.Clear(); #if UNITY_EDITOR // Emit scene view UI if (isSceneViewCamera) { ScriptableRenderContext.EmitWorldGeometryForSceneView(camera); } #endif var cullResults = context.Cull(ref cullingParameters); InitializeRenderingData(asset, ref cameraData, ref cullResults, anyPostProcessingEnabled, out var renderingData); #if ADAPTIVE_PERFORMANCE_2_0_0_OR_NEWER if (asset.useAdaptivePerformance) ApplyAdaptivePerformance(ref renderingData); #endif using (new ProfilingScope(null, Profiling.Pipeline.Renderer.setup)) { renderer.Setup(context, ref renderingData); } // Timing scope inside renderer.Execute(context, ref renderingData); CleanupLightData(ref renderingData.lightData); } // When ProfilingSample goes out of scope, an "EndSample" command is enqueued into CommandBuffer cmd cameraData.xr.EndCamera(cmd, cameraData); context.ExecuteCommandBuffer(cmd); // Sends to ScriptableRenderContext all the commands enqueued since cmd.Clear, i.e the "EndSample" command CommandBufferPool.Release(cmd); using (new ProfilingScope(null, Profiling.Pipeline.Context.submit)) { if (renderer.useRenderPassEnabled && !context.SubmitForRenderPassValidation()) { renderer.useRenderPassEnabled = false; CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.RenderPassEnabled, false); Debug.LogWarning("Rendering command not supported inside a native RenderPass found. Falling back to non-RenderPass rendering path"); } context.Submit(); // Actually execute the commands that we previously sent to the ScriptableRenderContext context } ScriptableRenderer.current = null; } /// /// Renders a camera stack. This method calls RenderSingleCamera for each valid camera in the stack. /// The last camera resolves the final target to screen. /// /// Render context used to record commands during execution. /// Camera to render. static void RenderCameraStack(ScriptableRenderContext context, Camera baseCamera) { using var profScope = new ProfilingScope(null, ProfilingSampler.Get(URPProfileId.RenderCameraStack)); baseCamera.TryGetComponent(out var baseCameraAdditionalData); // Overlay cameras will be rendered stacked while rendering base cameras if (baseCameraAdditionalData != null && baseCameraAdditionalData.renderType == CameraRenderType.Overlay) return; // Renderer contains a stack if it has additional data and the renderer supports stacking // The renderer is checked if it supports Base camera. Since Base is the only relevant type at this moment. var renderer = baseCameraAdditionalData?.scriptableRenderer; bool supportsCameraStacking = renderer != null && renderer.SupportsCameraStackingType(CameraRenderType.Base); List cameraStack = (supportsCameraStacking) ? baseCameraAdditionalData?.cameraStack : null; bool anyPostProcessingEnabled = baseCameraAdditionalData != null && baseCameraAdditionalData.renderPostProcessing; // We need to know the last active camera in the stack to be able to resolve // rendering to screen when rendering it. The last camera in the stack is not // necessarily the last active one as it users might disable it. int lastActiveOverlayCameraIndex = -1; if (cameraStack != null) { var baseCameraRendererType = baseCameraAdditionalData?.scriptableRenderer.GetType(); bool shouldUpdateCameraStack = false; for (int i = 0; i < cameraStack.Count; ++i) { Camera currCamera = cameraStack[i]; if (currCamera == null) { shouldUpdateCameraStack = true; continue; } if (currCamera.isActiveAndEnabled) { currCamera.TryGetComponent(out var data); // Checking if the base and the overlay camera is of the same renderer type. var currCameraRendererType = data?.scriptableRenderer.GetType(); if (currCameraRendererType != baseCameraRendererType) { Debug.LogWarning("Only cameras with compatible renderer types can be stacked. " + $"The camera: {currCamera.name} are using the renderer {currCameraRendererType.Name}, " + $"but the base camera: {baseCamera.name} are using {baseCameraRendererType.Name}. Will skip rendering"); continue; } var overlayRenderer = data.scriptableRenderer; // Checking if they are the same renderer type but just not supporting Overlay if ((overlayRenderer.SupportedCameraStackingTypes() & 1 << (int)CameraRenderType.Overlay) == 0) { Debug.LogWarning($"The camera: {currCamera.name} is using a renderer of type {renderer.GetType().Name} which does not support Overlay cameras in it's current state."); continue; } if (data == null || data.renderType != CameraRenderType.Overlay) { Debug.LogWarning($"Stack can only contain Overlay cameras. The camera: {currCamera.name} " + $"has a type {data.renderType} that is not supported. Will skip rendering."); continue; } anyPostProcessingEnabled |= data.renderPostProcessing; lastActiveOverlayCameraIndex = i; } } if (shouldUpdateCameraStack) { baseCameraAdditionalData.UpdateCameraStack(); } } // Post-processing not supported in GLES2. anyPostProcessingEnabled &= SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2; bool isStackedRendering = lastActiveOverlayCameraIndex != -1; #if ENABLE_VR && ENABLE_XR_MODULE var xrActive = false; var xrRendering = true; if (baseCameraAdditionalData != null) xrRendering = baseCameraAdditionalData.allowXRRendering; var xrPasses = m_XRSystem.SetupFrame(baseCamera, xrRendering); foreach (XRPass xrPass in xrPasses) { if (xrPass.enabled) { xrActive = true; UpdateCameraStereoMatrices(baseCamera, xrPass); } #endif using (new ProfilingScope(null, Profiling.Pipeline.beginCameraRendering)) { BeginCameraRendering(context, baseCamera); } // Update volumeframework before initializing additional camera data UpdateVolumeFramework(baseCamera, baseCameraAdditionalData); InitializeCameraData(baseCamera, baseCameraAdditionalData, !isStackedRendering, out var baseCameraData); RenderTextureDescriptor originalTargetDesc = baseCameraData.cameraTargetDescriptor; #if ENABLE_VR && ENABLE_XR_MODULE if (xrPass.enabled) { baseCameraData.xr = xrPass; // XRTODO: remove isStereoEnabled in 2021.x #pragma warning disable 0618 baseCameraData.isStereoEnabled = xrPass.enabled; #pragma warning restore 0618 // Helper function for updating cameraData with xrPass Data m_XRSystem.UpdateCameraData(ref baseCameraData, baseCameraData.xr); // Need to update XRSystem using baseCameraData to handle the case where camera position is modified in BeginCameraRendering m_XRSystem.UpdateFromCamera(ref baseCameraData.xr, baseCameraData); m_XRSystem.BeginLateLatching(baseCamera, xrPass); } #endif #if VISUAL_EFFECT_GRAPH_0_0_1_OR_NEWER //It should be called before culling to prepare material. When there isn't any VisualEffect component, this method has no effect. VFX.VFXManager.PrepareCamera(baseCamera); #endif #if ADAPTIVE_PERFORMANCE_2_0_0_OR_NEWER if (asset.useAdaptivePerformance) ApplyAdaptivePerformance(ref baseCameraData); #endif RenderSingleCamera(context, baseCameraData, anyPostProcessingEnabled); using (new ProfilingScope(null, Profiling.Pipeline.endCameraRendering)) { EndCameraRendering(context, baseCamera); } #if ENABLE_VR && ENABLE_XR_MODULE m_XRSystem.EndLateLatching(baseCamera, xrPass); #endif if (isStackedRendering) { for (int i = 0; i < cameraStack.Count; ++i) { var currCamera = cameraStack[i]; if (!currCamera.isActiveAndEnabled) continue; currCamera.TryGetComponent(out var currCameraData); // Camera is overlay and enabled if (currCameraData != null) { // Copy base settings from base camera data and initialize initialize remaining specific settings for this camera type. CameraData overlayCameraData = baseCameraData; bool lastCamera = i == lastActiveOverlayCameraIndex; #if ENABLE_VR && ENABLE_XR_MODULE UpdateCameraStereoMatrices(currCameraData.camera, xrPass); #endif using (new ProfilingScope(null, Profiling.Pipeline.beginCameraRendering)) { BeginCameraRendering(context, currCamera); } #if VISUAL_EFFECT_GRAPH_0_0_1_OR_NEWER //It should be called before culling to prepare material. When there isn't any VisualEffect component, this method has no effect. VFX.VFXManager.PrepareCamera(currCamera); #endif UpdateVolumeFramework(currCamera, currCameraData); InitializeAdditionalCameraData(currCamera, currCameraData, lastCamera, ref overlayCameraData); #if ENABLE_VR && ENABLE_XR_MODULE if (baseCameraData.xr.enabled) m_XRSystem.UpdateFromCamera(ref overlayCameraData.xr, overlayCameraData); #endif RenderSingleCamera(context, overlayCameraData, anyPostProcessingEnabled); using (new ProfilingScope(null, Profiling.Pipeline.endCameraRendering)) { EndCameraRendering(context, currCamera); } } } } #if ENABLE_VR && ENABLE_XR_MODULE if (baseCameraData.xr.enabled) baseCameraData.cameraTargetDescriptor = originalTargetDesc; } if (xrActive) { CommandBuffer cmd = CommandBufferPool.Get(); using (new ProfilingScope(cmd, Profiling.Pipeline.XR.mirrorView)) { m_XRSystem.RenderMirrorView(cmd, baseCamera); } context.ExecuteCommandBuffer(cmd); context.Submit(); CommandBufferPool.Release(cmd); } m_XRSystem.ReleaseFrame(); #endif } static void UpdateVolumeFramework(Camera camera, UniversalAdditionalCameraData additionalCameraData) { using var profScope = new ProfilingScope(null, ProfilingSampler.Get(URPProfileId.UpdateVolumeFramework)); // We update the volume framework for: // * All cameras in the editor when not in playmode // * scene cameras // * cameras with update mode set to EveryFrame // * cameras with update mode set to UsePipelineSettings and the URP Asset set to EveryFrame bool shouldUpdate = camera.cameraType == CameraType.SceneView; shouldUpdate |= additionalCameraData != null && additionalCameraData.requiresVolumeFrameworkUpdate; #if UNITY_EDITOR shouldUpdate |= Application.isPlaying == false; #endif // When we have volume updates per-frame disabled... if (!shouldUpdate && additionalCameraData) { // Create a local volume stack and cache the state if it's null if (additionalCameraData.volumeStack == null) { camera.UpdateVolumeStack(additionalCameraData); } VolumeManager.instance.stack = additionalCameraData.volumeStack; return; } // When we want to update the volumes every frame... // We destroy the volumeStack in the additional camera data, if present, to make sure // it gets recreated and initialized if the update mode gets later changed to ViaScripting... if (additionalCameraData && additionalCameraData.volumeStack != null) { camera.DestroyVolumeStack(additionalCameraData); } // Get the mask + trigger and update the stack camera.GetVolumeLayerMaskAndTrigger(additionalCameraData, out LayerMask layerMask, out Transform trigger); VolumeManager.instance.ResetMainStack(); VolumeManager.instance.Update(trigger, layerMask); } static bool CheckPostProcessForDepth(in CameraData cameraData) { if (!cameraData.postProcessEnabled) return false; if (cameraData.antialiasing == AntialiasingMode.SubpixelMorphologicalAntiAliasing) return true; var stack = VolumeManager.instance.stack; if (stack.GetComponent().IsActive()) return true; if (stack.GetComponent().IsActive()) return true; return false; } static void SetSupportedRenderingFeatures() { #if UNITY_EDITOR SupportedRenderingFeatures.active = new SupportedRenderingFeatures() { reflectionProbeModes = SupportedRenderingFeatures.ReflectionProbeModes.None, defaultMixedLightingModes = SupportedRenderingFeatures.LightmapMixedBakeModes.Subtractive, mixedLightingModes = SupportedRenderingFeatures.LightmapMixedBakeModes.Subtractive | SupportedRenderingFeatures.LightmapMixedBakeModes.IndirectOnly | SupportedRenderingFeatures.LightmapMixedBakeModes.Shadowmask, lightmapBakeTypes = LightmapBakeType.Baked | LightmapBakeType.Mixed | LightmapBakeType.Realtime, lightmapsModes = LightmapsMode.CombinedDirectional | LightmapsMode.NonDirectional, lightProbeProxyVolumes = false, motionVectors = false, receiveShadows = false, reflectionProbes = false, reflectionProbesBlendDistance = true, particleSystemInstancing = true }; SceneViewDrawMode.SetupDrawMode(); #endif } static void InitializeCameraData(Camera camera, UniversalAdditionalCameraData additionalCameraData, bool resolveFinalTarget, out CameraData cameraData) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.initializeCameraData); cameraData = new CameraData(); InitializeStackedCameraData(camera, additionalCameraData, ref cameraData); InitializeAdditionalCameraData(camera, additionalCameraData, resolveFinalTarget, ref cameraData); /////////////////////////////////////////////////////////////////// // Descriptor settings / /////////////////////////////////////////////////////////////////// var renderer = additionalCameraData?.scriptableRenderer; bool rendererSupportsMSAA = renderer != null && renderer.supportedRenderingFeatures.msaa; int msaaSamples = 1; if (camera.allowMSAA && asset.msaaSampleCount > 1 && rendererSupportsMSAA) msaaSamples = (camera.targetTexture != null) ? camera.targetTexture.antiAliasing : asset.msaaSampleCount; #if ENABLE_VR && ENABLE_XR_MODULE // Use XR's MSAA if camera is XR camera. XR MSAA needs special handle here because it is not per Camera. // Multiple cameras could render into the same XR display and they should share the same MSAA level. if (cameraData.xrRendering && rendererSupportsMSAA) msaaSamples = XRSystem.GetMSAALevel(); #endif bool needsAlphaChannel = Graphics.preserveFramebufferAlpha; cameraData.cameraTargetDescriptor = CreateRenderTextureDescriptor(camera, cameraData.renderScale, cameraData.isHdrEnabled, msaaSamples, needsAlphaChannel, cameraData.requiresOpaqueTexture); } /// /// Initialize camera data settings common for all cameras in the stack. Overlay cameras will inherit /// settings from base camera. /// /// Base camera to inherit settings from. /// Component that contains additional base camera data. /// Camera data to initialize setttings. static void InitializeStackedCameraData(Camera baseCamera, UniversalAdditionalCameraData baseAdditionalCameraData, ref CameraData cameraData) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.initializeStackedCameraData); var settings = asset; cameraData.targetTexture = baseCamera.targetTexture; cameraData.cameraType = baseCamera.cameraType; bool isSceneViewCamera = cameraData.isSceneViewCamera; /////////////////////////////////////////////////////////////////// // Environment and Post-processing settings / /////////////////////////////////////////////////////////////////// if (isSceneViewCamera) { cameraData.volumeLayerMask = 1; // "Default" cameraData.volumeTrigger = null; cameraData.isStopNaNEnabled = false; cameraData.isDitheringEnabled = false; cameraData.antialiasing = AntialiasingMode.None; cameraData.antialiasingQuality = AntialiasingQuality.High; #if ENABLE_VR && ENABLE_XR_MODULE cameraData.xrRendering = false; #endif } else if (baseAdditionalCameraData != null) { cameraData.volumeLayerMask = baseAdditionalCameraData.volumeLayerMask; cameraData.volumeTrigger = baseAdditionalCameraData.volumeTrigger == null ? baseCamera.transform : baseAdditionalCameraData.volumeTrigger; cameraData.isStopNaNEnabled = baseAdditionalCameraData.stopNaN && SystemInfo.graphicsShaderLevel >= 35; cameraData.isDitheringEnabled = baseAdditionalCameraData.dithering; cameraData.antialiasing = baseAdditionalCameraData.antialiasing; cameraData.antialiasingQuality = baseAdditionalCameraData.antialiasingQuality; #if ENABLE_VR && ENABLE_XR_MODULE cameraData.xrRendering = baseAdditionalCameraData.allowXRRendering && m_XRSystem.RefreshXrSdk(); #endif } else { cameraData.volumeLayerMask = 1; // "Default" cameraData.volumeTrigger = null; cameraData.isStopNaNEnabled = false; cameraData.isDitheringEnabled = false; cameraData.antialiasing = AntialiasingMode.None; cameraData.antialiasingQuality = AntialiasingQuality.High; #if ENABLE_VR && ENABLE_XR_MODULE cameraData.xrRendering = m_XRSystem.RefreshXrSdk(); #endif } /////////////////////////////////////////////////////////////////// // Settings that control output of the camera / /////////////////////////////////////////////////////////////////// cameraData.isHdrEnabled = baseCamera.allowHDR && settings.supportsHDR; Rect cameraRect = baseCamera.rect; cameraData.pixelRect = baseCamera.pixelRect; cameraData.pixelWidth = baseCamera.pixelWidth; cameraData.pixelHeight = baseCamera.pixelHeight; cameraData.aspectRatio = (float)cameraData.pixelWidth / (float)cameraData.pixelHeight; cameraData.isDefaultViewport = (!(Math.Abs(cameraRect.x) > 0.0f || Math.Abs(cameraRect.y) > 0.0f || Math.Abs(cameraRect.width) < 1.0f || Math.Abs(cameraRect.height) < 1.0f)); // Discard variations lesser than kRenderScaleThreshold. // Scale is only enabled for gameview. const float kRenderScaleThreshold = 0.05f; cameraData.renderScale = (Mathf.Abs(1.0f - settings.renderScale) < kRenderScaleThreshold) ? 1.0f : settings.renderScale; // Convert the upscaling filter selection from the pipeline asset into an image upscaling filter cameraData.upscalingFilter = ResolveUpscalingFilterSelection(new Vector2(cameraData.pixelWidth, cameraData.pixelHeight), cameraData.renderScale, settings.upscalingFilter); if (cameraData.renderScale > 1.0f) { cameraData.imageScalingMode = ImageScalingMode.Downscaling; } else if ((cameraData.renderScale < 1.0f) || (cameraData.upscalingFilter == ImageUpscalingFilter.FSR)) { // When FSR is enabled, we still consider 100% render scale an upscaling operation. // This allows us to run the FSR shader passes all the time since they improve visual quality even at 100% scale. cameraData.imageScalingMode = ImageScalingMode.Upscaling; } else { cameraData.imageScalingMode = ImageScalingMode.None; } cameraData.fsrOverrideSharpness = settings.fsrOverrideSharpness; cameraData.fsrSharpness = settings.fsrSharpness; #if ENABLE_VR && ENABLE_XR_MODULE cameraData.xr = m_XRSystem.emptyPass; XRSystem.UpdateRenderScale(cameraData.renderScale); #else cameraData.xr = XRPass.emptyPass; #endif var commonOpaqueFlags = SortingCriteria.CommonOpaque; var noFrontToBackOpaqueFlags = SortingCriteria.SortingLayer | SortingCriteria.RenderQueue | SortingCriteria.OptimizeStateChanges | SortingCriteria.CanvasOrder; bool hasHSRGPU = SystemInfo.hasHiddenSurfaceRemovalOnGPU; bool canSkipFrontToBackSorting = (baseCamera.opaqueSortMode == OpaqueSortMode.Default && hasHSRGPU) || baseCamera.opaqueSortMode == OpaqueSortMode.NoDistanceSort; cameraData.defaultOpaqueSortFlags = canSkipFrontToBackSorting ? noFrontToBackOpaqueFlags : commonOpaqueFlags; cameraData.captureActions = CameraCaptureBridge.GetCaptureActions(baseCamera); } /// /// Initialize settings that can be different for each camera in the stack. /// /// Camera to initialize settings from. /// Additional camera data component to initialize settings from. /// True if this is the last camera in the stack and rendering should resolve to camera target. /// Settings to be initilized. static void InitializeAdditionalCameraData(Camera camera, UniversalAdditionalCameraData additionalCameraData, bool resolveFinalTarget, ref CameraData cameraData) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.initializeAdditionalCameraData); var settings = asset; cameraData.camera = camera; bool anyShadowsEnabled = settings.supportsMainLightShadows || settings.supportsAdditionalLightShadows; cameraData.maxShadowDistance = Mathf.Min(settings.shadowDistance, camera.farClipPlane); cameraData.maxShadowDistance = (anyShadowsEnabled && cameraData.maxShadowDistance >= camera.nearClipPlane) ? cameraData.maxShadowDistance : 0.0f; // Getting the background color from preferences to add to the preview camera #if UNITY_EDITOR if (cameraData.camera.cameraType == CameraType.Preview) { camera.backgroundColor = CoreRenderPipelinePreferences.previewBackgroundColor; } #endif bool isSceneViewCamera = cameraData.isSceneViewCamera; if (isSceneViewCamera) { cameraData.renderType = CameraRenderType.Base; cameraData.clearDepth = true; cameraData.postProcessEnabled = CoreUtils.ArePostProcessesEnabled(camera); cameraData.requiresDepthTexture = settings.supportsCameraDepthTexture; cameraData.requiresOpaqueTexture = settings.supportsCameraOpaqueTexture; cameraData.renderer = asset.scriptableRenderer; } else if (additionalCameraData != null) { cameraData.renderType = additionalCameraData.renderType; cameraData.clearDepth = (additionalCameraData.renderType != CameraRenderType.Base) ? additionalCameraData.clearDepth : true; cameraData.postProcessEnabled = additionalCameraData.renderPostProcessing; cameraData.maxShadowDistance = (additionalCameraData.renderShadows) ? cameraData.maxShadowDistance : 0.0f; cameraData.requiresDepthTexture = additionalCameraData.requiresDepthTexture; cameraData.requiresOpaqueTexture = additionalCameraData.requiresColorTexture; cameraData.renderer = additionalCameraData.scriptableRenderer; } else { cameraData.renderType = CameraRenderType.Base; cameraData.clearDepth = true; cameraData.postProcessEnabled = false; cameraData.requiresDepthTexture = settings.supportsCameraDepthTexture; cameraData.requiresOpaqueTexture = settings.supportsCameraOpaqueTexture; cameraData.renderer = asset.scriptableRenderer; } // Disables post if GLes2 cameraData.postProcessEnabled &= SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2; cameraData.requiresDepthTexture |= isSceneViewCamera; cameraData.postProcessingRequiresDepthTexture |= CheckPostProcessForDepth(cameraData); cameraData.resolveFinalTarget = resolveFinalTarget; // Disable depth and color copy. We should add it in the renderer instead to avoid performance pitfalls // of camera stacking breaking render pass execution implicitly. bool isOverlayCamera = (cameraData.renderType == CameraRenderType.Overlay); if (isOverlayCamera) { cameraData.requiresDepthTexture = false; cameraData.requiresOpaqueTexture = false; cameraData.postProcessingRequiresDepthTexture = false; } Matrix4x4 projectionMatrix = camera.projectionMatrix; // Overlay cameras inherit viewport from base. // If the viewport is different between them we might need to patch the projection to adjust aspect ratio // matrix to prevent squishing when rendering objects in overlay cameras. if (isOverlayCamera && !camera.orthographic && cameraData.pixelRect != camera.pixelRect) { // m00 = (cotangent / aspect), therefore m00 * aspect gives us cotangent. float cotangent = camera.projectionMatrix.m00 * camera.aspect; // Get new m00 by dividing by base camera aspectRatio. float newCotangent = cotangent / cameraData.aspectRatio; projectionMatrix.m00 = newCotangent; } cameraData.SetViewAndProjectionMatrix(camera.worldToCameraMatrix, projectionMatrix); cameraData.worldSpaceCameraPos = camera.transform.position; } static void InitializeRenderingData(UniversalRenderPipelineAsset settings, ref CameraData cameraData, ref CullingResults cullResults, bool anyPostProcessingEnabled, out RenderingData renderingData) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.initializeRenderingData); var visibleLights = cullResults.visibleLights; int mainLightIndex = GetMainLightIndex(settings, visibleLights); bool mainLightCastShadows = false; bool additionalLightsCastShadows = false; if (cameraData.maxShadowDistance > 0.0f) { mainLightCastShadows = (mainLightIndex != -1 && visibleLights[mainLightIndex].light != null && visibleLights[mainLightIndex].light.shadows != LightShadows.None); // If additional lights are shaded per-pixel they cannot cast shadows if (settings.additionalLightsRenderingMode == LightRenderingMode.PerPixel) { for (int i = 0; i < visibleLights.Length; ++i) { if (i == mainLightIndex) continue; Light light = visibleLights[i].light; // UniversalRP doesn't support additional directional light shadows yet if ((visibleLights[i].lightType == LightType.Spot || visibleLights[i].lightType == LightType.Point) && light != null && light.shadows != LightShadows.None) { additionalLightsCastShadows = true; break; } } } } renderingData.cullResults = cullResults; renderingData.cameraData = cameraData; InitializeLightData(settings, visibleLights, mainLightIndex, out renderingData.lightData); InitializeShadowData(settings, visibleLights, mainLightCastShadows, additionalLightsCastShadows && !renderingData.lightData.shadeAdditionalLightsPerVertex, out renderingData.shadowData); InitializePostProcessingData(settings, out renderingData.postProcessingData); renderingData.supportsDynamicBatching = settings.supportsDynamicBatching; renderingData.perObjectData = GetPerObjectLightFlags(renderingData.lightData.additionalLightsCount); renderingData.postProcessingEnabled = anyPostProcessingEnabled; CheckAndApplyDebugSettings(ref renderingData); } static void InitializeShadowData(UniversalRenderPipelineAsset settings, NativeArray visibleLights, bool mainLightCastShadows, bool additionalLightsCastShadows, out ShadowData shadowData) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.initializeShadowData); m_ShadowBiasData.Clear(); m_ShadowResolutionData.Clear(); for (int i = 0; i < visibleLights.Length; ++i) { Light light = visibleLights[i].light; UniversalAdditionalLightData data = null; if (light != null) { light.gameObject.TryGetComponent(out data); } if (data && !data.usePipelineSettings) m_ShadowBiasData.Add(new Vector4(light.shadowBias, light.shadowNormalBias, 0.0f, 0.0f)); else m_ShadowBiasData.Add(new Vector4(settings.shadowDepthBias, settings.shadowNormalBias, 0.0f, 0.0f)); if (data && (data.additionalLightsShadowResolutionTier == UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierCustom)) { m_ShadowResolutionData.Add((int)light.shadowResolution); // native code does not clamp light.shadowResolution between -1 and 3 } else if (data && (data.additionalLightsShadowResolutionTier != UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierCustom)) { int resolutionTier = Mathf.Clamp(data.additionalLightsShadowResolutionTier, UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierLow, UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierHigh); m_ShadowResolutionData.Add(settings.GetAdditionalLightsShadowResolution(resolutionTier)); } else { m_ShadowResolutionData.Add(settings.GetAdditionalLightsShadowResolution(UniversalAdditionalLightData.AdditionalLightsShadowDefaultResolutionTier)); } } shadowData.bias = m_ShadowBiasData; shadowData.resolution = m_ShadowResolutionData; shadowData.supportsMainLightShadows = SystemInfo.supportsShadows && settings.supportsMainLightShadows && mainLightCastShadows; // We no longer use screen space shadows in URP. // This change allows us to have particles & transparent objects receive shadows. #pragma warning disable 0618 shadowData.requiresScreenSpaceShadowResolve = false; #pragma warning restore 0618 shadowData.mainLightShadowCascadesCount = settings.shadowCascadeCount; shadowData.mainLightShadowmapWidth = settings.mainLightShadowmapResolution; shadowData.mainLightShadowmapHeight = settings.mainLightShadowmapResolution; switch (shadowData.mainLightShadowCascadesCount) { case 1: shadowData.mainLightShadowCascadesSplit = new Vector3(1.0f, 0.0f, 0.0f); break; case 2: shadowData.mainLightShadowCascadesSplit = new Vector3(settings.cascade2Split, 1.0f, 0.0f); break; case 3: shadowData.mainLightShadowCascadesSplit = new Vector3(settings.cascade3Split.x, settings.cascade3Split.y, 0.0f); break; default: shadowData.mainLightShadowCascadesSplit = settings.cascade4Split; break; } shadowData.mainLightShadowCascadeBorder = settings.cascadeBorder; shadowData.supportsAdditionalLightShadows = SystemInfo.supportsShadows && settings.supportsAdditionalLightShadows && additionalLightsCastShadows; shadowData.additionalLightsShadowmapWidth = shadowData.additionalLightsShadowmapHeight = settings.additionalLightsShadowmapResolution; shadowData.supportsSoftShadows = settings.supportsSoftShadows && (shadowData.supportsMainLightShadows || shadowData.supportsAdditionalLightShadows); shadowData.shadowmapDepthBufferBits = 16; // This will be setup in AdditionalLightsShadowCasterPass. shadowData.isKeywordAdditionalLightShadowsEnabled = false; shadowData.isKeywordSoftShadowsEnabled = false; } static void InitializePostProcessingData(UniversalRenderPipelineAsset settings, out PostProcessingData postProcessingData) { postProcessingData.gradingMode = settings.supportsHDR ? settings.colorGradingMode : ColorGradingMode.LowDynamicRange; postProcessingData.lutSize = settings.colorGradingLutSize; postProcessingData.useFastSRGBLinearConversion = settings.useFastSRGBLinearConversion; } static void InitializeLightData(UniversalRenderPipelineAsset settings, NativeArray visibleLights, int mainLightIndex, out LightData lightData) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.initializeLightData); int maxPerObjectAdditionalLights = UniversalRenderPipeline.maxPerObjectLights; int maxVisibleAdditionalLights = UniversalRenderPipeline.maxVisibleAdditionalLights; lightData.mainLightIndex = mainLightIndex; if (settings.additionalLightsRenderingMode != LightRenderingMode.Disabled) { lightData.additionalLightsCount = Math.Min((mainLightIndex != -1) ? visibleLights.Length - 1 : visibleLights.Length, maxVisibleAdditionalLights); lightData.maxPerObjectAdditionalLightsCount = Math.Min(settings.maxAdditionalLightsCount, maxPerObjectAdditionalLights); } else { lightData.additionalLightsCount = 0; lightData.maxPerObjectAdditionalLightsCount = 0; } lightData.supportsAdditionalLights = settings.additionalLightsRenderingMode != LightRenderingMode.Disabled; lightData.shadeAdditionalLightsPerVertex = settings.additionalLightsRenderingMode == LightRenderingMode.PerVertex; lightData.visibleLights = visibleLights; lightData.supportsMixedLighting = settings.supportsMixedLighting; lightData.reflectionProbeBlending = settings.reflectionProbeBlending; lightData.reflectionProbeBoxProjection = settings.reflectionProbeBoxProjection; lightData.supportsLightLayers = RenderingUtils.SupportsLightLayers(SystemInfo.graphicsDeviceType) && settings.supportsLightLayers; lightData.originalIndices = new NativeArray(visibleLights.Length, Allocator.Temp); for (var i = 0; i < lightData.originalIndices.Length; i++) { lightData.originalIndices[i] = i; } } static void CleanupLightData(ref LightData lightData) { lightData.originalIndices.Dispose(); } static void UpdateCameraStereoMatrices(Camera camera, XRPass xr) { #if ENABLE_VR && ENABLE_XR_MODULE if (xr.enabled) { if (xr.singlePassEnabled) { for (int i = 0; i < Mathf.Min(2, xr.viewCount); i++) { camera.SetStereoProjectionMatrix((Camera.StereoscopicEye)i, xr.GetProjMatrix(i)); camera.SetStereoViewMatrix((Camera.StereoscopicEye)i, xr.GetViewMatrix(i)); } } else { camera.SetStereoProjectionMatrix((Camera.StereoscopicEye)xr.multipassId, xr.GetProjMatrix(0)); camera.SetStereoViewMatrix((Camera.StereoscopicEye)xr.multipassId, xr.GetViewMatrix(0)); } } #endif } static PerObjectData GetPerObjectLightFlags(int additionalLightsCount) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.getPerObjectLightFlags); var configuration = PerObjectData.ReflectionProbes | PerObjectData.Lightmaps | PerObjectData.LightProbe | PerObjectData.LightData | PerObjectData.OcclusionProbe | PerObjectData.ShadowMask; if (additionalLightsCount > 0) { configuration |= PerObjectData.LightData; // In this case we also need per-object indices (unity_LightIndices) if (!RenderingUtils.useStructuredBuffer) configuration |= PerObjectData.LightIndices; } return configuration; } // Main Light is always a directional light static int GetMainLightIndex(UniversalRenderPipelineAsset settings, NativeArray visibleLights) { using var profScope = new ProfilingScope(null, Profiling.Pipeline.getMainLightIndex); int totalVisibleLights = visibleLights.Length; if (totalVisibleLights == 0 || settings.mainLightRenderingMode != LightRenderingMode.PerPixel) return -1; Light sunLight = RenderSettings.sun; int brightestDirectionalLightIndex = -1; float brightestLightIntensity = 0.0f; for (int i = 0; i < totalVisibleLights; ++i) { VisibleLight currVisibleLight = visibleLights[i]; Light currLight = currVisibleLight.light; // Particle system lights have the light property as null. We sort lights so all particles lights // come last. Therefore, if first light is particle light then all lights are particle lights. // In this case we either have no main light or already found it. if (currLight == null) break; if (currVisibleLight.lightType == LightType.Directional) { // Sun source needs be a directional light if (currLight == sunLight) return i; // In case no sun light is present we will return the brightest directional light if (currLight.intensity > brightestLightIntensity) { brightestLightIntensity = currLight.intensity; brightestDirectionalLightIndex = i; } } } return brightestDirectionalLightIndex; } static void SetupPerFrameShaderConstants() { using var profScope = new ProfilingScope(null, Profiling.Pipeline.setupPerFrameShaderConstants); // When glossy reflections are OFF in the shader we set a constant color to use as indirect specular SphericalHarmonicsL2 ambientSH = RenderSettings.ambientProbe; Color linearGlossyEnvColor = new Color(ambientSH[0, 0], ambientSH[1, 0], ambientSH[2, 0]) * RenderSettings.reflectionIntensity; Color glossyEnvColor = CoreUtils.ConvertLinearToActiveColorSpace(linearGlossyEnvColor); Shader.SetGlobalVector(ShaderPropertyId.glossyEnvironmentColor, glossyEnvColor); // Used as fallback cubemap for reflections Shader.SetGlobalVector(ShaderPropertyId.glossyEnvironmentCubeMapHDR, ReflectionProbe.defaultTextureHDRDecodeValues); Shader.SetGlobalTexture(ShaderPropertyId.glossyEnvironmentCubeMap, ReflectionProbe.defaultTexture); // Ambient Shader.SetGlobalVector(ShaderPropertyId.ambientSkyColor, CoreUtils.ConvertSRGBToActiveColorSpace(RenderSettings.ambientSkyColor)); Shader.SetGlobalVector(ShaderPropertyId.ambientEquatorColor, CoreUtils.ConvertSRGBToActiveColorSpace(RenderSettings.ambientEquatorColor)); Shader.SetGlobalVector(ShaderPropertyId.ambientGroundColor, CoreUtils.ConvertSRGBToActiveColorSpace(RenderSettings.ambientGroundColor)); // Used when subtractive mode is selected Shader.SetGlobalVector(ShaderPropertyId.subtractiveShadowColor, CoreUtils.ConvertSRGBToActiveColorSpace(RenderSettings.subtractiveShadowColor)); // Required for 2D Unlit Shadergraph master node as it doesn't currently support hidden properties. Shader.SetGlobalColor(ShaderPropertyId.rendererColor, Color.white); } static void CheckAndApplyDebugSettings(ref RenderingData renderingData) { DebugDisplaySettings debugDisplaySettings = DebugDisplaySettings.Instance; ref CameraData cameraData = ref renderingData.cameraData; if (debugDisplaySettings.AreAnySettingsActive && !cameraData.isPreviewCamera) { DebugDisplaySettingsRendering renderingSettings = debugDisplaySettings.RenderingSettings; int msaaSamples = cameraData.cameraTargetDescriptor.msaaSamples; if (!renderingSettings.enableMsaa) msaaSamples = 1; if (!renderingSettings.enableHDR) cameraData.isHdrEnabled = false; if (!debugDisplaySettings.IsPostProcessingAllowed) cameraData.postProcessEnabled = false; cameraData.cameraTargetDescriptor.graphicsFormat = MakeRenderTextureGraphicsFormat(cameraData.isHdrEnabled, true); cameraData.cameraTargetDescriptor.msaaSamples = msaaSamples; } } /// /// Returns the best supported image upscaling filter based on the provided upscaling filter selection /// /// Size of the final image /// Scale being applied to the final image size /// Upscaling filter selected by the user /// Either the original filter provided, or the best replacement available static ImageUpscalingFilter ResolveUpscalingFilterSelection(Vector2 imageSize, float renderScale, UpscalingFilterSelection selection) { // By default we just use linear filtering since it's the most compatible choice ImageUpscalingFilter filter = ImageUpscalingFilter.Linear; // Fall back to the automatic filter if FSR was selected, but isn't supported on the current platform if ((selection == UpscalingFilterSelection.FSR) && !FSRUtils.IsSupported()) { selection = UpscalingFilterSelection.Auto; } switch (selection) { case UpscalingFilterSelection.Auto: { // The user selected "auto" for their upscaling filter so we should attempt to choose the best filter // for the current situation. When the current resolution and render scale are compatible with integer // scaling we use the point sampling filter. Otherwise we just use the default filter (linear). float pixelScale = (1.0f / renderScale); bool isIntegerScale = Mathf.Approximately((pixelScale - Mathf.Floor(pixelScale)), 0.0f); if (isIntegerScale) { float widthScale = (imageSize.x / pixelScale); float heightScale = (imageSize.y / pixelScale); bool isImageCompatible = (Mathf.Approximately((widthScale - Mathf.Floor(widthScale)), 0.0f) && Mathf.Approximately((heightScale - Mathf.Floor(heightScale)), 0.0f)); if (isImageCompatible) { filter = ImageUpscalingFilter.Point; } } break; } case UpscalingFilterSelection.Linear: { // Do nothing since linear is already the default break; } case UpscalingFilterSelection.Point: { filter = ImageUpscalingFilter.Point; break; } case UpscalingFilterSelection.FSR: { filter = ImageUpscalingFilter.FSR; break; } } return filter; } #if ADAPTIVE_PERFORMANCE_2_0_0_OR_NEWER static void ApplyAdaptivePerformance(ref CameraData cameraData) { var noFrontToBackOpaqueFlags = SortingCriteria.SortingLayer | SortingCriteria.RenderQueue | SortingCriteria.OptimizeStateChanges | SortingCriteria.CanvasOrder; if (AdaptivePerformance.AdaptivePerformanceRenderSettings.SkipFrontToBackSorting) cameraData.defaultOpaqueSortFlags = noFrontToBackOpaqueFlags; var MaxShadowDistanceMultiplier = AdaptivePerformance.AdaptivePerformanceRenderSettings.MaxShadowDistanceMultiplier; cameraData.maxShadowDistance *= MaxShadowDistanceMultiplier; var RenderScaleMultiplier = AdaptivePerformance.AdaptivePerformanceRenderSettings.RenderScaleMultiplier; cameraData.renderScale *= RenderScaleMultiplier; // TODO if (!cameraData.xr.enabled) { cameraData.cameraTargetDescriptor.width = (int)(cameraData.camera.pixelWidth * cameraData.renderScale); cameraData.cameraTargetDescriptor.height = (int)(cameraData.camera.pixelHeight * cameraData.renderScale); } var antialiasingQualityIndex = (int)cameraData.antialiasingQuality - AdaptivePerformance.AdaptivePerformanceRenderSettings.AntiAliasingQualityBias; if (antialiasingQualityIndex < 0) cameraData.antialiasing = AntialiasingMode.None; cameraData.antialiasingQuality = (AntialiasingQuality)Mathf.Clamp(antialiasingQualityIndex, (int)AntialiasingQuality.Low, (int)AntialiasingQuality.High); } static void ApplyAdaptivePerformance(ref RenderingData renderingData) { if (AdaptivePerformance.AdaptivePerformanceRenderSettings.SkipDynamicBatching) renderingData.supportsDynamicBatching = false; var MainLightShadowmapResolutionMultiplier = AdaptivePerformance.AdaptivePerformanceRenderSettings.MainLightShadowmapResolutionMultiplier; renderingData.shadowData.mainLightShadowmapWidth = (int)(renderingData.shadowData.mainLightShadowmapWidth * MainLightShadowmapResolutionMultiplier); renderingData.shadowData.mainLightShadowmapHeight = (int)(renderingData.shadowData.mainLightShadowmapHeight * MainLightShadowmapResolutionMultiplier); var MainLightShadowCascadesCountBias = AdaptivePerformance.AdaptivePerformanceRenderSettings.MainLightShadowCascadesCountBias; renderingData.shadowData.mainLightShadowCascadesCount = Mathf.Clamp(renderingData.shadowData.mainLightShadowCascadesCount - MainLightShadowCascadesCountBias, 0, 4); var shadowQualityIndex = AdaptivePerformance.AdaptivePerformanceRenderSettings.ShadowQualityBias; for (int i = 0; i < shadowQualityIndex; i++) { if (renderingData.shadowData.supportsSoftShadows) { renderingData.shadowData.supportsSoftShadows = false; continue; } if (renderingData.shadowData.supportsAdditionalLightShadows) { renderingData.shadowData.supportsAdditionalLightShadows = false; continue; } if (renderingData.shadowData.supportsMainLightShadows) { renderingData.shadowData.supportsMainLightShadows = false; continue; } break; } if (AdaptivePerformance.AdaptivePerformanceRenderSettings.LutBias >= 1 && renderingData.postProcessingData.lutSize == 32) renderingData.postProcessingData.lutSize = 16; } #endif } }