From d2e6eedf37d5687333abdab1123c951e595487f1 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Sat, 3 Jun 2023 18:58:04 +0200 Subject: [PATCH] Reworked the image effect integration, breaking up things and removing unnecessary parts to improve clarity and readability. This makes it more effective as an example of how to integrate FSR2 into a render pipeline. --- Assets/Scripts/Fsr2ImageEffect.cs | 173 +++++++++++++++++------------- 1 file changed, 101 insertions(+), 72 deletions(-) diff --git a/Assets/Scripts/Fsr2ImageEffect.cs b/Assets/Scripts/Fsr2ImageEffect.cs index e6e1f60..169ef2e 100644 --- a/Assets/Scripts/Fsr2ImageEffect.cs +++ b/Assets/Scripts/Fsr2ImageEffect.cs @@ -105,7 +105,8 @@ namespace FidelityFX private Fsr2Context _context; private Vector2Int _renderSize; private Vector2Int _displaySize; - private bool _reset; + private float _appliedBiasOffset; + private bool _resetHistory; private readonly Fsr2.DispatchDescription _dispatchDescription = new Fsr2.DispatchDescription(); private readonly Fsr2.GenerateReactiveDescription _genReactiveDescription = new Fsr2.GenerateReactiveDescription(); @@ -119,8 +120,7 @@ namespace FidelityFX private Fsr2.QualityMode _prevQualityMode; private Vector2Int _prevDisplaySize; - private bool _prevGenReactiveMask; - private bool _prevGenTcrMasks; + private bool _prevAutoExposure; private CommandBuffer _dispatchCommandBuffer; private CommandBuffer _opaqueInputCommandBuffer; @@ -141,13 +141,6 @@ namespace FidelityFX _displaySize = GetDisplaySize(); Fsr2.GetRenderResolutionFromQualityMode(out var renderWidth, out var renderHeight, _displaySize.x, _displaySize.y, qualityMode); _renderSize = new Vector2Int(renderWidth, renderHeight); - - // Apply a mipmap bias so that textures retain their sharpness - float biasOffset = Fsr2.GetMipmapBiasOffset(_renderSize.x, _displaySize.x); - if (!float.IsNaN(biasOffset)) - { - Callbacks.ApplyMipmapBias(biasOffset); - } if (!SystemInfo.supportsComputeShaders) { @@ -164,7 +157,30 @@ namespace FidelityFX } _helper = GetComponent(); + _copyWithDepthMaterial = new Material(Shader.Find("Hidden/BlitCopyWithDepth")); + CreateFsrContext(); + CreateCommandBuffers(); + } + + private void OnDisable() + { + DestroyCommandBuffers(); + DestroyFsrContext(); + + if (_copyWithDepthMaterial != null) + { + Destroy(_copyWithDepthMaterial); + _copyWithDepthMaterial = null; + } + + // Restore the camera's original state + _renderCamera.depthTextureMode = _originalDepthTextureMode; + _renderCamera.targetTexture = _originalRenderTarget; + } + + private void CreateFsrContext() + { // Initialize FSR2 context Fsr2.InitializationFlags flags = 0; if (_renderCamera.allowHDR) flags |= Fsr2.InitializationFlags.EnableHighDynamicRange; @@ -173,41 +189,33 @@ namespace FidelityFX _context = Fsr2.CreateContext(_displaySize, _renderSize, Callbacks, flags); - _dispatchCommandBuffer = new CommandBuffer { name = "FSR2 Dispatch" }; - _opaqueInputCommandBuffer = new CommandBuffer { name = "FSR2 Opaque Input" }; - - if (autoGenerateReactiveMask || autoGenerateTransparencyAndComposition) - { - _renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); - } - - _copyWithDepthMaterial = new Material(Shader.Find("Hidden/BlitCopyWithDepth")); - _prevDisplaySize = _displaySize; _prevQualityMode = qualityMode; - _prevGenReactiveMask = autoGenerateReactiveMask; - _prevGenTcrMasks = autoGenerateTransparencyAndComposition; + _prevAutoExposure = enableAutoExposure; + + ApplyMipmapBias(); } - private void OnDisable() + private void DestroyFsrContext() { - // Undo the current mipmap bias offset - float biasOffset = Fsr2.GetMipmapBiasOffset(_renderSize.x, _prevDisplaySize.x); - if (!float.IsNaN(biasOffset)) + UndoMipmapBias(); + + if (_context != null) { - Callbacks.ApplyMipmapBias(-biasOffset); + _context.Destroy(); + _context = null; } + } - // Restore the camera's original state - _renderCamera.depthTextureMode = _originalDepthTextureMode; - _renderCamera.targetTexture = _originalRenderTarget; + private void CreateCommandBuffers() + { + _dispatchCommandBuffer = new CommandBuffer { name = "FSR2 Dispatch" }; + _opaqueInputCommandBuffer = new CommandBuffer { name = "FSR2 Opaque Input" }; + _renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); + } - if (_copyWithDepthMaterial != null) - { - Destroy(_copyWithDepthMaterial); - _copyWithDepthMaterial = null; - } - + private void DestroyCommandBuffers() + { if (_opaqueInputCommandBuffer != null) { _renderCamera.RemoveCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); @@ -220,39 +228,49 @@ namespace FidelityFX _dispatchCommandBuffer.Release(); _dispatchCommandBuffer = null; } + } + + private void ApplyMipmapBias() + { + // Apply a mipmap bias so that textures retain their sharpness + float biasOffset = Fsr2.GetMipmapBiasOffset(_renderSize.x, _displaySize.x); + if (!float.IsNaN(biasOffset) && !float.IsInfinity(biasOffset)) + { + Callbacks.ApplyMipmapBias(biasOffset); + _appliedBiasOffset = biasOffset; + } + else + { + _appliedBiasOffset = 0f; + } + } - if (_context != null) + private void UndoMipmapBias() + { + // Undo the current mipmap bias offset + if (!float.IsNaN(_appliedBiasOffset) && !float.IsInfinity(_appliedBiasOffset) && _appliedBiasOffset != 0f) { - _context.Destroy(); - _context = null; + Callbacks.ApplyMipmapBias(-_appliedBiasOffset); + _appliedBiasOffset = 0f; } } private void Update() { + // Monitor for any changes in parameters that require a reset of the FSR2 context var displaySize = GetDisplaySize(); - if (displaySize.x != _prevDisplaySize.x || displaySize.y != _prevDisplaySize.y || qualityMode != _prevQualityMode) + if (displaySize.x != _prevDisplaySize.x || displaySize.y != _prevDisplaySize.y || qualityMode != _prevQualityMode || enableAutoExposure != _prevAutoExposure) { // Force all resources to be destroyed and recreated with the new settings OnDisable(); OnEnable(); } - - if ((autoGenerateReactiveMask || autoGenerateTransparencyAndComposition) != (_prevGenReactiveMask || _prevGenTcrMasks)) - { - if (autoGenerateReactiveMask || autoGenerateTransparencyAndComposition) - _renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); - else - _renderCamera.RemoveCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); - - _prevGenReactiveMask = autoGenerateReactiveMask; - _prevGenTcrMasks = autoGenerateTransparencyAndComposition; - } } - public void Reset() + public void ResetHistory() { - _reset = true; + // Reset the temporal accumulation, for when the camera cuts to a different location or angle + _resetHistory = true; } private void LateUpdate() @@ -278,21 +296,20 @@ namespace FidelityFX _opaqueInputCommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, _colorOpaqueOnly); } - // Set up the parameters to auto-generate a reactive mask if (autoGenerateReactiveMask) { - _genReactiveDescription.ColorOpaqueOnly = _colorOpaqueOnly; - _genReactiveDescription.ColorPreUpscale = null; - _genReactiveDescription.OutReactive = null; - _genReactiveDescription.RenderSize = _renderSize; - _genReactiveDescription.Scale = generateReactiveParameters.scale; - _genReactiveDescription.CutoffThreshold = generateReactiveParameters.cutoffThreshold; - _genReactiveDescription.BinaryValue = generateReactiveParameters.binaryValue; - _genReactiveDescription.Flags = generateReactiveParameters.flags; + SetupAutoReactiveDescription(); } + SetupDispatchDescription(); + + ApplyJitter(); + } + + private void SetupDispatchDescription() + { // Set up the main FSR2 dispatch parameters - // The input and output textures are left blank here, as they are already being bound elsewhere in this source file + // The input and output textures are left blank here, as they get bound directly through SetGlobalTexture and GetTemporaryRT elsewhere in this source file _dispatchDescription.Color = null; _dispatchDescription.Depth = null; _dispatchDescription.MotionVectors = null; @@ -316,8 +333,8 @@ namespace FidelityFX _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; + _dispatchDescription.Reset = _resetHistory; + _resetHistory = false; // Set up the parameters for the optional experimental auto-TCR feature _dispatchDescription.EnableAutoReactive = autoGenerateTransparencyAndComposition; @@ -335,7 +352,23 @@ namespace FidelityFX // Swap the near and far clip plane distances as FSR2 expects this when using inverted depth (_dispatchDescription.CameraNear, _dispatchDescription.CameraFar) = (_dispatchDescription.CameraFar, _dispatchDescription.CameraNear); } + } + + private void SetupAutoReactiveDescription() + { + // Set up the parameters to auto-generate a reactive mask + _genReactiveDescription.ColorOpaqueOnly = _colorOpaqueOnly; + _genReactiveDescription.ColorPreUpscale = null; + _genReactiveDescription.OutReactive = null; + _genReactiveDescription.RenderSize = _renderSize; + _genReactiveDescription.Scale = generateReactiveParameters.scale; + _genReactiveDescription.CutoffThreshold = generateReactiveParameters.cutoffThreshold; + _genReactiveDescription.BinaryValue = generateReactiveParameters.binaryValue; + _genReactiveDescription.Flags = generateReactiveParameters.flags; + } + private void ApplyJitter() + { // Perform custom jittering of the camera's projection matrix according to FSR2's recipe int jitterPhaseCount = Fsr2.GetJitterPhaseCount(_renderSize.x, _displaySize.x); Fsr2.GetJitterOffset(out float jitterX, out float jitterY, Time.frameCount, jitterPhaseCount); @@ -367,13 +400,13 @@ namespace FidelityFX if (autoGenerateReactiveMask) { + // The auto-reactive mask pass is executed separately from the main FSR2 passes _dispatchCommandBuffer.GetTemporaryRT(Fsr2ShaderIDs.UavAutoReactive, _renderSize.x, _renderSize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); _context.GenerateReactiveMask(_genReactiveDescription, _dispatchCommandBuffer); - _dispatchDescription.Reactive = Fsr2ShaderIDs.UavAutoReactive; } - // We are rendering to the backbuffer, so we need a temporary render texture for FSR2 to output to + // The backbuffer is not set up to allow random-write access, so we need a temporary render texture for FSR2 to output to _dispatchCommandBuffer.GetTemporaryRT(Fsr2ShaderIDs.UavUpscaledOutput, _displaySize.x, _displaySize.y, 0, default, GetDefaultFormat(), default, 1, true); _context.Dispatch(_dispatchDescription, _dispatchCommandBuffer); @@ -394,11 +427,7 @@ namespace FidelityFX } _dispatchCommandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavUpscaledOutput); - - if (autoGenerateReactiveMask) - { - _dispatchCommandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavAutoReactive); - } + _dispatchCommandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavAutoReactive); Graphics.ExecuteCommandBuffer(_dispatchCommandBuffer);