using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining

namespace UnityEngine.Rendering.Universal
{
    internal sealed class UberPostProcessPass : PostProcessPass
    {
        Material m_Material;
        Texture2D[] m_FilmGrainTextures;

        public enum FilteringOperation
        {
            Linear,
            Point
        }

        Texture m_DitherTexture;
        RTHandle m_UserLut;
        FilteringOperation m_FilteringOperation;
        HDROutputUtils.Operation m_HdrOperations;
        bool m_IsValid;
        bool m_IsFinalPass;
        bool m_RequireSRGBConversionBlit;
        bool m_UseFastSRGBLinearConversion;
        bool m_RenderOverlayUI;

        public UberPostProcessPass(Shader shader, Texture2D[] filmGrainTextures)
        {
            this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
            this.profilingSampler = new ProfilingSampler("Blit Post Processing");

            m_Material = PostProcessUtils.LoadShader(shader, passName);
            m_IsValid = m_Material != null;
            m_FilmGrainTextures = filmGrainTextures;
        }

        public override void Dispose()
        {
            m_UserLut?.Release();
            CoreUtils.Destroy(m_Material);
            m_IsValid = false;
        }

        public void Setup(Texture ditherTexture,
            FilteringOperation filteringOperation,
            HDROutputUtils.Operation hdrOperations,
            bool requireSRGBConversionBlit,
            bool useFastSRGBLinearConversion,
            bool isFinalPass,
            bool renderOverlayUI)
        {
            m_DitherTexture = ditherTexture;
            m_FilteringOperation = filteringOperation;
            m_HdrOperations = hdrOperations;
            m_RequireSRGBConversionBlit = requireSRGBConversionBlit;
            m_UseFastSRGBLinearConversion = useFastSRGBLinearConversion;
            m_IsFinalPass = isFinalPass;
            m_RenderOverlayUI = renderOverlayUI;
        }

        private class UberPostPassData
        {
            internal TextureHandle destinationTexture;
            internal TextureHandle sourceTexture;
            internal TextureHandle internalLutTexture;

            internal Material material; // NOTE: material is a ref, pass instance is not re-entrant within a frame!
            internal UniversalCameraData cameraData;

            internal Tonemapping tonemapping;
            internal FilteringOperation filteringOperation;
            internal HDROutputUtils.Operation hdrOperations;
            internal bool isHdrGrading;

            internal LutParams lut;
            internal BloomParams bloom;
            internal LensDistortionParams lensDistortion;
            internal ChromaticAberrationParams chromaticAberration;
            internal VignetteParams vignette;
            internal FilmGrainParams filmGrain;
            internal DitheringParams dither;

            internal bool isFinalPass;
            internal bool useFastSRGBLinearConversion;
            internal bool requireSRGBConversionBlit;
        }

        const string _CameraColorAfterPostProcessingName = "_CameraColorAfterPostProcessing";

        /// <inheritdoc />
        public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
        {
            if(!m_IsValid)
                return;

            var colorLookup = volumeStack.GetComponent<ColorLookup>();
            var colorAdjustments = volumeStack.GetComponent<ColorAdjustments>();
            var tonemapping = volumeStack.GetComponent<Tonemapping>();
            var bloom = volumeStack.GetComponent<Bloom>();
            var lensDistortion = volumeStack.GetComponent<LensDistortion>();
            var chromaticAberration = volumeStack.GetComponent<ChromaticAberration>();
            var vignette = volumeStack.GetComponent<Vignette>();
            var filmGrain = volumeStack.GetComponent<FilmGrain>();

            var cameraData = frameData.Get<UniversalCameraData>();
            var postProcessingData = frameData.Get<UniversalPostProcessingData>();
            var resourceData = frameData.Get<UniversalResourceData>();

            var sourceTexture = resourceData.cameraColor;
            var overlayUITexture = resourceData.overlayUITexture;
            var internalColorLut = resourceData.internalColorLut;
            var userColorLut = TryGetCachedUserLutTextureHandle(renderGraph, colorLookup);
            var bloomTexture = resourceData.bloom;

            TextureHandle destinationTexture;
            if (resourceData.isActiveTargetBackBuffer)
            {
                destinationTexture = resourceData.backBufferColor;
            }
            else
            {
                //Due to camera stacking we could be rendering to a persistent texture that is not the backbuffer
                destinationTexture = resourceData.destinationCameraColor.IsValid() ?
                    resourceData.destinationCameraColor
                    //TODO here we don't seem to apply PostProcessUtils.CreateCompatibleTexture. This seems completely out of sync with the other post process passes.
                    //However, changing it, breaks some tests because rendering with the color and depth after this pass when MSAA is on leads to mismatch.
                    //It seems completely arbitrary that we continue to write out color with MSAA if no other pp pass has been applied earlier.
                    : renderGraph.CreateTexture(sourceTexture, _CameraColorAfterPostProcessingName);
                resourceData.destinationCameraColor = TextureHandle.nullHandle;
            }

            using (var builder = renderGraph.AddRasterRenderPass<UberPostPassData>(passName, out var passData, profilingSampler))
            {
                var srcDesc = sourceTexture.GetDescriptor(renderGraph);

#if ENABLE_VR && ENABLE_XR_MODULE
                if (cameraData.xr.enabled)
                {
                    bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
                    // This is a screen-space pass, make sure foveated rendering is disabled for non-uniform renders
                    passSupportsFoveation &= !Experimental.Rendering.XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster);
                    builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);

                    // Apply MultiviewRenderRegionsCompatible flag only to the peripheral view in Quad Views
                    if (cameraData.xr.multipassId == 0)
                    {
                        builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
                    }
                }
#endif
                builder.AllowGlobalStateModification(true);
                passData.destinationTexture = destinationTexture;
                builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write);
                passData.sourceTexture = sourceTexture;
                builder.UseTexture(sourceTexture, AccessFlags.Read);

                if(m_RenderOverlayUI)
                    builder.UseTexture(overlayUITexture, AccessFlags.Read);

                builder.UseTexture(internalColorLut, AccessFlags.Read);
                if(userColorLut.IsValid())
                    builder.UseTexture(userColorLut, AccessFlags.Read);

                if(bloomTexture.IsValid())
                    builder.UseTexture(bloomTexture, AccessFlags.Read);

                passData.material = m_Material;
                passData.cameraData = cameraData;
                passData.useFastSRGBLinearConversion = m_UseFastSRGBLinearConversion;
                passData.requireSRGBConversionBlit = m_RequireSRGBConversionBlit;

                // HDR
                passData.tonemapping = tonemapping;
                passData.filteringOperation = m_FilteringOperation;
                passData.hdrOperations = m_HdrOperations;
                passData.isHdrGrading = postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange;

                passData.lut.Setup(colorAdjustments, colorLookup, postProcessingData.lutSize, internalColorLut, userColorLut);
                passData.bloom.Setup(bloom, in srcDesc, bloomTexture);
                passData.lensDistortion.Setup(lensDistortion, cameraData.isSceneViewCamera);
                passData.chromaticAberration.Setup(chromaticAberration);
                passData.vignette.Setup(vignette, srcDesc.width, srcDesc.height, cameraData.xr);

                // Final pass effects
                if (m_IsFinalPass)
                {
                    passData.filmGrain.Setup(filmGrain, m_FilmGrainTextures, cameraData.pixelWidth, cameraData.pixelHeight);
                    passData.dither.Setup(m_DitherTexture, cameraData.pixelWidth, cameraData.pixelHeight);
                }
                passData.isFinalPass = m_IsFinalPass;

                builder.SetRenderFunc(static (UberPostPassData data, RasterGraphContext context) =>
                {
                    var cmd = context.cmd;
                    var cameraData = data.cameraData;
                    var material = data.material;
                    var filteringOperation = data.filteringOperation;
                    RTHandle sourceTextureHdl = data.sourceTexture;

                    // Reset keywords
                    material.shaderKeywords = null;

                    switch (filteringOperation)
                    {
                        case FilteringOperation.Point:
                            material.EnableKeyword(ShaderKeywordStrings.PointSampling);
                        break;
                        case FilteringOperation.Linear: goto default;
                        default:
                        break;
                    }

                    data.lut.Apply(material);

                    if (data.bloom.IsActive())
                        data.bloom.Apply(material);

                    if(data.lensDistortion.IsActive())
                        data.lensDistortion.Apply(material);

                    if(data.chromaticAberration.IsActive())
                        data.chromaticAberration.Apply(material);

                    data.vignette.Apply(material, cameraData.xr);

                    if(data.filmGrain.IsActive())
                        data.filmGrain.Apply(material);

                    if(data.dither.IsActive())
                        data.dither.Apply(material);

                    if (data.requireSRGBConversionBlit)
                        material.EnableKeyword(ShaderKeywordStrings.LinearToSRGBConversion);

                    if (data.useFastSRGBLinearConversion)
                        material.EnableKeyword(ShaderKeywordStrings.UseFastSRGBLinearConversion);

                    if (cameraData.isAlphaOutputEnabled)
                        material.EnableKeyword(ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT);

                    if (data.isHdrGrading)
                    {
                        material.EnableKeyword(ShaderKeywordStrings.HDRGrading);
                    }
                    else
                    {
                        switch (data.tonemapping.mode.value)
                        {
                            case TonemappingMode.Neutral: material.EnableKeyword(ShaderKeywordStrings.TonemapNeutral); break;
                            case TonemappingMode.ACES: material.EnableKeyword(ShaderKeywordStrings.TonemapACES); break;
                            default: break; // None
                        }
                    }

                    if(PostProcessUtils.RequireHDROutput(cameraData))
                    {
                        PostProcessUtils.SetupHDROutput(material, cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, data.tonemapping, data.hdrOperations, cameraData.rendersOverlayUI);
                        RenderingUtils.SetupOffscreenUIViewportParams(material, ref cameraData.pixelRect, data.isFinalPass && cameraData.resolveFinalTarget);
                    }

                    // Done with Uber, blit it
#if ENABLE_VR && ENABLE_XR_MODULE
                    if (cameraData.xr.enabled && cameraData.xr.hasValidVisibleMesh)
                        PostProcessUtils.ScaleViewportAndDrawVisibilityMesh(context, data.sourceTexture, data.destinationTexture, data.cameraData, material, data.isFinalPass);
                    else
#endif
                        PostProcessUtils.ScaleViewportAndBlit(context, data.sourceTexture, data.destinationTexture, data.cameraData, material, data.isFinalPass);

                });
            }

            if (!resourceData.isActiveTargetBackBuffer)
            {
                resourceData.cameraColor = destinationTexture;
            }
        }

#region ColorLut
        TextureHandle TryGetCachedUserLutTextureHandle(RenderGraph renderGraph, ColorLookup colorLookup)
        {
            if (colorLookup.texture.value == null)
            {
                if (m_UserLut != null)
                {
                    m_UserLut.Release();
                    m_UserLut = null;
                }
            }
            else
            {
                if (m_UserLut == null || m_UserLut.externalTexture != colorLookup.texture.value)
                {
                    m_UserLut?.Release();
                    m_UserLut = RTHandles.Alloc(colorLookup.texture.value);
                }
            }
            return m_UserLut != null ? renderGraph.ImportTexture(m_UserLut) : TextureHandle.nullHandle;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void CalcColorLutParams(ColorAdjustments colorAdjustments, ColorLookup colorLookup, int lutHeight, out Vector4 internalLutParams, out Vector4 userLutParams)
        {
            Assertions.Assert.IsNotNull(colorAdjustments, "SetupColorLut colorAdjustments cannot be null.");
            Assertions.Assert.IsNotNull(colorLookup, "SetupColorLut colorLookup cannot be null.");

            int lutWidth = lutHeight * lutHeight;

            float postExposureLinear = Mathf.Pow(2f, colorAdjustments.postExposure.value);
            internalLutParams = new Vector4(1f / lutWidth, 1f / lutHeight, lutHeight - 1f, postExposureLinear);

            userLutParams = !colorLookup.IsActive()
                ? Vector4.zero
                : new Vector4(1f / colorLookup.texture.value.width,
                    1f / colorLookup.texture.value.height,
                    colorLookup.texture.value.height - 1f,
                    colorLookup.contribution.value);
        }

        public struct LutParams
        {
            public TextureHandle internalLutTexture;
            public TextureHandle activeUserLutTexture;
            public Vector4 internalLutParams;
            public Vector4 userLutParams;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Setup(ColorAdjustments colorAdjustments, ColorLookup colorLookup, int lutHeight, TextureHandle internalLutTexture, TextureHandle activeUserLutTexture)
            {
                this.internalLutTexture = internalLutTexture;
                this.activeUserLutTexture = activeUserLutTexture;
                CalcColorLutParams(colorAdjustments, colorLookup, lutHeight, out internalLutParams, out userLutParams);
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Apply(Material material)
            {
                material.SetTexture(ShaderConstants._InternalLut, internalLutTexture);
                material.SetTexture(ShaderConstants._UserLut, activeUserLutTexture);
                material.SetVector(ShaderConstants._Lut_Params, internalLutParams);
                material.SetVector(ShaderConstants._UserLut_Params, userLutParams);
            }

        }
#endregion

#region Bloom
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void CalcBloomParams(Bloom bloom, in TextureDesc srcDesc, out Vector4 bloomParams, out bool highQualityFiltering, out Texture dirtTexture, out Vector4 dirtScaleOffset, out float dirtIntensity)
        {
            using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_UberPostSetupBloomPass)))
            {
                // Setup bloom on uber
                var tint = bloom.tint.value.linear;
                var luma = ColorUtils.Luminance(tint);
                tint = luma > 0f ? tint * (1f / luma) : Color.white;
                bloomParams = new Vector4(bloom.intensity.value, tint.r, tint.g, tint.b);

                highQualityFiltering = bloom.highQualityFiltering.value;

                // Setup lens dirtiness on uber
                // Keep the aspect ratio correct & center the dirt texture, we don't want it to be
                // stretched or squashed
                dirtTexture = bloom.dirtTexture.value == null ? Texture2D.blackTexture : bloom.dirtTexture.value;
                float dirtRatio = dirtTexture.width / (float)dirtTexture.height;
                float screenRatio = srcDesc.width / (float)srcDesc.height;
                dirtScaleOffset = new Vector4(1f, 1f, 0f, 0f);
                dirtIntensity = bloom.dirtIntensity.value;

                if (dirtRatio > screenRatio)
                {
                    dirtScaleOffset.x = screenRatio / dirtRatio;
                    dirtScaleOffset.z = (1f - dirtScaleOffset.x) * 0.5f;
                }
                else if (screenRatio > dirtRatio)
                {
                    dirtScaleOffset.y = dirtRatio / screenRatio;
                    dirtScaleOffset.w = (1f - dirtScaleOffset.y) * 0.5f;
                }
            }
        }

        public struct BloomParams
        {
            public TextureHandle activeBloomTexture;
            public Vector4 bloomParams;
            public Texture dirtTexture;
            public Vector4 dirtScaleOffset;
            public float dirtIntensity;
            public bool highQualityFiltering;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public bool IsActive()
            {
                return activeBloomTexture.IsValid();
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Setup(Bloom bloom, in TextureDesc srcDesc, TextureHandle activeBloomTexture)
            {
                this.activeBloomTexture = activeBloomTexture;
                CalcBloomParams(bloom, in srcDesc, out bloomParams, out highQualityFiltering, out dirtTexture, out dirtScaleOffset, out dirtIntensity);
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Apply(Material material)
            {
                material.SetTexture(ShaderConstants._Bloom_Texture, activeBloomTexture);
                material.SetVector(ShaderConstants._Bloom_Params, bloomParams);

                material.SetTexture(ShaderConstants._LensDirt_Texture, dirtTexture);
                material.SetVector(ShaderConstants._LensDirt_Params, dirtScaleOffset);
                material.SetFloat(ShaderConstants._LensDirt_Intensity, dirtIntensity);

                // Keyword setup - a bit convoluted as we're trying to save some variants in Uber...
                if (highQualityFiltering)
                    material.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomHQDirt : ShaderKeywordStrings.BloomHQ);
                else
                    material.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomLQDirt : ShaderKeywordStrings.BloomLQ);
            }
        }
#endregion

#region Lens Distortion
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static public void CalcLensDistortionParams(LensDistortion lensDistortion, out Vector4 lensDistortionParams1, out Vector4 lensDistortionParams2)
        {
            float amount = 1.6f * Mathf.Max(Mathf.Abs(lensDistortion.intensity.value * 100f), 1f);
            float theta = Mathf.Deg2Rad * Mathf.Min(160f, amount);
            float sigma = 2f * Mathf.Tan(theta * 0.5f);
            var center = lensDistortion.center.value * 2f - Vector2.one;
            lensDistortionParams1 = new Vector4(
                center.x,
                center.y,
                Mathf.Max(lensDistortion.xMultiplier.value, 1e-4f),
                Mathf.Max(lensDistortion.yMultiplier.value, 1e-4f)
            );
            lensDistortionParams2 = new Vector4(
                lensDistortion.intensity.value >= 0f ? theta : 1f / theta,
                sigma,
                1f / lensDistortion.scale.value,
                lensDistortion.intensity.value * 100f
            );
        }

        public struct LensDistortionParams
        {
            public Vector4 lensDistortionParams1;
            public Vector4 lensDistortionParams2;
            public bool lensDistortionActive;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public bool IsActive()
            {
                return lensDistortionActive;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Setup(LensDistortion lensDistortion, bool isSceneViewCamera)
            {
                lensDistortionActive = lensDistortion.IsActive() && !isSceneViewCamera;
                CalcLensDistortionParams(lensDistortion, out lensDistortionParams1, out lensDistortionParams2);
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Apply(Material material)
            {
                material.SetVector(ShaderConstants._Distortion_Params1, lensDistortionParams1);
                material.SetVector(ShaderConstants._Distortion_Params2, lensDistortionParams2);

                material.EnableKeyword(ShaderKeywordStrings.Distortion);
            }
        }
#endregion

#region Chromatic Aberration

        public struct ChromaticAberrationParams
        {
            public float chromaticAberrationIntensity;
            public bool chromaticAberrationActive;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public bool IsActive()
            {
                return chromaticAberrationActive;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Setup(ChromaticAberration chromaticAberration)
            {
                chromaticAberrationActive = chromaticAberration.IsActive();
                chromaticAberrationIntensity = chromaticAberration.intensity.value;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Apply(Material material)
            {
                material.SetFloat(ShaderConstants._Chroma_Params, chromaticAberrationIntensity * 0.05f);

                material.EnableKeyword(ShaderKeywordStrings.ChromaticAberration);
            }
        }
#endregion

#region Vignette
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static public void CalcVignetteParams(Vignette vignette, int width, int height, Experimental.Rendering.XRPass xrPass, out Vector4 vignetteParams1, out Vector4 vignetteParams2)
        {
            var color = vignette.color.value;
            var center = vignette.center.value;
            var aspectRatio = width / (float)height;

#if ENABLE_VR && ENABLE_XR_MODULE
            if (xrPass != null && xrPass.enabled && !xrPass.singlePassEnabled)
            {
                // In multi-pass mode we need to modify the eye center with the values from .xy of the corrected
                // center since the version of the shader that is not single-pass will use the value in _Vignette_Params2
                center = xrPass.ApplyXRViewCenterOffset(center);
            }
#endif

            vignetteParams1 = new Vector4(
                color.r, color.g, color.b,
                vignette.rounded.value ? aspectRatio : 1f
            );
            vignetteParams2 = new Vector4(
                center.x, center.y,
                vignette.intensity.value * 3f,
                vignette.smoothness.value * 5f
            );
        }

        public struct VignetteParams
        {
            public Vector4 vignetteParams1;
            public Vector4 vignetteParams2;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Setup(Vignette vignette, int width, int height, Experimental.Rendering.XRPass xrPass)
            {
                CalcVignetteParams(vignette, width, height, xrPass, out vignetteParams1, out vignetteParams2);
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Apply(Material material, Experimental.Rendering.XRPass xrPass)
            {
                material.SetVector(ShaderConstants._Vignette_Params1, vignetteParams1);
                material.SetVector(ShaderConstants._Vignette_Params2, vignetteParams2);

#if ENABLE_VR && ENABLE_XR_MODULE
                if (xrPass != null && xrPass.enabled && xrPass.singlePassEnabled)
                {
                    Vector2 center = vignetteParams2;
                    material.SetVector(ShaderConstants._Vignette_ParamsXR, xrPass.ApplyXRViewCenterOffset(center));
                }
#endif
            }
        }
#endregion

#region Film Grain
        // NOTE: Procedural FilmGrain can be done using the custom texture with RenderTexture. No need to import it into the RenderGraph.
        const float k_FilmGrainIntensityScale = 4f;
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static public void CalcFilmGrainParams(FilmGrain filmGrain, Texture2D[] filmGrainTextures, out Texture grainTexture, out Vector2 grainParams)
        {
            grainTexture = (filmGrain.type.value == FilmGrainLookup.Custom) ? filmGrain.texture.value : filmGrainTextures[(int)filmGrain.type.value];
            grainParams = new Vector2(filmGrain.intensity.value * k_FilmGrainIntensityScale, filmGrain.response.value);
        }

        public struct FilmGrainParams
        {
            public Texture activeGrainTexture;
            public Vector4 tilingParams;
            public Vector2 grainParams;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public bool IsActive()
            {
                return activeGrainTexture != null;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Setup(FilmGrain filmGrain, Texture2D[] filmGrainTextures, int pixelWidth, int pixelHeight)
            {
                activeGrainTexture = null;  // Disable by default
                if (filmGrain.IsActive())
                {
                    CalcFilmGrainParams(filmGrain, filmGrainTextures, out activeGrainTexture, out grainParams);
                    tilingParams = PostProcessUtils.CalcNoiseTextureTilingParams(activeGrainTexture, pixelWidth, pixelHeight, PostProcessUtils.GetRandomOffset2D());
                }
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Apply(Material material)
            {
                PostProcessUtils.ConfigureFilmGrainMaterial(material, activeGrainTexture, grainParams, tilingParams);
                material.EnableKeyword(ShaderKeywordStrings.FilmGrain);
            }
        }
#endregion

#region 8-bit Dithering

        public struct DitheringParams
        {
            public Texture activeDitherTexture;
            public Vector4 tilingParams;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public bool IsActive()
            {
                return activeDitherTexture != null;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Setup(Texture ditherTexture, int pixelWidth, int pixelHeight)
            {
                activeDitherTexture = ditherTexture;
                tilingParams = PostProcessUtils.CalcNoiseTextureTilingParams(ditherTexture, pixelWidth, pixelHeight, PostProcessUtils.GetRandomOffset2D());
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Apply(Material material)
            {
                PostProcessUtils.ConfigureDitheringMaterial(material, activeDitherTexture, tilingParams);
                material.EnableKeyword(ShaderKeywordStrings.Dithering);
            }
        }
#endregion

        // Precomputed shader ids to same some CPU cycles (mostly affects mobile)
        internal static class ShaderConstants
        {
            // Uber
            public static readonly int _Distortion_Params1 = Shader.PropertyToID("_Distortion_Params1");
            public static readonly int _Distortion_Params2 = Shader.PropertyToID("_Distortion_Params2");
            public static readonly int _Chroma_Params = Shader.PropertyToID("_Chroma_Params");
            public static readonly int _Vignette_Params1 = Shader.PropertyToID("_Vignette_Params1");
            public static readonly int _Vignette_Params2 = Shader.PropertyToID("_Vignette_Params2");
            public static readonly int _Vignette_ParamsXR = Shader.PropertyToID("_Vignette_ParamsXR");

            // Uber Lut
            public static readonly int _InternalLut = Shader.PropertyToID("_InternalLut");
            public static readonly int _Lut_Params = Shader.PropertyToID("_Lut_Params");
            public static readonly int _UserLut = Shader.PropertyToID("_UserLut");
            public static readonly int _UserLut_Params = Shader.PropertyToID("_UserLut_Params");

            // Uber Bloom
            public static readonly int _Bloom_Texture = Shader.PropertyToID("_Bloom_Texture");
            public static readonly int _Bloom_Params = Shader.PropertyToID("_Bloom_Params");
            public static readonly int _LensDirt_Texture = Shader.PropertyToID("_LensDirt_Texture");
            public static readonly int _LensDirt_Params = Shader.PropertyToID("_LensDirt_Params");
            public static readonly int _LensDirt_Intensity = Shader.PropertyToID("_LensDirt_Intensity");
        }
    }
}