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;
    }
}