using System;
using System.Diagnostics;
using System.Collections.Generic;
using Unity.Collections;
using UnityEditor;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Class <c>ScriptableRenderer</c> implements a rendering strategy. It describes how culling and lighting work and
/// the effects supported. A custom scriptable renderer is the lowest level of extensibility of URP. It allows you
/// to implement a fully new rendering strategy at the expense of a lot more complexity and work. However, It's still
/// a lot less work and more maintainable than writing a full-fledged custom render pipeline.
/// If you want to simply extend the existing URP renderers (2D and 3D), using <c>ScriptableRendererFeature</c> should
/// always be considered first.
/// </summary>
/// <remarks>
/// A renderer can be used for all cameras or be overridden on a per-camera basis. It will implement light culling and setup
/// and describe a list of <c>ScriptableRenderPass</c> to execute in a frame. It will also define the RenderGraph to execute.
/// External users can then again extend your scriptable renderer to support more effects with additional <c>ScriptableRendererFeatures</c>.
///
/// The <c>ScriptableRenderer</c> is a run-time object. The resources and asset data for the renderer are serialized in
/// <c>ScriptableRendererData</c> (more specifically a class derived from <c>ScriptableRendererData</c> which contains additional data for your renderer).
///
/// The high-level steps needed to create and use your own scriptable renderer are:
///
/// 1. Create subclasses of <c>ScriptableRenderer</c> and <c>ScriptableRendererData</c> and implement the rendering logic. Key functions to implement here are:
/// <c>ScriptableRenderer.OnRecordRenderGraph</c> which will define the rendergraph to execute when rendering a camera. And <c>ScriptableRendererData.Create</c> to create
/// an instance of your new <c>ScriptableRenderer</c> subclass.
/// 2. Create an asset of your new <c>ScriptableRendererData</c> subclass and assign it to the renderer asset field in the URP asset so it gets picked
/// up at run time.
/// </remarks>
/// <example>
/// You can find a code sample in the URP tests package in the "Graphics/Tests/SRPTests/Packages/com.unity.testing.urp/Scripts/Runtime/CustomRenderPipeline/" folder
/// of the SRP repository.
/// </example>
public abstract partial class ScriptableRenderer : IDisposable
{
private static class Profiling
{
private const string k_Name = nameof(ScriptableRenderer);
public static readonly ProfilingSampler setPerCameraShaderVariables = new ProfilingSampler($"{k_Name}.{nameof(SetPerCameraShaderVariables)}");
public static readonly ProfilingSampler sortRenderPasses = new ProfilingSampler($"Sort Render Passes");
public static readonly ProfilingSampler recordRenderGraph = new ProfilingSampler($"On Record Render Graph");
public static readonly ProfilingSampler setupCamera = new ProfilingSampler($"Setup Camera Properties");
public static readonly ProfilingSampler vfxProcessCamera = new ProfilingSampler($"VFX Process Camera");
public static readonly ProfilingSampler addRenderPasses = new ProfilingSampler($"{k_Name}.{nameof(AddRenderPasses)}");
public static readonly ProfilingSampler clearRenderingState = new ProfilingSampler($"{k_Name}.{nameof(ClearRenderingState)}");
public static readonly ProfilingSampler internalFinishRenderingCommon = new ProfilingSampler($"{k_Name}.{nameof(InternalFinishRenderingCommon)}");
public static readonly ProfilingSampler drawGizmos = new ProfilingSampler("DrawGizmos"); //Todo: update to nameof(method reference) once RG version name is cleaned up
public static readonly ProfilingSampler drawWireOverlay = new ProfilingSampler("DrawWireOverlay"); //Todo: update to nameof(method reference) once RG version name is cleaned up
internal static readonly ProfilingSampler beginXRRendering = new ProfilingSampler($"Begin XR Rendering");
internal static readonly ProfilingSampler endXRRendering = new ProfilingSampler($"End XR Rendering");
internal static readonly ProfilingSampler initRenderGraphFrame = new ProfilingSampler($"Initialize Frame");
internal static readonly ProfilingSampler setEditorTarget = new ProfilingSampler($"Set Editor Target");
}
/// <summary>
/// This setting controls if the camera editor should display the camera stack category.
/// If your scriptable renderer is not supporting stacking this one should return 0.
/// For the UI to show the Camera Stack widget this must at least support CameraRenderType.Base.
/// </summary>
/// <seealso cref="CameraRenderType"/>
/// <returns>The bitmask of the supported camera render types in the renderer's current state.</returns>
public virtual int SupportedCameraStackingTypes()
{
return 0;
}
/// <summary>
/// Check if the given camera render type is supported in the renderer's current state. The default implementation
/// simply checks if the camera type is part of the <see cref="SupportedCameraStackingTypes'"/> bitmask.
/// </summary>
/// <seealso cref="CameraRenderType"/>
/// <param name="cameraRenderType">The camera render type that is checked if supported.</param>
/// <returns>True if the given camera render type is supported in the renderer's current state.</returns>
public bool SupportsCameraStackingType(CameraRenderType cameraRenderType)
{
return (SupportedCameraStackingTypes() & 1 << (int)cameraRenderType) != 0;
}
// NOTE: This is a temporary solution until ScriptableRenderer has a system for partially shared features.
// TAA (and similar) affect the whole pipe. The code is split into two parts in terms of ownership.
// The ScriptableRenderer "shared" code (Camera) and the ScriptableRenderer "specific" code (the ScriptableRenderPasses).
// For example: TAA is enabled and configured from the Camera, which is used by any ScriptableRenderer.
// TAA also jitters the Camera matrix for all ScriptableRenderers.
// However a Renderer might not implement a motion vector pass, which the TAA needs to function correctly.
//
/// <summary>
/// Check if the ScriptableRenderer implements a motion vector pass for temporal techniques.
/// The Camera will check this to enable/disable features and/or apply jitter when required.
///
/// For example, Temporal Anti-aliasing in the Camera settings is enabled only if the ScriptableRenderer can support motion vectors.
/// </summary>
/// <returns>Returns true if the ScriptableRenderer implements a motion vector pass. False otherwise.</returns>
protected internal virtual bool SupportsMotionVectors()
{
return false;
}
/// <summary>
/// Check if the ScriptableRenderer implements a camera opaque pass.
/// </summary>
/// <returns>Returns true if the ScriptableRenderer implements a camera opaque pass. False otherwise.</returns>
protected internal virtual bool SupportsCameraOpaque()
{
return false;
}
/// <summary>
/// Check if the ScriptableRenderer implements a camera normal pass.
/// </summary>
/// <returns>Returns true if the ScriptableRenderer implements a camera normal pass. False otherwise.</returns>
protected internal virtual bool SupportsCameraNormals()
{
return false;
}
/// <summary>
/// Configures the supported features for this renderer. When creating custom renderers
/// for Universal Render Pipeline you can choose to opt-in or out for specific features.
/// </summary>
public class RenderingFeatures
{
/// <summary>
/// This setting controls if the camera editor should display the camera stack category.
/// Renderers that don't support camera stacking will only render cameras of type CameraRenderType.Base
/// </summary>
/// <seealso cref="CameraRenderType"/>
/// <seealso cref="UniversalAdditionalCameraData.cameraStack"/>
[Obsolete("cameraStacking has been deprecated use SupportedCameraRenderTypes() in ScriptableRenderer instead. #from(2022.2) #breakingFrom(2023.1)", true)]
public bool cameraStacking { get; set; } = false;
/// <summary>
/// This setting controls if the Universal Render Pipeline asset should expose the MSAA option.
/// </summary>
public bool msaa { get; set; } = true;
}
/// <summary>
/// The class responsible for providing access to debug view settings to renderers and render passes.
/// </summary>
internal DebugHandler DebugHandler { get; }
/// <summary>
/// The renderer we are currently rendering with, for low-level render control only.
/// <c>current</c> is null outside rendering scope.
/// Similar to https://docs.unity3d.com/ScriptReference/Camera-current.html
/// </summary>
internal static ScriptableRenderer current = null;
internal static void SetCameraMatrices(RasterCommandBuffer cmd, UniversalCameraData cameraData, bool setInverseMatrices, bool isTargetFlipped)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
{
cameraData.PushBuiltinShaderConstantsXR(cmd, isTargetFlipped);
XRSystemUniversal.MarkShaderProperties(cmd, cameraData.xrUniversal, isTargetFlipped);
return;
}
#endif
// NOTE: the URP default main view/projection matrices are the CameraData view/projection matrices.
Matrix4x4 viewMatrix = cameraData.GetViewMatrix();
Matrix4x4 projectionMatrix = cameraData.GetProjectionMatrix(); // Jittered, non-gpu
// TODO: Investigate why SetViewAndProjectionMatrices is causing y-flip / winding order issue
// for now using cmd.SetViewProjecionMatrices
//SetViewAndProjectionMatrices(cmd, viewMatrix, cameraData.GetDeviceProjectionMatrix(), setInverseMatrices);
// Set the default view/projection, note: projectionMatrix will be set as a gpu-projection (gfx api adjusted) for rendering.
cmd.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
if (setInverseMatrices)
{
Matrix4x4 gpuProjectionMatrix = cameraData.GetGPUProjectionMatrix(isTargetFlipped); // TODO: invProjection might NOT match the actual projection (invP*P==I) as the target flip logic has diverging paths.
Matrix4x4 inverseViewMatrix = Matrix4x4.Inverse(viewMatrix);
Matrix4x4 inverseProjectionMatrix = Matrix4x4.Inverse(gpuProjectionMatrix);
Matrix4x4 inverseViewProjection = inverseViewMatrix * inverseProjectionMatrix;
// There's an inconsistency in handedness between unity_matrixV and unity_WorldToCamera
// Unity changes the handedness of unity_WorldToCamera (see Camera::CalculateMatrixShaderProps)
// we will also change it here to avoid breaking existing shaders. (case 1257518)
Matrix4x4 worldToCameraMatrix = Matrix4x4.Scale(new Vector3(1.0f, 1.0f, -1.0f)) * viewMatrix;
Matrix4x4 cameraToWorldMatrix = worldToCameraMatrix.inverse;
cmd.SetGlobalMatrix(ShaderPropertyId.worldToCameraMatrix, worldToCameraMatrix);
cmd.SetGlobalMatrix(ShaderPropertyId.cameraToWorldMatrix, cameraToWorldMatrix);
cmd.SetGlobalMatrix(ShaderPropertyId.inverseViewMatrix, inverseViewMatrix);
cmd.SetGlobalMatrix(ShaderPropertyId.inverseProjectionMatrix, inverseProjectionMatrix);
cmd.SetGlobalMatrix(ShaderPropertyId.inverseViewAndProjectionMatrix, inverseViewProjection);
}
// TODO: Add SetPerCameraClippingPlaneProperties here once we are sure it correctly behaves in overlay camera for some time
}
void SetPerCameraShaderVariables(RasterCommandBuffer cmd, UniversalCameraData cameraData, Vector2Int cameraTargetSizeCopy, bool isTargetFlipped)
{
using var profScope = new ProfilingScope(Profiling.setPerCameraShaderVariables);
Camera camera = cameraData.camera;
float scaledCameraTargetWidth = (float)cameraTargetSizeCopy.x;
float scaledCameraTargetHeight = (float)cameraTargetSizeCopy.y;
float cameraWidth = (float)camera.pixelWidth;
float cameraHeight = (float)camera.pixelHeight;
// Overlay cameras don't have a viewport. Must use the computed/inherited viewport instead of the camera one.
if (cameraData.renderType == CameraRenderType.Overlay)
{
// Overlay cameras inherits viewport from base.
// pixelRect/Width/Height is the viewport in pixels.
cameraWidth = cameraData.pixelWidth;
cameraHeight = cameraData.pixelHeight;
}
// Use eye texture's width and height as screen params when XR is enabled
if (cameraData.xr.enabled)
{
cameraWidth = (float)cameraTargetSizeCopy.x;
cameraHeight = (float)cameraTargetSizeCopy.y;
useRenderPassEnabled = false;
// Multi-pass needs to set unity_StereoEyeIndex builtin param for skybox-panoramic.shader to work correctly (UUM-120719)
if (!cameraData.xr.singlePassEnabled)
cmd.SetGlobalVector(XRBuiltinShaderConstants.unity_StereoEyeIndex, new Vector4(cameraData.xr.multipassId, 0, 0, 0));
}
if (camera.allowDynamicResolution)
{
scaledCameraTargetWidth *= ScalableBufferManager.widthScaleFactor;
scaledCameraTargetHeight *= ScalableBufferManager.heightScaleFactor;
}
float near = camera.nearClipPlane;
float far = camera.farClipPlane;
float invNear = Mathf.Approximately(near, 0.0f) ? 0.0f : 1.0f / near;
float invFar = Mathf.Approximately(far, 0.0f) ? 0.0f : 1.0f / far;
float isOrthographic = camera.orthographic ? 1.0f : 0.0f;
// From http://www.humus.name/temp/Linearize%20depth.txt
// But as depth component textures on OpenGL always return in 0..1 range (as in D3D), we have to use
// the same constants for both D3D and OpenGL here.
// OpenGL would be this:
// zc0 = (1.0 - far / near) / 2.0;
// zc1 = (1.0 + far / near) / 2.0;
// D3D is this:
float zc0 = 1.0f - far * invNear;
float zc1 = far * invNear;
Vector4 zBufferParams = new Vector4(zc0, zc1, zc0 * invFar, zc1 * invFar);
if (SystemInfo.usesReversedZBuffer)
{
zBufferParams.y += zBufferParams.x;
zBufferParams.x = -zBufferParams.x;
zBufferParams.w += zBufferParams.z;
zBufferParams.z = -zBufferParams.z;
}
// Projection flip sign logic is very deep in GfxDevice::SetInvertProjectionMatrix
// This setup is tailored especially for overlay camera game view
// For other scenarios this will be overwritten correctly by SetupCameraProperties
if (cameraData.renderType == CameraRenderType.Overlay)
{
float projectionFlipSign = isTargetFlipped ? -1.0f : 1.0f;
Vector4 projectionParams = new Vector4(projectionFlipSign, near, far, 1.0f * invFar);
cmd.SetGlobalVector(ShaderPropertyId.projectionParams, projectionParams);
}
Vector4 orthoParams = new Vector4(camera.orthographicSize * cameraData.aspectRatio, camera.orthographicSize, 0.0f, isOrthographic);
// Camera and Screen variables as described in https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
cmd.SetGlobalVector(ShaderPropertyId.worldSpaceCameraPos, cameraData.worldSpaceCameraPos);
cmd.SetGlobalVector(ShaderPropertyId.screenParams, new Vector4(cameraWidth, cameraHeight, 1.0f + 1.0f / cameraWidth, 1.0f + 1.0f / cameraHeight));
cmd.SetGlobalVector(ShaderPropertyId.scaledScreenParams, new Vector4(scaledCameraTargetWidth, scaledCameraTargetHeight, 1.0f + 1.0f / scaledCameraTargetWidth, 1.0f + 1.0f / scaledCameraTargetHeight));
cmd.SetGlobalVector(ShaderPropertyId.zBufferParams, zBufferParams);
cmd.SetGlobalVector(ShaderPropertyId.orthoParams, orthoParams);
cmd.SetGlobalVector(ShaderPropertyId.screenSize, new Vector4(scaledCameraTargetWidth, scaledCameraTargetHeight, 1.0f / scaledCameraTargetWidth, 1.0f / scaledCameraTargetHeight));
cmd.SetKeyword(ShaderGlobalKeywords.SCREEN_COORD_OVERRIDE, cameraData.useScreenCoordOverride);
cmd.SetGlobalVector(ShaderPropertyId.screenSizeOverride, cameraData.screenSizeOverride);
cmd.SetGlobalVector(ShaderPropertyId.screenCoordScaleBias, cameraData.screenCoordScaleBias);
// { w / RTHandle.maxWidth, h / RTHandle.maxHeight } : xy = currFrame, zw = prevFrame
// TODO(@sandy-carter) set to RTHandles.rtHandleProperties.rtHandleScale once dynamic scaling is set up
cmd.SetGlobalVector(ShaderPropertyId.rtHandleScale, Vector4.one);
// Calculate a bias value which corrects the mip lod selection logic when image scaling is active.
// We clamp this value to 0.0 or less to make sure we don't end up reducing image detail in the downsampling case.
float mipBias = Math.Min((float)-Math.Log(cameraWidth / scaledCameraTargetWidth, 2.0f), 0.0f);
// Temporal Anti-aliasing can use negative mip bias to increase texture sharpness and new information for the jitter.
float taaMipBias = Math.Min(cameraData.taaSettings.mipBias, 0.0f);
mipBias = Math.Min(mipBias, taaMipBias);
cmd.SetGlobalVector(ShaderPropertyId.globalMipBias, new Vector2(mipBias, Mathf.Pow(2.0f, mipBias)));
//Set per camera matrices.
SetCameraMatrices(cmd, cameraData, true, isTargetFlipped);
}
/// <summary>
/// Set the Camera billboard properties.
/// </summary>
/// <param name="cmd">CommandBuffer to submit data to GPU.</param>
/// <param name="cameraData">CameraData containing camera matrices information.</param>
void SetPerCameraBillboardProperties(RasterCommandBuffer cmd, UniversalCameraData cameraData)
{
Matrix4x4 worldToCameraMatrix = cameraData.GetViewMatrix();
Vector3 cameraPos = cameraData.worldSpaceCameraPos;
cmd.SetKeyword(ShaderGlobalKeywords.BillboardFaceCameraPos, QualitySettings.billboardsFaceCameraPosition);
Vector3 billboardTangent;
Vector3 billboardNormal;
float cameraXZAngle;
CalculateBillboardProperties(worldToCameraMatrix, out billboardTangent, out billboardNormal, out cameraXZAngle);
cmd.SetGlobalVector(ShaderPropertyId.billboardNormal, new Vector4(billboardNormal.x, billboardNormal.y, billboardNormal.z, 0.0f));
cmd.SetGlobalVector(ShaderPropertyId.billboardTangent, new Vector4(billboardTangent.x, billboardTangent.y, billboardTangent.z, 0.0f));
cmd.SetGlobalVector(ShaderPropertyId.billboardCameraParams, new Vector4(cameraPos.x, cameraPos.y, cameraPos.z, cameraXZAngle));
}
private static void CalculateBillboardProperties(
in Matrix4x4 worldToCameraMatrix,
out Vector3 billboardTangent,
out Vector3 billboardNormal,
out float cameraXZAngle)
{
Matrix4x4 cameraToWorldMatrix = worldToCameraMatrix;
cameraToWorldMatrix = cameraToWorldMatrix.transpose;
Vector3 cameraToWorldMatrixAxisX = new Vector3(cameraToWorldMatrix.m00, cameraToWorldMatrix.m10, cameraToWorldMatrix.m20);
Vector3 cameraToWorldMatrixAxisY = new Vector3(cameraToWorldMatrix.m01, cameraToWorldMatrix.m11, cameraToWorldMatrix.m21);
Vector3 cameraToWorldMatrixAxisZ = new Vector3(cameraToWorldMatrix.m02, cameraToWorldMatrix.m12, cameraToWorldMatrix.m22);
Vector3 front = cameraToWorldMatrixAxisZ;
Vector3 worldUp = Vector3.up;
Vector3 cross = Vector3.Cross(front, worldUp);
billboardTangent = !Mathf.Approximately(cross.sqrMagnitude, 0.0f)
? cross.normalized
: cameraToWorldMatrixAxisX;
billboardNormal = Vector3.Cross(worldUp, billboardTangent);
billboardNormal = !Mathf.Approximately(billboardNormal.sqrMagnitude, 0.0f)
? billboardNormal.normalized
: cameraToWorldMatrixAxisY;
// SpeedTree generates billboards starting from looking towards X- and rotates counter clock-wisely
Vector3 worldRight = new Vector3(0, 0, 1);
// signed angle is calculated on X-Z plane
float s = worldRight.x * billboardTangent.z - worldRight.z * billboardTangent.x;
float c = worldRight.x * billboardTangent.x + worldRight.z * billboardTangent.z;
cameraXZAngle = Mathf.Atan2(s, c);
// convert to [0,2PI)
if (cameraXZAngle < 0)
cameraXZAngle += 2 * Mathf.PI;
}
private void SetPerCameraClippingPlaneProperties(RasterCommandBuffer cmd, in UniversalCameraData cameraData, bool isTargetFlipped)
{
Matrix4x4 projectionMatrix = cameraData.GetGPUProjectionMatrix(isTargetFlipped);
Matrix4x4 viewMatrix = cameraData.GetViewMatrix();
Matrix4x4 viewProj = CoreMatrixUtils.MultiplyProjectionMatrix(projectionMatrix, viewMatrix, cameraData.camera.orthographic);
Plane[] planes = s_Planes;
GeometryUtility.CalculateFrustumPlanes(viewProj, planes);
Vector4[] cameraWorldClipPlanes = s_VectorPlanes;
for (int i = 0; i < planes.Length; ++i)
cameraWorldClipPlanes[i] = new Vector4(planes[i].normal.x, planes[i].normal.y, planes[i].normal.z, planes[i].distance);
cmd.SetGlobalVectorArray(ShaderPropertyId.cameraWorldClipPlanes, cameraWorldClipPlanes);
}
/// <summary>
/// Set shader time variables as described in https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
/// </summary>
/// <param name="cmd">CommandBuffer to submit data to GPU.</param>
/// <param name="time">Time.</param>
/// <param name="deltaTime">Delta time.</param>
/// <param name="smoothDeltaTime">Smooth delta time.</param>
static void SetShaderTimeValues(IBaseCommandBuffer cmd, float time, float deltaTime, float smoothDeltaTime)
{
float timeEights = time / 8f;
float timeFourth = time / 4f;
float timeHalf = time / 2f;
float lastTime = time - ShaderUtils.PersistentDeltaTime;
// Time values
Vector4 timeVector = time * new Vector4(1f / 20f, 1f, 2f, 3f);
Vector4 sinTimeVector = new Vector4(Mathf.Sin(timeEights), Mathf.Sin(timeFourth), Mathf.Sin(timeHalf), Mathf.Sin(time));
Vector4 cosTimeVector = new Vector4(Mathf.Cos(timeEights), Mathf.Cos(timeFourth), Mathf.Cos(timeHalf), Mathf.Cos(time));
Vector4 deltaTimeVector = new Vector4(deltaTime, 1f / deltaTime, smoothDeltaTime, 1f / smoothDeltaTime);
Vector4 timeParametersVector = new Vector4(time, Mathf.Sin(time), Mathf.Cos(time), 0.0f);
Vector4 lastTimeParametersVector = new Vector4(lastTime, Mathf.Sin(lastTime), Mathf.Cos(lastTime), 0.0f);
cmd.SetGlobalVector(ShaderPropertyId.time, timeVector);
cmd.SetGlobalVector(ShaderPropertyId.sinTime, sinTimeVector);
cmd.SetGlobalVector(ShaderPropertyId.cosTime, cosTimeVector);
cmd.SetGlobalVector(ShaderPropertyId.deltaTime, deltaTimeVector);
cmd.SetGlobalVector(ShaderPropertyId.timeParameters, timeParametersVector);
cmd.SetGlobalVector(ShaderPropertyId.lastTimeParameters, lastTimeParametersVector);
}
/// <summary>
/// Returns a list of renderer features added to this renderer.
/// </summary>
/// <seealso cref="ScriptableRendererFeature"/>
protected List<ScriptableRendererFeature> rendererFeatures
{
get => m_RendererFeatures;
}
/// <summary>
/// Returns a list of render passes scheduled to be executed by this renderer.
/// </summary>
/// <seealso cref="ScriptableRenderPass"/>
protected List<ScriptableRenderPass> activeRenderPassQueue
{
get => m_ActiveRenderPassQueue;
}
/// <summary>
/// Supported rendering features by this renderer. The scriptable renderer framework will use the returned information
/// to adjust things like inspectors, etc.
/// </summary>
/// <seealso cref="SupportedRenderingFeatures"/>
public RenderingFeatures supportedRenderingFeatures { get; set; } = new RenderingFeatures();
/// <summary>
/// List of unsupported Graphics APIs for this renderer.The scriptable renderer framework will use the returned information
/// to adjust things like inspectors, etc.
/// </summary>
/// <seealso cref="GraphicsDeviceType"/>
public GraphicsDeviceType[] unsupportedGraphicsDeviceTypes { get; set; } = new GraphicsDeviceType[0];
List<ScriptableRenderPass> m_ActiveRenderPassQueue = new List<ScriptableRenderPass>(32);
List<ScriptableRendererFeature> m_RendererFeatures = new List<ScriptableRendererFeature>(10);
// The pipeline can only guarantee the camera target texture are valid when the pipeline is executing.
// Trying to access the camera target before or after might be that the pipeline texture have already been disposed.
bool m_IsPipelineExecuting = false;
internal bool useRenderPassEnabled = false;
ContextContainer m_frameData = new();
internal ContextContainer frameData => m_frameData;
private static Plane[] s_Planes = new Plane[6];
private static Vector4[] s_VectorPlanes = new Vector4[6];
/// <summary>
/// In URP RenderGraph (likely not in Compatibility Mode), this returns if the pipeline will actually perform depth priming.
/// Depth priming is done with a prepass to the activeCameraDepth.
/// Even when the settings on the URP asset requests depth priming the pipeline can decide not to do it (or vice versa).
/// </summary>
internal bool useDepthPriming { get; set; } = false;
internal bool stripShadowsOffVariants { get; set; } = false;
internal bool stripAdditionalLightOffVariants { get; set; } = false;
/// <summary>
/// Creates a new <c>ScriptableRenderer</c> instance.
/// </summary>
/// <param name="data">The <c>ScriptableRendererData</c> data to initialize the renderer.</param>
/// <seealso cref="ScriptableRendererData"/>
public ScriptableRenderer(ScriptableRendererData data)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
DebugHandler = new DebugHandler();
#endif
foreach (var feature in data.rendererFeatures)
{
if (feature == null)
continue;
feature.Create();
m_RendererFeatures.Add(feature);
}
useRenderPassEnabled = data.useNativeRenderPass;
m_ActiveRenderPassQueue.Clear();
}
/// <summary>
/// Disposable pattern implementation.
/// Cleans up resources used by the renderer.
/// </summary>
public void Dispose()
{
// Dispose all renderer features...
for (int i = 0; i < m_RendererFeatures.Count; ++i)
{
if (rendererFeatures[i] == null)
continue;
try
{
// Guard the renderer feature Dispose() call so if it raises any exception,
// it doesn't leave the renderer in a partially destructed state.
rendererFeatures[i].Dispose();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Called by Dispose().
/// Override this function to clean up resources in your renderer.
/// Be sure to call this base dispose in your overridden function to free resources allocated by the base.
/// </summary>
/// <param name="disposing">See the definition of IDisposable.</param>
protected virtual void Dispose(bool disposing)
{
DebugHandler?.Dispose();
}
internal virtual void ReleaseRenderTargets()
{
}
/// <summary>
/// Override this method to configure the culling parameters for the renderer. You can use this to configure if
/// lights should be culled per-object or the maximum shadow distance for example.
/// </summary>
/// <param name="cullingParameters">Use this to change culling parameters used by the render pipeline.</param>
/// <param name="cameraData">Current render state information.</param>
public virtual void SetupCullingParameters(ref ScriptableCullingParameters cullingParameters,
ref CameraData cameraData)
{
}
/// <summary>
/// Called upon finishing rendering the camera stack. You can release any resources created by the renderer here.
/// </summary>
/// <param name="cmd">The command buffer where any work should be recorded on..</param>
public virtual void FinishRendering(CommandBuffer cmd)
{
}
/// <summary>
/// Override this method to initialize anything before starting the recording of the render graph, such as resources.
/// This is the last point where it is ok to call <c>ScriptableRenderer.EnqueuePass</c> as after this function the
/// queue will be sorted for the frame.
/// </summary>
public virtual void OnBeginRenderGraphFrame()
{
}
/// <summary>
/// Override this method to record the RenderGraph passes to be used by the RenderGraph render path.
/// </summary>
/// <param name="renderGraph">The rendergraph to schedule passes on.</param>
/// <param name="context">The render context to use when creating rendering lists or performing culling operations. Ideally, graphics work should be executed through rendergraph so is is not recommended to use <c>ScriptableRenderContext.ExecuteCommandBuffer</c>. </param>
internal virtual void OnRecordRenderGraph(RenderGraph renderGraph, ScriptableRenderContext context)
{
}
/// <summary>
/// Override this method to cleanup things after recording the render graph, such as resources.
/// This executes after the render graph is recorded but before it is compiled and executed.
/// </summary>
public virtual void OnEndRenderGraphFrame()
{
}
private void InitRenderGraphFrame(RenderGraph renderGraph)
{
using (var builder = renderGraph.AddUnsafePass<PassData>(Profiling.initRenderGraphFrame.name, out var passData,
Profiling.initRenderGraphFrame))
{
passData.renderer = this;
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (PassData data, UnsafeGraphContext rgContext) =>
{
UnsafeCommandBuffer cmd = rgContext.cmd;
#if UNITY_EDITOR
float time = Application.isPlaying ? Time.time : Time.realtimeSinceStartup;
#else
float time = Time.time;
#endif
float deltaTime = Time.deltaTime;
float smoothDeltaTime = Time.smoothDeltaTime;
ClearRenderingState(cmd);
SetShaderTimeValues(cmd, time, deltaTime, smoothDeltaTime);
});
}
}
private class VFXProcessCameraPassData
{
internal UniversalRenderingData renderingData;
internal Camera camera;
internal VFX.VFXCameraXRSettings cameraXRSettings;
internal XRPass xrPass;
};
internal void ProcessVFXCameraCommand(RenderGraph renderGraph)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
XRPass xr = cameraData.xr;
using (var builder = renderGraph.AddUnsafePass<VFXProcessCameraPassData>("ProcessVFXCameraCommand", out var passData,
Profiling.vfxProcessCamera))
{
passData.camera = cameraData.camera;
passData.renderingData = renderingData;
passData.cameraXRSettings.viewTotal = xr.enabled ? 2u : 1u;
passData.cameraXRSettings.viewCount = xr.enabled ? (uint)xr.viewCount : 1u;
passData.cameraXRSettings.viewOffset = (uint)xr.multipassId;
passData.xrPass = xr.enabled ? xr : null;
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (VFXProcessCameraPassData data, UnsafeGraphContext context) =>
{
if (data.xrPass != null)
data.xrPass.StartSinglePass(context.cmd);
//Triggers dispatch per camera, all global parameters should have been setup at this stage.
CommandBufferHelpers.VFXManager_ProcessCameraCommand(data.camera, context.cmd, data.cameraXRSettings, data.renderingData.cullResults);
if (data.xrPass != null)
data.xrPass.StopSinglePass(context.cmd);
});
}
}
internal void SetupRenderGraphCameraProperties(RenderGraph renderGraph, TextureHandle target)
{
using (var builder = renderGraph.AddRasterRenderPass<PassData>(Profiling.setupCamera.name, out var passData,
Profiling.setupCamera))
{
passData.renderer = this;
passData.cameraData = frameData.Get<UniversalCameraData>();
passData.cameraTargetSizeCopy = new Vector2Int(passData.cameraData.cameraTargetDescriptor.width, passData.cameraData.cameraTargetDescriptor.height);
passData.target = target;
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
bool yFlipped = SystemInfo.graphicsUVStartsAtTop && RenderingUtils.IsHandleYFlipped(context, in data.target);
// This is still required because of the following reasons:
// - Camera billboard properties.
// - Camera frustum planes: unity_CameraWorldClipPlanes[6]
// - _ProjectionParams.x logic is deep inside GfxDevice
// NOTE: The only reason we have to call this here and not at the beginning (before shadows)
// is because this need to be called for each eye in multi pass VR.
// The side effect is that this will override some shader properties we already setup and we will have to
// reset them.
if (data.cameraData.renderType == CameraRenderType.Base)
{
context.cmd.SetupCameraProperties(data.cameraData.camera);
data.renderer.SetPerCameraShaderVariables(context.cmd, data.cameraData, data.cameraTargetSizeCopy, yFlipped);
}
else
{
// Set new properties
data.renderer.SetPerCameraShaderVariables(context.cmd, data.cameraData, data.cameraTargetSizeCopy, yFlipped);
data.renderer.SetPerCameraClippingPlaneProperties(context.cmd, in data.cameraData, yFlipped);
data.renderer.SetPerCameraBillboardProperties(context.cmd, data.cameraData);
}
#if UNITY_EDITOR
float time = Application.isPlaying ? Time.time : Time.realtimeSinceStartup;
#else
float time = Time.time;
#endif
float deltaTime = Time.deltaTime;
float smoothDeltaTime = Time.smoothDeltaTime;
// Reset shader time variables as they were overridden in SetupCameraProperties. If we don't do it we might have a mismatch between shadows and main rendering
SetShaderTimeValues(context.cmd, time, deltaTime, smoothDeltaTime);
});
}
}
private class DrawGizmosPassData
{
public RendererListHandle gizmoRenderList;
public TextureHandle color;
public TextureHandle depth;
};
/// <summary>
/// TODO RENDERGRAPH
/// </summary>
/// <param name="color"></param>
/// <param name="depth"></param>
/// <param name="gizmoSubset"></param>
/// <param name="renderingData"></param>
internal void DrawRenderGraphGizmos(RenderGraph renderGraph, ContextContainer frameData, TextureHandle color, TextureHandle depth, GizmoSubset gizmoSubset)
{
#if UNITY_EDITOR
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (!Handles.ShouldRenderGizmos() || cameraData.camera.sceneViewFilterMode == Camera.SceneViewFilterMode.ShowFiltered)
return;
// We cannot draw gizmo rendererlists from an raster pass as the gizmo rendering triggers the MonoBehaviour.OnDrawGizmos or MonoBehaviour.OnDrawGizmosSelected callbacks that could run arbitrary graphics code
// like SetRenderTarget, texture and resource loading, ...
using (var builder = renderGraph.AddUnsafePass<DrawGizmosPassData>("Draw Gizmos Pass", out var passData,
Profiling.drawGizmos))
{
builder.UseTexture(color, AccessFlags.Write);
builder.UseTexture(depth, AccessFlags.ReadWrite);
passData.gizmoRenderList = renderGraph.CreateGizmoRendererList(cameraData.camera, gizmoSubset);
passData.color = color;
passData.depth = depth;
builder.UseRendererList(passData.gizmoRenderList);
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (DrawGizmosPassData data, UnsafeGraphContext rgContext) =>
{
using (new ProfilingScope(rgContext.cmd, Profiling.drawGizmos))
{
rgContext.cmd.SetRenderTarget(data.color, data.depth);
rgContext.cmd.DrawRendererList(data.gizmoRenderList);
}
});
}
#endif
}
private class DrawWireOverlayPassData
{
public RendererListHandle wireOverlayList;
};
internal void DrawRenderGraphWireOverlay(RenderGraph renderGraph, ContextContainer frameData, TextureHandle color)
{
#if UNITY_EDITOR
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (!cameraData.isSceneViewCamera)
return;
using (var builder = renderGraph.AddRasterRenderPass<DrawWireOverlayPassData>(Profiling.drawWireOverlay.name, out var passData,
Profiling.drawWireOverlay))
{
builder.SetRenderAttachment(color, 0, AccessFlags.Write);
passData.wireOverlayList = renderGraph.CreateWireOverlayRendererList(cameraData.camera);
builder.UseRendererList(passData.wireOverlayList);
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (DrawWireOverlayPassData data, RasterGraphContext rgContext) =>
{
using (new ProfilingScope(rgContext.cmd, Profiling.drawWireOverlay))
{
rgContext.cmd.DrawRendererList(data.wireOverlayList);
}
});
}
#endif
}
private class BeginXRPassData
{
internal UniversalCameraData cameraData;
};
internal void BeginRenderGraphXRRendering(RenderGraph renderGraph)
{
#if ENABLE_VR && ENABLE_XR_MODULE
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (!cameraData.xr.enabled)
return;
bool isDefaultXRViewport = XRSystem.GetRenderViewportScale() == 1.0f;
// For untethered XR, intermediate pass' foveation is currenlty unsupported with non-default viewport.
// Must be configured during the recording timeline before adding other XR intermediate passes.
cameraData.xrUniversal.canFoveateIntermediatePasses = !PlatformAutoDetect.isXRMobile || isDefaultXRViewport;
using (var builder = renderGraph.AddRasterRenderPass<BeginXRPassData>("BeginXRRendering", out var passData,
Profiling.beginXRRendering))
{
passData.cameraData = cameraData;
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc((BeginXRPassData data, RasterGraphContext context) =>
{
if (data.cameraData.xr.enabled)
{
if (data.cameraData.xrUniversal.isLateLatchEnabled)
data.cameraData.xrUniversal.canMarkLateLatch = true;
data.cameraData.xr.StartSinglePass(context.cmd);
if (data.cameraData.xr.supportsFoveatedRendering)
{
context.cmd.ConfigureFoveatedRendering(data.cameraData.xr.foveatedRenderingInfo);
if (XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster))
context.cmd.SetKeyword(ShaderGlobalKeywords.FoveatedRenderingNonUniformRaster, true);
}
}
});
}
#endif
}
private class EndXRPassData
{
public UniversalCameraData cameraData;
};
internal void EndRenderGraphXRRendering(RenderGraph renderGraph)
{
#if ENABLE_VR && ENABLE_XR_MODULE
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (!cameraData.xr.enabled)
return;
using (var builder = renderGraph.AddRasterRenderPass<EndXRPassData>("EndXRRendering", out var passData,
Profiling.endXRRendering))
{
passData.cameraData = cameraData;
builder.AllowGlobalStateModification(true);
// Apply MultiviewRenderRegionsCompatible flag only for the first pass in multipass
if (cameraData.xr.multipassId == 0)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
builder.SetRenderFunc((EndXRPassData data, RasterGraphContext context) =>
{
if (data.cameraData.xr.enabled)
{
data.cameraData.xr.StopSinglePass(context.cmd);
}
if (XRSystem.foveatedRenderingCaps != FoveatedRenderingCaps.None)
{
if (XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster))
context.cmd.SetKeyword(ShaderGlobalKeywords.FoveatedRenderingNonUniformRaster, false);
context.cmd.ConfigureFoveatedRendering(IntPtr.Zero);
}
});
}
#endif
}
private class DummyData
{
};
private void SetEditorTarget(RenderGraph renderGraph)
{
using (var builder = renderGraph.AddUnsafePass<DummyData>("SetEditorTarget", out var passData,
Profiling.setEditorTarget))
{
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (DummyData data, UnsafeGraphContext context) =>
{
context.cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget,
RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, // color
RenderBufferLoadAction.Load, RenderBufferStoreAction.DontCare); // depth
});
}
}
private class PassData
{
internal ScriptableRenderer renderer;
internal UniversalCameraData cameraData;
internal TextureHandle target;
// The size of the camera target changes during the frame so we must make a copy of it here to preserve its record-time value.
internal Vector2Int cameraTargetSizeCopy;
};
/// <summary>
/// TODO RENDERGRAPH
/// </summary>
/// <param name="context"></param>
/// <param name="renderingData"></param>
internal void RecordRenderGraph(RenderGraph renderGraph, ScriptableRenderContext context)
{
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RecordRenderGraph)))
{
OnBeginRenderGraphFrame();
using (new ProfilingScope(Profiling.sortRenderPasses))
{
// Sort the render pass queue
SortStable(m_ActiveRenderPassQueue);
}
InitRenderGraphFrame(renderGraph);
using (new ProfilingScope(Profiling.recordRenderGraph))
{
OnRecordRenderGraph(renderGraph, context);
}
OnEndRenderGraphFrame();
// The editor scene view still relies on some builtin passes (i.e. drawing the scene grid). The builtin
// passes are not explicitly setting RTs and rely on the last active render target being set. Unfortunately
// this does not play nice with the NRP RG path, since we don't use the SetRenderTarget API anymore.
// For this reason, as a workaround, in editor scene view we set explicitly set the RT to SceneViewRT.
// TODO: this will go away once we remove the builtin dependencies and implement the grid in SRP.
#if UNITY_EDITOR
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.isSceneViewCamera)
SetEditorTarget(renderGraph);
#endif
}
}
/// <summary>
/// TODO RENDERGRAPH
/// </summary>
/// <param name="context"></param>
/// <param name="renderingData"></param>
internal void FinishRenderGraphRendering(CommandBuffer cmd)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
OnFinishRenderGraphRendering(cmd);
InternalFinishRenderingCommon(cmd, cameraData.resolveFinalTarget);
}
/// <summary>
/// TODO RENDERGRAPH
/// </summary>
/// <param name="context"></param>
/// <param name="renderingData"></param>
internal virtual void OnFinishRenderGraphRendering(CommandBuffer cmd)
{
}
internal void RecordCustomRenderGraphPassesInEventRange(RenderGraph renderGraph, RenderPassEvent eventStart, RenderPassEvent eventEnd)
{
// Only iterate over the active pass queue if we have a non-empty range
if (eventStart != eventEnd)
{
foreach (ScriptableRenderPass pass in m_ActiveRenderPassQueue)
{
if (pass.renderPassEvent >= eventStart && pass.renderPassEvent < eventEnd)
pass.RecordRenderGraph(renderGraph, m_frameData);
}
}
}
internal void CalculateSplitEventRange(RenderPassEvent startInjectionPoint, RenderPassEvent targetEvent, out RenderPassEvent startEvent, out RenderPassEvent splitEvent, out RenderPassEvent endEvent)
{
int range = ScriptableRenderPass.GetRenderPassEventRange(startInjectionPoint);
startEvent = startInjectionPoint;
endEvent = startEvent + range;
splitEvent = (RenderPassEvent)Math.Clamp((int)targetEvent, (int)startEvent, (int)endEvent);
}
internal void RecordCustomRenderGraphPasses(RenderGraph renderGraph, RenderPassEvent startInjectionPoint, RenderPassEvent endInjectionPoint)
{
int range = ScriptableRenderPass.GetRenderPassEventRange(endInjectionPoint);
RecordCustomRenderGraphPassesInEventRange(renderGraph, startInjectionPoint, endInjectionPoint + range);
}
internal void RecordCustomRenderGraphPasses(RenderGraph renderGraph, RenderPassEvent injectionPoint)
{
RecordCustomRenderGraphPasses(renderGraph, injectionPoint, injectionPoint);
}
/// <summary>
/// Enqueues a render pass for execution.
/// </summary>
/// <param name="pass">Render pass to be enqueued.</param>
public void EnqueuePass(ScriptableRenderPass pass)
{
m_ActiveRenderPassQueue.Add(pass);
}
/// <summary>
/// Returns a clear flag based on CameraClearFlags.
/// </summary>
/// <param name="cameraData">The Camera data.</param>
/// <returns>A clear flag that tells if color and/or depth should be cleared.</returns>
/// <seealso cref="CameraData"/>
protected static ClearFlag GetCameraClearFlag(ref CameraData cameraData)
{
var universalCameraData = cameraData.universalCameraData;
return GetCameraClearFlag(universalCameraData);
}
/// <summary>
/// Returns a clear flag based on CameraClearFlags.
/// </summary>
/// <param name="cameraData">The Camera data.</param>
/// <returns>A clear flag that tells if color and/or depth should be cleared.</returns>
/// <seealso cref="CameraData"/>
protected static ClearFlag GetCameraClearFlag(UniversalCameraData cameraData)
{
var cameraClearFlags = cameraData.camera.clearFlags;
// Universal RP doesn't support CameraClearFlags.DepthOnly and CameraClearFlags.Nothing.
// CameraClearFlags.DepthOnly has the same effect of CameraClearFlags.SolidColor
// CameraClearFlags.Nothing clears Depth on PC/Desktop and in mobile it clears both
// depth and color.
// CameraClearFlags.Skybox clears depth only.
// Implementation details:
// Camera clear flags are used to initialize the attachments on the first render pass.
// ClearFlag is used together with Tile Load action to figure out how to clear the camera render target.
// In Tile Based GPUs ClearFlag.Depth + RenderBufferLoadAction.DontCare becomes DontCare load action.
// RenderBufferLoadAction.DontCare in PC/Desktop behaves as not clearing screen
// RenderBufferLoadAction.DontCare in Vulkan/Metal behaves as DontCare load action
// RenderBufferLoadAction.DontCare in GLES behaves as glInvalidateBuffer
// Overlay cameras composite on top of previous ones. They don't clear color.
// For overlay cameras we check if depth should be cleared on not.
if (cameraData.renderType == CameraRenderType.Overlay)
return (cameraData.clearDepth) ? ClearFlag.DepthStencil : ClearFlag.None;
// Certain debug modes (e.g. wireframe/overdraw modes) require that we override clear flags and clear everything.
var debugHandler = cameraData.renderer.DebugHandler;
if (debugHandler != null && debugHandler.IsActiveForCamera(cameraData.isPreviewCamera) && debugHandler.IsScreenClearNeeded)
return ClearFlag.All;
// XRTODO: remove once we have visible area of occlusion mesh available
if (cameraClearFlags == CameraClearFlags.Skybox && RenderSettings.skybox != null && cameraData.postProcessEnabled && cameraData.xr.enabled)
return ClearFlag.All;
if ((cameraClearFlags == CameraClearFlags.Skybox && RenderSettings.skybox != null) ||
cameraClearFlags == CameraClearFlags.Nothing)
{
// Clear color if msaa is used. If color is not cleared will alpha to coverage blend with previous frame if alpha clipping is enabled of any opaque objects.
if (cameraData.cameraTargetDescriptor.msaaSamples > 1)
{
// Sets the clear color to black to make the alpha to coverage blending blend with black when using alpha clipping.
cameraData.camera.backgroundColor = Color.black;
return ClearFlag.DepthStencil | ClearFlag.Color;
}
else
{
return ClearFlag.DepthStencil;
}
}
return ClearFlag.All;
}
/// <summary>
/// Calls <c>OnCull</c> for each feature added to this renderer.
/// <seealso cref="ScriptableRendererFeature.OnCameraPreCull(ScriptableRenderer, in CameraData)"/>
/// </summary>
/// <param name="cameraData">Current render state information.</param>
internal void OnPreCullRenderPasses(in CameraData cameraData)
{
// Add render passes from custom renderer features
for (int i = 0; i < rendererFeatures.Count; ++i)
{
if (!rendererFeatures[i].isActive)
{
continue;
}
rendererFeatures[i].OnCameraPreCull(this, in cameraData);
}
}
/// <summary>
/// Calls <c>AddRenderPasses</c> for each feature added to this renderer.
/// <seealso cref="ScriptableRendererFeature.AddRenderPasses(ScriptableRenderer, ref RenderingData)"/>
/// </summary>
/// <param name="renderingData"></param>
internal void AddRenderPasses(ref RenderingData renderingData)
{
using var profScope = new ProfilingScope(Profiling.addRenderPasses);
// Add render passes from custom renderer features
for (int i = 0; i < rendererFeatures.Count; ++i)
{
if (!rendererFeatures[i].isActive)
{
continue;
}
rendererFeatures[i].AddRenderPasses(this, ref renderingData);
}
// Remove any null render pass that might have been added by user by mistake
int count = activeRenderPassQueue.Count;
for (int i = count - 1; i >= 0; i--)
{
if (activeRenderPassQueue[i] == null)
activeRenderPassQueue.RemoveAt(i);
}
}
static void ClearRenderingState(IBaseCommandBuffer cmd)
{
using var profScope = new ProfilingScope(Profiling.clearRenderingState);
// Reset per-camera shader keywords. They are enabled depending on which render passes are executed.
cmd.SetKeyword(ShaderGlobalKeywords.MainLightShadows, false);
cmd.SetKeyword(ShaderGlobalKeywords.MainLightShadowCascades, false);
cmd.SetKeyword(ShaderGlobalKeywords.AdditionalLightsVertex, false);
cmd.SetKeyword(ShaderGlobalKeywords.AdditionalLightsPixel, false);
cmd.SetKeyword(ShaderGlobalKeywords.ClusterLightLoop, false);
cmd.SetKeyword(ShaderGlobalKeywords.ForwardPlus, false); // Backward compatibility. Deprecated in 6.1.
cmd.SetKeyword(ShaderGlobalKeywords.AdditionalLightShadows, false);
cmd.SetKeyword(ShaderGlobalKeywords.ReflectionProbeBlending, false);
cmd.SetKeyword(ShaderGlobalKeywords.ReflectionProbeBoxProjection, false);
cmd.SetKeyword(ShaderGlobalKeywords.ReflectionProbeAtlas, false);
cmd.SetKeyword(ShaderGlobalKeywords.SoftShadows, false);
cmd.SetKeyword(ShaderGlobalKeywords.SoftShadowsLow, false);
cmd.SetKeyword(ShaderGlobalKeywords.SoftShadowsMedium, false);
cmd.SetKeyword(ShaderGlobalKeywords.SoftShadowsHigh, false);
cmd.SetKeyword(ShaderGlobalKeywords.MixedLightingSubtractive, false);
cmd.SetKeyword(ShaderGlobalKeywords.LightmapShadowMixing, false);
cmd.SetKeyword(ShaderGlobalKeywords.ShadowsShadowMask, false);
cmd.SetKeyword(ShaderGlobalKeywords.LinearToSRGBConversion, false);
cmd.SetKeyword(ShaderGlobalKeywords.LightLayers, false);
cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceOcclusion, false);
cmd.SetGlobalVector(ScreenSpaceAmbientOcclusionPass.s_AmbientOcclusionParamID, Vector4.zero);
}
// Scene filtering is enabled when in prefab editing mode
internal bool IsSceneFilteringEnabled(Camera camera)
{
#if UNITY_EDITOR
if (CoreUtils.IsSceneFilteringEnabled() && camera.sceneViewFilterMode == Camera.SceneViewFilterMode.ShowFiltered)
return true;
#endif
return false;
}
// Common ScriptableRenderer.Execute and RenderGraph path
void InternalFinishRenderingCommon(CommandBuffer cmd, bool resolveFinalTarget)
{
using (new ProfilingScope(Profiling.internalFinishRenderingCommon))
{
for (int i = 0; i < m_ActiveRenderPassQueue.Count; ++i)
m_ActiveRenderPassQueue[i].FrameCleanup(cmd);
// Happens when rendering the last camera in the camera stack.
if (resolveFinalTarget)
{
FinishRendering(cmd);
// We finished camera stacking and released all intermediate pipeline textures.
m_IsPipelineExecuting = false;
}
m_ActiveRenderPassQueue.Clear();
}
}
private protected int AdjustAndGetScreenMSAASamples(RenderGraph renderGraph, bool useIntermediateColorTarget)
{
// In the editor (ConfigureTargetTexture in PlayModeView.cs) and many platforms, the system render target is always allocated without MSAA
if (!SystemInfo.supportsMultisampledBackBuffer) return 1;
// For mobile platforms, when URP main rendering is done to an intermediate target and NRP enabled
// we disable multisampling for the system render target as a bandwidth optimization
// doing so, we avoid storing costly MSAA samples back to system memory for nothing
bool canOptimizeScreenMSAASamples = UniversalRenderPipeline.canOptimizeScreenMSAASamples
&& useIntermediateColorTarget
&& renderGraph.nativeRenderPassesEnabled
&& Screen.msaaSamples > 1;
if (canOptimizeScreenMSAASamples)
{
Screen.SetMSAASamples(1);
}
// iOS and macOS corner case
bool screenAPIHasOneFrameDelay = (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.IPhonePlayer);
return screenAPIHasOneFrameDelay ? Mathf.Max(UniversalRenderPipeline.startFrameScreenMSAASamples, 1) : Mathf.Max(Screen.msaaSamples, 1);
}
internal static void SortStable(List<ScriptableRenderPass> list)
{
int j;
for (int i = 1; i < list.Count; ++i)
{
ScriptableRenderPass curr = list[i];
j = i - 1;
for (; j >= 0 && curr < list[j]; --j)
list[j + 1] = list[j];
list[j + 1] = curr;
}
}
/// <summary>
/// Used to determine if this renderer supports the use of GPU occlusion culling.
/// </summary>
public virtual bool supportsGPUOcclusion => false;
}
}