diff --git a/Assets/Scripts/FSR2Thing.cs b/Assets/Scripts/FSR2Thing.cs deleted file mode 100644 index 98f2ec9..0000000 --- a/Assets/Scripts/FSR2Thing.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using FidelityFX; -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; - - public Fsr2Callbacks callbacks; - - private RenderTexture _upscaleRT; - private RenderTexture _rcasOutput; - private Texture2D _exposure; - - private Material _testMaterial; - private Material TestMaterial - { - get - { - if (_testMaterial == null) - { - var testShader = callbacks.LoadShader("FSR2/FSRTest"); - _testMaterial = new Material(testShader); - } - - return _testMaterial; - } - } - - private ComputeShader _rcasComputeShader; - private ComputeShader RCASComputeShader - { - get - { - if (_rcasComputeShader == null) - { - _rcasComputeShader = callbacks.LoadComputeShader("FSR2/ffx_fsr2_rcas_pass"); - } - - return _rcasComputeShader; - } - } - - private ComputeBuffer _fsr2ConstantsBuffer; - private readonly Fsr2Constants[] _fsr2ConstantsArray = { new Fsr2Constants() }; - - private ComputeBuffer _spdConstantsBuffer; - private readonly SpdConstants[] _spdConstantsArray = { new SpdConstants() }; - - private ComputeBuffer _rcasConstantsBuffer; - private readonly RcasConstants[] _rcasConstantsArray = new RcasConstants[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(); - - _fsr2ConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); - _spdConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); - _rcasConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); - } - - private void OnDisable() - { - if (_rcasConstantsBuffer != null) - { - _rcasConstantsBuffer.Release(); - _rcasConstantsBuffer = null; - } - - if (_spdConstantsBuffer != null) - { - _spdConstantsBuffer.Release(); - _spdConstantsBuffer = null; - } - - if (_fsr2ConstantsBuffer != null) - { - _fsr2ConstantsBuffer.Release(); - _fsr2ConstantsBuffer = 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); - - _fsr2ConstantsArray[0].preExposure = 1.0f; - _fsr2ConstantsBuffer.SetData(_fsr2ConstantsArray); - - if (performSharpenPass) - { - int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(sharpness) * (RcasConfigs.Count - 1)); - _rcasConstantsArray[0] = RcasConfigs[sharpnessIndex]; - _rcasConstantsBuffer.SetData(_rcasConstantsArray); - - // Run the RCAS sharpening filter on the upscaled image - int rcasKernel = RCASComputeShader.FindKernel("CS"); - RCASComputeShader.SetTexture(rcasKernel, "r_exposure", _exposure); - RCASComputeShader.SetTexture(rcasKernel, "r_rcas_input", _upscaleRT); - RCASComputeShader.SetTexture(rcasKernel, "rw_upscaled_output", _rcasOutput); - RCASComputeShader.SetConstantBuffer("cbFSR2", _fsr2ConstantsBuffer, 0, Marshal.SizeOf()); - RCASComputeShader.SetConstantBuffer("cbRCAS", _rcasConstantsBuffer, 0, Marshal.SizeOf()); - - const int threadGroupWorkRegionDimRcas = 16; - int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; - int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; - - RCASComputeShader.Dispatch(rcasKernel, threadGroupsX, threadGroupsY, 1); - - // Output sharpened image to screen - Graphics.Blit(_rcasOutput, dest); - } - else - { - Graphics.Blit(_upscaleRT, dest); - } - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - private struct RcasConstants - { - public RcasConstants(uint sharpness, uint halfSharp) - { - this.sharpness = sharpness; - this.halfSharp = halfSharp; - dummy0 = dummy1 = 0; - } - - public readonly uint sharpness; - public readonly uint halfSharp; - public readonly uint dummy0; - public readonly uint dummy1; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - private struct Fsr2Constants - { - public Vector2Int renderSize; - public Vector2Int displaySize; - public uint lumaMipDimensionsX, lumaMipDimensionsY; - public uint lumaMipLevelToUse; - public uint frameIndex; - public Vector2 displaySizeRcp; - public Vector2 jitterOffset; - public Vector4 deviceToViewDepth; - public Vector2 depthClipUVScale; - public Vector2 postLockStatusUVScale; - public Vector2 reactiveMaskDimRcp; - public Vector2 motionVectorScale; - public Vector2 downscaleFactor; - public float preExposure; - public float tanHalfFOV; - public Vector2 motionVectorJitterCancellation; - public float jitterPhaseCount; - public float lockInitialLifetime; - public float lockTickDelta; - public float deltaTime; - public float dynamicResChangeFactor; - public float lumaMipRcp; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - private struct SpdConstants - { - public uint mips; - public uint numWorkGroups; - public uint workGroupOffsetX, workGroupOffsetY; - public uint renderSizeX, renderSizeY; - } - - 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), - }; -} diff --git a/Assets/Scripts/Fsr2.cs b/Assets/Scripts/Fsr2.cs index e97a966..2232dd6 100644 --- a/Assets/Scripts/Fsr2.cs +++ b/Assets/Scripts/Fsr2.cs @@ -5,6 +5,9 @@ namespace FidelityFX { public static class Fsr2 { + // Allow overriding of certain Unity resource management behaviors by the programmer + public static Fsr2Callbacks Callbacks { get; set; } = new Fsr2Callbacks(); + public enum QualityMode { Quality = 1, @@ -28,7 +31,9 @@ namespace FidelityFX public static Fsr2Context CreateContext() { - throw new NotImplementedException(); + var context = new Fsr2Context(); + context.Create(Callbacks); + return context; } public static float GetUpscaleRatioFromQualityMode(QualityMode qualityMode) diff --git a/Assets/Scripts/Fsr2Context.cs b/Assets/Scripts/Fsr2Context.cs index e0ea63e..dd904f3 100644 --- a/Assets/Scripts/Fsr2Context.cs +++ b/Assets/Scripts/Fsr2Context.cs @@ -1,20 +1,160 @@ -namespace FidelityFX +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace FidelityFX { public class Fsr2Context { - public void Create() + private ComputeShader _rcasComputeShader; + + private ComputeBuffer _fsr2ConstantsBuffer; + private readonly Fsr2Constants[] _fsr2ConstantsArray = { new Fsr2Constants() }; + + private ComputeBuffer _spdConstantsBuffer; + private readonly SpdConstants[] _spdConstantsArray = { new SpdConstants() }; + + private ComputeBuffer _rcasConstantsBuffer; + private readonly RcasConstants[] _rcasConstantsArray = new RcasConstants[1]; + + public void Create(Fsr2Callbacks callbacks) { + if (_rcasComputeShader == null) + _rcasComputeShader = callbacks.LoadComputeShader("FSR2/ffx_fsr2_rcas_pass"); + _fsr2ConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); + _spdConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); + _rcasConstantsBuffer = new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); } - public void Dispatch() + public void Dispatch(RenderTexture src, RenderTexture dest, Texture2D exposure, bool enableSharpening, float sharpness) // TODO: collect parameters into a single object { + _fsr2ConstantsArray[0].preExposure = 1.0f; + _fsr2ConstantsBuffer.SetData(_fsr2ConstantsArray); + + if (enableSharpening) + { + int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(sharpness) * (RcasConfigs.Count - 1)); + _rcasConstantsArray[0] = RcasConfigs[sharpnessIndex]; + _rcasConstantsBuffer.SetData(_rcasConstantsArray); + // Run the RCAS sharpening filter on the upscaled image + int rcasKernel = _rcasComputeShader.FindKernel("CS"); + _rcasComputeShader.SetTexture(rcasKernel, "r_exposure", exposure); + _rcasComputeShader.SetTexture(rcasKernel, "r_rcas_input", src); + _rcasComputeShader.SetTexture(rcasKernel, "rw_upscaled_output", dest); + _rcasComputeShader.SetConstantBuffer("cbFSR2", _fsr2ConstantsBuffer, 0, Marshal.SizeOf()); + _rcasComputeShader.SetConstantBuffer("cbRCAS", _rcasConstantsBuffer, 0, Marshal.SizeOf()); + + const int threadGroupWorkRegionDimRcas = 16; + int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; + int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; + + _rcasComputeShader.Dispatch(rcasKernel, threadGroupsX, threadGroupsY, 1); + } + else + { + Graphics.Blit(src, dest); + } } public void Destroy() { - + if (_rcasConstantsBuffer != null) + { + _rcasConstantsBuffer.Release(); + _rcasConstantsBuffer = null; + } + + if (_spdConstantsBuffer != null) + { + _spdConstantsBuffer.Release(); + _spdConstantsBuffer = null; + } + + if (_fsr2ConstantsBuffer != null) + { + _fsr2ConstantsBuffer.Release(); + _fsr2ConstantsBuffer = null; + } + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + private struct Fsr2Constants + { + public Vector2Int renderSize; + public Vector2Int displaySize; + public uint lumaMipDimensionsX, lumaMipDimensionsY; + public uint lumaMipLevelToUse; + public uint frameIndex; + public Vector2 displaySizeRcp; + public Vector2 jitterOffset; + public Vector4 deviceToViewDepth; + public Vector2 depthClipUVScale; + public Vector2 postLockStatusUVScale; + public Vector2 reactiveMaskDimRcp; + public Vector2 motionVectorScale; + public Vector2 downscaleFactor; + public float preExposure; + public float tanHalfFOV; + public Vector2 motionVectorJitterCancellation; + public float jitterPhaseCount; + public float lockInitialLifetime; + public float lockTickDelta; + public float deltaTime; + public float dynamicResChangeFactor; + public float lumaMipRcp; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + private struct SpdConstants + { + public uint mips; + public uint numWorkGroups; + public uint workGroupOffsetX, workGroupOffsetY; + public uint renderSizeX, renderSizeY; } + + [Serializable, StructLayout(LayoutKind.Sequential)] + private struct RcasConstants + { + public RcasConstants(uint sharpness, uint halfSharp) + { + this.sharpness = sharpness; + this.halfSharp = halfSharp; + dummy0 = dummy1 = 0; + } + + public readonly uint sharpness; + public readonly uint halfSharp; + public readonly uint dummy0; + public readonly 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), + }; } } diff --git a/Assets/Scripts/Fsr2Controller.cs b/Assets/Scripts/Fsr2Controller.cs new file mode 100644 index 0000000..aaab6c7 --- /dev/null +++ b/Assets/Scripts/Fsr2Controller.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using FidelityFX; +using UnityEditor; +using UnityEngine; +using UnityEngine.Rendering; + +/// +/// This class is responsible for hooking into various Unity events and translating them to the FSR2 subsystem. +/// This includes creation and destruction of the FSR2 context, as well as dispatching commands at the right time. +/// This class also exposes various FSR2 parameters to the Unity inspector. +/// +public class Fsr2Controller : 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 Fsr2Context _context; + + private RenderTexture _upscaleRT; + private RenderTexture _rcasOutput; + private Texture2D _exposure; + + private Material _testMaterial; + private Material TestMaterial + { + get + { + if (_testMaterial == null) + { + var testShader = Fsr2.Callbacks.LoadShader("FSR2/FSRTest"); + _testMaterial = new Material(testShader); + } + + return _testMaterial; + } + } + + private void OnEnable() + { + RenderPipelineManager.endContextRendering += OnEndContextRendering; + + _context = Fsr2.CreateContext(); + + _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(); + } + + private void OnDisable() + { + if (_exposure != null) + { + Destroy(_exposure); + _exposure = null; + } + + if (_rcasOutput != null) + { + _rcasOutput.Release(); + _rcasOutput = null; + } + + if (_upscaleRT != null) + { + _upscaleRT.Release(); + _upscaleRT = null; + } + + if (_context != null) + { + _context.Destroy(); + _context = 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); + + _context.Dispatch(_upscaleRT, _rcasOutput, _exposure, performSharpenPass, sharpness); + + // Output sharpened image to screen + Graphics.Blit(_rcasOutput, dest); + } +} diff --git a/Assets/Scripts/FSR2Thing.cs.meta b/Assets/Scripts/Fsr2Controller.cs.meta similarity index 100% rename from Assets/Scripts/FSR2Thing.cs.meta rename to Assets/Scripts/Fsr2Controller.cs.meta diff --git a/Assets/Scripts/SubsampleTest.cs b/Assets/Scripts/SubsampleTest.cs index a95e569..8ffe1dd 100644 --- a/Assets/Scripts/SubsampleTest.cs +++ b/Assets/Scripts/SubsampleTest.cs @@ -18,9 +18,7 @@ public class SubsampleTest : MonoBehaviour [SerializeField] private bool enableJitter; - private Fsr2Callbacks callbacks = new Fsr2Callbacks(); - - private FSR2Thing fsr2Thing; + private Fsr2Controller _fsr2Controller; private void OnEnable() { @@ -35,12 +33,11 @@ public class SubsampleTest : MonoBehaviour outputCamera = outputCameraObject.AddComponent(); outputCamera.eventMask = 0; - fsr2Thing = outputCameraObject.AddComponent(); - fsr2Thing.callbacks = callbacks; - fsr2Thing.gameCamera = gameCamera; - fsr2Thing.outputCamera = outputCamera; - fsr2Thing.renderScale = renderScale; - fsr2Thing.enabled = true; + _fsr2Controller = outputCameraObject.AddComponent(); + _fsr2Controller.gameCamera = gameCamera; + _fsr2Controller.outputCamera = outputCamera; + _fsr2Controller.renderScale = renderScale; + _fsr2Controller.enabled = true; //outputCamera.CopyFrom(gameCamera); @@ -49,7 +46,7 @@ public class SubsampleTest : MonoBehaviour } else { - fsr2Thing.enabled = true; + _fsr2Controller.enabled = true; } gameCamera.targetTexture = new RenderTexture( @@ -65,18 +62,18 @@ public class SubsampleTest : MonoBehaviour // Adjust texture mipmap LOD bias by log2(renderResolution/displayResolution) - 1.0; // May need to leave this to the game dev, as we don't know which textures do and don't belong to the 3D scene float biasOffset = GetMipmapBiasOffset(); - callbacks.ApplyMipmapBias(biasOffset); + Fsr2.Callbacks.ApplyMipmapBias(biasOffset); } private void OnDisable() { float biasOffset = GetMipmapBiasOffset(); - callbacks.ApplyMipmapBias(-biasOffset); + Fsr2.Callbacks.ApplyMipmapBias(-biasOffset); gameCamera.targetTexture.Release(); gameCamera.targetTexture = null; - fsr2Thing.enabled = false; + _fsr2Controller.enabled = false; outputCamera.enabled = false; }