From 97af0d9d8aaba3e8b1065f89beb0b163ff6515c3 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Sun, 11 May 2025 11:48:08 +0200 Subject: [PATCH] Changed auto-exposure texture into a double buffered resource (FSR2), to avoid issues with reading and writing to the same texture in the compute luminance pass. This allows fast exposure convergence without issues on OpenGL and Metal. --- Runtime/FSR2/Fsr2Context.cs | 6 +++--- Runtime/FSR2/Fsr2Pass.cs | 5 +++-- Runtime/FSR2/Fsr2Resources.cs | 11 +++++------ .../ffx_fsr2_compute_luminance_pyramid_pass.hlsl | 1 + Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h | 4 +++- .../shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h | 2 +- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Runtime/FSR2/Fsr2Context.cs b/Runtime/FSR2/Fsr2Context.cs index 5b0b238..cd02699 100644 --- a/Runtime/FSR2/Fsr2Context.cs +++ b/Runtime/FSR2/Fsr2Context.cs @@ -163,7 +163,7 @@ namespace FidelityFX.FSR2 // If auto exposure is enabled use the auto exposure SRV, otherwise what the app sends if ((_contextDescription.Flags & Fsr2.InitializationFlags.EnableAutoExposure) != 0) - dispatchParams.Exposure = new ResourceView(_resources.AutoExposure); + dispatchParams.Exposure = new ResourceView(_resources.AutoExposure[frameIndex]); else if (!dispatchParams.Exposure.IsValid) dispatchParams.Exposure = new ResourceView(_resources.DefaultExposure); @@ -211,8 +211,8 @@ namespace FidelityFX.FSR2 commandBuffer.ClearRenderTarget(false, true, Color.clear); // Auto exposure always used to track luma changes in locking logic - commandBuffer.SetRenderTarget(_resources.AutoExposure); - commandBuffer.ClearRenderTarget(false, true, new Color(0f, 1f, 0f, 0f)); + commandBuffer.SetRenderTarget(_resources.AutoExposure[frameIndex ^ 1]); + commandBuffer.ClearRenderTarget(false, true, new Color(0f, 1e8f, 0f, 0f)); // Reset atomic counter to 0 commandBuffer.SetRenderTarget(_resources.SpdAtomicCounter); diff --git a/Runtime/FSR2/Fsr2Pass.cs b/Runtime/FSR2/Fsr2Pass.cs index 94ef9e5..d791f79 100644 --- a/Runtime/FSR2/Fsr2Pass.cs +++ b/Runtime/FSR2/Fsr2Pass.cs @@ -127,11 +127,12 @@ namespace FidelityFX.FSR2 { ref var color = ref dispatchParams.Color; commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvAutoExposure, Resources.AutoExposure[frameIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavExposureMipLumaChange, Resources.SceneLuminance, ShadingChangeMipLevel); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavExposureMip5, Resources.SceneLuminance, 5); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoExposure, Resources.AutoExposure); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoExposure, Resources.AutoExposure[frameIndex]); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbSpd, _spdConstants, 0, Marshal.SizeOf()); @@ -271,7 +272,7 @@ namespace FidelityFX.FSR2 commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLanczosLut, Resources.LanczosLut); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvUpscaleMaximumBiasLut, Resources.MaximumBiasLut); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvSceneLuminanceMips, Resources.SceneLuminance); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvAutoExposure, Resources.AutoExposure); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvAutoExposure, Resources.AutoExposure[frameIndex]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLumaHistory, Resources.LumaHistory[frameIndex ^ 1]); commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavInternalUpscaled, Resources.InternalUpscaled[frameIndex]); diff --git a/Runtime/FSR2/Fsr2Resources.cs b/Runtime/FSR2/Fsr2Resources.cs index 4c1c50e..39a7120 100644 --- a/Runtime/FSR2/Fsr2Resources.cs +++ b/Runtime/FSR2/Fsr2Resources.cs @@ -36,10 +36,10 @@ namespace FidelityFX.FSR2 public Texture2D LanczosLut; public Texture2D MaximumBiasLut; public RenderTexture SpdAtomicCounter; - public RenderTexture AutoExposure; public RenderTexture SceneLuminance; public RenderTexture AutoReactive; public RenderTexture AutoComposition; + public readonly RenderTexture[] AutoExposure = new RenderTexture[2]; public readonly RenderTexture[] DilatedMotionVectors = new RenderTexture[2]; public readonly RenderTexture[] LockStatus = new RenderTexture[2]; public readonly RenderTexture[] InternalUpscaled = new RenderTexture[2]; @@ -91,10 +91,6 @@ namespace FidelityFX.FSR2 SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR2_SpdAtomicCounter", enableRandomWrite = true }; SpdAtomicCounter.Create(); - // Resource FSR2_AutoExposure: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE - AutoExposure = new RenderTexture(1, 1, 0, GraphicsFormat.R32G32_SFloat) { name = "FSR2_AutoExposure", enableRandomWrite = true }; - AutoExposure.Create(); - // Resource FSR2_ExposureMips: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE // This is a rather special case: it's an aliasable resource, but because we require a mipmap chain and bind specific mip levels per shader, we can't easily use temporary RTs for this. int w = contextDescription.MaxRenderSize.x / 2, h = contextDescription.MaxRenderSize.y / 2; @@ -102,6 +98,9 @@ namespace FidelityFX.FSR2 SceneLuminance = new RenderTexture(w, h, 0, GraphicsFormat.R16_SFloat, mipCount) { name = "FSR2_ExposureMips", enableRandomWrite = true, useMipMap = true, autoGenerateMips = false }; SceneLuminance.Create(); + // Resource FSR2_AutoExposure: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(AutoExposure, "FSR2_AutoExposure", Vector2Int.one, GraphicsFormat.R32G32_SFloat); + // Resources FSR2_InternalDilatedVelocity1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE CreateDoubleBufferedResource(DilatedMotionVectors, "FSR2_InternalDilatedVelocity", contextDescription.MaxRenderSize, GraphicsFormat.R16G16_SFloat); @@ -186,8 +185,8 @@ namespace FidelityFX.FSR2 DestroyResource(InternalUpscaled); DestroyResource(LockStatus); DestroyResource(DilatedMotionVectors); + DestroyResource(AutoExposure); DestroyResource(ref SceneLuminance); - DestroyResource(ref AutoExposure); DestroyResource(ref DefaultReactive); DestroyResource(ref DefaultExposure); DestroyResource(ref MaximumBiasLut); diff --git a/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl b/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl index 31de05f..08fe456 100644 --- a/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl +++ b/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl @@ -21,6 +21,7 @@ // THE SOFTWARE. #define FSR2_BIND_SRV_INPUT_COLOR 0 +#define FSR2_BIND_SRV_AUTO_EXPOSURE 1 #define FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC 0 #define FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE 1 diff --git a/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h b/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h index 9007343..25d8e52 100644 --- a/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h +++ b/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h @@ -908,7 +908,9 @@ void StorePrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN F FfxFloat32x2 SPD_LoadExposureBuffer() { -#if defined FSR2_BIND_UAV_AUTO_EXPOSURE +#if defined FSR2_BIND_SRV_AUTO_EXPOSURE + return r_auto_exposure[FfxInt32x2(0, 0)]; +#elif defined FSR2_BIND_UAV_AUTO_EXPOSURE return rw_auto_exposure[FfxInt32x2(0, 0)]; #else return FfxFloat32x2(0.f, 0.f); diff --git a/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h b/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h index a0e74b0..62b9899 100644 --- a/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h +++ b/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -FFX_GROUPSHARED FfxUInt32 spdCounter; +FFX_GROUPSHARED FfxUInt32 spdCounter = 0u; void SpdIncreaseAtomicCounter(FfxUInt32 slice) {