using RoR2;

namespace UnityEngine.Rendering.PostProcessing;

public sealed class HopooSSRRenderer : PostProcessEffectRenderer<HopooSSR>
{
	private class QualityPreset
	{
		public int maximumIterationCount;

		public float thickness;

		public ScreenSpaceReflectionResolution downsampling;
	}

	private enum Pass
	{
		Test,
		Resolve,
		Reproject,
		Composite
	}

	private RenderTexture m_Resolve;

	private RenderTexture m_History;

	private int[] m_MipIDs;

	private readonly QualityPreset[] m_Presets = new QualityPreset[7]
	{
		new QualityPreset
		{
			maximumIterationCount = 10,
			thickness = 32f,
			downsampling = ScreenSpaceReflectionResolution.Downsampled
		},
		new QualityPreset
		{
			maximumIterationCount = 16,
			thickness = 32f,
			downsampling = ScreenSpaceReflectionResolution.Downsampled
		},
		new QualityPreset
		{
			maximumIterationCount = 32,
			thickness = 16f,
			downsampling = ScreenSpaceReflectionResolution.Downsampled
		},
		new QualityPreset
		{
			maximumIterationCount = 48,
			thickness = 8f,
			downsampling = ScreenSpaceReflectionResolution.Downsampled
		},
		new QualityPreset
		{
			maximumIterationCount = 16,
			thickness = 32f,
			downsampling = ScreenSpaceReflectionResolution.FullSize
		},
		new QualityPreset
		{
			maximumIterationCount = 48,
			thickness = 16f,
			downsampling = ScreenSpaceReflectionResolution.FullSize
		},
		new QualityPreset
		{
			maximumIterationCount = 128,
			thickness = 12f,
			downsampling = ScreenSpaceReflectionResolution.Supersampled
		}
	};

	public override DepthTextureMode GetCameraFlags()
	{
		return DepthTextureMode.Depth | DepthTextureMode.MotionVectors;
	}

	internal void CheckRT(ref RenderTexture rt, int width, int height, RenderTextureFormat format, FilterMode filterMode, bool useMipMap)
	{
		if (rt == null || !rt.IsCreated() || rt.width != width || rt.height != height || rt.format != format)
		{
			if (rt != null)
			{
				rt.Release();
			}
			rt = new RenderTexture(width, height, 0, format)
			{
				filterMode = filterMode,
				useMipMap = useMipMap,
				autoGenerateMips = false,
				hideFlags = HideFlags.HideAndDontSave
			};
			rt.Create();
		}
	}

	public override void Render(PostProcessRenderContext context)
	{
		CommandBuffer command = context.command;
		command.BeginSample("Screen-space Reflections");
		if (base.settings.preset.value != ScreenSpaceReflectionPreset.Custom)
		{
			int value = (int)base.settings.preset.value;
			base.settings.maximumIterationCount.value = m_Presets[value].maximumIterationCount;
			base.settings.thickness.value = m_Presets[value].thickness;
			base.settings.resolution.value = m_Presets[value].downsampling;
		}
		base.settings.maximumMarchDistance.value = Mathf.Max(0f, base.settings.maximumMarchDistance.value);
		int num = Mathf.ClosestPowerOfTwo(Mathf.Min(context.width, context.height));
		if (base.settings.resolution.value == ScreenSpaceReflectionResolution.Downsampled)
		{
			num >>= 1;
		}
		else if (base.settings.resolution.value == ScreenSpaceReflectionResolution.Supersampled)
		{
			num <<= 1;
		}
		int a = Mathf.FloorToInt(Mathf.Log(num, 2f) - 3f);
		a = Mathf.Min(a, 12);
		CheckRT(ref m_Resolve, num, num, context.sourceFormat, FilterMode.Trilinear, useMipMap: true);
		Texture2D texture2D = context.resources.blueNoise256[0];
		PropertySheet propertySheet = context.propertySheets.Get(LegacyShaderAPI.Find("Hidden/PostProcessing/HopooSSR"));
		propertySheet.properties.SetTexture(Shader.PropertyToID("_Noise"), texture2D);
		Matrix4x4 value2 = default(Matrix4x4);
		value2.SetRow(0, new Vector4((float)num * 0.5f, 0f, 0f, (float)num * 0.5f));
		value2.SetRow(1, new Vector4(0f, (float)num * 0.5f, 0f, (float)num * 0.5f));
		value2.SetRow(2, new Vector4(0f, 0f, 1f, 0f));
		value2.SetRow(3, new Vector4(0f, 0f, 0f, 1f));
		Matrix4x4 gPUProjectionMatrix = GL.GetGPUProjectionMatrix(context.camera.projectionMatrix, renderIntoTexture: false);
		value2 *= gPUProjectionMatrix;
		propertySheet.properties.SetMatrix(Shader.PropertyToID("_ViewMatrix"), context.camera.worldToCameraMatrix);
		propertySheet.properties.SetMatrix(Shader.PropertyToID("_InverseViewMatrix"), context.camera.worldToCameraMatrix.inverse);
		propertySheet.properties.SetMatrix(Shader.PropertyToID("_InverseProjectionMatrix"), gPUProjectionMatrix.inverse);
		propertySheet.properties.SetMatrix(Shader.PropertyToID("_ScreenSpaceProjectionMatrix"), value2);
		propertySheet.properties.SetVector(Shader.PropertyToID("_Params"), new Vector4(base.settings.vignette.value, base.settings.distanceFade.value, base.settings.maximumMarchDistance.value, a));
		propertySheet.properties.SetVector(Shader.PropertyToID("_Params2"), new Vector4((float)context.width / (float)context.height, (float)num / (float)texture2D.width, base.settings.thickness.value, base.settings.maximumIterationCount.value));
		command.GetTemporaryRT(Shader.PropertyToID("_Test"), num, num, 0, FilterMode.Point, context.sourceFormat);
		command.BlitFullscreenTriangle(context.source, Shader.PropertyToID("_Test"), propertySheet, 0);
		if (context.isSceneView)
		{
			command.BlitFullscreenTriangle(context.source, m_Resolve, propertySheet, 1);
		}
		else
		{
			CheckRT(ref m_History, num, num, context.sourceFormat, FilterMode.Bilinear, useMipMap: false);
			if (m_ResetHistory)
			{
				context.command.BlitFullscreenTriangle(context.source, m_History);
				m_ResetHistory = false;
			}
			command.GetTemporaryRT(Shader.PropertyToID("_SSRResolveTemp"), num, num, 0, FilterMode.Bilinear, context.sourceFormat);
			command.BlitFullscreenTriangle(context.source, Shader.PropertyToID("_SSRResolveTemp"), propertySheet, 1);
			propertySheet.properties.SetTexture(Shader.PropertyToID("_History"), m_History);
			command.BlitFullscreenTriangle(Shader.PropertyToID("_SSRResolveTemp"), m_Resolve, propertySheet, 2);
			command.CopyTexture(m_Resolve, 0, 0, m_History, 0, 0);
			command.ReleaseTemporaryRT(Shader.PropertyToID("_SSRResolveTemp"));
		}
		command.ReleaseTemporaryRT(Shader.PropertyToID("_Test"));
		if (m_MipIDs == null || m_MipIDs.Length == 0)
		{
			m_MipIDs = new int[12];
			for (int i = 0; i < 12; i++)
			{
				m_MipIDs[i] = Shader.PropertyToID("_SSRGaussianMip" + i);
			}
		}
		ComputeShader gaussianDownsample = context.resources.computeShaders.gaussianDownsample;
		int kernelIndex = gaussianDownsample.FindKernel("KMain");
		RenderTargetIdentifier rt = new RenderTargetIdentifier(m_Resolve);
		for (int j = 0; j < a; j++)
		{
			num >>= 1;
			command.GetTemporaryRT(m_MipIDs[j], num, num, 0, FilterMode.Bilinear, context.sourceFormat, RenderTextureReadWrite.Default, 1, enableRandomWrite: true);
			command.SetComputeTextureParam(gaussianDownsample, kernelIndex, "_Source", rt);
			command.SetComputeTextureParam(gaussianDownsample, kernelIndex, "_Result", m_MipIDs[j]);
			command.SetComputeVectorParam(gaussianDownsample, "_Size", new Vector4(num, num, 1f / (float)num, 1f / (float)num));
			command.DispatchCompute(gaussianDownsample, kernelIndex, num / 8, num / 8, 1);
			command.CopyTexture(m_MipIDs[j], 0, 0, m_Resolve, 0, j + 1);
			rt = m_MipIDs[j];
		}
		for (int k = 0; k < a; k++)
		{
			command.ReleaseTemporaryRT(m_MipIDs[k]);
		}
		propertySheet.properties.SetTexture(Shader.PropertyToID("_Resolve"), m_Resolve);
		command.BlitFullscreenTriangle(context.source, context.destination, propertySheet, 3);
		command.EndSample("Screen-space Reflections");
	}

	public override void Release()
	{
		RuntimeUtilities.Destroy(m_Resolve);
		RuntimeUtilities.Destroy(m_History);
		m_Resolve = null;
		m_History = null;
	}
}
