diff --git a/Assets/Scripts/Fsr2Controller.cs b/Assets/Scripts/Fsr2Controller.cs index f1b2287..54ab307 100644 --- a/Assets/Scripts/Fsr2Controller.cs +++ b/Assets/Scripts/Fsr2Controller.cs @@ -5,29 +5,42 @@ using UnityEngine.Rendering; namespace FidelityFX { + /// + /// 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. + /// [RequireComponent(typeof(Camera))] public class Fsr2Controller : MonoBehaviour { [SerializeField] private Fsr2.QualityMode qualityMode; + + [SerializeField] private bool performSharpenPass = true; + + [SerializeField, Range(0, 1)] private float sharpness = 0.8f; + private Fsr2Context _context; + private Camera _renderCamera; private RenderTexture _originalRenderTarget; + private DepthTextureMode _originalDepthTextureMode; private GameObject _displayCameraObject; private Camera _displayCamera; private Fsr2Dispatcher _dispatcher; + private readonly Fsr2.DispatchDescription _dispatchDescription = new Fsr2.DispatchDescription(); private Fsr2.QualityMode _prevQualityMode; private Vector2Int _prevScreenSize; private CommandBuffer _opaqueOnlyCommandBuffer; private CommandBuffer _inputsCommandBuffer; - + private void OnEnable() { - _renderCamera = GetComponent(); if (_displayCameraObject == null) { + // Create a helper object with a camera that outputs at screen resolution _displayCameraObject = new GameObject("FSR2 Camera Object"); _displayCameraObject.transform.SetParent(transform); _displayCameraObject.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); @@ -45,15 +58,19 @@ namespace FidelityFX _displayCamera.renderingPath = RenderingPath.Forward; _dispatcher = _displayCameraObject.AddComponent(); + _dispatcher.DispatchDescription = _dispatchDescription; } + + Fsr2.GetRenderResolutionFromQualityMode(out var renderWidth, out var renderHeight, Screen.width, Screen.height, qualityMode); + _context = Fsr2.CreateContext(new Vector2Int(Screen.width, Screen.height), new Vector2Int(renderWidth, renderHeight), Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation); - _dispatcher.renderCamera = _renderCamera; - _dispatcher.renderScale = 1.0f / Fsr2.GetUpscaleRatioFromQualityMode(qualityMode); + _dispatcher.Context = _context; _dispatcher.enabled = true; - Fsr2.GetRenderResolutionFromQualityMode(out var renderWidth, out var renderHeight, Screen.width, Screen.height, qualityMode); - + // Set up the original camera to output all of the required FSR2 input resources at the desired resolution + _renderCamera = GetComponent(); _originalRenderTarget = _renderCamera.targetTexture; // TODO: if this isn't null, could maybe reuse this for the output texture? + _originalDepthTextureMode = _renderCamera.depthTextureMode; _renderCamera.targetTexture = new RenderTexture( renderWidth, renderHeight, @@ -64,6 +81,7 @@ namespace FidelityFX _renderCamera.depthTextureMode |= DepthTextureMode.Depth | DepthTextureMode.MotionVectors; + // Create command buffers to bind the camera's output at the right moments in the rendering pipeline _opaqueOnlyCommandBuffer = new CommandBuffer { name = "FSR2 Opaque Input" }; // TODO: may need to copy the opaque-only render buffer to a temp RT here, in which case we'll need an additional CommandBuffer to release the temp RT again _opaqueOnlyCommandBuffer.SetGlobalTexture(Fsr2Pipeline.SrvOpaqueOnly, BuiltinRenderTextureType.CameraTarget, RenderTextureSubElement.Color); @@ -99,9 +117,17 @@ namespace FidelityFX _renderCamera.targetTexture.Release(); _renderCamera.targetTexture = _originalRenderTarget; + _renderCamera.depthTextureMode = _originalDepthTextureMode; + _dispatcher.Context = null; _dispatcher.enabled = false; _displayCamera.enabled = false; + + if (_context != null) + { + _context.Destroy(); + _context = null; + } } private void Update() @@ -116,21 +142,48 @@ namespace FidelityFX } } - private Rect _tempRect; + public void Reset() + { + _reset = true; + } + private Rect _tempRect; + private bool _reset; + private void OnPreRender() { _tempRect = _renderCamera.rect; _renderCamera.aspect = (Screen.width * _tempRect.width) / (Screen.height * _tempRect.height); _renderCamera.rect = new Rect(0, 0, 1, 1); + var targetTexture = _renderCamera.targetTexture; + + _dispatchDescription.Color = null; + _dispatchDescription.Depth = null; + _dispatchDescription.MotionVectors = null; + _dispatchDescription.Output = null; + _dispatchDescription.Exposure = null; + _dispatchDescription.Reactive = null; + _dispatchDescription.PreExposure = 0; + _dispatchDescription.EnableSharpening = performSharpenPass; + _dispatchDescription.Sharpness = sharpness; + _dispatchDescription.MotionVectorScale.x = -_renderCamera.pixelWidth; + _dispatchDescription.MotionVectorScale.y = -_renderCamera.pixelHeight; + _dispatchDescription.RenderSize = new Vector2Int(targetTexture.width, targetTexture.height); + _dispatchDescription.FrameTimeDelta = Time.unscaledDeltaTime; + _dispatchDescription.CameraNear = _renderCamera.nearClipPlane; + _dispatchDescription.CameraFar = _renderCamera.farClipPlane; + _dispatchDescription.CameraFovAngleVertical = _renderCamera.fieldOfView * Mathf.Deg2Rad; + _dispatchDescription.ViewSpaceToMetersFactor = 1.0f; // 1 unit is 1 meter in Unity + _dispatchDescription.Reset = _reset; + _reset = false; + // Perform custom jittering of the camera's projection matrix according to FSR2's instructions - int jitterPhaseCount = Fsr2.GetJitterPhaseCount(_renderCamera.targetTexture.width, Screen.width); + int jitterPhaseCount = Fsr2.GetJitterPhaseCount(targetTexture.width, Screen.width); Fsr2.GetJitterOffset(out float jitterX, out float jitterY, Time.frameCount, jitterPhaseCount); - _dispatcher.SetJitterOffset(new Vector2(jitterX, jitterY)); + _dispatchDescription.JitterOffset = new Vector2(jitterX, jitterY); - var targetTexture = _renderCamera.targetTexture; jitterX = 2.0f * jitterX / targetTexture.width; jitterY = 2.0f * jitterY / targetTexture.height; @@ -146,44 +199,17 @@ namespace FidelityFX } /// - /// 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. + /// Helper class to dispatch FSR2 commands on the display camera object, and render the final output texture. /// internal class Fsr2Dispatcher : MonoBehaviour { - [SerializeField] private bool performSharpenPass = true; - - [SerializeField, Range(0, 1)] private float sharpness = 0.8f; - - [SerializeField] private bool reset; - - [HideInInspector] public Camera renderCamera; - - [HideInInspector] public float renderScale; - - private bool _started; - - private Vector2Int DisplaySize => new Vector2Int(Screen.width, Screen.height); - private Vector2Int RenderSize => new Vector2Int(Mathf.FloorToInt(Screen.width * renderScale), Mathf.FloorToInt(Screen.height * renderScale)); - - private Fsr2Context _context; - private readonly Fsr2.DispatchDescription _dispatchDescription = new Fsr2.DispatchDescription(); + public Fsr2Context Context; + public Fsr2.DispatchDescription DispatchDescription; + private CommandBuffer _commandBuffer; - private void Start() - { - _started = true; - OnEnable(); - } - private void OnEnable() { - // Delay OnEnable until we're sure all fields and properties are set - if (!_started) - return; - - _context = Fsr2.CreateContext(DisplaySize, RenderSize, Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation); _commandBuffer = new CommandBuffer { name = "FSR2 Dispatch" }; } @@ -194,20 +220,8 @@ namespace FidelityFX _commandBuffer.Release(); _commandBuffer = null; } - - if (_context != null) - { - _context.Destroy(); - _context = null; - } - } - - public void SetJitterOffset(Vector2 jitterOffset) - { - _dispatchDescription.JitterOffset = jitterOffset; } - // For legacy built-in render pipeline private void OnRenderImage(RenderTexture src, RenderTexture dest) { _commandBuffer.Clear(); @@ -221,30 +235,10 @@ namespace FidelityFX else { // We are rendering to the backbuffer, so we need a temporary render texture for FSR2 to output to - _commandBuffer.GetTemporaryRT(Fsr2Pipeline.UavUpscaledOutput, DisplaySize.x, DisplaySize.y, 0, default, GraphicsFormat.R16G16B16A16_SFloat, 1, true); + _commandBuffer.GetTemporaryRT(Fsr2Pipeline.UavUpscaledOutput, Screen.width, Screen.height, 0, default, GraphicsFormat.R16G16B16A16_SFloat, 1, true); } - _dispatchDescription.Color = null; - _dispatchDescription.Depth = null; - _dispatchDescription.MotionVectors = null; - _dispatchDescription.Output = null; - _dispatchDescription.Exposure = null; - _dispatchDescription.Reactive = null; - _dispatchDescription.PreExposure = 0; - _dispatchDescription.EnableSharpening = performSharpenPass; - _dispatchDescription.Sharpness = sharpness; - _dispatchDescription.MotionVectorScale.x = -renderCamera.pixelWidth; - _dispatchDescription.MotionVectorScale.y = -renderCamera.pixelHeight; - _dispatchDescription.RenderSize = RenderSize; - _dispatchDescription.FrameTimeDelta = Time.unscaledDeltaTime; - _dispatchDescription.CameraNear = renderCamera.nearClipPlane; - _dispatchDescription.CameraFar = renderCamera.farClipPlane; - _dispatchDescription.CameraFovAngleVertical = renderCamera.fieldOfView * Mathf.Deg2Rad; - _dispatchDescription.ViewSpaceToMetersFactor = 1.0f; // 1 unit is 1 meter in Unity - _dispatchDescription.Reset = reset; - reset = false; - - _context.Dispatch(_dispatchDescription, _commandBuffer); + Context.Dispatch(DispatchDescription, _commandBuffer); // Output upscaled image to screen // TODO: if `dest` is null, we likely don't care about the depth & motion vectors anymore