using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;

namespace UnityEngine.Rendering.Universal
{
    class DebugHandler : IDebugDisplaySettingsQuery
    {
        #region Property Id Constants

        static readonly int k_DebugColorInvalidModePropertyId = Shader.PropertyToID("_DebugColorInvalidMode");
        static readonly int k_DebugCurrentRealTimeId = Shader.PropertyToID("_DebugCurrentRealTime");

        static readonly int k_DebugColorPropertyId = Shader.PropertyToID("_DebugColor");
        static readonly int k_DebugTexturePropertyId = Shader.PropertyToID("_DebugTexture");
        static readonly int k_DebugFontId = Shader.PropertyToID("_DebugFont");
        static readonly int k_DebugTextureNoStereoPropertyId = Shader.PropertyToID("_DebugTextureNoStereo");
        static readonly int k_DebugTextureDisplayRect = Shader.PropertyToID("_DebugTextureDisplayRect");
        static readonly int k_DebugRenderTargetSupportsStereo = Shader.PropertyToID("_DebugRenderTargetSupportsStereo");
        static readonly int k_DebugRenderTargetRangeRemap = Shader.PropertyToID("_DebugRenderTargetRangeRemap");

        // Material settings...
        static readonly int k_DebugMaterialModeId = Shader.PropertyToID("_DebugMaterialMode");
        static readonly int k_DebugVertexAttributeModeId = Shader.PropertyToID("_DebugVertexAttributeMode");
        static readonly int k_DebugMaterialValidationModeId = Shader.PropertyToID("_DebugMaterialValidationMode");

        // Rendering settings...
        static readonly int k_DebugMipInfoModeId = Shader.PropertyToID("_DebugMipInfoMode");
        static readonly int k_DebugMipMapStatusModeId = Shader.PropertyToID("_DebugMipMapStatusMode");
        static readonly int k_DebugMipMapShowStatusCodeId = Shader.PropertyToID("_DebugMipMapShowStatusCode");
        static readonly int k_DebugMipMapOpacityId = Shader.PropertyToID("_DebugMipMapOpacity");
        static readonly int k_DebugMipMapRecentlyUpdatedCooldownId = Shader.PropertyToID("_DebugMipMapRecentlyUpdatedCooldown");
        static readonly int k_DebugMipMapTerrainTextureModeId = Shader.PropertyToID("_DebugMipMapTerrainTextureMode");
        static readonly int k_DebugSceneOverrideModeId = Shader.PropertyToID("_DebugSceneOverrideMode");
        static readonly int k_DebugFullScreenModeId = Shader.PropertyToID("_DebugFullScreenMode");
        static readonly int k_DebugValidationModeId = Shader.PropertyToID("_DebugValidationMode");
        static readonly int k_DebugValidateBelowMinThresholdColorPropertyId = Shader.PropertyToID("_DebugValidateBelowMinThresholdColor");
        static readonly int k_DebugValidateAboveMaxThresholdColorPropertyId = Shader.PropertyToID("_DebugValidateAboveMaxThresholdColor");
        static readonly int k_DebugMaxPixelCost = Shader.PropertyToID("_DebugMaxPixelCost");


        // Lighting settings...
        static readonly int k_DebugLightingModeId = Shader.PropertyToID("_DebugLightingMode");
        static readonly int k_DebugLightingFeatureFlagsId = Shader.PropertyToID("_DebugLightingFeatureFlags");

        static readonly int k_DebugValidateAlbedoMinLuminanceId = Shader.PropertyToID("_DebugValidateAlbedoMinLuminance");
        static readonly int k_DebugValidateAlbedoMaxLuminanceId = Shader.PropertyToID("_DebugValidateAlbedoMaxLuminance");
        static readonly int k_DebugValidateAlbedoSaturationToleranceId = Shader.PropertyToID("_DebugValidateAlbedoSaturationTolerance");
        static readonly int k_DebugValidateAlbedoHueToleranceId = Shader.PropertyToID("_DebugValidateAlbedoHueTolerance");
        static readonly int k_DebugValidateAlbedoCompareColorId = Shader.PropertyToID("_DebugValidateAlbedoCompareColor");

        static readonly int k_DebugValidateMetallicMinValueId = Shader.PropertyToID("_DebugValidateMetallicMinValue");
        static readonly int k_DebugValidateMetallicMaxValueId = Shader.PropertyToID("_DebugValidateMetallicMaxValue");

        static readonly int k_ValidationChannelsId = Shader.PropertyToID("_ValidationChannels");
        static readonly int k_RangeMinimumId = Shader.PropertyToID("_RangeMinimum");
        static readonly int k_RangeMaximumId = Shader.PropertyToID("_RangeMaximum");

        #endregion

        #region Pass Data

        private static readonly ProfilingSampler s_DebugSetupSampler = new ProfilingSampler("Setup Debug Properties");
        private static readonly ProfilingSampler s_DebugFinalValidationSampler = new ProfilingSampler(nameof(UpdateShaderGlobalPropertiesForFinalValidationPass));

        DebugSetupPassData s_DebugSetupPassData = new DebugSetupPassData();
        DebugFinalValidationPassData s_DebugFinalValidationPassData = new DebugFinalValidationPassData();

        #endregion

        readonly Material m_ReplacementMaterial;
        readonly Material m_HDRDebugViewMaterial;

        HDRDebugViewPass m_HDRDebugViewPass;
        RTHandle m_DebugScreenColorHandle;
        RTHandle m_DebugScreenDepthHandle;

        readonly UniversalRenderPipelineRuntimeTextures m_RuntimeTextures;

        bool m_HasDebugRenderTarget;
        bool m_DebugRenderTargetSupportsStereo;
        Vector4 m_DebugRenderTargetPixelRect;
        Vector4 m_DebugRenderTargetRangeRemap;
        RTHandle m_DebugRenderTarget;

        RTHandle m_DebugFontTexture;

        private GraphicsBuffer m_debugDisplayConstant;

        readonly UniversalRenderPipelineDebugDisplaySettings m_DebugDisplaySettings;

        DebugDisplaySettingsLighting LightingSettings => m_DebugDisplaySettings.lightingSettings;
        DebugDisplaySettingsMaterial MaterialSettings => m_DebugDisplaySettings.materialSettings;
        DebugDisplaySettingsRendering RenderingSettings => m_DebugDisplaySettings.renderingSettings;

        #region IDebugDisplaySettingsQuery

        /// <inheritdoc/>
        public bool AreAnySettingsActive => m_DebugDisplaySettings.AreAnySettingsActive;

        /// <inheritdoc/>
        public bool IsPostProcessingAllowed => m_DebugDisplaySettings.IsPostProcessingAllowed;

        /// <inheritdoc/>
        public bool IsLightingActive => m_DebugDisplaySettings.IsLightingActive;

        // These modes would require putting custom data into gbuffer, so instead we just disable deferred mode.
        internal bool IsActiveModeUnsupportedForDeferred =>
            m_DebugDisplaySettings.lightingSettings.lightingDebugMode != DebugLightingMode.None ||
            m_DebugDisplaySettings.lightingSettings.lightingFeatureFlags != DebugLightingFeatureFlags.None ||
            m_DebugDisplaySettings.renderingSettings.sceneOverrideMode != DebugSceneOverrideMode.None ||
            m_DebugDisplaySettings.materialSettings.materialDebugMode != DebugMaterialMode.None ||
            m_DebugDisplaySettings.materialSettings.vertexAttributeDebugMode != DebugVertexAttributeMode.None ||
            m_DebugDisplaySettings.materialSettings.materialValidationMode != DebugMaterialValidationMode.None ||
            m_DebugDisplaySettings.renderingSettings.mipInfoMode != DebugMipInfoMode.None;

        /// <inheritdoc/>
        public bool TryGetScreenClearColor(ref Color color)
        {
            return m_DebugDisplaySettings.TryGetScreenClearColor(ref color);
        }

        #endregion

        internal Material ReplacementMaterial => m_ReplacementMaterial;
        internal UniversalRenderPipelineDebugDisplaySettings DebugDisplaySettings => m_DebugDisplaySettings;
        internal ref RTHandle DebugScreenColorHandle => ref m_DebugScreenColorHandle;
        internal ref RTHandle DebugScreenDepthHandle => ref m_DebugScreenDepthHandle;
        internal HDRDebugViewPass hdrDebugViewPass => m_HDRDebugViewPass;

        internal bool HDRDebugViewIsActive(bool resolveFinalTarget)
        {
            // HDR debug views should only apply to the last camera in the stack
            return DebugDisplaySettings.lightingSettings.hdrDebugMode != HDRDebugMode.None && resolveFinalTarget;
        }

        internal bool WriteToDebugScreenTexture(bool resolveFinalTarget)
        {
            return HDRDebugViewIsActive(resolveFinalTarget);
        }

        internal bool IsScreenClearNeeded
        {
            get
            {
                Color color = Color.black;

                return TryGetScreenClearColor(ref color);
            }
        }

        internal bool IsRenderPassSupported
        {
            get
            {
                return RenderingSettings.sceneOverrideMode == DebugSceneOverrideMode.None || RenderingSettings.sceneOverrideMode == DebugSceneOverrideMode.Overdraw;
            }
        }

        internal bool IsDepthPrimingCompatible => RenderingSettings.sceneOverrideMode != DebugSceneOverrideMode.Wireframe;

        internal int stpDebugViewIndex { get { return RenderingSettings.stpDebugViewIndex; } }

        internal DebugHandler()
        {
            m_DebugDisplaySettings = UniversalRenderPipelineDebugDisplaySettings.Instance;

            if (GraphicsSettings.TryGetRenderPipelineSettings<UniversalRenderPipelineDebugShaders>(out var shaders))
            {
                m_ReplacementMaterial = (shaders.debugReplacementPS != null) ? CoreUtils.CreateEngineMaterial(shaders.debugReplacementPS) : null;
                m_HDRDebugViewMaterial = (shaders.hdrDebugViewPS != null) ? CoreUtils.CreateEngineMaterial(shaders.hdrDebugViewPS) : null;
            }

            m_HDRDebugViewPass = new HDRDebugViewPass(m_HDRDebugViewMaterial);

            m_RuntimeTextures = GraphicsSettings.GetRenderPipelineSettings<UniversalRenderPipelineRuntimeTextures>();
            if (m_RuntimeTextures != null)
            {
                m_DebugFontTexture = RTHandles.Alloc(m_RuntimeTextures.debugFontTexture);
            }

            m_debugDisplayConstant = new GraphicsBuffer(GraphicsBuffer.Target.Constant, 32, Marshal.SizeOf(typeof(Vector4)));
        }

        public void Dispose()
        {
            m_HDRDebugViewPass.Dispose();
            m_DebugScreenColorHandle?.Release();
            m_DebugScreenDepthHandle?.Release();
            m_DebugFontTexture?.Release();
            m_debugDisplayConstant.Dispose();
            CoreUtils.Destroy(m_HDRDebugViewMaterial);
            CoreUtils.Destroy(m_ReplacementMaterial);
        }

        internal bool IsActiveForCamera(bool isPreviewCamera)
        {
            return !isPreviewCamera && AreAnySettingsActive;
        }

        internal bool TryGetFullscreenDebugMode(out DebugFullScreenMode debugFullScreenMode)
        {
            return TryGetFullscreenDebugMode(out debugFullScreenMode, out _);
        }

        internal bool TryGetFullscreenDebugMode(out DebugFullScreenMode debugFullScreenMode, out int textureHeightPercent)
        {
            debugFullScreenMode = RenderingSettings.fullScreenDebugMode;
            textureHeightPercent = RenderingSettings.fullScreenDebugModeOutputSizeScreenPercent;
            return debugFullScreenMode != DebugFullScreenMode.None;
        }

        internal static void ConfigureColorDescriptorForDebugScreen(ref RenderTextureDescriptor descriptor, int cameraWidth, int cameraHeight)
        {
            descriptor.width = cameraWidth;
            descriptor.height = cameraHeight;
            descriptor.useMipMap = false;
            descriptor.autoGenerateMips = false;
            descriptor.useDynamicScale = true;
            descriptor.depthStencilFormat = GraphicsFormat.None;
        }

        internal static void ConfigureDepthDescriptorForDebugScreen(ref RenderTextureDescriptor descriptor, GraphicsFormat depthStencilFormat, int cameraWidth, int cameraHeight)
        {
            descriptor.width = cameraWidth;
            descriptor.height = cameraHeight;
            descriptor.useMipMap = false;
            descriptor.autoGenerateMips = false;
            descriptor.useDynamicScale = true;
            descriptor.depthStencilFormat = depthStencilFormat;
            descriptor.graphicsFormat = GraphicsFormat.None;
        }

        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        internal void SetupShaderProperties(RasterCommandBuffer cmd, int passIndex = 0)
        {
            if (LightingSettings.lightingDebugMode == DebugLightingMode.ShadowCascades)
            {
                // we disable cubemap reflections, too distracting (in TemplateLWRP for ex.)
                cmd.EnableShaderKeyword("_DEBUG_ENVIRONMENTREFLECTIONS_OFF");
            }
            else
            {
                cmd.DisableShaderKeyword("_DEBUG_ENVIRONMENTREFLECTIONS_OFF");
            }

            m_debugDisplayConstant.SetData(MaterialSettings.debugRenderingLayersColors, 0, 0, 32);

            cmd.SetGlobalConstantBuffer(m_debugDisplayConstant, "_DebugDisplayConstant", 0, m_debugDisplayConstant.count * m_debugDisplayConstant.stride);

            if (MaterialSettings.renderingLayersSelectedLight)
                cmd.SetGlobalInt("_DebugRenderingLayerMask", (int)MaterialSettings.GetDebugLightLayersMask());
            else
                cmd.SetGlobalInt("_DebugRenderingLayerMask", (int)MaterialSettings.renderingLayerMask);

            switch (RenderingSettings.sceneOverrideMode)
            {
                case DebugSceneOverrideMode.Overdraw:
                {
                    // Target texture can contains only a value between 0 and 1
                    // So we encode the number of overdraw in the range (0, max displayed overdaw count)
                    // The value will be clamped by the GPU driver
                    var value = 1 / (float)RenderingSettings.maxOverdrawCount;
                    cmd.SetGlobalColor(k_DebugColorPropertyId, new Color(value, value, value, 1));
                    break;
                }

                case DebugSceneOverrideMode.Wireframe:
                {
                    cmd.SetGlobalColor(k_DebugColorPropertyId, Color.black);
                    break;
                }

                case DebugSceneOverrideMode.SolidWireframe:
                {
                    cmd.SetGlobalColor(k_DebugColorPropertyId, (passIndex == 0) ? Color.white : Color.black);
                    break;
                }

                case DebugSceneOverrideMode.ShadedWireframe:
                {
                    if (passIndex == 0)
                    {
                        cmd.SetKeyword(ShaderGlobalKeywords.DEBUG_DISPLAY, false);
                    }
                    else if (passIndex == 1)
                    {
                        cmd.SetGlobalColor(k_DebugColorPropertyId, Color.black);
                        cmd.SetKeyword(ShaderGlobalKeywords.DEBUG_DISPLAY, true);
                    }

                    break;
                }
            }

            switch (MaterialSettings.materialValidationMode)
            {
                case DebugMaterialValidationMode.Albedo:
                    cmd.SetGlobalFloat(k_DebugValidateAlbedoMinLuminanceId, MaterialSettings.albedoMinLuminance);
                    cmd.SetGlobalFloat(k_DebugValidateAlbedoMaxLuminanceId, MaterialSettings.albedoMaxLuminance);
                    cmd.SetGlobalFloat(k_DebugValidateAlbedoSaturationToleranceId, MaterialSettings.albedoSaturationTolerance);
                    cmd.SetGlobalFloat(k_DebugValidateAlbedoHueToleranceId, MaterialSettings.albedoHueTolerance);
                    cmd.SetGlobalColor(k_DebugValidateAlbedoCompareColorId, MaterialSettings.albedoCompareColor.linear);
                    break;

                case DebugMaterialValidationMode.Metallic:
                    cmd.SetGlobalFloat(k_DebugValidateMetallicMinValueId, MaterialSettings.metallicMinValue);
                    cmd.SetGlobalFloat(k_DebugValidateMetallicMaxValueId, MaterialSettings.metallicMaxValue);
                    break;
            }
        }

        internal void SetDebugRenderTarget(RTHandle renderTarget, Rect displayRect, bool supportsStereo, Vector4 dataRangeRemap)
        {
            m_HasDebugRenderTarget = true;
            m_DebugRenderTargetSupportsStereo = supportsStereo;
            m_DebugRenderTarget = renderTarget;
            m_DebugRenderTargetPixelRect = new Vector4(displayRect.x, displayRect.y, displayRect.width, displayRect.height);
            m_DebugRenderTargetRangeRemap = dataRangeRemap;
        }

        internal void ResetDebugRenderTarget()
        {
            m_HasDebugRenderTarget = false;
        }

        class DebugFinalValidationPassData
        {
            public bool isFinalPass;
            public bool resolveFinalTarget;
            public bool isActiveForCamera;
            public bool hasDebugRenderTarget;

            public TextureHandle debugRenderTargetHandle;
            public int debugTexturePropertyId;

            public Vector4 debugRenderTargetPixelRect;
            public int debugRenderTargetSupportsStereo;
            public Vector4 debugRenderTargetRangeRemap;

            public TextureHandle debugFontTextureHandle;

            // NOTE: The settings are references.
            // It's assumed they're the same for the whole frame. Build timeline != execution timeline!
            // Ideally these would be copied without any allocs.
            public DebugDisplaySettingsRendering renderingSettings;
        }

        DebugFinalValidationPassData InitDebugFinalValidationPassData(DebugFinalValidationPassData passData, UniversalCameraData cameraData, bool isFinalPass)
        {
            passData.isFinalPass = isFinalPass;
            passData.resolveFinalTarget = cameraData.resolveFinalTarget;
            passData.isActiveForCamera = IsActiveForCamera(cameraData.isPreviewCamera);
            passData.hasDebugRenderTarget = m_HasDebugRenderTarget;

            passData.debugRenderTargetHandle = TextureHandle.nullHandle;
            passData.debugTexturePropertyId = m_DebugRenderTargetSupportsStereo ? k_DebugTexturePropertyId : k_DebugTextureNoStereoPropertyId;

            passData.debugRenderTargetPixelRect = m_DebugRenderTargetPixelRect;
            passData.debugRenderTargetSupportsStereo = m_DebugRenderTargetSupportsStereo ? 1 : 0;
            passData.debugRenderTargetRangeRemap = m_DebugRenderTargetRangeRemap;

            passData.debugFontTextureHandle = TextureHandle.nullHandle;

            passData.renderingSettings = RenderingSettings;

            return passData;
        }

        static void UpdateShaderGlobalPropertiesForFinalValidationPass(RasterCommandBuffer cmd, DebugFinalValidationPassData data)
        {
            // Ensure final validation & fullscreen debug modes are only done once in the very final pass, for the last camera on the stack.
            bool isFinal = data.isFinalPass && data.resolveFinalTarget;
            if (!isFinal)
            {
                cmd.SetKeyword(ShaderGlobalKeywords.DEBUG_DISPLAY, false);
                return;
            }

            if (data.isActiveForCamera)
            {
                cmd.SetKeyword(ShaderGlobalKeywords.DEBUG_DISPLAY, true);
            }
            else
            {
                cmd.SetKeyword(ShaderGlobalKeywords.DEBUG_DISPLAY, false);
            }

            if (data.hasDebugRenderTarget)
            {
                if(data.debugRenderTargetHandle.IsValid())
                    cmd.SetGlobalTexture(data.debugTexturePropertyId,  data.debugRenderTargetHandle);

                cmd.SetGlobalVector(k_DebugTextureDisplayRect, data.debugRenderTargetPixelRect);
                cmd.SetGlobalInteger(k_DebugRenderTargetSupportsStereo, data.debugRenderTargetSupportsStereo);
                cmd.SetGlobalVector(k_DebugRenderTargetRangeRemap, data.debugRenderTargetRangeRemap);
            }

            var renderingSettings = data.renderingSettings;
            if (renderingSettings.validationMode == DebugValidationMode.HighlightOutsideOfRange)
            {
                cmd.SetGlobalInteger(k_ValidationChannelsId, (int)renderingSettings.validationChannels);
                cmd.SetGlobalFloat(k_RangeMinimumId, renderingSettings.validationRangeMin);
                cmd.SetGlobalFloat(k_RangeMaximumId, renderingSettings.validationRangeMax);
            }

            if (renderingSettings.mipInfoMode != DebugMipInfoMode.None)
            {
                // some (not all) of these need text rendering
                cmd.SetGlobalTexture(k_DebugFontId, data.debugFontTextureHandle);
            }
        }

        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        internal void UpdateShaderGlobalPropertiesForFinalValidationPass(CommandBuffer cmd, UniversalCameraData cameraData, bool isFinalPass)
        {
            UpdateShaderGlobalPropertiesForFinalValidationPass(CommandBufferHelpers.GetRasterCommandBuffer(cmd), InitDebugFinalValidationPassData(s_DebugFinalValidationPassData, cameraData, isFinalPass));
            cmd.SetGlobalTexture(s_DebugFinalValidationPassData.debugTexturePropertyId,  m_DebugRenderTarget);
            if (RenderingSettings.mipInfoMode != DebugMipInfoMode.None)
            {
                // some (not all) of these need text rendering
                cmd.SetGlobalTexture(k_DebugFontId, m_RuntimeTextures.debugFontTexture);
            }
        }

        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        internal void UpdateShaderGlobalPropertiesForFinalValidationPass(RenderGraph renderGraph, UniversalCameraData cameraData, bool isFinalPass)
        {
            using (var builder = renderGraph.AddRasterRenderPass<DebugFinalValidationPassData>(nameof(UpdateShaderGlobalPropertiesForFinalValidationPass), out var passData, s_DebugFinalValidationSampler))
            {
                InitDebugFinalValidationPassData(passData, cameraData, isFinalPass);

                if (m_DebugRenderTarget != null)
                    passData.debugRenderTargetHandle = renderGraph.ImportTexture(m_DebugRenderTarget);

                if (m_DebugFontTexture != null)
                    passData.debugFontTextureHandle = renderGraph.ImportTexture(m_DebugFontTexture);

                builder.AllowGlobalStateModification(true);

                if (passData.debugRenderTargetHandle.IsValid())
                {
                    builder.UseTexture(passData.debugRenderTargetHandle);
                    builder.SetGlobalTextureAfterPass(passData.debugRenderTargetHandle, passData.debugTexturePropertyId);
                }

                if (passData.debugFontTextureHandle.IsValid())
                {
                    builder.UseTexture(passData.debugFontTextureHandle);
                    builder.SetGlobalTextureAfterPass(passData.debugFontTextureHandle, k_DebugFontId);
                }

                builder.SetRenderFunc(static (DebugFinalValidationPassData data, RasterGraphContext context) =>
                {
                    UpdateShaderGlobalPropertiesForFinalValidationPass(context.cmd, data);
                });
            }
        }

        class DebugSetupPassData
        {
            public bool isActiveForCamera;

            // NOTE: The settings are references.
            // It's assumed they're the same for the whole frame. Build timeline != execution timeline!
            // Ideally these would be copied without any allocs.
            public DebugDisplaySettingsMaterial  materialSettings;
            public DebugDisplaySettingsRendering renderingSettings;
            public DebugDisplaySettingsLighting  lightingSettings;
        }

        DebugSetupPassData InitDebugSetupPassData(DebugSetupPassData passData, bool isPreviewCamera)
        {
            passData.isActiveForCamera = IsActiveForCamera(isPreviewCamera);
            passData.materialSettings  = MaterialSettings;
            passData.renderingSettings = RenderingSettings;
            passData.lightingSettings  = LightingSettings;
            return passData;
        }

        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        static void Setup(RasterCommandBuffer cmd, DebugSetupPassData passData)
        {
            if (passData.isActiveForCamera)
            {
                cmd.SetKeyword(ShaderGlobalKeywords.DEBUG_DISPLAY, true);

                // Material settings...
                cmd.SetGlobalFloat(k_DebugMaterialModeId, (int)passData.materialSettings.materialDebugMode);
                cmd.SetGlobalFloat(k_DebugVertexAttributeModeId, (int)passData.materialSettings.vertexAttributeDebugMode);

                cmd.SetGlobalInteger(k_DebugMaterialValidationModeId, (int)passData.materialSettings.materialValidationMode);

                // Rendering settings...
                cmd.SetGlobalInteger(k_DebugMipInfoModeId, (int)passData.renderingSettings.mipInfoMode);
                cmd.SetGlobalInteger(k_DebugMipMapStatusModeId, (int)passData.renderingSettings.mipDebugStatusMode);
                cmd.SetGlobalInteger(k_DebugMipMapShowStatusCodeId, passData.renderingSettings.mipDebugStatusShowCode ? 1 : 0);
                cmd.SetGlobalFloat(k_DebugMipMapOpacityId, passData.renderingSettings.mipDebugOpacity);
                cmd.SetGlobalFloat(k_DebugMipMapRecentlyUpdatedCooldownId, passData.renderingSettings.mipDebugRecentUpdateCooldown);
                cmd.SetGlobalFloat(k_DebugMipMapTerrainTextureModeId, (int)passData.renderingSettings.mipDebugTerrainTexture);
                cmd.SetGlobalInteger(k_DebugSceneOverrideModeId, (int)passData.renderingSettings.sceneOverrideMode);
                cmd.SetGlobalInteger(k_DebugFullScreenModeId, (int)passData.renderingSettings.fullScreenDebugMode);
                cmd.SetGlobalInteger(k_DebugMaxPixelCost, (int)passData.renderingSettings.maxOverdrawCount);
                cmd.SetGlobalInteger(k_DebugValidationModeId, (int)passData.renderingSettings.validationMode);
                cmd.SetGlobalColor(k_DebugValidateBelowMinThresholdColorPropertyId, Color.red);
                cmd.SetGlobalColor(k_DebugValidateAboveMaxThresholdColorPropertyId, Color.blue);

                // Lighting settings...
                cmd.SetGlobalFloat(k_DebugLightingModeId, (int)passData.lightingSettings.lightingDebugMode);
                cmd.SetGlobalInteger(k_DebugLightingFeatureFlagsId, (int)passData.lightingSettings.lightingFeatureFlags);

                // Set-up any other persistent properties...
                cmd.SetGlobalColor(k_DebugColorInvalidModePropertyId, Color.red);
#if UNITY_EDITOR
                cmd.SetGlobalFloat(k_DebugCurrentRealTimeId, (float)EditorApplication.timeSinceStartup);
#else
                cmd.SetGlobalFloat(k_DebugCurrentRealTimeId, Time.realtimeSinceStartup);
#endif
            }
            else
            {
                cmd.SetKeyword(ShaderGlobalKeywords.DEBUG_DISPLAY, false);
            }
        }

        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        internal void Setup(CommandBuffer cmd, bool isPreviewCamera)
        {
            Setup(CommandBufferHelpers.GetRasterCommandBuffer(cmd), InitDebugSetupPassData(s_DebugSetupPassData, isPreviewCamera));
        }

        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        internal void Setup(RenderGraph renderGraph, bool isPreviewCamera)
        {
            using (var builder = renderGraph.AddRasterRenderPass<DebugSetupPassData>(s_DebugSetupSampler.name, out var passData, s_DebugSetupSampler))
            {
                InitDebugSetupPassData(passData, isPreviewCamera);
                builder.AllowGlobalStateModification(true);
                builder.SetRenderFunc(static (DebugSetupPassData data, RasterGraphContext context) =>
                {
                    Setup(context.cmd, data);
                });
            }
        }

        [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
        internal void Render(RenderGraph renderGraph, UniversalCameraData cameraData, TextureHandle srcColor, TextureHandle overlayTexture, TextureHandle dstColor)
        {
            if (IsActiveForCamera(cameraData.isPreviewCamera) && HDRDebugViewIsActive(cameraData.resolveFinalTarget))
            {
                m_HDRDebugViewPass.RenderHDRDebug(renderGraph, cameraData, srcColor, overlayTexture, dstColor, LightingSettings.hdrDebugMode);
            }
        }

        #region DebugRendererLists
        internal DebugRendererLists CreateRendererListsWithDebugRenderState(
            RenderGraph renderGraph,
            ref CullingResults cullResults,
            ref DrawingSettings drawingSettings,
            ref FilteringSettings filteringSettings,
            ref RenderStateBlock renderStateBlock)
        {
            DebugRendererLists debug = new DebugRendererLists(this, filteringSettings);
            debug.CreateRendererListsWithDebugRenderState(renderGraph, ref cullResults, ref drawingSettings, ref filteringSettings, ref renderStateBlock);
            return debug;
        }
        #endregion
    }

    internal class DebugRendererLists
    {
        private readonly DebugHandler m_DebugHandler;
        readonly FilteringSettings m_FilteringSettings;
        List<DebugRenderSetup> m_DebugRenderSetups = new List<DebugRenderSetup>(2);
        List<RendererList> m_ActiveDebugRendererList = new List<RendererList>(2);
        List<RendererListHandle> m_ActiveDebugRendererListHdl = new List<RendererListHandle>(2);

        public DebugRendererLists(DebugHandler debugHandler,
            FilteringSettings filteringSettings)
        {
            m_DebugHandler = debugHandler;
            m_FilteringSettings = filteringSettings;
        }

        private void CreateDebugRenderSetups(FilteringSettings filteringSettings)
        {
            var sceneOverrideMode = m_DebugHandler.DebugDisplaySettings.renderingSettings.sceneOverrideMode;
            var numIterations = ((sceneOverrideMode == DebugSceneOverrideMode.SolidWireframe) || (sceneOverrideMode == DebugSceneOverrideMode.ShadedWireframe)) ? 2 : 1;
            for (var i = 0; i < numIterations; i++)
                m_DebugRenderSetups.Add(new DebugRenderSetup(m_DebugHandler, i, filteringSettings));
        }

        void DisposeDebugRenderLists()
        {
            foreach (var debugRenderSetup in m_DebugRenderSetups)
            {
                debugRenderSetup.Dispose();
            }
            m_DebugRenderSetups.Clear();
            m_ActiveDebugRendererList.Clear();
            m_ActiveDebugRendererListHdl.Clear();
        }

        internal void CreateRendererListsWithDebugRenderState(
            RenderGraph renderGraph,
            ref CullingResults cullResults,
            ref DrawingSettings drawingSettings,
            ref FilteringSettings filteringSettings,
            ref RenderStateBlock renderStateBlock)
        {
            CreateDebugRenderSetups(filteringSettings);
            foreach (DebugRenderSetup debugRenderSetup in m_DebugRenderSetups)
            {
                DrawingSettings debugDrawingSettings = debugRenderSetup.CreateDrawingSettings(drawingSettings);
                RenderStateBlock debugRenderStateBlock = debugRenderSetup.GetRenderStateBlock(renderStateBlock);
                RendererListHandle rendererListHdl = new RendererListHandle();
                RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref cullResults, debugDrawingSettings, filteringSettings, debugRenderStateBlock, ref rendererListHdl);
                m_ActiveDebugRendererListHdl.Add((rendererListHdl));
            }
        }

        internal void PrepareRendererListForRasterPass(IRasterRenderGraphBuilder builder)
        {
            foreach (RendererListHandle rendererListHdl in m_ActiveDebugRendererListHdl)
            {
                builder.UseRendererList(rendererListHdl);
            }
        }

        internal void DrawWithRendererList(RasterCommandBuffer cmd)
        {
            foreach (DebugRenderSetup debugRenderSetup in m_DebugRenderSetups)
            {
                debugRenderSetup.Begin(cmd);
                RendererList rendererList = new RendererList();
                if (m_ActiveDebugRendererList.Count > 0)
                {
                    rendererList = m_ActiveDebugRendererList[debugRenderSetup.GetIndex()];
                }
                else if(m_ActiveDebugRendererListHdl.Count > 0)
                {
                    rendererList = m_ActiveDebugRendererListHdl[debugRenderSetup.GetIndex()];
                }

                debugRenderSetup.DrawWithRendererList(cmd, ref rendererList);
                debugRenderSetup.End(cmd);
            }

            DisposeDebugRenderLists();
        }
    }
}