Browse Source

Reworked SPD atomic counter to use a compute buffer (which has atomic operations available on Metal) instead of a one-pixel read/write texture.

srp
Nico de Poel 3 weeks ago
parent
commit
d6ab273f8b
  1. 7
      Runtime/FSR2/Fsr2Context.cs
  2. 2
      Runtime/FSR2/Fsr2Pass.cs
  3. 17
      Runtime/FSR2/Fsr2Resources.cs
  4. 7
      Runtime/FSR3/Fsr3UpscalerContext.cs
  5. 4
      Runtime/FSR3/Fsr3UpscalerPass.cs
  6. 16
      Runtime/FSR3/Fsr3UpscalerResources.cs
  7. 2
      Shaders/ffx_fsr_unity_common.cginc
  8. 6
      Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h
  9. 6
      Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h

7
Runtime/FSR2/Fsr2Context.cs

@ -213,12 +213,11 @@ namespace FidelityFX.FSR2
// Auto exposure always used to track luma changes in locking logic
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);
commandBuffer.ClearRenderTarget(false, true, Color.clear);
}
// Reset atomic counter to 0
commandBuffer.SetBufferCounterValue(_resources.SpdAtomicCounter, 0);
// FSR3: need to clear here since we need the content of this surface for frame interpolation, so clearing in the lock pass is not an option
bool depthInverted = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDepthInverted) == Fsr2.InitializationFlags.EnableDepthInverted;
commandBuffer.SetRenderTarget(Fsr2ShaderIDs.UavReconstructedPrevNearestDepth);

2
Runtime/FSR2/Fsr2Pass.cs

@ -129,7 +129,7 @@ namespace FidelityFX.FSR2
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.SetComputeBufferParam(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[frameIndex]);

17
Runtime/FSR2/Fsr2Resources.cs

@ -35,7 +35,7 @@ namespace FidelityFX.FSR2
public Texture2D DefaultReactive;
public Texture2D LanczosLut;
public Texture2D MaximumBiasLut;
public RenderTexture SpdAtomicCounter;
public ComputeBuffer SpdAtomicCounter;
public RenderTexture SceneLuminance;
public RenderTexture AutoReactive;
public RenderTexture AutoComposition;
@ -87,9 +87,8 @@ namespace FidelityFX.FSR2
DefaultReactive.Apply();
// Resource FSR2_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE
// Despite what the original FSR2 codebase says, this resource really isn't aliasable. Resetting this counter to 0 every frame breaks auto-exposure on MacOS Metal.
SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR2_SpdAtomicCounter", enableRandomWrite = true };
SpdAtomicCounter.Create();
// Despite what the original FSR2 codebase says, this resource really isn't aliasable.
SpdAtomicCounter = new ComputeBuffer(1, sizeof(uint), ComputeBufferType.Counter, ComputeBufferMode.Dynamic);
// 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.
@ -191,6 +190,7 @@ namespace FidelityFX.FSR2
DestroyResource(ref DefaultExposure);
DestroyResource(ref MaximumBiasLut);
DestroyResource(ref LanczosLut);
DestroyResource(ref SpdAtomicCounter);
}
public void DestroyTcrAutogenResources()
@ -226,6 +226,15 @@ namespace FidelityFX.FSR2
resource = null;
}
private static void DestroyResource(ref ComputeBuffer resource)
{
if (resource == null)
return;
resource.Release();
resource = null;
}
private static void DestroyResource(RenderTexture[] resource)
{
for (int i = 0; i < resource.Length; ++i)

7
Runtime/FSR3/Fsr3UpscalerContext.cs

@ -232,12 +232,11 @@ namespace FidelityFX.FSR3
// Auto exposure always used to track luma changes in locking logic
commandBuffer.SetRenderTarget(_resources.FrameInfo[frameIndex ^ 1]);
commandBuffer.ClearRenderTarget(false, true, new Color(0f, 1e8f, 0f, 0f));
// Reset atomic counter to 0
commandBuffer.SetRenderTarget(_resources.SpdAtomicCounter);
commandBuffer.ClearRenderTarget(false, true, Color.clear);
}
// Reset atomic counter to 0
commandBuffer.SetBufferCounterValue(_resources.SpdAtomicCounter, 0);
// FSR3: need to clear here since we need the content of this surface for frame interpolation, so clearing in the lock pass is not an option
bool depthInverted = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) == Fsr3Upscaler.InitializationFlags.EnableDepthInverted;
commandBuffer.SetRenderTarget(_resources.ReconstructedPrevNearestDepth);

4
Runtime/FSR3/Fsr3UpscalerPass.cs

@ -157,7 +157,7 @@ namespace FidelityFX.FSR3
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvFarthestDepth, Fsr3ShaderIDs.UavIntermediate);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvFrameInfo, Resources.FrameInfo[frameIndex ^ 1]);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter);
commandBuffer.SetComputeBufferParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavFrameInfo, Resources.FrameInfo[frameIndex]);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip0, Resources.SpdMips, 0);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip1, Resources.SpdMips, 1);
@ -194,7 +194,7 @@ namespace FidelityFX.FSR3
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedVelocity);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter);
commandBuffer.SetComputeBufferParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip0, Resources.SpdMips, 0);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip1, Resources.SpdMips, 1);
commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip2, Resources.SpdMips, 2);

16
Runtime/FSR3/Fsr3UpscalerResources.cs

@ -35,7 +35,7 @@ namespace FidelityFX.FSR3
public Texture2D DefaultExposure;
public Texture2D DefaultReactive;
public RenderTexture SpdAtomicCounter;
public ComputeBuffer SpdAtomicCounter;
public RenderTexture SpdMips;
public RenderTexture DilatedVelocity;
public RenderTexture DilatedDepth;
@ -83,9 +83,8 @@ namespace FidelityFX.FSR3
DefaultExposure.Apply();
// Resource FSR3UPSCALER_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE
// Despite what the original FSR3 codebase says, this resource really isn't aliasable. Resetting this counter to 0 every frame breaks auto-exposure on MacOS Metal.
SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR3UPSCALER_SpdAtomicCounter", enableRandomWrite = true };
SpdAtomicCounter.Create();
// Despite what the original FSR3 codebase says, this resource really isn't aliasable.
SpdAtomicCounter = new ComputeBuffer(1, sizeof(uint), ComputeBufferType.Counter, ComputeBufferMode.Dynamic);
// Resource FSR3UPSCALER_SpdMips: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_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.
@ -235,6 +234,15 @@ namespace FidelityFX.FSR3
resource = null;
}
private static void DestroyResource(ref ComputeBuffer resource)
{
if (resource == null)
return;
resource.Release();
resource = null;
}
private static void DestroyResource(RenderTexture[] resource)
{
for (int i = 0; i < resource.Length; ++i)

2
Shaders/ffx_fsr_unity_common.cginc

@ -51,9 +51,7 @@
// #endif
// Hack to work around the lack of texture atomics on Metal
// TODO: replace texture atomics with a compute buffer setup
#if defined(SHADER_API_METAL)
#define InterlockedAdd(dest, val, orig) { (orig) = (dest); (dest) += (val); }
#define InterlockedMin(dest, val) { (dest) = min((dest), (val)); }
#define InterlockedMax(dest, val) { (dest) = max((dest), (val)); }
#endif

6
Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h

@ -467,7 +467,7 @@ SamplerState s_LinearClamp : register(s1);
RWTexture2D<FfxFloat32x2> rw_auto_exposure : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_AUTO_EXPOSURE);
#endif
#if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC
globallycoherent RWTexture2D<FfxUInt32> rw_spd_global_atomic : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC);
globallycoherent RWStructuredBuffer<FfxUInt32> rw_spd_global_atomic : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC);
#endif
#if defined FSR2_BIND_UAV_AUTOREACTIVE
@ -948,14 +948,14 @@ void SPD_SetMipmap(FfxInt32x2 iPxPos, FfxUInt32 slice, FfxFloat32 value)
void SPD_IncreaseAtomicCounter(inout FfxUInt32 spdCounter)
{
#if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC
InterlockedAdd(rw_spd_global_atomic[FfxInt32x2(0, 0)], 1, spdCounter);
InterlockedAdd(rw_spd_global_atomic[0], 1, spdCounter);
#endif // #if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC
}
void SPD_ResetAtomicCounter()
{
#if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC
rw_spd_global_atomic[FfxInt32x2(0, 0)] = 0;
rw_spd_global_atomic[0] = 0;
#endif // #if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC
}

6
Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h

@ -982,16 +982,16 @@ void StorePyramid(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat3
#endif
#if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC
globallycoherent RWTexture2D<FfxUInt32> rw_spd_global_atomic : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC);
globallycoherent RWStructuredBuffer<FfxUInt32> rw_spd_global_atomic : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC);
void SPD_IncreaseAtomicCounter(inout FfxUInt32 spdCounter)
{
InterlockedAdd(rw_spd_global_atomic[FfxInt32x2(0, 0)], 1, spdCounter);
InterlockedAdd(rw_spd_global_atomic[0], 1, spdCounter);
}
void SPD_ResetAtomicCounter()
{
rw_spd_global_atomic[FfxInt32x2(0, 0)] = 0;
rw_spd_global_atomic[0] = 0;
}
#endif

Loading…
Cancel
Save