using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; public class FSR2Thing : MonoBehaviour { [SerializeField] private bool performSharpenPass = true; [SerializeField, Range(0, 1)] private float sharpness = 0.8f; [HideInInspector] public Camera gameCamera; [HideInInspector] public Camera outputCamera; [HideInInspector] public float renderScale; private RenderTexture _upscaleRT; private RenderTexture _rcasOutput; private Texture2D _exposure; private Material _testMaterial; private Material TestMaterial { get { if (_testMaterial == null) { var testShader = Shader.Find("FSR2/FSRTest"); _testMaterial = new Material(testShader); } return _testMaterial; } } private ComputeShader _rcasComputeShader; private ComputeShader RCASComputeShader { get { if (_rcasComputeShader == null) { // TODO: this is nasty, I don't like this. How do we manage/bind compute shaders best? // GPUInstancer used Resources, we modified that to load stuff from asset bundles instead. Maybe provide a custom loader callback interface? _rcasComputeShader = Resources.Load("Shaders/ffx_fsr2_rcas_pass"); } return _rcasComputeShader; } } private ComputeBuffer _rcasConstantBuffer; private RcasConfig[] _rcasConfig = new RcasConfig[1]; private void OnEnable() { RenderPipelineManager.endContextRendering += OnEndContextRendering; _upscaleRT = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGBHalf); _upscaleRT.Create(); _rcasOutput = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGBHalf); _rcasOutput.enableRandomWrite = true; _rcasOutput.Create(); _exposure = new Texture2D(1, 1); _exposure.name = "FSR2 Exposure"; _exposure.SetPixel(0, 0, Color.white); _exposure.Apply(); _rcasConstantBuffer = new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); } private void OnDisable() { if (_rcasConstantBuffer != null) { _rcasConstantBuffer.Release(); _rcasConstantBuffer = null; } if (_exposure != null) { Destroy(_exposure); _exposure = null; } if (_rcasOutput != null) { _rcasOutput.Release(); _rcasOutput = null; } if (_upscaleRT != null) { _upscaleRT.Release(); _upscaleRT = null; } RenderPipelineManager.endContextRendering -= OnEndContextRendering; } // For scriptable rendering pipeline private void OnEndContextRendering(ScriptableRenderContext context, List cameras) { Debug.Log($"OnEndContentRendering, cameras = {string.Join(", ", cameras.Select(c => c.name))}"); } // For legacy built-in render pipeline private void OnRenderImage(RenderTexture src, RenderTexture dest) { // Do a dumb upscale first Graphics.Blit(gameCamera.targetTexture, _upscaleRT, TestMaterial); if (performSharpenPass) { int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(sharpness) * (RcasConfigs.Count - 1)); _rcasConfig[0] = RcasConfigs[sharpnessIndex]; _rcasConstantBuffer.SetData(_rcasConfig); // Run the RCAS sharpening filter on the upscaled image int rcasKernel = RCASComputeShader.FindKernel("CS"); // TODO: create constant buffer (cbFSR2) with fPreExposure set to 1.0 RCASComputeShader.SetTexture(rcasKernel, "r_exposure", _exposure); RCASComputeShader.SetTexture(rcasKernel, "r_rcas_input", _upscaleRT); RCASComputeShader.SetTexture(rcasKernel, "rw_upscaled_output", _rcasOutput); RCASComputeShader.SetConstantBuffer("cbRCAS", _rcasConstantBuffer, 0, Marshal.SizeOf()); RCASComputeShader.Dispatch(rcasKernel, Screen.width, Screen.height, 1); // TODO: not sure how these thread groups work... // Output sharpened image to screen Graphics.Blit(_rcasOutput, dest); } else { Graphics.Blit(_upscaleRT, dest); } } [Serializable] private struct RcasConfig { public RcasConfig(uint sharpness, uint halfSharp) { this.sharpness = sharpness; this.halfSharp = halfSharp; dummy0 = dummy1 = 0; } public uint sharpness; public uint halfSharp; public uint dummy0; public uint dummy1; } private static readonly List RcasConfigs = new() { new(1048576000u, 872428544u), new(1049178080u, 877212745u), new(1049823372u, 882390168u), new(1050514979u, 887895276u), new(1051256227u, 893859143u), new(1052050675u, 900216232u), new(1052902144u, 907032080u), new(1053814727u, 914306687u), new(1054792807u, 922105590u), new(1055841087u, 930494326u), new(1056964608u, 939538432u), new(1057566688u, 944322633u), new(1058211980u, 949500056u), new(1058903587u, 955005164u), new(1059644835u, 960969031u), new(1060439283u, 967326120u), new(1061290752u, 974141968u), new(1062203335u, 981416575u), new(1063181415u, 989215478u), new(1064229695u, 997604214u), new(1065353216u, 1006648320), }; }