aboutsummaryrefslogtreecommitdiff
path: root/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler
diff options
context:
space:
mode:
Diffstat (limited to 'externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler')
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_accumulate.h172
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_callbacks_glsl.h903
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_common.h403
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h115
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h192
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h152
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h283
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_rcas.h112
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_reproject.h79
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_resources.h100
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_sample.h628
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change.h68
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h297
-rw-r--r--externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_upsample.h644
14 files changed, 4148 insertions, 0 deletions
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_accumulate.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_accumulate.h
new file mode 100644
index 0000000000..a8c6daa0e6
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_accumulate.h
@@ -0,0 +1,172 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+void Accumulate(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data)
+{
+ // Avoid invalid values when accumulation and upsampled weight is 0
+ data.fHistoryWeight *= FfxFloat32(data.fHistoryWeight > FSR3UPSCALER_FP16_MIN);
+ data.fHistoryWeight = ffxMax(FSR3UPSCALER_EPSILON, data.fHistoryWeight + data.fUpsampledWeight);
+
+#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT
+ //YCoCg -> RGB -> Tonemap -> YCoCg (Use RGB tonemapper to avoid color desaturation)
+ data.fUpsampledColor = RGBToYCoCg(Tonemap(YCoCgToRGB(data.fUpsampledColor)));
+ data.fHistoryColor = RGBToYCoCg(Tonemap(YCoCgToRGB(data.fHistoryColor)));
+#endif
+
+ const FfxFloat32 fAlpha = ffxSaturate(data.fUpsampledWeight / data.fHistoryWeight);
+ data.fHistoryColor = ffxLerp(data.fHistoryColor, data.fUpsampledColor, fAlpha);
+ data.fHistoryColor = YCoCgToRGB(data.fHistoryColor);
+
+#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT
+ data.fHistoryColor = InverseTonemap(data.fHistoryColor);
+#endif
+}
+
+void RectifyHistory(
+ const AccumulationPassCommonParams params,
+ FFX_PARAMETER_INOUT AccumulationPassData data
+)
+{
+ const FfxFloat32 f4kVelocityFactor = ffxSaturate(params.f4KVelocity / 20.0f);
+ const FfxFloat32 fDistanceFactor = ffxSaturate(0.75f - params.fFarthestDepthInMeters / 20.0f);
+ const FfxFloat32 fAccumulationFactor = 1.0f - params.fAccumulation;
+ const FfxFloat32 fReactiveFactor = ffxPow(params.fReactiveMask, 1.0f / 2.0f);
+ const FfxFloat32 fShadingChangeFactor = params.fShadingChange;
+ const FfxFloat32 fBoxScaleT = ffxMax(f4kVelocityFactor, ffxMax(fDistanceFactor, ffxMax(fAccumulationFactor, ffxMax(fReactiveFactor, fShadingChangeFactor))));
+
+ const FfxFloat32 fBoxScale = ffxLerp(3.0f, 1.0f, fBoxScaleT);
+ const FfxFloat32x3 fScaledBoxVec = data.clippingBox.boxVec * FfxFloat32x3(1.7f, 1.0f, 1.0f) * fBoxScale;
+
+ const FfxFloat32x3 fClampedScaledBoxVec = ffxMax(fScaledBoxVec, FfxFloat32x3(1.193e-7f, 1.193e-7f, 1.193e-7f));
+ const FfxFloat32x3 fTransformedHistoryColor = (data.fHistoryColor - data.clippingBox.boxCenter) / fClampedScaledBoxVec;
+
+ if (length(fTransformedHistoryColor)>1.f) {
+ const FfxFloat32x3 fClampedHistoryColor = normalize(fTransformedHistoryColor);
+ const FfxFloat32x3 fFinalClampedHistoryColor = (fClampedHistoryColor * fScaledBoxVec) + data.clippingBox.boxCenter;
+
+ // Scale history color using rectification info, also using accumulation mask to avoid potential invalid color protection
+ const FfxFloat32 fHistoryContribution = ffxMax(params.fLumaInstabilityFactor, data.fLockContributionThisFrame) * params.fAccumulation * (1 - params.fDisocclusion);
+ data.fHistoryColor = ffxLerp(fFinalClampedHistoryColor, data.fHistoryColor, ffxSaturate(fHistoryContribution));
+ }
+}
+
+void UpdateLockStatus(AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data)
+{
+ data.fLock *= FfxFloat32(params.bIsNewSample == false);
+
+ const FfxFloat32 fLifetimeDecreaseFactor = ffxMax(ffxSaturate(params.fShadingChange), ffxMax(params.fReactiveMask, params.fDisocclusion));
+ data.fLock = ffxMax(0.0f, data.fLock - fLifetimeDecreaseFactor * fLockMax);
+
+ // Compute this frame lock contribution
+ data.fLockContributionThisFrame = ffxSaturate(ffxSaturate(data.fLock - fLockThreshold) * (fLockMax - fLockThreshold));
+
+ const FfxFloat32 fNewLockIntensity = LoadRwNewLocks(params.iPxHrPos) * (1.0f - ffxMax(params.fShadingChange * 0, params.fReactiveMask));
+ data.fLock = ffxMax(0.0f, ffxMin(data.fLock + fNewLockIntensity, fLockMax));
+
+ // Preparing for next frame
+ const FfxFloat32 fLifetimeDecrease = (0.1f / JitterSequenceLength()) * (1.0f - fLifetimeDecreaseFactor);
+ data.fLock = ffxMax(0.0f, data.fLock - fLifetimeDecrease);
+
+ // we expect similar motion for next frame
+ // kill lock if that location is outside screen, avoid locks to be clamped to screen borders
+ const FfxFloat32x2 fEstimatedUvNextFrame = params.fHrUv - params.fMotionVector;
+ data.fLock *= FfxFloat32(IsUvInside(fEstimatedUvNextFrame) == true);
+}
+
+void ComputeBaseAccumulationWeight(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data)
+{
+ FfxFloat32 fBaseAccumulation = params.fAccumulation;
+
+ fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, 0.15f, ffxSaturate(ffxMax(0.0f, (params.f4KVelocity * VelocityFactor()) / 0.5f))));
+
+ data.fHistoryWeight = fBaseAccumulation;
+}
+
+void InitPassData(FfxInt32x2 iPxHrPos, FFX_PARAMETER_INOUT AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data)
+{
+ // Init constant params
+ params.iPxHrPos = iPxHrPos;
+ const FfxFloat32x2 fHrUv = (iPxHrPos + 0.5f) / UpscaleSize();
+ params.fHrUv = fHrUv;
+ params.fLrUvJittered = fHrUv + Jitter() / RenderSize();
+ params.fLrUv_HwSampler = ClampUv(params.fLrUvJittered, RenderSize(), MaxRenderSize());
+
+ params.fMotionVector = GetMotionVector(iPxHrPos, fHrUv);
+ params.f4KVelocity = Get4KVelocity(params.fMotionVector);
+
+ ComputeReprojectedUVs(params);
+
+ const FfxFloat32x2 fLumaInstabilityUv_HW = ClampUv(fHrUv, RenderSize(), MaxRenderSize());
+ params.fLumaInstabilityFactor = SampleLumaInstability(fLumaInstabilityUv_HW);
+
+ const FfxFloat32x2 fFarthestDepthUv = ClampUv(params.fLrUvJittered, RenderSize() / 2, GetFarthestDepthMip1ResourceDimensions());
+ params.fFarthestDepthInMeters = SampleFarthestDepthMip1(fFarthestDepthUv);
+ params.bIsNewSample = (params.bIsExistingSample == false || 0 == FrameIndex());
+
+ const FfxFloat32x4 fDilatedReactiveMasks = SampleDilatedReactiveMasks(params.fLrUv_HwSampler);
+ params.fReactiveMask = ffxSaturate(fDilatedReactiveMasks[REACTIVE]);
+ params.fDisocclusion = ffxSaturate(fDilatedReactiveMasks[DISOCCLUSION]);
+ params.fShadingChange = ffxSaturate(fDilatedReactiveMasks[SHADING_CHANGE]);
+ params.fAccumulation = ffxSaturate(fDilatedReactiveMasks[ACCUMULAION]);
+ params.fAccumulation *= FfxFloat32(round(params.fAccumulation * 100.0f) > 1.0f);
+
+ // Init variable data
+ data.fUpsampledColor = FfxFloat32x3(0.0f, 0.0f, 0.0f);
+ data.fHistoryColor = FfxFloat32x3(0.0f, 0.0f, 0.0f);
+ data.fHistoryWeight = 1.0f;
+ data.fUpsampledWeight = 0.0f;
+ data.fLock = 0.0f;
+ data.fLockContributionThisFrame = 0.0f;
+}
+
+void Accumulate(FfxInt32x2 iPxHrPos)
+{
+ AccumulationPassCommonParams params;
+ AccumulationPassData data;
+ InitPassData(iPxHrPos, params, data);
+
+ if (params.bIsExistingSample && !params.bIsNewSample) {
+ ReprojectHistoryColor(params, data);
+ }
+
+ UpdateLockStatus(params, data);
+
+ ComputeBaseAccumulationWeight(params, data);
+
+ ComputeUpsampledColorAndWeight(params, data);
+
+ RectifyHistory(params, data);
+
+ Accumulate(params, data);
+
+ data.fHistoryColor /= Exposure();
+
+ data.fHistoryColor = ffxMax(data.fHistoryColor, FfxFloat32x3(0.0f, 0.0f, 0.0f));
+
+ StoreInternalColorAndWeight(iPxHrPos, FfxFloat32x4(data.fHistoryColor, data.fLock));
+
+ // Output final color when RCAS is disabled
+#if FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING == 0
+ StoreUpscaledOutput(iPxHrPos, data.fHistoryColor);
+#endif
+ StoreNewLocks(iPxHrPos, 0);
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_callbacks_glsl.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_callbacks_glsl.h
new file mode 100644
index 0000000000..d0cbf9d32e
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_callbacks_glsl.h
@@ -0,0 +1,903 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include "ffx_fsr3upscaler_resources.h"
+
+#if defined(FFX_GPU)
+#include "ffx_core.h"
+#endif // #if defined(FFX_GPU)
+
+#if defined(FFX_GPU)
+#ifndef FFX_PREFER_WAVE64
+#define FFX_PREFER_WAVE64
+#endif // FFX_PREFER_WAVE64
+
+#if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER)
+ layout (set = 0, binding = FSR3UPSCALER_BIND_CB_FSR3UPSCALER, std140) uniform cbFSR3UPSCALER_t
+ {
+ FfxInt32x2 iRenderSize;
+ FfxInt32x2 iPreviousFrameRenderSize;
+
+ FfxInt32x2 iUpscaleSize;
+ FfxInt32x2 iPreviousFrameUpscaleSize;
+
+ FfxInt32x2 iMaxRenderSize;
+ FfxInt32x2 iMaxUpscaleSize;
+
+ FfxFloat32x4 fDeviceToViewDepth;
+
+ FfxFloat32x2 fJitter;
+ FfxFloat32x2 fPreviousFrameJitter;
+
+ FfxFloat32x2 fMotionVectorScale;
+ FfxFloat32x2 fDownscaleFactor;
+
+ FfxFloat32x2 fMotionVectorJitterCancellation;
+ FfxFloat32 fTanHalfFOV;
+ FfxFloat32 fJitterSequenceLength;
+
+ FfxFloat32 fDeltaTime;
+ FfxFloat32 fDeltaPreExposure;
+ FfxFloat32 fViewSpaceToMetersFactor;
+ FfxFloat32 fFrameIndex;
+
+ FfxFloat32 fVelocityFactor;
+ FfxFloat32 fReactivenessScale;
+ FfxFloat32 fShadingChangeScale;
+ FfxFloat32 fAccumulationAddedPerFrame;
+ FfxFloat32 fMinDisocclusionAccumulation;
+ } cbFSR3Upscaler;
+
+
+FfxInt32x2 RenderSize()
+{
+ return cbFSR3Upscaler.iRenderSize;
+}
+
+FfxInt32x2 PreviousFrameRenderSize()
+{
+ return cbFSR3Upscaler.iPreviousFrameRenderSize;
+}
+
+FfxInt32x2 MaxRenderSize()
+{
+ return cbFSR3Upscaler.iMaxRenderSize;
+}
+
+FfxInt32x2 UpscaleSize()
+{
+ return cbFSR3Upscaler.iUpscaleSize;
+}
+
+FfxInt32x2 PreviousFrameUpscaleSize()
+{
+ return cbFSR3Upscaler.iPreviousFrameUpscaleSize;
+}
+
+FfxInt32x2 MaxUpscaleSize()
+{
+ return cbFSR3Upscaler.iMaxUpscaleSize;
+}
+
+FfxFloat32x2 Jitter()
+{
+ return cbFSR3Upscaler.fJitter;
+}
+
+FfxFloat32x2 PreviousFrameJitter()
+{
+ return cbFSR3Upscaler.fPreviousFrameJitter;
+}
+
+FfxFloat32x4 DeviceToViewSpaceTransformFactors()
+{
+ return cbFSR3Upscaler.fDeviceToViewDepth;
+}
+
+FfxFloat32x2 MotionVectorScale()
+{
+ return cbFSR3Upscaler.fMotionVectorScale;
+}
+
+FfxFloat32x2 DownscaleFactor()
+{
+ return cbFSR3Upscaler.fDownscaleFactor;
+}
+
+FfxFloat32x2 MotionVectorJitterCancellation()
+{
+ return cbFSR3Upscaler.fMotionVectorJitterCancellation;
+}
+
+FfxFloat32 TanHalfFoV()
+{
+ return cbFSR3Upscaler.fTanHalfFOV;
+}
+
+FfxFloat32 JitterSequenceLength()
+{
+ return cbFSR3Upscaler.fJitterSequenceLength;
+}
+
+FfxFloat32 DeltaTime()
+{
+ return cbFSR3Upscaler.fDeltaTime;
+}
+
+FfxFloat32 DeltaPreExposure()
+{
+ return cbFSR3Upscaler.fDeltaPreExposure;
+}
+
+FfxFloat32 ViewSpaceToMetersFactor()
+{
+ return cbFSR3Upscaler.fViewSpaceToMetersFactor;
+}
+
+FfxFloat32 FrameIndex()
+{
+ return cbFSR3Upscaler.fFrameIndex;
+}
+
+FfxFloat32 VelocityFactor()
+{
+ return cbFSR3Upscaler.fVelocityFactor;
+}
+
+FfxFloat32 AccumulationAddedPerFrame()
+{
+ return cbFSR3Upscaler.fAccumulationAddedPerFrame;
+}
+
+FfxFloat32 MinDisocclusionAccumulation()
+{
+ return cbFSR3Upscaler.fMinDisocclusionAccumulation;
+}
+
+#endif // #if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER)
+
+
+#if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE)
+layout(set = 0, binding = FSR3UPSCALER_BIND_CB_AUTOREACTIVE, std140) uniform cbGenerateReactive_t
+{
+ FfxFloat32 fTcThreshold; // 0.1 is a good starting value, lower will result in more TC pixels
+ FfxFloat32 fTcScale;
+ FfxFloat32 fReactiveScale;
+ FfxFloat32 fReactiveMax;
+} cbGenerateReactive;
+
+FfxFloat32 TcThreshold()
+{
+ return cbGenerateReactive.fTcThreshold;
+}
+
+FfxFloat32 TcScale()
+{
+ return cbGenerateReactive.fTcScale;
+}
+
+FfxFloat32 ReactiveScale()
+{
+ return cbGenerateReactive.fReactiveScale;
+}
+
+FfxFloat32 ReactiveMax()
+{
+ return cbGenerateReactive.fReactiveMax;
+}
+#endif // #if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE)
+
+#if defined(FSR3UPSCALER_BIND_CB_RCAS)
+layout(set = 0, binding = FSR3UPSCALER_BIND_CB_RCAS, std140) uniform cbRCAS_t
+{
+ FfxUInt32x4 rcasConfig;
+} cbRCAS;
+
+FfxUInt32x4 RCASConfig()
+{
+ return cbRCAS.rcasConfig;
+}
+#endif // #if defined(FSR3UPSCALER_BIND_CB_RCAS)
+
+
+#if defined(FSR3UPSCALER_BIND_CB_REACTIVE)
+layout(set = 0, binding = FSR3UPSCALER_BIND_CB_REACTIVE, std140) uniform cbGenerateReactive_t
+{
+ FfxFloat32 gen_reactive_scale;
+ FfxFloat32 gen_reactive_threshold;
+ FfxFloat32 gen_reactive_binaryValue;
+ FfxUInt32 gen_reactive_flags;
+} cbGenerateReactive;
+
+FfxFloat32 GenReactiveScale()
+{
+ return cbGenerateReactive.gen_reactive_scale;
+}
+
+FfxFloat32 GenReactiveThreshold()
+{
+ return cbGenerateReactive.gen_reactive_threshold;
+}
+
+FfxFloat32 GenReactiveBinaryValue()
+{
+ return cbGenerateReactive.gen_reactive_binaryValue;
+}
+
+FfxUInt32 GenReactiveFlags()
+{
+ return cbGenerateReactive.gen_reactive_flags;
+}
+#endif // #if defined(FSR3UPSCALER_BIND_CB_REACTIVE)
+
+
+#if defined(FSR3UPSCALER_BIND_CB_SPD)
+layout(set = 0, binding = FSR3UPSCALER_BIND_CB_SPD, std140) uniform cbSPD_t
+{
+ FfxUInt32 mips;
+ FfxUInt32 numWorkGroups;
+ FfxUInt32x2 workGroupOffset;
+ FfxUInt32x2 renderSize;
+} cbSPD;
+
+FfxUInt32 MipCount()
+{
+ return cbSPD.mips;
+}
+
+FfxUInt32 NumWorkGroups()
+{
+ return cbSPD.numWorkGroups;
+}
+
+FfxUInt32x2 WorkGroupOffset()
+{
+ return cbSPD.workGroupOffset;
+}
+
+FfxUInt32x2 SPD_RenderSize()
+{
+ return cbSPD.renderSize;
+}
+#endif // #if defined(FSR3UPSCALER_BIND_CB_SPD)
+
+layout (set = 0, binding = 1000) uniform sampler s_PointClamp;
+layout (set = 0, binding = 1001) uniform sampler s_LinearClamp;
+
+#if defined(FSR3UPSCALER_BIND_SRV_SPD_MIPS)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_SPD_MIPS) uniform texture2D r_spd_mips;
+
+FfxInt32x2 GetSPDMipDimensions(FfxUInt32 uMipLevel)
+{
+ return textureSize(r_spd_mips, int(uMipLevel)).xy;
+}
+
+FfxFloat32x2 SampleSPDMipLevel(FfxFloat32x2 fUV, FfxUInt32 mipLevel)
+{
+ return textureLod(sampler2D(r_spd_mips, s_LinearClamp), fUV, float(mipLevel)).rg;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_INPUT_DEPTH) uniform texture2D r_input_depth;
+
+FfxFloat32 LoadInputDepth(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_input_depth, iPxPos, 0).r;
+}
+
+FfxFloat32 SampleInputDepth(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_input_depth, s_LinearClamp), fUV, 0.0).x;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_REACTIVE_MASK)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_REACTIVE_MASK) uniform texture2D r_reactive_mask;
+
+FfxFloat32 LoadReactiveMask(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_reactive_mask, FfxInt32x2(iPxPos), 0).r * cbFSR3Upscaler.fReactivenessScale;
+}
+
+FfxInt32x2 GetReactiveMaskResourceDimensions()
+{
+ return textureSize(r_reactive_mask, 0).xy;
+}
+
+FfxFloat32 SampleReactiveMask(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_reactive_mask, s_LinearClamp), fUV, 0.0).x * cbFSR3Upscaler.fReactivenessScale;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK) uniform texture2D r_transparency_and_composition_mask;
+
+FfxFloat32 LoadTransparencyAndCompositionMask(FfxUInt32x2 iPxPos)
+{
+ return texelFetch(r_transparency_and_composition_mask, FfxInt32x2(iPxPos), 0).r;
+}
+
+FfxInt32x2 GetTransparencyAndCompositionMaskResourceDimensions()
+{
+ return textureSize(r_transparency_and_composition_mask, 0).xy;
+}
+
+FfxFloat32 SampleTransparencyAndCompositionMask(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_transparency_and_composition_mask, s_LinearClamp), fUV, 0.0).x;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_INPUT_COLOR)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_INPUT_COLOR) uniform texture2D r_input_color_jittered;
+
+FfxFloat32x3 LoadInputColor(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_input_color_jittered, iPxPos, 0).rgb;
+}
+
+FfxFloat32x3 SampleInputColor(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_input_color_jittered, s_LinearClamp), fUV, 0.0).rgb;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS) uniform texture2D r_input_motion_vectors;
+
+FfxFloat32x2 LoadInputMotionVector(FfxInt32x2 iPxDilatedMotionVectorPos)
+{
+ FfxFloat32x2 fSrcMotionVector = texelFetch(r_input_motion_vectors, iPxDilatedMotionVectorPos, 0).xy;
+
+ FfxFloat32x2 fUvMotionVector = fSrcMotionVector * MotionVectorScale();
+
+#if FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS
+ fUvMotionVector -= MotionVectorJitterCancellation();
+#endif
+
+ return fUvMotionVector;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED) uniform texture2D r_internal_upscaled_color;
+
+FfxFloat32x4 LoadHistory(FfxInt32x2 iPxHistory)
+{
+ return texelFetch(r_internal_upscaled_color, iPxHistory, 0);
+}
+
+FfxFloat32x4 SampleHistory(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_internal_upscaled_color, s_LinearClamp), fUV, 0.0);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_LUMA_HISTORY)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_LUMA_HISTORY, rgba8) uniform image2D rw_luma_history;
+
+void StoreLumaHistory(FfxInt32x2 iPxPos, FfxFloat32x4 fLumaHistory)
+{
+ imageStore(rw_luma_history, iPxPos, fLumaHistory);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_LUMA_HISTORY)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_LUMA_HISTORY) uniform texture2D r_luma_history;
+
+FfxFloat32x4 LoadLumaHistory(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_luma_history, iPxPos, 0);
+}
+
+FfxFloat32x4 SampleLumaHistory(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_luma_history, s_LinearClamp), fUV, 0.0);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_RCAS_INPUT)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_RCAS_INPUT) uniform texture2D r_rcas_input;
+
+FfxFloat32x4 LoadRCAS_Input(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_rcas_input, iPxPos, 0);
+}
+
+FfxFloat32x3 SampleRCAS_Input(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_rcas_input, s_LinearClamp), fUV, 0.0).rgb;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED, rgba16f) writeonly uniform image2D rw_internal_upscaled_color;
+
+void StoreReprojectedHistory(FfxInt32x2 iPxHistory, FfxFloat32x4 fHistory)
+{
+ imageStore(rw_internal_upscaled_color, iPxHistory, fHistory);
+}
+
+void StoreInternalColorAndWeight(FfxInt32x2 iPxPos, FfxFloat32x4 fColorAndWeight)
+{
+ imageStore(rw_internal_upscaled_color, iPxPos, fColorAndWeight);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT /* app controlled format */) writeonly uniform image2D rw_upscaled_output;
+
+void StoreUpscaledOutput(FfxInt32x2 iPxPos, FfxFloat32x3 fColor)
+{
+ imageStore(rw_upscaled_output, iPxPos, FfxFloat32x4(fColor, 1.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_ACCUMULATION)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_ACCUMULATION) uniform texture2D r_accumulation;
+
+FfxFloat32 SampleAccumulation(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_accumulation, s_LinearClamp), fUV, 0.0).x;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_ACCUMULATION)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_ACCUMULATION, r8) uniform image2D rw_accumulation;
+
+void StoreAccumulation(FfxInt32x2 iPxPos, FfxFloat32 fAccumulation)
+{
+ imageStore(rw_accumulation, iPxPos, vec4(fAccumulation, 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_SHADING_CHANGE)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_SHADING_CHANGE) uniform texture2D r_shading_change;
+
+FfxFloat32 LoadShadingChange(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_shading_change, iPxPos, 0).x * cbFSR3Upscaler.fShadingChangeScale;
+}
+
+FfxFloat32 SampleShadingChange(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_shading_change, s_LinearClamp), fUV, 0.0).x * cbFSR3Upscaler.fShadingChangeScale;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_SHADING_CHANGE)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SHADING_CHANGE, r8) uniform image2D rw_shading_change;
+
+void StoreShadingChange(FfxInt32x2 iPxPos, FfxFloat32 fShadingChange)
+{
+ imageStore(rw_shading_change, iPxPos, vec4(fShadingChange, 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH) uniform texture2D r_farthest_depth;
+
+FfxInt32x2 GetFarthestDepthResourceDimensions()
+{
+ return textureSize(r_farthest_depth, 0).xy;
+}
+
+FfxFloat32 LoadFarthestDepth(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_farthest_depth, iPxPos, 0).x;
+}
+
+FfxFloat32 SampleFarthestDepth(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_farthest_depth, s_LinearClamp), fUV, 0.0).x;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH, r16f) uniform image2D rw_farthest_depth;
+
+void StoreFarthestDepth(FfxInt32x2 iPxPos, FfxFloat32 fDepth)
+{
+ imageStore(rw_farthest_depth, iPxPos, vec4(fDepth, 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH_MIP1)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH_MIP1) uniform texture2D r_farthest_depth_mip1;
+
+FfxInt32x2 GetFarthestDepthMip1ResourceDimensions()
+{
+ return textureSize(r_farthest_depth_mip1, 0).xy;
+}
+
+FfxFloat32 LoadFarthestDepthMip1(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_farthest_depth_mip1, iPxPos, 0).x;
+}
+
+FfxFloat32 SampleFarthestDepthMip1(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_farthest_depth_mip1, s_LinearClamp), fUV, 0.0).x;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH_MIP1)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH_MIP1, r16f) uniform image2D rw_farthest_depth_mip1;
+
+void StoreFarthestDepthMip1(FfxInt32x2 iPxPos, FfxFloat32 fDepth)
+{
+ imageStore(rw_farthest_depth_mip1, iPxPos, vec4(fDepth, 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_CURRENT_LUMA)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_CURRENT_LUMA) uniform texture2D r_current_luma;
+
+FfxFloat32 LoadCurrentLuma(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_current_luma, iPxPos, 0).r;
+}
+
+FfxFloat32 SampleCurrentLuma(FfxFloat32x2 uv)
+{
+ return textureLod(sampler2D(r_current_luma, s_LinearClamp), uv, 0.0).r;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_CURRENT_LUMA)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_CURRENT_LUMA, r16f) uniform image2D rw_current_luma;
+
+void StoreCurrentLuma(FfxInt32x2 iPxPos, FfxFloat32 fLuma)
+{
+ imageStore(rw_current_luma, iPxPos, vec4(fLuma, 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_LUMA_INSTABILITY)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_LUMA_INSTABILITY) uniform texture2D r_luma_instability;
+
+FfxFloat32 SampleLumaInstability(FfxFloat32x2 uv)
+{
+ return textureLod(sampler2D(r_luma_instability, s_LinearClamp), uv, 0.0).x;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_LUMA_INSTABILITY)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_LUMA_INSTABILITY, r16f) uniform image2D rw_luma_instability;
+
+void StoreLumaInstability(FfxInt32x2 iPxPos, FfxFloat32 fLumaInstability)
+{
+ imageStore(rw_luma_instability, iPxPos, vec4(fLumaInstability, 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_PREVIOUS_LUMA)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_PREVIOUS_LUMA) uniform texture2D r_previous_luma;
+
+FfxFloat32 LoadPreviousLuma(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_previous_luma, iPxPos, 0).r;
+}
+
+FfxFloat32 SamplePreviousLuma(FfxFloat32x2 uv)
+{
+ return textureLod(sampler2D(r_previous_luma, s_LinearClamp), uv, 0.0).r;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_NEW_LOCKS)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_NEW_LOCKS) uniform texture2D r_new_locks;
+
+FfxFloat32 LoadNewLocks(FfxInt32x2 iPxPos)
+{
+ return texelFetch(r_new_locks, iPxPos, 0).r;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_NEW_LOCKS)
+layout(set = 0, binding = FSR3UPSCALER_BIND_UAV_NEW_LOCKS, r8) uniform image2D rw_new_locks;
+
+FfxFloat32 LoadRwNewLocks(FfxInt32x2 iPxPos)
+{
+ return imageLoad(rw_new_locks, iPxPos).r;
+}
+
+void StoreNewLocks(FfxInt32x2 iPxPos, FfxFloat32 newLock)
+{
+ imageStore(rw_new_locks, iPxPos, vec4(newLock, 0, 0, 0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH) uniform utexture2D r_reconstructed_previous_nearest_depth;
+
+FfxFloat32 LoadReconstructedPrevDepth(FfxInt32x2 iPxPos)
+{
+ return uintBitsToFloat(texelFetch(r_reconstructed_previous_nearest_depth, iPxPos, 0).r);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH, r32ui) uniform uimage2D rw_reconstructed_previous_nearest_depth;
+
+void StoreReconstructedDepth(FfxInt32x2 iPxSample, FfxFloat32 fDepth)
+{
+ FfxUInt32 uDepth = floatBitsToUint(fDepth);
+
+ #if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH
+ imageAtomicMax(rw_reconstructed_previous_nearest_depth, iPxSample, uDepth);
+ #else
+ imageAtomicMin(rw_reconstructed_previous_nearest_depth, iPxSample, uDepth); // min for standard, max for inverted depth
+ #endif
+}
+
+void SetReconstructedDepth(FfxInt32x2 iPxSample, FfxUInt32 uValue)
+{
+ imageStore(rw_reconstructed_previous_nearest_depth, iPxSample, uvec4(uValue, 0, 0, 0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_DILATED_DEPTH)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_DILATED_DEPTH, r32f) writeonly uniform image2D rw_dilated_depth;
+
+void StoreDilatedDepth(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32 fDepth)
+{
+ imageStore(rw_dilated_depth, iPxPos, vec4(fDepth, 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS, rg16f) writeonly uniform image2D rw_dilated_motion_vectors;
+
+void StoreDilatedMotionVector(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fMotionVector)
+{
+ imageStore(rw_dilated_motion_vectors, iPxPos, vec4(fMotionVector, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS) uniform texture2D r_dilated_motion_vectors;
+
+FfxFloat32x2 LoadDilatedMotionVector(FfxInt32x2 iPxInput)
+{
+ return texelFetch(r_dilated_motion_vectors, iPxInput, 0).xy;
+}
+
+FfxFloat32x2 SampleDilatedMotionVector(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_dilated_motion_vectors, s_LinearClamp), fUV, 0.0).xy;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_DILATED_DEPTH)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_DILATED_DEPTH) uniform texture2D r_dilated_depth;
+
+FfxFloat32 LoadDilatedDepth(FfxInt32x2 iPxInput)
+{
+ return texelFetch(r_dilated_depth, iPxInput, 0).r;
+}
+
+FfxFloat32 SampleDilatedDepth(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_dilated_depth, s_LinearClamp), fUV, 0.0).r;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE) uniform texture2D r_input_exposure;
+
+FfxFloat32 Exposure()
+{
+ FfxFloat32 exposure = texelFetch(r_input_exposure, FfxInt32x2(0, 0), 0).x;
+
+ if (exposure == 0.0) {
+ exposure = 1.0;
+ }
+
+ return exposure;
+}
+#endif
+
+// BEGIN: FSR3UPSCALER_BIND_SRV_LANCZOS_LUT
+#if defined(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_LANCZOS_LUT) uniform texture2D r_lanczos_lut;
+#endif
+
+FfxFloat32 SampleLanczos2Weight(FfxFloat32 x)
+{
+#if defined(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT)
+ return textureLod(sampler2D(r_lanczos_lut, s_LinearClamp), FfxFloat32x2(x / 2.0, 0.5), 0.0).x;
+#else
+ return 0.f;
+#endif
+}
+// END: FSR3UPSCALER_BIND_SRV_LANCZOS_LUT
+
+#if defined(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS) uniform texture2D r_dilated_reactive_masks;
+
+FfxFloat32x4 SampleDilatedReactiveMasks(FfxFloat32x2 fUV)
+{
+ return textureLod(sampler2D(r_dilated_reactive_masks, s_LinearClamp), fUV, 0.0);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS, rgba8) writeonly uniform image2D rw_dilated_reactive_masks;
+
+void StoreDilatedReactiveMasks(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x4 fDilatedReactiveMasks)
+{
+ imageStore(rw_dilated_reactive_masks, iPxPos, fDilatedReactiveMasks);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY)
+layout (set = 0, binding = FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY) uniform texture2D r_input_opaque_only;
+
+FfxFloat32x3 LoadOpaqueOnly(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos)
+{
+ return texelFetch(r_input_opaque_only, iPxPos, 0).xyz;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR) uniform texture2D r_input_prev_color_pre_alpha;
+
+FfxFloat32x3 LoadPrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos)
+{
+ return texelFetch(r_input_prev_color_pre_alpha, iPxPos, 0).xyz;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR) uniform texture2D r_input_prev_color_post_alpha;
+
+FfxFloat32x3 LoadPrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos)
+{
+ return texelFetch(r_input_prev_color_post_alpha, iPxPos, 0).xyz;
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_AUTOREACTIVE) && \
+ defined(FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION)
+
+layout(set = 0, binding = FSR3UPSCALER_BIND_UAV_AUTOREACTIVE, r32f) uniform image2D rw_output_autoreactive;
+layout(set = 0, binding = FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION, r32f) uniform image2D rw_output_autocomposition;
+
+void StoreAutoReactive(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F2 fReactive)
+{
+ imageStore(rw_output_autoreactive, iPxPos, FfxFloat32x4(FfxFloat32(fReactive.x), 0.0, 0.0, 0.0));
+
+ imageStore(rw_output_autocomposition, iPxPos, FfxFloat32x4(FfxFloat32(fReactive.y), 0.0, 0.0, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR)
+layout(set = 0, binding = FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR, r11f_g11f_b10f) uniform image2D rw_output_prev_color_pre_alpha;
+
+void StorePrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color)
+{
+ imageStore(rw_output_prev_color_pre_alpha, iPxPos, FfxFloat32x4(color, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR)
+layout(set = 0, binding = FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR, r11f_g11f_b10f) uniform image2D rw_output_prev_color_post_alpha;
+
+void StorePrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color)
+{
+ imageStore(rw_output_prev_color_post_alpha, iPxPos, FfxFloat32x4(color, 0.0));
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_FRAME_INFO)
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_FRAME_INFO, rgba32f) uniform image2D rw_frame_info;
+
+FfxFloat32x4 LoadFrameInfo()
+{
+ return imageLoad(rw_frame_info, ivec2(0, 0));
+}
+
+void StoreFrameInfo(FfxFloat32x4 fInfo)
+{
+ imageStore(rw_frame_info, ivec2(0, 0), fInfo);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_SRV_FRAME_INFO)
+layout(set = 0, binding = FSR3UPSCALER_BIND_SRV_FRAME_INFO) uniform texture2D r_frame_info;
+
+FfxFloat32x4 FrameInfo()
+{
+ return texelFetch(r_frame_info, ivec2(0, 0), 0);
+}
+#endif
+
+#if defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_0) && \
+ defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_1) && \
+ defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_2) && \
+ defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_3) && \
+ defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_4) && \
+ defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_5)
+
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_0, rg16f) uniform image2D rw_spd_mip0;
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_1, rg16f) uniform image2D rw_spd_mip1;
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_2, rg16f) uniform image2D rw_spd_mip2;
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_3, rg16f) uniform image2D rw_spd_mip3;
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_4, rg16f) uniform image2D rw_spd_mip4;
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_5, rg16f) coherent uniform image2D rw_spd_mip5;
+
+FfxFloat32x2 RWLoadPyramid(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxUInt32 index)
+{
+#define LOAD(idx) \
+ if (index == idx) \
+ { \
+ return imageLoad(rw_spd_mip##idx, iPxPos).xy; \
+ }
+ LOAD(0);
+ LOAD(1);
+ LOAD(2);
+ LOAD(3);
+ LOAD(4);
+ LOAD(5);
+
+ return FfxFloat32x2(0.0, 0.0);
+
+#undef LOAD
+}
+
+void StorePyramid(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 outValue, FFX_PARAMETER_IN FfxUInt32 index)
+{
+#define STORE(idx) \
+ if (index == idx) \
+ { \
+ imageStore(rw_spd_mip##idx, iPxPos, vec4(outValue, 0.0, 0.0)); \
+ }
+
+ STORE(0);
+ STORE(1);
+ STORE(2);
+ STORE(3);
+ STORE(4);
+ STORE(5);
+
+#undef STORE
+}
+#endif
+
+#if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC
+layout (set = 0, binding = FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC, r32ui) coherent uniform uimage2D rw_spd_global_atomic;
+
+void SPD_IncreaseAtomicCounter(inout FfxUInt32 spdCounter)
+{
+ spdCounter = imageAtomicAdd(rw_spd_global_atomic, ivec2(0, 0), 1);
+}
+
+void SPD_ResetAtomicCounter()
+{
+ imageStore(rw_spd_global_atomic, ivec2(0, 0), uvec4(0));
+}
+#endif
+
+#endif // #if defined(FFX_GPU)
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_common.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_common.h
new file mode 100644
index 0000000000..87aa596db4
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_common.h
@@ -0,0 +1,403 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#if !defined(FFX_FSR3UPSCALER_COMMON_H)
+#define FFX_FSR3UPSCALER_COMMON_H
+
+#if defined(FFX_GPU)
+#pragma warning(error : 3206) // treat vector truncation warnings as errors
+#pragma warning(disable : 3205) // conversion from larger type to smaller
+#pragma warning(disable : 3571) // in ffxPow(f, e), f could be negative
+
+FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MIN = 6.10e-05f;
+FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MAX = 65504.0f;
+FFX_STATIC const FfxFloat32 FSR3UPSCALER_EPSILON = FSR3UPSCALER_FP16_MIN;
+FFX_STATIC const FfxFloat32 FSR3UPSCALER_TONEMAP_EPSILON = FSR3UPSCALER_FP16_MIN;
+FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP32_MAX = 3.402823466e+38f;
+FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP32_MIN = 1.175494351e-38f;
+
+// Reconstructed depth usage
+FFX_STATIC const FfxFloat32 fReconstructedDepthBilinearWeightThreshold = FSR3UPSCALER_EPSILON * 10;
+
+FfxFloat32 ReconstructedDepthMvPxThreshold(FfxFloat32 fNearestDepthInMeters)
+{
+ return ffxLerp(0.25f, 0.75f, ffxSaturate(fNearestDepthInMeters / 100.0f));
+}
+
+// Accumulation
+FFX_STATIC const FfxFloat32 fUpsampleLanczosWeightScale = 1.0f / 16.0f;
+FFX_STATIC const FfxFloat32 fAverageLanczosWeightPerFrame = 0.74f * fUpsampleLanczosWeightScale; // Average lanczos weight for jitter accumulated samples
+FFX_STATIC const FfxFloat32 fAccumulationMaxOnMotion = 3.0f * fUpsampleLanczosWeightScale;
+
+#define SHADING_CHANGE_SET_SIZE 5
+FFX_STATIC const FfxInt32 iShadingChangeMipStart = 0;
+FFX_STATIC const FfxFloat32 fShadingChangeSamplePow = 1.0f / 1.0f;
+
+
+FFX_STATIC const FfxFloat32 fLockThreshold = 1.0f;
+FFX_STATIC const FfxFloat32 fLockMax = 2.0f;
+
+FFX_STATIC const FfxInt32 REACTIVE = 0;
+FFX_STATIC const FfxInt32 DISOCCLUSION = 1;
+FFX_STATIC const FfxInt32 SHADING_CHANGE = 2;
+FFX_STATIC const FfxInt32 ACCUMULAION = 3;
+
+FFX_STATIC const FfxInt32 FRAME_INFO_EXPOSURE = 0;
+FFX_STATIC const FfxInt32 FRAME_INFO_LOG_LUMA = 1;
+FFX_STATIC const FfxInt32 FRAME_INFO_SCENE_AVERAGE_LUMA = 2;
+
+FfxBoolean TonemapFirstFrame()
+{
+ const FfxBoolean bEnabled = true;
+ return FrameIndex() == 0 && bEnabled;
+}
+
+FfxFloat32 AverageLanczosWeightPerFrame()
+{
+ return 0.74f;
+}
+
+FfxInt32x2 ShadingChangeRenderSize()
+{
+ return FfxInt32x2(RenderSize() * 0.5f);
+}
+
+FfxInt32x2 ShadingChangeMaxRenderSize()
+{
+ return FfxInt32x2(MaxRenderSize() * 0.5f);
+}
+
+FfxInt32x2 PreviousFrameShadingChangeRenderSize()
+{
+ return FfxInt32x2(PreviousFrameRenderSize() * 0.5f);
+}
+
+#if defined(FSR3UPSCALER_BIND_SRV_FRAME_INFO)
+FfxFloat32 SceneAverageLuma()
+{
+ return FrameInfo()[FRAME_INFO_SCENE_AVERAGE_LUMA];
+}
+#endif
+
+// Auto exposure
+FFX_STATIC const FfxFloat32 resetAutoExposureAverageSmoothing = 1e4f;
+
+struct AccumulationPassCommonParams
+{
+ FfxInt32x2 iPxHrPos;
+ FfxFloat32x2 fHrUv;
+ FfxFloat32x2 fLrUvJittered;
+ FfxFloat32x2 fLrUv_HwSampler;
+ FfxFloat32x2 fMotionVector;
+ FfxFloat32x2 fReprojectedHrUv;
+ FfxFloat32 f4KVelocity;
+ FfxFloat32 fDisocclusion;
+ FfxFloat32 fReactiveMask;
+ FfxFloat32 fShadingChange;
+ FfxFloat32 fAccumulation;
+ FfxFloat32 fLumaInstabilityFactor;
+ FfxFloat32 fFarthestDepthInMeters;
+
+ FfxBoolean bIsExistingSample;
+ FfxBoolean bIsNewSample;
+};
+
+FfxFloat32 Get4KVelocity(FfxFloat32x2 fMotionVector)
+{
+ return length(fMotionVector * FfxFloat32x2(3840.0f, 2160.0f));
+}
+
+struct RectificationBox
+{
+ FfxFloat32x3 boxCenter;
+ FfxFloat32x3 boxVec;
+ FfxFloat32x3 aabbMin;
+ FfxFloat32x3 aabbMax;
+ FfxFloat32 fBoxCenterWeight;
+};
+
+struct AccumulationPassData
+{
+ RectificationBox clippingBox;
+ FfxFloat32x3 fUpsampledColor;
+ FfxFloat32 fUpsampledWeight;
+ FfxFloat32x3 fHistoryColor;
+ FfxFloat32 fHistoryWeight;
+ FfxFloat32 fLock;
+ FfxFloat32 fLockContributionThisFrame;
+};
+
+void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight)
+{
+ rectificationBox.aabbMin = colorSample;
+ rectificationBox.aabbMax = colorSample;
+
+ FfxFloat32x3 weightedSample = colorSample * fSampleWeight;
+ rectificationBox.boxCenter = weightedSample;
+ rectificationBox.boxVec = colorSample * weightedSample;
+ rectificationBox.fBoxCenterWeight = fSampleWeight;
+}
+
+void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight)
+{
+ if (bInitialSample) {
+ RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight);
+ } else {
+ rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample);
+ rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample);
+
+ FfxFloat32x3 weightedSample = colorSample * fSampleWeight;
+ rectificationBox.boxCenter += weightedSample;
+ rectificationBox.boxVec += colorSample * weightedSample;
+ rectificationBox.fBoxCenterWeight += fSampleWeight;
+ }
+}
+
+void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBox rectificationBox)
+{
+ rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FfxFloat32(FSR3UPSCALER_FP32_MIN) ? rectificationBox.fBoxCenterWeight : FfxFloat32(1.f));
+ rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight;
+ rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight;
+ FfxFloat32x3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter));
+ rectificationBox.boxVec = stdDev;
+}
+
+FfxFloat32x3 SafeRcp3(FfxFloat32x3 v)
+{
+ return (all(FFX_NOT_EQUAL(v, FfxFloat32x3(0, 0, 0)))) ? (FfxFloat32x3(1, 1, 1) / v) : FfxFloat32x3(0, 0, 0);
+}
+
+FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1, const FfxFloat32 fOnZeroReturnValue)
+{
+ const FfxFloat32 m = ffxMax(v0, v1);
+ return m != 0 ? ffxMin(v0, v1) / m : fOnZeroReturnValue;
+}
+
+FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1)
+{
+ const FfxFloat32 m = ffxMax(v0, v1);
+ return m != 0 ? ffxMin(v0, v1) / m : 0;
+}
+
+FfxFloat32x3 YCoCgToRGB(FfxFloat32x3 fYCoCg)
+{
+ FfxFloat32x3 fRgb;
+
+ fRgb = FfxFloat32x3(
+ fYCoCg.x + fYCoCg.y - fYCoCg.z,
+ fYCoCg.x + fYCoCg.z,
+ fYCoCg.x - fYCoCg.y - fYCoCg.z);
+
+ return fRgb;
+}
+
+FfxFloat32x3 RGBToYCoCg(FfxFloat32x3 fRgb)
+{
+ FfxFloat32x3 fYCoCg;
+
+ fYCoCg = FfxFloat32x3(
+ 0.25f * fRgb.r + 0.5f * fRgb.g + 0.25f * fRgb.b,
+ 0.5f * fRgb.r - 0.5f * fRgb.b,
+ -0.25f * fRgb.r + 0.5f * fRgb.g - 0.25f * fRgb.b);
+
+ return fYCoCg;
+}
+
+FfxFloat32 RGBToLuma(FfxFloat32x3 fLinearRgb)
+{
+ return dot(fLinearRgb, FfxFloat32x3(0.2126f, 0.7152f, 0.0722f));
+}
+
+FfxFloat32 RGBToPerceivedLuma(FfxFloat32x3 fLinearRgb)
+{
+ FfxFloat32 fLuminance = RGBToLuma(fLinearRgb);
+
+ FfxFloat32 fPercievedLuminance = 0;
+ if (fLuminance <= 216.0f / 24389.0f) {
+ fPercievedLuminance = fLuminance * (24389.0f / 27.0f);
+ }
+ else {
+ fPercievedLuminance = ffxPow(fLuminance, 1.0f / 3.0f) * 116.0f - 16.0f;
+ }
+
+ return fPercievedLuminance * 0.01f;
+}
+
+FfxFloat32x3 Tonemap(FfxFloat32x3 fRgb)
+{
+ return fRgb / (ffxMax(ffxMax(0.f, fRgb.r), ffxMax(fRgb.g, fRgb.b)) + 1.f).xxx;
+}
+
+FfxFloat32x3 InverseTonemap(FfxFloat32x3 fRgb)
+{
+ return fRgb / ffxMax(FSR3UPSCALER_TONEMAP_EPSILON, 1.f - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx;
+}
+
+FfxBoolean IsUvInside(FfxFloat32x2 fUv)
+{
+ return (fUv.x >= 0.0f && fUv.x <= 1.0f) && (fUv.y >= 0.0f && fUv.y <= 1.0f);
+}
+
+FfxInt32x2 ClampLoad(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize)
+{
+ FfxInt32x2 result = iPxSample + iPxOffset;
+ result.x = ffxMax(0, ffxMin(result.x, iTextureSize.x - 1));
+ result.y = ffxMax(0, ffxMin(result.y, iTextureSize.y - 1));
+ return result;
+}
+
+FfxFloat32x2 ClampUv(FfxFloat32x2 fUv, FfxInt32x2 iTextureSize, FfxInt32x2 iResourceSize)
+{
+ const FfxFloat32x2 fSampleLocation = fUv * iTextureSize;
+ const FfxFloat32x2 fClampedLocation = ffxMax(FfxFloat32x2(0.5f, 0.5f), ffxMin(fSampleLocation, FfxFloat32x2(iTextureSize) - FfxFloat32x2(0.5f, 0.5f)));
+ const FfxFloat32x2 fClampedUv = fClampedLocation / FfxFloat32x2(iResourceSize);
+
+ return fClampedUv;
+}
+
+FfxBoolean IsOnScreen(FfxInt32x2 pos, FfxInt32x2 size)
+{
+ return all(FFX_LESS_THAN(FfxUInt32x2(pos), FfxUInt32x2(size)));
+}
+
+FfxFloat32 ComputeAutoExposureFromLavg(FfxFloat32 Lavg)
+{
+ Lavg = exp(Lavg);
+
+ const FfxFloat32 S = 100.0f; //ISO arithmetic speed
+ const FfxFloat32 K = 12.5f;
+ FfxFloat32 ExposureISO100 = log2((Lavg * S) / K);
+
+ const FfxFloat32 q = 0.65f;
+ FfxFloat32 Lmax = (78.0f / (q * S)) * ffxPow(2.0f, ExposureISO100);
+
+ return 1.0f / Lmax;
+}
+
+FfxInt32x2 ComputeHrPosFromLrPos(FfxInt32x2 iPxLrPos)
+{
+ FfxFloat32x2 fSrcJitteredPos = FfxFloat32x2(iPxLrPos) + 0.5f - Jitter();
+ FfxFloat32x2 fLrPosInHr = (fSrcJitteredPos / RenderSize()) * UpscaleSize();
+ FfxInt32x2 iPxHrPos = FfxInt32x2(floor(fLrPosInHr));
+ return iPxHrPos;
+}
+
+FfxFloat32x2 ComputeNdc(FfxFloat32x2 fPxPos, FfxInt32x2 iSize)
+{
+ return fPxPos / FfxFloat32x2(iSize) * FfxFloat32x2(2.0f, -2.0f) + FfxFloat32x2(-1.0f, 1.0f);
+}
+
+FfxFloat32 GetViewSpaceDepth(FfxFloat32 fDeviceDepth)
+{
+ const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors();
+
+ // fDeviceToViewDepth details found in ffx_fsr3upscaler.cpp
+ return (fDeviceToViewDepth[1] / (fDeviceDepth - fDeviceToViewDepth[0]));
+}
+
+FfxFloat32 GetViewSpaceDepthInMeters(FfxFloat32 fDeviceDepth)
+{
+ return GetViewSpaceDepth(fDeviceDepth) * ViewSpaceToMetersFactor();
+}
+
+FfxFloat32x3 GetViewSpacePosition(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth)
+{
+ const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors();
+
+ const FfxFloat32 Z = GetViewSpaceDepth(fDeviceDepth);
+
+ const FfxFloat32x2 fNdcPos = ComputeNdc(iViewportPos, iViewportSize);
+ const FfxFloat32 X = fDeviceToViewDepth[2] * fNdcPos.x * Z;
+ const FfxFloat32 Y = fDeviceToViewDepth[3] * fNdcPos.y * Z;
+
+ return FfxFloat32x3(X, Y, Z);
+}
+
+FfxFloat32x3 GetViewSpacePositionInMeters(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth)
+{
+ return GetViewSpacePosition(iViewportPos, iViewportSize, fDeviceDepth) * ViewSpaceToMetersFactor();
+}
+
+FfxFloat32 GetMaxDistanceInMeters()
+{
+#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH
+ return GetViewSpaceDepth(0.0f) * ViewSpaceToMetersFactor();
+#else
+ return GetViewSpaceDepth(1.0f) * ViewSpaceToMetersFactor();
+#endif
+}
+
+
+struct BilinearSamplingData
+{
+ FfxInt32x2 iOffsets[4];
+ FfxFloat32 fWeights[4];
+ FfxInt32x2 iBasePos;
+};
+
+BilinearSamplingData GetBilinearSamplingData(FfxFloat32x2 fUv, FfxInt32x2 iSize)
+{
+ BilinearSamplingData data;
+
+ FfxFloat32x2 fPxSample = (fUv * iSize) - FfxFloat32x2(0.5f, 0.5f);
+ data.iBasePos = FfxInt32x2(floor(fPxSample));
+ FfxFloat32x2 fPxFrac = ffxFract(fPxSample);
+
+ data.iOffsets[0] = FfxInt32x2(0, 0);
+ data.iOffsets[1] = FfxInt32x2(1, 0);
+ data.iOffsets[2] = FfxInt32x2(0, 1);
+ data.iOffsets[3] = FfxInt32x2(1, 1);
+
+ data.fWeights[0] = (1 - fPxFrac.x) * (1 - fPxFrac.y);
+ data.fWeights[1] = (fPxFrac.x) * (1 - fPxFrac.y);
+ data.fWeights[2] = (1 - fPxFrac.x) * (fPxFrac.y);
+ data.fWeights[3] = (fPxFrac.x) * (fPxFrac.y);
+
+ return data;
+}
+
+struct PlaneData
+{
+ FfxFloat32x3 fNormal;
+ FfxFloat32 fDistanceFromOrigin;
+};
+
+PlaneData GetPlaneFromPoints(FfxFloat32x3 fP0, FfxFloat32x3 fP1, FfxFloat32x3 fP2)
+{
+ PlaneData plane;
+
+ FfxFloat32x3 v0 = fP0 - fP1;
+ FfxFloat32x3 v1 = fP0 - fP2;
+ plane.fNormal = normalize(cross(v0, v1));
+ plane.fDistanceFromOrigin = -dot(fP0, plane.fNormal);
+
+ return plane;
+}
+
+FfxFloat32 PointToPlaneDistance(PlaneData plane, FfxFloat32x3 fPoint)
+{
+ return abs(dot(plane.fNormal, fPoint) + plane.fDistanceFromOrigin);
+}
+
+#endif // #if defined(FFX_GPU)
+
+#endif //!defined(FFX_FSR3UPSCALER_COMMON_H)
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h
new file mode 100644
index 0000000000..624b7ca941
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h
@@ -0,0 +1,115 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+struct LumaInstabilityFactorData
+{
+ FfxFloat32x4 fLumaHistory;
+ FfxFloat32 fLumaInstabilityFactor;
+};
+
+LumaInstabilityFactorData ComputeLumaInstabilityFactor(LumaInstabilityFactorData data, FfxFloat32 fCurrentFrameLuma, FfxFloat32 fFarthestDepthInMeters)
+{
+ const FfxInt32 N_MINUS_1 = 0;
+ const FfxInt32 N_MINUS_2 = 1;
+ const FfxInt32 N_MINUS_3 = 2;
+ const FfxInt32 N_MINUS_4 = 3;
+
+ FfxFloat32 fLumaInstability = 0.0f;
+ const FfxFloat32 fDiffs0 = (fCurrentFrameLuma - data.fLumaHistory[N_MINUS_1]);
+ const FfxFloat32 fSimilarity0 = MinDividedByMax(fCurrentFrameLuma, data.fLumaHistory[N_MINUS_1], 1.0f);
+
+ FfxFloat32 fMaxSimilarity = fSimilarity0;
+
+ if (fSimilarity0 < 1.0f) {
+ for (int i = N_MINUS_2; i <= N_MINUS_4; i++) {
+ const FfxFloat32 fDiffs1 = (fCurrentFrameLuma - data.fLumaHistory[i]);
+ const FfxFloat32 fSimilarity1 = MinDividedByMax(fCurrentFrameLuma, data.fLumaHistory[i]);
+
+ if (sign(fDiffs0) == sign(fDiffs1)) {
+
+ fMaxSimilarity = ffxMax(fMaxSimilarity, fSimilarity1);
+ }
+ }
+
+ fLumaInstability = FfxFloat32(fMaxSimilarity > fSimilarity0);
+ }
+
+ // Shift history
+ data.fLumaHistory[N_MINUS_4] = data.fLumaHistory[N_MINUS_3];
+ data.fLumaHistory[N_MINUS_3] = data.fLumaHistory[N_MINUS_2];
+ data.fLumaHistory[N_MINUS_2] = data.fLumaHistory[N_MINUS_1];
+ data.fLumaHistory[N_MINUS_1] = fCurrentFrameLuma;
+
+ data.fLumaHistory /= Exposure();
+
+ data.fLumaInstabilityFactor = fLumaInstability * FfxFloat32(data.fLumaHistory[N_MINUS_4] != 0);
+
+ return data;
+}
+
+void LumaInstability(FfxInt32x2 iPxPos)
+{
+ LumaInstabilityFactorData data;
+ data.fLumaInstabilityFactor = 0.0f;
+ data.fLumaHistory = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f);
+
+ const FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(iPxPos);
+ const FfxFloat32x2 fUv = (iPxPos + 0.5f) / RenderSize();
+ const FfxFloat32x2 fUvCurrFrameJittered = fUv + Jitter() / RenderSize();
+ const FfxFloat32x2 fUvPrevFrameJittered = fUv + PreviousFrameJitter() / PreviousFrameRenderSize();
+ const FfxFloat32x2 fReprojectedUv = fUvPrevFrameJittered + fDilatedMotionVector;
+
+ if (IsUvInside(fReprojectedUv))
+ {
+ const FfxFloat32x2 fUvReactive_HW = ClampUv(fUvCurrFrameJittered, RenderSize(), MaxRenderSize());
+
+ const FfxFloat32x4 fDilatedReactiveMasks = SampleDilatedReactiveMasks(fUvReactive_HW);
+ const FfxFloat32 fReactiveMask = ffxSaturate(fDilatedReactiveMasks[REACTIVE]);
+ const FfxFloat32 fDisocclusion = ffxSaturate(fDilatedReactiveMasks[DISOCCLUSION]);
+ const FfxFloat32 fShadingChange = ffxSaturate(fDilatedReactiveMasks[SHADING_CHANGE]);
+ const FfxFloat32 fAccumulation = ffxSaturate(fDilatedReactiveMasks[ACCUMULAION]);
+
+ const FfxBoolean bAccumulationFactor = fAccumulation > 0.9f;
+
+ const FfxBoolean bComputeInstability = bAccumulationFactor;
+
+ if (bComputeInstability) {
+
+ const FfxFloat32x2 fUv_HW = ClampUv(fUvCurrFrameJittered, RenderSize(), MaxRenderSize());
+ const FfxFloat32 fCurrentFrameLuma = SampleCurrentLuma(fUv_HW) * Exposure();
+
+ const FfxFloat32x2 fReprojectedUv_HW = ClampUv(fReprojectedUv, PreviousFrameRenderSize(), MaxRenderSize());
+ data.fLumaHistory = SampleLumaHistory(fReprojectedUv_HW) * DeltaPreExposure() * Exposure();
+
+ const FfxFloat32x2 fFarthestDepthUv_HW = ClampUv(fUvCurrFrameJittered, RenderSize() / 2, GetFarthestDepthMip1ResourceDimensions());
+ const FfxFloat32 fFarthestDepthInMeters = SampleFarthestDepthMip1(fFarthestDepthUv_HW);
+
+ data = ComputeLumaInstabilityFactor(data, fCurrentFrameLuma, fFarthestDepthInMeters);
+
+ const FfxFloat32 fVelocityWeight = 1.0f - ffxSaturate(Get4KVelocity(fDilatedMotionVector) / 20.0f);
+ data.fLumaInstabilityFactor *= fVelocityWeight * (1.0f - fDisocclusion) * (1.0f - fReactiveMask) * (1.0f - fShadingChange);
+ }
+ }
+
+ StoreLumaHistory(iPxPos, data.fLumaHistory);
+ StoreLumaInstability(iPxPos, data.fLumaInstabilityFactor);
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h
new file mode 100644
index 0000000000..41c5dc7f45
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h
@@ -0,0 +1,192 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+FFX_GROUPSHARED FfxUInt32 spdCounter;
+
+void SpdIncreaseAtomicCounter(FfxUInt32 slice)
+{
+ SPD_IncreaseAtomicCounter(spdCounter);
+}
+
+FfxUInt32 SpdGetAtomicCounter()
+{
+ return spdCounter;
+}
+
+void SpdResetAtomicCounter(FfxUInt32 slice)
+{
+ SPD_ResetAtomicCounter();
+}
+
+#ifndef SPD_PACKED_ONLY
+FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16];
+FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16];
+FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16];
+FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16];
+
+FFX_STATIC const FfxInt32 LOG_LUMA = 0;
+FFX_STATIC const FfxInt32 LUMA = 1;
+FFX_STATIC const FfxInt32 DEPTH_IN_METERS = 2;
+
+FfxFloat32x4 SpdLoadSourceImage(FfxFloat32x2 iPxPos, FfxUInt32 slice)
+{
+ //We assume linear data. if non-linear input (sRGB, ...),
+ //then we should convert to linear first and back to sRGB on output.
+ const FfxInt32x2 iPxSamplePos = ClampLoad(FfxInt32x2(iPxPos), FfxInt32x2(0, 0), FfxInt32x2(RenderSize()));
+
+ const FfxFloat32 fLuma = LoadCurrentLuma(iPxSamplePos);
+ const FfxFloat32 fLogLuma = ffxMax(FSR3UPSCALER_EPSILON, log(fLuma));
+ const FfxFloat32 fFarthestDepthInMeters = LoadFarthestDepth(iPxSamplePos);
+
+ FfxFloat32x4 fOutput = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f);
+ fOutput[LOG_LUMA] = fLogLuma;
+ fOutput[LUMA] = fLuma;
+ fOutput[DEPTH_IN_METERS] = fFarthestDepthInMeters;
+
+ return fOutput;
+}
+
+FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice)
+{
+ return FfxFloat32x4(RWLoadPyramid(tex, 5), 0, 0);
+}
+
+FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3)
+{
+ return (v0 + v1 + v2 + v3) * 0.25f;
+}
+
+void SpdStore(FfxInt32x2 pix, FfxFloat32x4 outValue, FfxUInt32 index, FfxUInt32 slice)
+{
+ if (index == 5)
+ {
+ StorePyramid(pix, outValue.xy, index);
+ }
+ else if (index == 0) {
+ StoreFarthestDepthMip1(pix, outValue[DEPTH_IN_METERS]);
+ }
+
+ if (index == MipCount() - 1) { //accumulate on 1x1 level
+
+ if (all(FFX_EQUAL(pix, FfxInt32x2(0, 0))))
+ {
+ FfxFloat32x4 frameInfo = LoadFrameInfo();
+ const FfxFloat32 fSceneAvgLuma = outValue[LUMA];
+ const FfxFloat32 fPrevLogLuma = frameInfo[FRAME_INFO_LOG_LUMA];
+ FfxFloat32 fLogLuma = outValue[LOG_LUMA];
+
+ if (fPrevLogLuma < resetAutoExposureAverageSmoothing) // Compare Lavg, so small or negative values
+ {
+ fLogLuma = fPrevLogLuma + (fLogLuma - fPrevLogLuma) * (1.0f - exp(-DeltaTime()));
+ fLogLuma = ffxMax(0.0f, fLogLuma);
+ }
+
+ frameInfo[FRAME_INFO_EXPOSURE] = ComputeAutoExposureFromLavg(fLogLuma);
+ frameInfo[FRAME_INFO_LOG_LUMA] = fLogLuma;
+ frameInfo[FRAME_INFO_SCENE_AVERAGE_LUMA] = fSceneAvgLuma;
+
+ StoreFrameInfo(frameInfo);
+ }
+ }
+}
+
+FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y)
+{
+ return FfxFloat32x4(
+ spdIntermediateR[x][y],
+ spdIntermediateG[x][y],
+ spdIntermediateB[x][y],
+ spdIntermediateA[x][y]);
+}
+void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value)
+{
+ spdIntermediateR[x][y] = value.x;
+ spdIntermediateG[x][y] = value.y;
+ spdIntermediateB[x][y] = value.z;
+ spdIntermediateA[x][y] = value.w;
+}
+
+#endif
+
+// define fetch and store functions Packed
+#if FFX_HALF
+
+FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16];
+FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16];
+
+FfxFloat16x4 SpdLoadSourceImageH(FfxFloat32x2 tex, FfxUInt32 slice)
+{
+ return FfxFloat16x4(0, 0, 0, 0);
+}
+
+FfxFloat16x4 SpdLoadH(FfxInt32x2 p, FfxUInt32 slice)
+{
+ return FfxFloat16x4(0, 0, 0, 0);
+}
+
+void SpdStoreH(FfxInt32x2 p, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice)
+{
+}
+
+FfxFloat16x4 SpdLoadIntermediateH(FfxUInt32 x, FfxUInt32 y)
+{
+ return FfxFloat16x4(
+ spdIntermediateRG[x][y].x,
+ spdIntermediateRG[x][y].y,
+ spdIntermediateBA[x][y].x,
+ spdIntermediateBA[x][y].y);
+}
+
+void SpdStoreIntermediateH(FfxUInt32 x, FfxUInt32 y, FfxFloat16x4 value)
+{
+ spdIntermediateRG[x][y] = value.xy;
+ spdIntermediateBA[x][y] = value.zw;
+}
+
+FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3)
+{
+ return (v0 + v1 + v2 + v3) * FfxFloat16(0.25);
+}
+#endif
+
+#include "spd/ffx_spd.h"
+
+void ComputeAutoExposure(FfxUInt32x3 WorkGroupId, FfxUInt32 LocalThreadIndex)
+{
+#if FFX_HALF
+ SpdDownsampleH(
+ FfxUInt32x2(WorkGroupId.xy),
+ FfxUInt32(LocalThreadIndex),
+ FfxUInt32(MipCount()),
+ FfxUInt32(NumWorkGroups()),
+ FfxUInt32(WorkGroupId.z),
+ FfxUInt32x2(WorkGroupOffset()));
+#else
+ SpdDownsample(
+ FfxUInt32x2(WorkGroupId.xy),
+ FfxUInt32(LocalThreadIndex),
+ FfxUInt32(MipCount()),
+ FfxUInt32(NumWorkGroups()),
+ FfxUInt32(WorkGroupId.z),
+ FfxUInt32x2(WorkGroupOffset()));
+#endif
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h
new file mode 100644
index 0000000000..59c765b527
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h
@@ -0,0 +1,152 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+void ReconstructPrevDepth(FfxInt32x2 iPxPos, FfxFloat32 fDepth, FfxFloat32x2 fMotionVector)
+{
+ const FfxFloat32 fNearestDepthInMeters = ffxMin(GetViewSpaceDepthInMeters(fDepth), FSR3UPSCALER_FP16_MAX);
+ const FfxFloat32 fReconstructedDeptMvThreshold = ReconstructedDepthMvPxThreshold(fNearestDepthInMeters);
+
+ // Discard small mvs
+ fMotionVector *= FfxFloat32(Get4KVelocity(fMotionVector) > fReconstructedDeptMvThreshold);
+
+ const FfxFloat32x2 fUv = (iPxPos + FfxFloat32(0.5)) / RenderSize();
+ const FfxFloat32x2 fReprojectedUv = fUv + fMotionVector;
+ const BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, RenderSize());
+
+ // Project current depth into previous frame locations.
+ // Push to all pixels having some contribution if reprojection is using bilinear logic.
+ for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) {
+
+ const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex];
+ const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex];
+
+ if (fWeight > fReconstructedDepthBilinearWeightThreshold) {
+
+ const FfxInt32x2 iStorePos = bilinearInfo.iBasePos + iOffset;
+ if (IsOnScreen(iStorePos, RenderSize())) {
+ StoreReconstructedDepth(iStorePos, fDepth);
+ }
+ }
+ }
+}
+
+struct DepthExtents
+{
+ FfxFloat32 fNearest;
+ FfxInt32x2 fNearestCoord;
+ FfxFloat32 fFarthest;
+};
+
+DepthExtents FindDepthExtents(FFX_PARAMETER_IN FfxInt32x2 iPxPos)
+{
+ DepthExtents extents;
+ const FfxInt32 iSampleCount = 9;
+ const FfxInt32x2 iSampleOffsets[iSampleCount] = {
+ FfxInt32x2(+0, +0),
+ FfxInt32x2(+1, +0),
+ FfxInt32x2(+0, +1),
+ FfxInt32x2(+0, -1),
+ FfxInt32x2(-1, +0),
+ FfxInt32x2(-1, +1),
+ FfxInt32x2(+1, +1),
+ FfxInt32x2(-1, -1),
+ FfxInt32x2(+1, -1),
+ };
+
+ // pull out the depth loads to allow SC to batch them
+ FfxFloat32 depth[9];
+ FfxInt32 iSampleIndex = 0;
+ FFX_UNROLL
+ for (iSampleIndex = 0; iSampleIndex < iSampleCount; ++iSampleIndex) {
+
+ FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex];
+ depth[iSampleIndex] = LoadInputDepth(iPos);
+ }
+
+ // find closest depth
+ extents.fNearestCoord = iPxPos;
+ extents.fNearest = depth[0];
+ extents.fFarthest = depth[0];
+ FFX_UNROLL
+ for (iSampleIndex = 1; iSampleIndex < iSampleCount; ++iSampleIndex) {
+
+ const FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex];
+ if (IsOnScreen(iPos, RenderSize())) {
+
+ FfxFloat32 fNdDepth = depth[iSampleIndex];
+#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH
+ if (fNdDepth > extents.fNearest) {
+ extents.fFarthest = ffxMin(extents.fFarthest, fNdDepth);
+#else
+ if (fNdDepth < extents.fNearest) {
+ extents.fFarthest = ffxMax(extents.fFarthest, fNdDepth);
+#endif
+ extents.fNearestCoord = iPos;
+ extents.fNearest = fNdDepth;
+ }
+ }
+ }
+
+ return extents;
+}
+
+FfxFloat32x2 DilateMotionVector(FfxInt32x2 iPxPos, const DepthExtents depthExtents)
+{
+#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS
+ const FfxInt32x2 iSamplePos = iPxPos;
+ const FfxInt32x2 iMotionVectorPos = depthExtents.fNearestCoord;
+#else
+ const FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxPos);
+ const FfxInt32x2 iMotionVectorPos = ComputeHrPosFromLrPos(depthExtents.fNearestCoord);
+#endif
+
+ const FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iMotionVectorPos);
+
+ return fDilatedMotionVector;
+}
+
+FfxFloat32 GetCurrentFrameLuma(FfxInt32x2 iPxPos)
+{
+ //We assume linear data. if non-linear input (sRGB, ...),
+ //then we should convert to linear first and back to sRGB on output.
+ const FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxPos));
+ const FfxFloat32 fLuma = RGBToLuma(fRgb);
+
+ return fLuma;
+}
+
+void PrepareInputs(FfxInt32x2 iPxPos)
+{
+ const DepthExtents depthExtents = FindDepthExtents(iPxPos);
+ const FfxFloat32x2 fDilatedMotionVector = DilateMotionVector(iPxPos, depthExtents);
+
+ ReconstructPrevDepth(iPxPos, depthExtents.fNearest, fDilatedMotionVector);
+
+ StoreDilatedMotionVector(iPxPos, fDilatedMotionVector);
+ StoreDilatedDepth(iPxPos, depthExtents.fNearest);
+
+ const FfxFloat32 fFarthestDepthInMeters = ffxMin(GetViewSpaceDepthInMeters(depthExtents.fFarthest), FSR3UPSCALER_FP16_MAX);
+ StoreFarthestDepth(iPxPos, fFarthestDepthInMeters);
+
+ const FfxFloat32 fLuma = GetCurrentFrameLuma(iPxPos);
+ StoreCurrentLuma(iPxPos, fLuma);
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h
new file mode 100644
index 0000000000..ae24545c80
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h
@@ -0,0 +1,283 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+FfxFloat32 ComputeDisocclusions(FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector, FfxFloat32 fCurrentDepthViewSpace)
+{
+ const FfxFloat32 fNearestDepthInMeters = ffxMin(fCurrentDepthViewSpace * ViewSpaceToMetersFactor(), FSR3UPSCALER_FP16_MAX);
+ const FfxFloat32 fReconstructedDeptMvThreshold = ReconstructedDepthMvPxThreshold(fNearestDepthInMeters);
+
+ fMotionVector *= FfxFloat32(Get4KVelocity(fMotionVector) > fReconstructedDeptMvThreshold);
+
+ const FfxFloat32x2 fReprojectedUv = fUv + fMotionVector;
+ const BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, RenderSize());
+
+ FfxFloat32 fDisocclusion = 0.0f;
+ FfxFloat32 fWeightSum = 0.0f;
+ FfxBoolean bPotentialDisocclusion = true;
+
+ for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4 && bPotentialDisocclusion; iSampleIndex++)
+ {
+
+ const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex];
+ const FfxInt32x2 iSamplePos = ClampLoad(bilinearInfo.iBasePos, iOffset, FfxInt32x2(RenderSize()));
+
+ if (IsOnScreen(iSamplePos, RenderSize())) {
+ const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex];
+ if (fWeight > fReconstructedDepthBilinearWeightThreshold) {
+
+ const FfxFloat32 fPrevNearestDepthViewSpace = GetViewSpaceDepth(LoadReconstructedPrevDepth(iSamplePos));
+ const FfxFloat32 fDepthDifference = fCurrentDepthViewSpace - fPrevNearestDepthViewSpace;
+
+ bPotentialDisocclusion = bPotentialDisocclusion && (fDepthDifference > FSR3UPSCALER_FP32_MIN);
+
+ if (bPotentialDisocclusion) {
+ const FfxFloat32 fHalfViewportWidth = length(FfxFloat32x2(RenderSize()) * 0.5f);
+ const FfxFloat32 fDepthThreshold = ffxMax(fCurrentDepthViewSpace, fPrevNearestDepthViewSpace);
+
+ const FfxFloat32 Ksep = 1.37e-05f;
+ const FfxFloat32 fRequiredDepthSeparation = Ksep * fHalfViewportWidth * fDepthThreshold;
+
+ fDisocclusion += ffxSaturate(FfxFloat32(fRequiredDepthSeparation / fDepthDifference)) * fWeight;
+ fWeightSum += fWeight;
+ }
+ }
+ }
+ }
+
+ fDisocclusion = (bPotentialDisocclusion && fWeightSum > 0) ? ffxSaturate(1.0f - fDisocclusion / fWeightSum) : 0.0f;
+
+ return fDisocclusion;
+}
+
+FfxFloat32 ComputeMotionDivergence(FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector, FfxFloat32 fCurrentDepthSample)
+{
+ const FfxInt32x2 iPxReprojectedPos = FfxInt32x2((fUv + fMotionVector) * RenderSize());
+ const FfxFloat32 fReprojectedDepth = LoadDilatedDepth(iPxReprojectedPos);
+ const FfxFloat32x2 fReprojectedMotionVector = LoadDilatedMotionVector(iPxReprojectedPos);
+
+ const FfxFloat32 fReprojectedVelocity = Get4KVelocity(fReprojectedMotionVector);
+ const FfxFloat32 f4KVelocity = Get4KVelocity(fMotionVector);
+
+ const FfxFloat32 fMaxLen = max(length(fMotionVector), length(fReprojectedMotionVector));
+
+ const FfxFloat32 fNucleusDepthInMeters = GetViewSpaceDepthInMeters(fReprojectedDepth);
+ const FfxFloat32 fCurrentDepthInMeters = GetViewSpaceDepthInMeters(fCurrentDepthSample);
+
+ const FfxFloat32 fDistanceFactor = MinDividedByMax(fNucleusDepthInMeters, fCurrentDepthInMeters);
+ const FfxFloat32 fVelocityFactor = ffxSaturate(f4KVelocity / 10.0f);
+ const FfxFloat32 fMotionVectorFieldConfidence = (1.0f - ffxSaturate(fReprojectedVelocity / f4KVelocity)) * fDistanceFactor * fVelocityFactor;
+
+ return fMotionVectorFieldConfidence;
+}
+
+FfxFloat32 DilateReactiveMasks(FfxInt32x2 iPxPos, FfxFloat32x2 fUv)
+{
+ FfxFloat32 fDilatedReactiveMasks = 0.0f;
+
+ FFX_UNROLL
+ for (FfxInt32 y = -1; y <=1; y++)
+ {
+ FFX_UNROLL
+ for (FfxInt32 x = -1; x <= 1; x++)
+ {
+ const FfxInt32x2 sampleCoord = ClampLoad(iPxPos, FfxInt32x2(x, y), FfxInt32x2(RenderSize()));
+ fDilatedReactiveMasks = ffxMax(fDilatedReactiveMasks, LoadReactiveMask(sampleCoord));
+ }
+ }
+
+ return fDilatedReactiveMasks;
+}
+
+FfxFloat32 DilateTransparencyAndCompositionMasks(FfxInt32x2 iPxPos, FfxFloat32x2 fUv)
+{
+ const FfxFloat32x2 fUvTransparencyAndCompositionMask = ClampUv(fUv, RenderSize(), GetTransparencyAndCompositionMaskResourceDimensions());
+ return SampleTransparencyAndCompositionMask(fUvTransparencyAndCompositionMask);
+}
+
+FfxFloat32 ComputeThinFeatureConfidence(FfxInt32x2 iPxPos)
+{
+ /*
+ 1 2 3
+ 4 0 5
+ 6 7 8
+ */
+
+ const FfxInt32 iNucleusIndex = 0;
+ const FfxInt32 iSampleCount = 9;
+ const FfxInt32x2 iSampleOffsets[iSampleCount] = {
+ FfxInt32x2(+0, +0),
+ FfxInt32x2(-1, -1),
+ FfxInt32x2(+0, -1),
+ FfxInt32x2(+1, -1),
+ FfxInt32x2(-1, +0),
+ FfxInt32x2(+1, +0),
+ FfxInt32x2(-1, +1),
+ FfxInt32x2(+0, +1),
+ FfxInt32x2(+1, +1),
+ };
+
+ FfxFloat32 fSamples[iSampleCount];
+
+ FfxFloat32 fLumaMin = FSR3UPSCALER_FP32_MAX;
+ FfxFloat32 fLumaMax = FSR3UPSCALER_FP32_MIN;
+
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 0; iSampleIndex < iSampleCount; ++iSampleIndex) {
+ const FfxInt32x2 iPxSamplePos = ClampLoad(iPxPos, iSampleOffsets[iSampleIndex], FfxInt32x2(RenderSize()));
+ fSamples[iSampleIndex] = LoadCurrentLuma(iPxSamplePos) * Exposure();
+
+ fLumaMin = ffxMin(fLumaMin, fSamples[iSampleIndex]);
+ fLumaMax = ffxMax(fLumaMax, fSamples[iSampleIndex]);
+ }
+
+ const FfxFloat32 fThreshold = 0.9f;
+ FfxFloat32 fDissimilarLumaMin = FSR3UPSCALER_FP32_MAX;
+ FfxFloat32 fDissimilarLumaMax = 0;
+
+#define SETBIT(x) (1U << x)
+
+ FfxUInt32 uPatternMask = SETBIT(iNucleusIndex); // Flag nucleus as similar
+
+ const FfxUInt32 uNumRejectionMasks = 4;
+ const FfxUInt32 uRejectionMasks[uNumRejectionMasks] = {
+ SETBIT(1) | SETBIT(2) | SETBIT(4) | SETBIT(iNucleusIndex), // Upper left
+ SETBIT(2) | SETBIT(3) | SETBIT(5) | SETBIT(iNucleusIndex), // Upper right
+ SETBIT(4) | SETBIT(6) | SETBIT(7) | SETBIT(iNucleusIndex), // Lower left
+ SETBIT(5) | SETBIT(7) | SETBIT(8) | SETBIT(iNucleusIndex) // Lower right
+ };
+
+ FfxInt32 iBitIndex = 1;
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 1; iSampleIndex < iSampleCount; ++iSampleIndex, ++iBitIndex) {
+
+ const FfxFloat32 fDifference = abs(fSamples[iSampleIndex] - fSamples[iNucleusIndex]) / (fLumaMax - fLumaMin);
+
+ if (fDifference < fThreshold)
+ {
+ uPatternMask |= SETBIT(iBitIndex);
+ }
+ else
+ {
+ fDissimilarLumaMin = ffxMin(fDissimilarLumaMin, fSamples[iSampleIndex]);
+ fDissimilarLumaMax = ffxMax(fDissimilarLumaMax, fSamples[iSampleIndex]);
+ }
+ }
+
+ const FfxBoolean bIsRidge = fSamples[iNucleusIndex] > fDissimilarLumaMax || fSamples[iNucleusIndex] < fDissimilarLumaMin;
+
+ if (FFX_FALSE == bIsRidge)
+ {
+ return 0.0f;
+ }
+
+ FFX_UNROLL
+ for (FfxInt32 i = 0; i < uNumRejectionMasks; i++)
+ {
+ if ((uPatternMask & uRejectionMasks[i]) == uRejectionMasks[i])
+ {
+ return 0.0f;
+ }
+ }
+
+ return 1.0f - fLumaMin / fLumaMax;
+}
+
+FfxFloat32 UpdateAccumulation(FfxInt32x2 iPxPos, FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector, FfxFloat32 fDisocclusion, FfxFloat32 fShadingChange)
+{
+ const FfxFloat32x2 fReprojectedUv = fUv + fMotionVector;
+ FfxFloat32 fAccumulation = 0.0f;
+
+ if (IsUvInside(fReprojectedUv)) {
+ const FfxFloat32x2 fReprojectedUv_HW = ClampUv(fReprojectedUv, PreviousFrameRenderSize(), MaxRenderSize());
+ fAccumulation = ffxSaturate(SampleAccumulation(fReprojectedUv_HW));
+ }
+ const FfxFloat32 fAccumulationAddedPerFrame= AccumulationAddedPerFrame(); //default is 0.333
+
+ // Assume at frame N+0 fShadingChange is 1.0, and all subsequent frames fShadingChange is 0.0 and fDisocclusion is 0.0. Then,
+ // frame N+0 fAccumulation will be 0.000
+ // frame N+2 fAccumulation will be 0.000 + 0.333 * 1 == 0.333
+ // frame N+3 fAccumulation will be 0.000 + 0.333 * 2 == 0.666
+ // frame N+4 fAccumulation will be 0.000 + 0.333 * 3 == 0.999
+ fAccumulation = ffxLerp(fAccumulation, 0.0f, fShadingChange);
+
+ const FfxFloat32 fMinDisocclusionAccumulation = MinDisocclusionAccumulation(); //default is -0.333
+ // Assume at frame N+0 fDisocclusion is 1.0, and all subsequent frames fShadingChange is 0.0 and fDisocclusion is 0.0. Then,
+ // frame N+0 fAccumulation will be -0.333f (but normalized to store in unorm)
+ // frame N+1 fAccumulation will be -0.333f + 0.333 * 1 == 0.000
+ // frame N+2 fAccumulation will be -0.333f + 0.333 * 2 == 0.333
+ // frame N+3 fAccumulation will be -0.333f + 0.333 * 3 == 0.666
+ // frame N+4 fAccumulation will be -0.333f + 0.333 * 4 == 0.999
+ fAccumulation = ffxLerp(fAccumulation, ffxMin(fMinDisocclusionAccumulation, fAccumulation), fDisocclusion);
+
+ fAccumulation *= FfxFloat32(round(fAccumulation * 100.0f) > 1.0f);
+
+ // Update for next frame, normalize to store in unorm
+ const FfxFloat32 fAccumulatedFramesToStore = ffxSaturate(fAccumulation + fAccumulationAddedPerFrame);
+ StoreAccumulation(iPxPos, fAccumulatedFramesToStore);
+
+ return fAccumulation;
+}
+
+FfxFloat32 ComputeShadingChange(FfxFloat32x2 fUv)
+{
+ // NOTE: Here we re-apply jitter, will be reverted again when sampled in accumulation pass
+ const FfxFloat32x2 fShadingChangeUv = ClampUv(fUv - Jitter() / RenderSize(), ShadingChangeRenderSize(), ShadingChangeMaxRenderSize());
+ const FfxFloat32 fShadingChange = ffxSaturate(SampleShadingChange(fShadingChangeUv));
+
+ return fShadingChange;
+}
+
+void PrepareReactivity(FfxInt32x2 iPxPos)
+{
+ const FfxFloat32x2 fUv = (iPxPos + 0.5f) / RenderSize();
+ const FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos);
+
+ // Discard small mvs
+ const FfxFloat32 f4KVelocity = Get4KVelocity(fMotionVector);
+
+ const FfxFloat32x2 fDilatedUv = fUv + fMotionVector;
+ const FfxFloat32 fDilatedDepth = LoadDilatedDepth(iPxPos);
+ const FfxFloat32 fDepthInMeters = GetViewSpaceDepthInMeters(fDilatedDepth);
+
+ const FfxFloat32 fDisocclusion = ComputeDisocclusions(fUv, fMotionVector, GetViewSpaceDepth(fDilatedDepth));
+ const FfxFloat32 fShadingChange = ffxMax(DilateReactiveMasks(iPxPos, fUv), ComputeShadingChange(fUv));
+
+ const FfxFloat32 fMotionDivergence = ComputeMotionDivergence(fUv, fMotionVector, fDilatedDepth);
+ const FfxFloat32 fDilatedTransparencyAndComposition = DilateTransparencyAndCompositionMasks(iPxPos, fUv);
+ const FfxFloat32 fFinalReactiveness = ffxMax(fMotionDivergence, fDilatedTransparencyAndComposition);
+
+ const FfxFloat32 fAccumulation = UpdateAccumulation(iPxPos, fUv, fMotionVector, fDisocclusion, fShadingChange);
+
+ FfxFloat32x4 fOutput;
+ fOutput[REACTIVE] = fFinalReactiveness;
+ fOutput[DISOCCLUSION] = fDisocclusion;
+ fOutput[SHADING_CHANGE] = fShadingChange;
+ fOutput[ACCUMULAION] = fAccumulation;
+
+ StoreDilatedReactiveMasks(iPxPos, fOutput);
+
+ const FfxFloat32 fLockStrength = ComputeThinFeatureConfidence(iPxPos);
+ if (fLockStrength > (1.0f / 100.0f))
+ {
+ StoreNewLocks(ComputeHrPosFromLrPos(FfxInt32x2(iPxPos)), fLockStrength);
+ }
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_rcas.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_rcas.h
new file mode 100644
index 0000000000..f48cffb93f
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_rcas.h
@@ -0,0 +1,112 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#define GROUP_SIZE 8
+#define FSR_RCAS_DENOISE 1
+
+#include "ffx_core.h"
+
+#if FFX_HALF && defined(__XBOX_SCARLETT) && defined(__XBATG_EXTRA_16_BIT_OPTIMISATION) && (__XBATG_EXTRA_16_BIT_OPTIMISATION == 1)
+ #define FSR_RCAS_PREFER_PAIRED_VERSION 1
+#else
+ #define FSR_RCAS_PREFER_PAIRED_VERSION 0
+#endif
+
+void WriteUpscaledOutput(FFX_MIN16_U2 iPxHrPos, FfxFloat32x3 fUpscaledColor)
+{
+ StoreUpscaledOutput(FFX_MIN16_I2(iPxHrPos), fUpscaledColor);
+}
+
+#if FSR_RCAS_PREFER_PAIRED_VERSION
+ #define FSR_RCAS_HX2 1
+ FfxFloat16x4 FsrRcasLoadHx2(FfxInt16x2 p)
+ {
+ return FfxFloat16x4(LoadRCAS_Input(p));
+ }
+ void FsrRcasInputHx2(inout FfxFloat16x2 r, inout FfxFloat16x2 g, inout FfxFloat16x2 b)
+ {
+ FfxFloat32 e = Exposure();
+ r = FfxFloat16x2(r * e);
+ g = FfxFloat16x2(g * e);
+ b = FfxFloat16x2(b * e);
+ }
+
+ #include "fsr1/ffx_fsr1.h"
+
+ void CurrFilterPaired(FFX_MIN16_U2 pos)
+ {
+ FfxFloat16x2 cr;
+ FfxFloat16x2 cg;
+ FfxFloat16x2 cb;
+ FsrRcasHx2(cr, cg, cb, pos, RCASConfig());
+ FfxFloat32 InvExposure = 1.0f / Exposure();
+ cr = FfxFloat16x2(cr * InvExposure);
+ cg = FfxFloat16x2(cg * InvExposure);
+ cb = FfxFloat16x2(cb * InvExposure);
+ WriteUpscaledOutput(pos, FfxFloat16x3(cr.x, cg.x, cb.x)); //TODO: fix type
+ pos.x += 8;
+ WriteUpscaledOutput(pos, FfxFloat16x3(cr.y, cg.y, cb.y)); //TODO: fix type
+ }
+#else
+ #define FSR_RCAS_F 1
+ FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p)
+ {
+ FfxFloat32x4 fColor = LoadRCAS_Input(p);
+
+ fColor.rgb *= Exposure();
+
+ return fColor;
+ }
+ void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {}
+
+ #include "fsr1/ffx_fsr1.h"
+
+ void CurrFilter(FFX_MIN16_U2 pos)
+ {
+ FfxFloat32x3 c;
+ FsrRcasF(c.r, c.g, c.b, pos, RCASConfig());
+
+ c /= Exposure();
+
+ WriteUpscaledOutput(pos, c);
+ }
+
+#endif // #if FSR_RCAS_PREFER_PAIRED_VERSION
+
+void RCAS(FfxUInt32x3 LocalThreadId, FfxUInt32x3 WorkGroupId, FfxUInt32x3 Dtid)
+{
+ // Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
+ FfxUInt32x2 gxy = ffxRemapForQuad(LocalThreadId.x) + FfxUInt32x2(WorkGroupId.x << 4u, WorkGroupId.y << 4u);
+#if FSR_RCAS_PREFER_PAIRED_VERSION
+ CurrFilterPaired(FFX_MIN16_U2(gxy));
+ gxy.y += 8u;
+ CurrFilterPaired(FFX_MIN16_U2(gxy));
+#else
+ CurrFilter(FFX_MIN16_U2(gxy));
+ gxy.x += 8u;
+ CurrFilter(FFX_MIN16_U2(gxy));
+ gxy.y += 8u;
+ CurrFilter(FFX_MIN16_U2(gxy));
+ gxy.x -= 8u;
+ CurrFilter(FFX_MIN16_U2(gxy));
+#endif
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_reproject.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_reproject.h
new file mode 100644
index 0000000000..c72f40fb6f
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_reproject.h
@@ -0,0 +1,79 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE
+#define FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE 0 // Reference
+#endif
+
+FfxFloat32x4 WrapHistory(FfxInt32x2 iPxSample)
+{
+ return LoadHistory(iPxSample);
+}
+
+DeclareCustomFetchBicubicSamples(FetchHistorySamples, WrapHistory)
+DeclareCustomTextureSample(HistorySample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples)
+
+#if FFX_HALF
+FFX_MIN16_F4 WrapHistory16(FfxInt32x2 iPxSample)
+{
+ return FFX_MIN16_F4(LoadHistory(iPxSample));
+}
+
+DeclareCustomFetchBicubicSamplesMin16(FetchHistorySamples16, WrapHistory16)
+DeclareCustomTextureSampleMin16(HistorySample16, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples16)
+#endif
+
+FfxFloat32x2 GetMotionVector(FfxInt32x2 iPxHrPos, FfxFloat32x2 fHrUv)
+{
+#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS
+ const FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(FFX_MIN16_I2(fHrUv * RenderSize()));
+#else
+ const FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iPxHrPos);
+#endif
+
+ return fDilatedMotionVector;
+}
+
+void ComputeReprojectedUVs(FFX_PARAMETER_INOUT AccumulationPassCommonParams params)
+{
+ params.fReprojectedHrUv = params.fHrUv + params.fMotionVector;
+
+ params.bIsExistingSample = IsUvInside(params.fReprojectedHrUv);
+}
+
+void ReprojectHistoryColor(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data)
+
+{
+#if FFX_HALF && FFX_FSR3UPSCALER_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF
+ const FfxFloat32x4 fReprojectedHistory = FfxFloat32x4(HistorySample16(params.fReprojectedHrUv, UpscaleSize()));
+#else
+ const FfxFloat32x4 fReprojectedHistory = HistorySample(params.fReprojectedHrUv, PreviousFrameUpscaleSize());
+#endif
+
+ data.fHistoryColor = fReprojectedHistory.rgb;
+ data.fHistoryColor *= DeltaPreExposure();
+ data.fHistoryColor *= Exposure();
+
+ data.fHistoryColor = RGBToYCoCg(data.fHistoryColor);
+
+ data.fLock = fReprojectedHistory.w;
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_resources.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_resources.h
new file mode 100644
index 0000000000..b3d8ddb17a
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_resources.h
@@ -0,0 +1,100 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef FFX_FSR3UPSCALER_RESOURCES_H
+#define FFX_FSR3UPSCALER_RESOURCES_H
+
+#if defined(FFX_CPU) || defined(FFX_GPU)
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NULL 0
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_OPAQUE_ONLY 1
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_COLOR 2
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_MOTION_VECTORS 3
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_DEPTH 4
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_EXPOSURE 5
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_REACTIVE_MASK 6
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_TRANSPARENCY_AND_COMPOSITION_MASK 7
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RECONSTRUCTED_PREVIOUS_NEAREST_DEPTH 8
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_MOTION_VECTORS 9
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_DEPTH 10
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR 11
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_ACCUMULATION 12
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NEW_LOCKS 13
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY 14
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DEBUG_OUTPUT 15
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LANCZOS_LUT 16
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_ATOMIC_COUNT 17
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_UPSCALED_OUTPUT 18
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RCAS_INPUT 19
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_ACCUMULATION_1 20
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_ACCUMULATION_2 21
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_1 22
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_2 23
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_REACTIVITY 24
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_TRANSPARENCY_AND_COMPOSITION 25
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_REACTIVE_MASKS 26
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS 27 // same as FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_0
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_0 27
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_1 28
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_2 29
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_3 30
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_4 31
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_5 32
+
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_EXPOSURE 33
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_FRAME_INFO 34
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOREACTIVE 35
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOCOMPOSITION_DEPRECATED 36
+
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_1 37
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_2 38
+
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_1 40
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_2 41
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SHADING_CHANGE 42
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_FARTHEST_DEPTH 43
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_FARTHEST_DEPTH_MIP1 44
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_CURRENT_LUMA 45
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREVIOUS_LUMA 46
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_INSTABILITY 48
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERMEDIATE_FP16x1 49
+
+
+// Shading change detection mip level setting, value must be in the range [FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0, FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12]
+//#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4
+//#define FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL (FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE - FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE)
+
+#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT 60
+
+#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_FSR3UPSCALER 0
+#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_SPD 1
+#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_RCAS 2
+#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_GENREACTIVE 3
+#define FFX_FSR3UPSCALER_CONSTANTBUFFER_COUNT 4
+
+#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_TONEMAP 1
+#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP 2
+#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_THRESHOLD 4
+#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX 8
+
+#endif // #if defined(FFX_CPU) || defined(FFX_GPU)
+
+#endif //!defined( FFX_FSR3UPSCALER_RESOURCES_H )
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_sample.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_sample.h
new file mode 100644
index 0000000000..7a723d5562
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_sample.h
@@ -0,0 +1,628 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef FFX_FSR3UPSCALER_SAMPLE_H
+#define FFX_FSR3UPSCALER_SAMPLE_H
+
+// suppress warnings
+#ifdef FFX_HLSL
+#pragma warning(disable: 4008) // potentially divide by zero
+#endif //FFX_HLSL
+
+struct FetchedBilinearSamples {
+
+ FfxFloat32x4 fColor00;
+ FfxFloat32x4 fColor10;
+
+ FfxFloat32x4 fColor01;
+ FfxFloat32x4 fColor11;
+};
+
+struct FetchedBicubicSamples {
+
+ FfxFloat32x4 fColor00;
+ FfxFloat32x4 fColor10;
+ FfxFloat32x4 fColor20;
+ FfxFloat32x4 fColor30;
+
+ FfxFloat32x4 fColor01;
+ FfxFloat32x4 fColor11;
+ FfxFloat32x4 fColor21;
+ FfxFloat32x4 fColor31;
+
+ FfxFloat32x4 fColor02;
+ FfxFloat32x4 fColor12;
+ FfxFloat32x4 fColor22;
+ FfxFloat32x4 fColor32;
+
+ FfxFloat32x4 fColor03;
+ FfxFloat32x4 fColor13;
+ FfxFloat32x4 fColor23;
+ FfxFloat32x4 fColor33;
+};
+
+#if FFX_HALF
+struct FetchedBilinearSamplesMin16 {
+
+ FFX_MIN16_F4 fColor00;
+ FFX_MIN16_F4 fColor10;
+
+ FFX_MIN16_F4 fColor01;
+ FFX_MIN16_F4 fColor11;
+};
+
+struct FetchedBicubicSamplesMin16 {
+
+ FFX_MIN16_F4 fColor00;
+ FFX_MIN16_F4 fColor10;
+ FFX_MIN16_F4 fColor20;
+ FFX_MIN16_F4 fColor30;
+
+ FFX_MIN16_F4 fColor01;
+ FFX_MIN16_F4 fColor11;
+ FFX_MIN16_F4 fColor21;
+ FFX_MIN16_F4 fColor31;
+
+ FFX_MIN16_F4 fColor02;
+ FFX_MIN16_F4 fColor12;
+ FFX_MIN16_F4 fColor22;
+ FFX_MIN16_F4 fColor32;
+
+ FFX_MIN16_F4 fColor03;
+ FFX_MIN16_F4 fColor13;
+ FFX_MIN16_F4 fColor23;
+ FFX_MIN16_F4 fColor33;
+};
+#else //FFX_HALF
+#define FetchedBicubicSamplesMin16 FetchedBicubicSamples
+#define FetchedBilinearSamplesMin16 FetchedBilinearSamples
+#endif //FFX_HALF
+
+FfxFloat32x4 Linear(FfxFloat32x4 A, FfxFloat32x4 B, FfxFloat32 t)
+{
+ return A + (B - A) * t;
+}
+
+FfxFloat32x4 Bilinear(FetchedBilinearSamples BilinearSamples, FfxFloat32x2 fPxFrac)
+{
+ FfxFloat32x4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x);
+ FfxFloat32x4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x);
+ FfxFloat32x4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y);
+ return fColorXY;
+}
+
+#if FFX_HALF
+FFX_MIN16_F4 Linear(FFX_MIN16_F4 A, FFX_MIN16_F4 B, FFX_MIN16_F t)
+{
+ return A + (B - A) * t;
+}
+
+FFX_MIN16_F4 Bilinear(FetchedBilinearSamplesMin16 BilinearSamples, FFX_MIN16_F2 fPxFrac)
+{
+ FFX_MIN16_F4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x);
+ FFX_MIN16_F4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x);
+ FFX_MIN16_F4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y);
+ return fColorXY;
+}
+#endif
+
+FfxFloat32 Lanczos2NoClamp(FfxFloat32 x)
+{
+ const FfxFloat32 PI = 3.141592653589793f; // TODO: share SDK constants
+ return abs(x) < FSR3UPSCALER_EPSILON ? 1.f : (sin(PI * x) / (PI * x)) * (sin(0.5f * PI * x) / (0.5f * PI * x));
+}
+
+FfxFloat32 Lanczos2(FfxFloat32 x)
+{
+ x = ffxMin(abs(x), 2.0f);
+ return Lanczos2NoClamp(x);
+}
+
+#if FFX_HALF
+
+#if 0
+FFX_MIN16_F Lanczos2NoClamp(FFX_MIN16_F x)
+{
+ const FFX_MIN16_F PI = FFX_MIN16_F(3.141592653589793f); // TODO: share SDK constants
+ return abs(x) < FFX_MIN16_F(FSR3UPSCALER_EPSILON) ? FFX_MIN16_F(1.f) : (sin(PI * x) / (PI * x)) * (sin(FFX_MIN16_F(0.5f) * PI * x) / (FFX_MIN16_F(0.5f) * PI * x));
+}
+#endif
+
+FFX_MIN16_F Lanczos2(FFX_MIN16_F x)
+{
+ x = ffxMin(abs(x), FFX_MIN16_F(2.0f));
+ return FFX_MIN16_F(Lanczos2NoClamp(x));
+}
+#endif //FFX_HALF
+
+// FSR1 lanczos approximation. Input is x*x and must be <= 4.
+FfxFloat32 Lanczos2ApproxSqNoClamp(FfxFloat32 x2)
+{
+ FfxFloat32 a = (2.0f / 5.0f) * x2 - 1;
+ FfxFloat32 b = (1.0f / 4.0f) * x2 - 1;
+ return ((25.0f / 16.0f) * a * a - (25.0f / 16.0f - 1)) * (b * b);
+}
+
+#if FFX_HALF
+FFX_MIN16_F Lanczos2ApproxSqNoClamp(FFX_MIN16_F x2)
+{
+ FFX_MIN16_F a = FFX_MIN16_F(2.0f / 5.0f) * x2 - FFX_MIN16_F(1);
+ FFX_MIN16_F b = FFX_MIN16_F(1.0f / 4.0f) * x2 - FFX_MIN16_F(1);
+ return (FFX_MIN16_F(25.0f / 16.0f) * a * a - FFX_MIN16_F(25.0f / 16.0f - 1)) * (b * b);
+}
+
+#if defined(__XBOX_SCARLETT) && defined(__XBATG_EXTRA_16_BIT_OPTIMISATION) && (__XBATG_EXTRA_16_BIT_OPTIMISATION == 1)
+FFX_MIN16_F2 PairedLanczos2ApproxSqNoClamp(FFX_MIN16_F2 x2)
+{
+ // Xbox ATG (Pavel):
+ //
+ // 2.0 * x2 - 5.0 25.0 25.0 - 16.0 (2.0 * x2 - 5.0)^2 - (3.0)^2 (2.0 * x2 - 8.0) * (2.0 * x2 - 2.0) (x2 - 4.0) * (x2 - 1.0)
+ // a = -------------- ==> ---- * a^2 - -------------- = ----------------------------- = ---------------------------------- = ----------------------- = b * (x2 - 1.0)
+ // 5.0 16.0 16.0 16.0 16.0 4.0
+ //
+ // so we need to compute just (b * b) * (b * x2 - b), so we should get four packed instructions: 2 fma + 2 mul
+ //
+
+ FFX_MIN16_F2 b = (0.25 * x2 - 1.0);
+ return (b * b) * (b * x2 - b);
+}
+#endif
+
+#endif //FFX_HALF
+
+FfxFloat32 Lanczos2ApproxSq(FfxFloat32 x2)
+{
+ x2 = ffxMin(x2, 4.0f);
+ return Lanczos2ApproxSqNoClamp(x2);
+}
+
+#if FFX_HALF
+FFX_MIN16_F Lanczos2ApproxSq(FFX_MIN16_F x2)
+{
+ x2 = ffxMin(x2, FFX_MIN16_F(4.0f));
+ return Lanczos2ApproxSqNoClamp(x2);
+}
+
+#if defined(__XBOX_SCARLETT) && defined(__XBATG_EXTRA_16_BIT_OPTIMISATION) && (__XBATG_EXTRA_16_BIT_OPTIMISATION == 1)
+FFX_MIN16_F2 PairedLanczos2ApproxSq(FFX_MIN16_F2 x2)
+{
+ x2 = ffxMin(x2, FFX_MIN16_F2(4.0, 4.0));
+ return PairedLanczos2ApproxSqNoClamp(x2);
+}
+#endif
+#endif //FFX_HALF
+
+FfxFloat32 Lanczos2ApproxNoClamp(FfxFloat32 x)
+{
+ return Lanczos2ApproxSqNoClamp(x * x);
+}
+
+#if FFX_HALF
+FFX_MIN16_F Lanczos2ApproxNoClamp(FFX_MIN16_F x)
+{
+ return Lanczos2ApproxSqNoClamp(x * x);
+}
+#endif //FFX_HALF
+
+FfxFloat32 Lanczos2Approx(FfxFloat32 x)
+{
+ return Lanczos2ApproxSq(x * x);
+}
+
+#if FFX_HALF
+FFX_MIN16_F Lanczos2Approx(FFX_MIN16_F x)
+{
+ return Lanczos2ApproxSq(x * x);
+}
+#endif //FFX_HALF
+
+FfxFloat32 Lanczos2_UseLUT(FfxFloat32 x)
+{
+ return SampleLanczos2Weight(abs(x));
+}
+
+#if FFX_HALF
+FFX_MIN16_F Lanczos2_UseLUT(FFX_MIN16_F x)
+{
+ return FFX_MIN16_F(SampleLanczos2Weight(abs(x)));
+}
+#endif //FFX_HALF
+
+FfxFloat32x4 Lanczos2_UseLUT(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t)
+{
+ FfxFloat32 fWeight0 = Lanczos2_UseLUT(-1.f - t);
+ FfxFloat32 fWeight1 = Lanczos2_UseLUT(-0.f - t);
+ FfxFloat32 fWeight2 = Lanczos2_UseLUT(+1.f - t);
+ FfxFloat32 fWeight3 = Lanczos2_UseLUT(+2.f - t);
+ return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3);
+}
+#if FFX_HALF
+FFX_MIN16_F4 Lanczos2_UseLUT(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t)
+{
+ FFX_MIN16_F fWeight0 = Lanczos2_UseLUT(FFX_MIN16_F(-1.f) - t);
+ FFX_MIN16_F fWeight1 = Lanczos2_UseLUT(FFX_MIN16_F(-0.f) - t);
+ FFX_MIN16_F fWeight2 = Lanczos2_UseLUT(FFX_MIN16_F(+1.f) - t);
+ FFX_MIN16_F fWeight3 = Lanczos2_UseLUT(FFX_MIN16_F(+2.f) - t);
+ return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3);
+}
+#endif
+
+FfxFloat32x4 Lanczos2(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t)
+{
+ FfxFloat32 fWeight0 = Lanczos2(-1.f - t);
+ FfxFloat32 fWeight1 = Lanczos2(-0.f - t);
+ FfxFloat32 fWeight2 = Lanczos2(+1.f - t);
+ FfxFloat32 fWeight3 = Lanczos2(+2.f - t);
+ return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3);
+}
+
+FfxFloat32x4 Lanczos2(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac)
+{
+ FfxFloat32x4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x);
+ FfxFloat32x4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x);
+ FfxFloat32x4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x);
+ FfxFloat32x4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x);
+ FfxFloat32x4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y);
+
+ // Deringing
+
+ // TODO: only use 4 by checking jitter
+ const FfxInt32 iDeringingSampleCount = 4;
+ const FfxFloat32x4 fDeringingSamples[4] = {
+ Samples.fColor11,
+ Samples.fColor21,
+ Samples.fColor12,
+ Samples.fColor22,
+ };
+
+ FfxFloat32x4 fDeringingMin = fDeringingSamples[0];
+ FfxFloat32x4 fDeringingMax = fDeringingSamples[0];
+
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) {
+
+ fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]);
+ fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]);
+ }
+
+ fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax);
+
+ return fColorXY;
+}
+
+#if FFX_HALF
+FFX_MIN16_F4 Lanczos2(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t)
+{
+ FFX_MIN16_F fWeight0 = Lanczos2(FFX_MIN16_F(-1.f) - t);
+ FFX_MIN16_F fWeight1 = Lanczos2(FFX_MIN16_F(-0.f) - t);
+ FFX_MIN16_F fWeight2 = Lanczos2(FFX_MIN16_F(+1.f) - t);
+ FFX_MIN16_F fWeight3 = Lanczos2(FFX_MIN16_F(+2.f) - t);
+ return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3);
+}
+
+FFX_MIN16_F4 Lanczos2(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac)
+{
+ FFX_MIN16_F4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x);
+ FFX_MIN16_F4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x);
+ FFX_MIN16_F4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x);
+ FFX_MIN16_F4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x);
+ FFX_MIN16_F4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y);
+
+ // Deringing
+
+ // TODO: only use 4 by checking jitter
+ const FfxInt32 iDeringingSampleCount = 4;
+ const FFX_MIN16_F4 fDeringingSamples[4] = {
+ Samples.fColor11,
+ Samples.fColor21,
+ Samples.fColor12,
+ Samples.fColor22,
+ };
+
+ FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0];
+ FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0];
+
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex)
+ {
+ fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]);
+ fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]);
+ }
+
+ fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax);
+
+ return fColorXY;
+}
+#endif //FFX_HALF
+
+
+FfxFloat32x4 Lanczos2LUT(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac)
+{
+ FfxFloat32x4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x);
+ FfxFloat32x4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x);
+ FfxFloat32x4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x);
+ FfxFloat32x4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x);
+ FfxFloat32x4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y);
+
+ // Deringing
+
+ // TODO: only use 4 by checking jitter
+ const FfxInt32 iDeringingSampleCount = 4;
+ const FfxFloat32x4 fDeringingSamples[4] = {
+ Samples.fColor11,
+ Samples.fColor21,
+ Samples.fColor12,
+ Samples.fColor22,
+ };
+
+ FfxFloat32x4 fDeringingMin = fDeringingSamples[0];
+ FfxFloat32x4 fDeringingMax = fDeringingSamples[0];
+
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) {
+
+ fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]);
+ fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]);
+ }
+
+ fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax);
+
+ return fColorXY;
+}
+
+#if FFX_HALF
+FFX_MIN16_F4 Lanczos2LUT(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac)
+{
+ FFX_MIN16_F4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x);
+ FFX_MIN16_F4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x);
+ FFX_MIN16_F4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x);
+ FFX_MIN16_F4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x);
+ FFX_MIN16_F4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y);
+
+ // Deringing
+
+ // TODO: only use 4 by checking jitter
+ const FfxInt32 iDeringingSampleCount = 4;
+ const FFX_MIN16_F4 fDeringingSamples[4] = {
+ Samples.fColor11,
+ Samples.fColor21,
+ Samples.fColor12,
+ Samples.fColor22,
+ };
+
+ FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0];
+ FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0];
+
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex)
+ {
+ fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]);
+ fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]);
+ }
+
+ fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax);
+
+ return fColorXY;
+}
+#endif //FFX_HALF
+
+
+
+FfxFloat32x4 Lanczos2Approx(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t)
+{
+ FfxFloat32 fWeight0 = Lanczos2ApproxNoClamp(-1.f - t);
+ FfxFloat32 fWeight1 = Lanczos2ApproxNoClamp(-0.f - t);
+ FfxFloat32 fWeight2 = Lanczos2ApproxNoClamp(+1.f - t);
+ FfxFloat32 fWeight3 = Lanczos2ApproxNoClamp(+2.f - t);
+ return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3);
+}
+
+#if FFX_HALF
+FFX_MIN16_F4 Lanczos2Approx(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t)
+{
+ FFX_MIN16_F fWeight0 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-1.f) - t);
+ FFX_MIN16_F fWeight1 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-0.f) - t);
+ FFX_MIN16_F fWeight2 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+1.f) - t);
+ FFX_MIN16_F fWeight3 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+2.f) - t);
+ return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3);
+}
+#endif //FFX_HALF
+
+FfxFloat32x4 Lanczos2Approx(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac)
+{
+ FfxFloat32x4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x);
+ FfxFloat32x4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x);
+ FfxFloat32x4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x);
+ FfxFloat32x4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x);
+ FfxFloat32x4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y);
+
+ // Deringing
+
+ // TODO: only use 4 by checking jitter
+ const FfxInt32 iDeringingSampleCount = 4;
+ const FfxFloat32x4 fDeringingSamples[4] = {
+ Samples.fColor11,
+ Samples.fColor21,
+ Samples.fColor12,
+ Samples.fColor22,
+ };
+
+ FfxFloat32x4 fDeringingMin = fDeringingSamples[0];
+ FfxFloat32x4 fDeringingMax = fDeringingSamples[0];
+
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex)
+ {
+ fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]);
+ fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]);
+ }
+
+ fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax);
+
+ return fColorXY;
+}
+
+#if FFX_HALF
+FFX_MIN16_F4 Lanczos2Approx(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac)
+{
+ FFX_MIN16_F4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x);
+ FFX_MIN16_F4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x);
+ FFX_MIN16_F4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x);
+ FFX_MIN16_F4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x);
+ FFX_MIN16_F4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y);
+
+ // Deringing
+
+ // TODO: only use 4 by checking jitter
+ const FfxInt32 iDeringingSampleCount = 4;
+ const FFX_MIN16_F4 fDeringingSamples[4] = {
+ Samples.fColor11,
+ Samples.fColor21,
+ Samples.fColor12,
+ Samples.fColor22,
+ };
+
+ FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0];
+ FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0];
+
+ FFX_UNROLL
+ for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex)
+ {
+ fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]);
+ fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]);
+ }
+
+ fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax);
+
+ return fColorXY;
+}
+#endif
+
+// Clamp by offset direction. Assuming iPxSample is already in range and iPxOffset is compile time constant.
+FfxInt32x2 ClampCoord(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize)
+{
+ FfxInt32x2 result = iPxSample + iPxOffset;
+ result.x = ffxMax(1, ffxMin(result.x, iTextureSize.x - 2));
+ result.y = ffxMax(1, ffxMin(result.y, iTextureSize.y - 2));
+ return result;
+}
+#if FFX_HALF
+FFX_MIN16_I2 ClampCoord(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize)
+{
+ FFX_MIN16_I2 result = iPxSample + iPxOffset;
+ result.x = ffxMax(FFX_MIN16_I(1), ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(2)));
+ result.y = ffxMax(FFX_MIN16_I(1), ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(2)));
+ return result;
+}
+#endif //FFX_HALF
+
+
+#define DeclareCustomFetchBicubicSamplesWithType(SampleType, TextureType, AddrType, Name, LoadTexture) \
+ SampleType Name(AddrType iPxSample, AddrType iTextureSize) \
+ { \
+ SampleType Samples; \
+ \
+ Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, -1), iTextureSize))); \
+ Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, -1), iTextureSize))); \
+ Samples.fColor20 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, -1), iTextureSize))); \
+ Samples.fColor30 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, -1), iTextureSize))); \
+ \
+ Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +0), iTextureSize))); \
+ Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \
+ Samples.fColor21 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \
+ Samples.fColor31 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +0), iTextureSize))); \
+ \
+ Samples.fColor02 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +1), iTextureSize))); \
+ Samples.fColor12 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \
+ Samples.fColor22 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \
+ Samples.fColor32 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +1), iTextureSize))); \
+ \
+ Samples.fColor03 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +2), iTextureSize))); \
+ Samples.fColor13 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +2), iTextureSize))); \
+ Samples.fColor23 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +2), iTextureSize))); \
+ Samples.fColor33 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +2), iTextureSize))); \
+ \
+ return Samples; \
+ }
+
+#define DeclareCustomFetchBicubicSamples(Name, LoadTexture) \
+ DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture)
+
+#define DeclareCustomFetchBicubicSamplesMin16(Name, LoadTexture) \
+ DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture)
+
+#define DeclareCustomFetchBilinearSamplesWithType(SampleType, TextureType,AddrType, Name, LoadTexture) \
+ SampleType Name(AddrType iPxSample, AddrType iTextureSize) \
+ { \
+ SampleType Samples; \
+ Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \
+ Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \
+ Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \
+ Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \
+ return Samples; \
+ }
+
+#define DeclareCustomFetchBilinearSamples(Name, LoadTexture) \
+ DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture)
+
+#define DeclareCustomFetchBilinearSamplesMin16(Name, LoadTexture) \
+ DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture)
+
+// BE CAREFUL: there is some precision issues and (3253, 125) leading to (3252.9989778, 125.001102)
+// is common, so iPxSample can "jitter"
+#define DeclareCustomTextureSample(Name, InterpolateSamples, FetchSamples) \
+ FfxFloat32x4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \
+ { \
+ FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \
+ FfxFloat32x2 fPxFrac = ffxFract(fPxSample); \
+ /* Clamp base coords */ \
+ fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x-1), fPxSample.x)); \
+ fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y-1), fPxSample.y)); \
+ /* */ \
+ FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \
+ FfxFloat32x4 fColorXY = FfxFloat32x4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \
+ return fColorXY; \
+ }
+
+#define DeclareCustomTextureSampleMin16(Name, InterpolateSamples, FetchSamples) \
+ FFX_MIN16_F4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \
+ { \
+ FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \
+ FFX_MIN16_F2 fPxFrac = FFX_MIN16_F2(ffxFract(fPxSample)); \
+ /* Clamp base coords */ \
+ fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \
+ fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \
+ /* */ \
+ FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \
+ FFX_MIN16_F4 fColorXY = FFX_MIN16_F4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \
+ return fColorXY; \
+ }
+
+#define FFX_FSR3UPSCALER_CONCAT_ID(x, y) x ## y
+#define FFX_FSR3UPSCALER_CONCAT(x, y) FFX_FSR3UPSCALER_CONCAT_ID(x, y)
+#define FFX_FSR3UPSCALER_SAMPLER_1D_0 Lanczos2
+#define FFX_FSR3UPSCALER_SAMPLER_1D_1 Lanczos2LUT
+#define FFX_FSR3UPSCALER_SAMPLER_1D_2 Lanczos2Approx
+
+#define FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(x) FFX_FSR3UPSCALER_CONCAT(FFX_FSR3UPSCALER_SAMPLER_1D_, x)
+
+#endif //!defined( FFX_FSR3UPSCALER_SAMPLE_H )
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change.h
new file mode 100644
index 0000000000..2eb23aacdf
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change.h
@@ -0,0 +1,68 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+FFX_STATIC const FfxInt32 s_MipLevelsToUse = 3;
+
+struct ShadingChangeLumaInfo
+{
+ FfxFloat32 fSamples[s_MipLevelsToUse];
+};
+
+ShadingChangeLumaInfo ComputeShadingChangeLuma(FfxInt32x2 iPxPos, FfxFloat32x2 fUv, const FfxInt32x2 iCurrentSize)
+{
+ ShadingChangeLumaInfo info;
+
+ const FfxFloat32x2 fMipUv = ClampUv(fUv, ShadingChangeRenderSize(), GetSPDMipDimensions(0));
+
+ FFX_UNROLL
+ for (FfxInt32 iMipLevel = iShadingChangeMipStart; iMipLevel < s_MipLevelsToUse; iMipLevel++) {
+
+ const FfxFloat32x2 fSample = SampleSPDMipLevel(fMipUv, iMipLevel);
+
+ info.fSamples[iMipLevel] = abs(fSample.x * fSample.y);
+ }
+
+ return info;
+}
+
+void ShadingChange(FfxInt32x2 iPxPos)
+{
+ if (IsOnScreen(FfxInt32x2(iPxPos), ShadingChangeRenderSize())) {
+
+ const FfxFloat32x2 fUv = (iPxPos + 0.5f) / ShadingChangeRenderSize();
+ const FfxFloat32x2 fUvJittered = fUv + Jitter() / RenderSize();
+
+ const ShadingChangeLumaInfo info = ComputeShadingChangeLuma(iPxPos, fUvJittered, ShadingChangeRenderSize());
+
+ const FfxFloat32 fScale = 1.0f + iShadingChangeMipStart / s_MipLevelsToUse;
+ FfxFloat32 fShadingChange = 0.0f;
+ FFX_UNROLL
+ for (int iMipLevel = iShadingChangeMipStart; iMipLevel < s_MipLevelsToUse; iMipLevel++)
+ {
+ if (info.fSamples[iMipLevel] > 0) {
+ fShadingChange = ffxMax(fShadingChange, info.fSamples[iMipLevel]) * fScale;
+ }
+ }
+
+ StoreShadingChange(iPxPos, ffxSaturate(fShadingChange));
+ }
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h
new file mode 100644
index 0000000000..63ca5d6d5f
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h
@@ -0,0 +1,297 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+FFX_GROUPSHARED FfxUInt32 spdCounter;
+
+void SpdIncreaseAtomicCounter(FfxUInt32 slice)
+{
+ SPD_IncreaseAtomicCounter(spdCounter);
+}
+
+FfxUInt32 SpdGetAtomicCounter()
+{
+ return spdCounter;
+}
+
+void SpdResetAtomicCounter(FfxUInt32 slice)
+{
+ SPD_ResetAtomicCounter();
+}
+
+#ifndef SPD_PACKED_ONLY
+FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16];
+FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16];
+FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16];
+FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16];
+
+FFX_STATIC const FfxInt32 DIFFERENCE = 0;
+FFX_STATIC const FfxInt32 SIGN_SUM = 1;
+FFX_STATIC const FfxInt32 MIP0_INDICATOR = 2;
+
+FfxFloat32x2 Sort2(FfxFloat32x2 v)
+{
+ return FfxFloat32x2(ffxMin(v.x, v.y), ffxMax(v.x, v.y));
+}
+
+struct SampleSet
+{
+ FfxFloat32 fSamples[SHADING_CHANGE_SET_SIZE];
+};
+
+#define CompareSwap(i, j) \
+{ \
+FfxFloat32 fTmp = ffxMin(fSet.fSamples[i], fSet.fSamples[j]);\
+fSet.fSamples[j] = ffxMax(fSet.fSamples[i], fSet.fSamples[j]);\
+fSet.fSamples[i] = fTmp;\
+}
+
+#if SHADING_CHANGE_SET_SIZE == 5
+FFX_STATIC const FfxInt32x2 iSampleOffsets[5] = {FfxInt32x2(+0, +0), FfxInt32x2(-1, +0), FfxInt32x2(+1, +0), FfxInt32x2(+0, -1), FfxInt32x2(+0, +1)};
+
+void SortSet(FFX_PARAMETER_INOUT SampleSet fSet)
+{
+ CompareSwap(0, 3);
+ CompareSwap(1, 4);
+ CompareSwap(0, 2);
+ CompareSwap(1, 3);
+ CompareSwap(0, 1);
+ CompareSwap(2, 4);
+ CompareSwap(1, 2);
+ CompareSwap(3, 4);
+ CompareSwap(2, 3);
+}
+#endif
+
+FfxFloat32 ComputeMinimumDifference(FfxInt32x2 iPxPos, SampleSet fSet0, SampleSet fSet1)
+{
+ FfxFloat32 fMinDiff = FSR3UPSCALER_FP16_MAX - 1;
+ FfxInt32 a = 0;
+ FfxInt32 b = 0;
+
+ SortSet(fSet0);
+ SortSet(fSet1);
+
+ const FfxFloat32 fMax = ffxMin(fSet0.fSamples[SHADING_CHANGE_SET_SIZE-1], fSet1.fSamples[SHADING_CHANGE_SET_SIZE-1]);
+
+ if (fMax > FSR3UPSCALER_FP32_MIN) {
+
+ FFX_UNROLL
+ for (FfxInt32 i = 0; i < SHADING_CHANGE_SET_SIZE && (fMinDiff < FSR3UPSCALER_FP16_MAX); i++) {
+
+ FfxFloat32 fDiff = fSet0.fSamples[a] - fSet1.fSamples[b];
+
+ if (abs(fDiff) > FSR3UPSCALER_FP16_MIN) {
+
+ fDiff = sign(fDiff) * (1.0f - MinDividedByMax(fSet0.fSamples[a], fSet1.fSamples[b]));
+
+ fMinDiff = (abs(fDiff) < abs(fMinDiff)) ? fDiff : fMinDiff;
+
+ a += FfxInt32(fSet0.fSamples[a] < fSet1.fSamples[b]);
+ b += FfxInt32(fSet0.fSamples[a] >= fSet1.fSamples[b]);
+ }
+ else
+ {
+ fMinDiff = FSR3UPSCALER_FP16_MAX;
+ }
+ }
+ }
+
+ return fMinDiff * FfxFloat32(fMinDiff < (FSR3UPSCALER_FP16_MAX - 1));
+}
+
+SampleSet GetCurrentLumaBilinearSamples(FfxFloat32x2 fUv)
+{
+ const FfxFloat32x2 fUvJittered = fUv + Jitter() / RenderSize();
+ const FfxInt32x2 iBasePos = FfxInt32x2(floor(fUvJittered * RenderSize()));
+
+ SampleSet fSet;
+
+ for (FfxInt32 iSampleIndex = 0; iSampleIndex < SHADING_CHANGE_SET_SIZE; iSampleIndex++) {
+ const FfxInt32x2 iSamplePos = ClampLoad(iBasePos, iSampleOffsets[iSampleIndex], RenderSize());
+ fSet.fSamples[iSampleIndex] = LoadCurrentLuma(iSamplePos) * Exposure();
+ fSet.fSamples[iSampleIndex] = ffxPow(fSet.fSamples[iSampleIndex], fShadingChangeSamplePow);
+ fSet.fSamples[iSampleIndex] = ffxMax(fSet.fSamples[iSampleIndex], FSR3UPSCALER_EPSILON);
+ }
+
+ return fSet;
+}
+
+struct PreviousLumaBilinearSamplesData
+{
+ SampleSet fSet;
+ FfxBoolean bIsExistingSample;
+};
+
+PreviousLumaBilinearSamplesData GetPreviousLumaBilinearSamples(FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector)
+{
+ PreviousLumaBilinearSamplesData data;
+
+ const FfxFloat32x2 fUvJittered = fUv + PreviousFrameJitter() / PreviousFrameRenderSize();
+ const FfxFloat32x2 fReprojectedUv = fUvJittered + fMotionVector;
+
+ data.bIsExistingSample = IsUvInside(fReprojectedUv);
+
+ if (data.bIsExistingSample) {
+
+ const FfxInt32x2 iBasePos = FfxInt32x2(floor(fReprojectedUv * PreviousFrameRenderSize()));
+
+ for (FfxInt32 iSampleIndex = 0; iSampleIndex < SHADING_CHANGE_SET_SIZE; iSampleIndex++) {
+
+ const FfxInt32x2 iSamplePos = ClampLoad(iBasePos, iSampleOffsets[iSampleIndex], PreviousFrameRenderSize());
+ data.fSet.fSamples[iSampleIndex] = LoadPreviousLuma(iSamplePos) * DeltaPreExposure() * Exposure();
+ data.fSet.fSamples[iSampleIndex] = ffxPow(data.fSet.fSamples[iSampleIndex], fShadingChangeSamplePow);
+ data.fSet.fSamples[iSampleIndex] = ffxMax(data.fSet.fSamples[iSampleIndex], FSR3UPSCALER_EPSILON);
+ }
+ }
+
+ return data;
+}
+
+FfxFloat32 ComputeDiff(FfxInt32x2 iPxPos, FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector)
+{
+ FfxFloat32 fMinDiff = 0.0f;
+
+ const SampleSet fCurrentSamples = GetCurrentLumaBilinearSamples(fUv);
+ const PreviousLumaBilinearSamplesData previousData = GetPreviousLumaBilinearSamples(fUv, fMotionVector);
+
+ if (previousData.bIsExistingSample) {
+ fMinDiff = ComputeMinimumDifference(iPxPos, fCurrentSamples, previousData.fSet);
+ }
+
+ return fMinDiff;
+}
+
+FfxFloat32x4 SpdLoadSourceImage(FfxFloat32x2 iPxPos, FfxUInt32 slice)
+{
+ const FfxInt32x2 iPxSamplePos = ClampLoad(FfxInt32x2(iPxPos), FfxInt32x2(0, 0), FfxInt32x2(RenderSize()));
+ const FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(iPxSamplePos);
+ const FfxFloat32x2 fUv = (iPxSamplePos + 0.5f) / RenderSize();
+
+ const FfxFloat32 fScaledAndSignedLumaDiff = ComputeDiff(iPxSamplePos, fUv, fDilatedMotionVector);
+
+ FfxFloat32x4 fOutput = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f);
+ fOutput[DIFFERENCE] = fScaledAndSignedLumaDiff;
+ fOutput[SIGN_SUM] = (fScaledAndSignedLumaDiff != 0.0f) ? sign(fScaledAndSignedLumaDiff) : 0.0f;
+ fOutput[MIP0_INDICATOR] = 1.0f;
+
+ return fOutput;
+}
+
+FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice)
+{
+ return FfxFloat32x4(RWLoadPyramid(tex, 5), 0, 0);
+}
+
+FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3)
+{
+ return (v0 + v1 + v2 + v3) * 0.25f;
+}
+
+void SpdStore(FfxInt32x2 pix, FfxFloat32x4 outValue, FfxUInt32 index, FfxUInt32 slice)
+{
+ if (index >= iShadingChangeMipStart)
+ {
+ StorePyramid(pix, outValue.xy, index);
+ }
+}
+
+FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y)
+{
+ return FfxFloat32x4(
+ spdIntermediateR[x][y],
+ spdIntermediateG[x][y],
+ spdIntermediateB[x][y],
+ spdIntermediateA[x][y]);
+}
+void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value)
+{
+ spdIntermediateR[x][y] = value.x;
+ spdIntermediateG[x][y] = value.y;
+ spdIntermediateB[x][y] = value.z;
+ spdIntermediateA[x][y] = value.w;
+}
+
+#endif
+
+// define fetch and store functions Packed
+#if FFX_HALF
+
+FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16];
+FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16];
+
+FfxFloat16x4 SpdLoadSourceImageH(FfxFloat32x2 tex, FfxUInt32 slice)
+{
+ return FfxFloat16x4(0, 0, 0, 0);
+}
+
+FfxFloat16x4 SpdLoadH(FfxInt32x2 p, FfxUInt32 slice)
+{
+ return FfxFloat16x4(0, 0, 0, 0);
+}
+
+void SpdStoreH(FfxInt32x2 p, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice)
+{
+}
+
+FfxFloat16x4 SpdLoadIntermediateH(FfxUInt32 x, FfxUInt32 y)
+{
+ return FfxFloat16x4(
+ spdIntermediateRG[x][y].x,
+ spdIntermediateRG[x][y].y,
+ spdIntermediateBA[x][y].x,
+ spdIntermediateBA[x][y].y);
+}
+
+void SpdStoreIntermediateH(FfxUInt32 x, FfxUInt32 y, FfxFloat16x4 value)
+{
+ spdIntermediateRG[x][y] = value.xy;
+ spdIntermediateBA[x][y] = value.zw;
+}
+
+FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3)
+{
+ return (v0 + v1 + v2 + v3) * FfxFloat16(0.25);
+}
+#endif
+
+#include "spd/ffx_spd.h"
+
+void ComputeShadingChangePyramid(FfxUInt32x3 WorkGroupId, FfxUInt32 LocalThreadIndex)
+{
+#if FFX_HALF
+ SpdDownsampleH(
+ FfxUInt32x2(WorkGroupId.xy),
+ FfxUInt32(LocalThreadIndex),
+ FfxUInt32(MipCount()),
+ FfxUInt32(NumWorkGroups()),
+ FfxUInt32(WorkGroupId.z),
+ FfxUInt32x2(WorkGroupOffset()));
+#else
+ SpdDownsample(
+ FfxUInt32x2(WorkGroupId.xy),
+ FfxUInt32(LocalThreadIndex),
+ FfxUInt32(MipCount()),
+ FfxUInt32(NumWorkGroups()),
+ FfxUInt32(WorkGroupId.z),
+ FfxUInt32x2(WorkGroupOffset()));
+#endif
+}
diff --git a/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_upsample.h b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_upsample.h
new file mode 100644
index 0000000000..801a0a907e
--- /dev/null
+++ b/externals/FidelityFX-SDK/include/FidelityFX/gpu/fsr3upscaler/ffx_fsr3upscaler_upsample.h
@@ -0,0 +1,644 @@
+// This file is part of the FidelityFX SDK.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+void Deringing(RectificationBox clippingBox, FFX_PARAMETER_INOUT FfxFloat32x3 fColor)
+{
+ fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax);
+}
+
+#ifndef FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE
+#define FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE 2 // Approximate
+#endif
+
+FfxFloat32 GetUpsampleLanczosWeight(FfxFloat32x2 fSrcSampleOffset, FfxFloat32 fKernelWeight)
+{
+ FfxFloat32x2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx;
+#if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE
+ FfxFloat32 fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased));
+#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT
+ FfxFloat32 fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased));
+#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE
+ FfxFloat32 fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased));
+#else
+#error "Invalid Lanczos type"
+#endif
+ return fSampleWeight;
+}
+
+#if FFX_HALF
+FFX_MIN16_F GetUpsampleLanczosWeight(FFX_MIN16_F2 fSrcSampleOffset, FFX_MIN16_F fKernelWeight)
+{
+ FFX_MIN16_F2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx;
+#if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE
+ FFX_MIN16_F fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased));
+#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT
+ FFX_MIN16_F fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased));
+#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE
+ FFX_MIN16_F fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased));
+
+ // To Test: Save reciproqual sqrt compute
+ // FfxFloat32 fSampleWeight = Lanczos2Sq_UseLUT(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased));
+#else
+#error "Invalid Lanczos type"
+#endif
+ return fSampleWeight;
+}
+#endif
+
+FfxFloat32 ComputeMaxKernelWeight(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) {
+
+ const FfxFloat32 fKernelSizeBias = 1.0f + (1.0f / FfxFloat32x2(DownscaleFactor()) - 1.0f).x;
+
+ return ffxMin(FfxFloat32(1.99f), fKernelSizeBias);
+}
+
+FfxFloat32x3 LoadPreparedColor(FfxInt32x2 iSamplePos)
+{
+ const FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iSamplePos)) * Exposure();
+ const FfxFloat32x3 fPreparedYCoCg = RGBToYCoCg(fRgb);
+
+ return fPreparedYCoCg;
+}
+
+#if FFX_HALF && (FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2) && defined(__XBOX_SCARLETT) && defined(__XBATG_EXTRA_16_BIT_OPTIMISATION) && (__XBATG_EXTRA_16_BIT_OPTIMISATION == 1)
+#define FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS 1
+#else
+#define FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS 0
+#endif
+
+#if FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS
+
+void LoadPreparedColorPairedRgb(FFX_PARAMETER_OUT FFX_MIN16_F2 r,
+ FFX_PARAMETER_OUT FFX_MIN16_F2 g,
+ FFX_PARAMETER_OUT FFX_MIN16_F2 b,
+ FfxInt32x2 iSamplePos0,
+ FfxInt32x2 iSamplePos1)
+{
+ const FFX_MIN16_F3 sample0 = FFX_MIN16_F3(LoadInputColor(iSamplePos0));
+ const FFX_MIN16_F3 sample1 = FFX_MIN16_F3(LoadInputColor(iSamplePos1));
+
+ r = ffxMax(FFX_MIN16_F2(0, 0), FFX_MIN16_F2(sample0.r, sample1.r));
+ g = ffxMax(FFX_MIN16_F2(0, 0), FFX_MIN16_F2(sample0.g, sample1.g));
+ b = ffxMax(FFX_MIN16_F2(0, 0), FFX_MIN16_F2(sample0.b, sample1.b));
+
+ r = FFX_MIN16_F2(r * Exposure());
+ g = FFX_MIN16_F2(g * Exposure());
+ b = FFX_MIN16_F2(b * Exposure());
+}
+
+void TonemapPaired(FFX_PARAMETER_INOUT FFX_MIN16_F2 r, FFX_PARAMETER_INOUT FFX_MIN16_F2 g, FFX_PARAMETER_INOUT FFX_MIN16_F2 b)
+{
+ FFX_MIN16_F2 denomF16 = ffxMax(ffxMax(ffxMax(0.0, r), g), b) + FFX_MIN16_F2(1.0, 1.0);
+
+ // NOTE: expect 2 x v_cvt_f32_f16
+ FfxFloat32x2 denomF32 = FfxFloat32x2(denomF16);
+ // NOTE: expect 2 x v_rcp_f32
+ FfxFloat32x2 normF32 = FfxFloat32x2(1.0, 1.0) / denomF32;
+ // NOTE: expect 2 x v_cvt_f16_f32
+ FFX_MIN16_F2 normF16 = FFX_MIN16_F2(normF32);
+
+ r *= normF16;
+ g *= normF16;
+ b *= normF16;
+}
+
+void RGBToYCoCgPaired(FFX_PARAMETER_INOUT FFX_MIN16_F2 r, FFX_PARAMETER_INOUT FFX_MIN16_F2 g, FFX_PARAMETER_INOUT FFX_MIN16_F2 b)
+{
+ /**
+ * NOTE: given the following conversion
+ *
+ * fYCoCg = FfxFloat32x3(
+ * 0.25f * fRgb.r + 0.5f * fRgb.g + 0.25f * fRgb.b,
+ * 0.5f * fRgb.r - 0.5f * fRgb.b,
+ * -0.25f * fRgb.r + 0.5f * fRgb.g - 0.25f * fRgb.b);
+ *
+ * it's possible to notice that we can compute:
+ * RplusBdiv4 = 0.25 * (R + B)
+ *
+ * so everything else is computed in 3 instructions
+ * Y = G * 0.5 + RplusBdiv4
+ * Co = 2 * RplusBdiv4 - G
+ * Cg = G * 0.5 - RplusBdiv4
+ */
+
+ // NOTE: expect v_pk_add_f32 + v_pk_mul_f32
+ FFX_MIN16_F2 RplusBdiv4 = (r + b) * 0.25;
+ FFX_MIN16_F2 G = g;
+ FFX_MIN16_F2 B = b;
+
+ // NOTE: expect 3x v_pk_fma_f32
+ r = G * 0.5 + RplusBdiv4;
+ g = RplusBdiv4 * 2.0 - B;
+ b = G * 0.5 - RplusBdiv4;
+}
+
+FFX_MIN16_F2 Compute3x3SamplesMinMaxPaired(FFX_PARAMETER_IN FFX_MIN16_F2 sampleCenter,
+ FFX_PARAMETER_IN FFX_MIN16_F2 sample0,
+ FFX_PARAMETER_IN FFX_MIN16_F2 sample1,
+ FFX_PARAMETER_IN FFX_MIN16_F2 sample2,
+ FFX_PARAMETER_IN FFX_MIN16_F2 sample3)
+{
+ FFX_MIN16_F2 twoMinValues = ffxMin(ffxMin(sample0, sample1), ffxMin(sample2, sample3));
+ FFX_MIN16_F2 twoMaxValues = ffxMax(ffxMax(sample0, sample1), ffxMax(sample2, sample3));
+
+ return FFX_MIN16_F2(
+ ffxMin3Half(twoMinValues.x, twoMinValues.y, sampleCenter.x),
+ ffxMax3Half(twoMaxValues.x, twoMaxValues.y, sampleCenter.x)
+ );
+}
+
+
+FFX_MIN16_F2 Bool2ToFloat16x2(bool x, bool y)
+{
+ uint lo = x ? 0x00003c00 : 0x00000000;
+ uint hi = y ? 0x3c000000 : 0x00000000;
+ return FFX_MIN16_F2(__XB_AsHalf(lo).x, __XB_AsHalf(hi).y);
+}
+
+struct PairedRectificationBoxAndAccumulatedColorAndWeight
+{
+ FFX_MIN16_F2 boxCenterR;
+ FFX_MIN16_F2 boxCenterG;
+ FFX_MIN16_F2 boxCenterB;
+
+ FFX_MIN16_F2 boxVecR;
+ FFX_MIN16_F2 boxVecG;
+ FFX_MIN16_F2 boxVecB;
+
+ FFX_MIN16_F2 fBoxCenterWeight;
+
+ FFX_MIN16_F2 fColorR;
+ FFX_MIN16_F2 fColorG;
+ FFX_MIN16_F2 fColorB;
+ FFX_MIN16_F2 fWeight;
+
+ FFX_MIN16_F fKernelBiasSq;
+ FFX_MIN16_F fRectificationCurveBias;
+
+ void setKernelBiasAndRectificationCurveBias(FfxFloat32 kernelBias, FfxFloat32 rectificationCurveBias)
+ {
+ fKernelBiasSq = FFX_MIN16_F(kernelBias * kernelBias);
+ fRectificationCurveBias = FFX_MIN16_F(rectificationCurveBias);
+ }
+
+ void initUpscaledColor(FFX_MIN16_F fSrcSampleOffsetSq, FFX_MIN16_F fOnScreenWeight, FFX_MIN16_F2 sampleR, FFX_MIN16_F2 sampleG, FFX_MIN16_F2 sampleB)
+ {
+ #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE
+ const FFX_MIN16_F2 LanczosUpsampleWeight = FFX_MIN16_F2(
+ PairedLanczos2ApproxSq(fSrcSampleOffsetSq * fKernelBiasSq).x,
+ 0.0
+ );
+ #else
+ #error "Only LANCZOS_TYPE_APPROXIMATE is supported in paired version so far"
+ #endif
+ const FFX_MIN16_F2 fSampleWeight = fOnScreenWeight * LanczosUpsampleWeight;
+
+ fColorR = sampleR * fSampleWeight;
+ fColorG = sampleG * fSampleWeight;
+ fColorB = sampleB * fSampleWeight;
+ fWeight = fSampleWeight;
+ }
+
+ void initBox(FFX_MIN16_F fSrcSampleOffsetSq, FFX_MIN16_F fOnScreenWeight, FFX_MIN16_F2 sampleR, FFX_MIN16_F2 sampleG, FFX_MIN16_F2 sampleB)
+ {
+ const FFX_MIN16_F2 fBoxSampleWeight = FFX_MIN16_F2(
+ exp(fRectificationCurveBias * fSrcSampleOffsetSq) * fOnScreenWeight,
+ 0.0
+ );
+
+ FFX_MIN16_F2 weightedSampleR = sampleR * fBoxSampleWeight;
+ FFX_MIN16_F2 weightedSampleG = sampleG * fBoxSampleWeight;
+ FFX_MIN16_F2 weightedSampleB = sampleB * fBoxSampleWeight;
+
+ boxCenterR = weightedSampleR;
+ boxCenterG = weightedSampleG;
+ boxCenterB = weightedSampleB;
+
+ boxVecR = sampleR * weightedSampleR;
+ boxVecG = sampleG * weightedSampleG;
+ boxVecB = sampleB * weightedSampleB;
+
+ fBoxCenterWeight = fBoxSampleWeight;
+ }
+
+ void addUpscaledColorSample(FFX_MIN16_F2 fSrcSampleOffsetSq, FFX_MIN16_F2 fOnScreenWeight, FFX_MIN16_F2 sampleR, FFX_MIN16_F2 sampleG, FFX_MIN16_F2 sampleB)
+ {
+ #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE
+ const FFX_MIN16_F2 LanczosUpsampleWeight = PairedLanczos2ApproxSq(fSrcSampleOffsetSq * fKernelBiasSq);
+ #else
+ #error "Only LANCZOS_TYPE_APPROXIMATE is supported in paired version so far"
+ #endif
+ const FFX_MIN16_F2 fSampleWeight = fOnScreenWeight * LanczosUpsampleWeight;
+
+ fColorR += sampleR * fSampleWeight;
+ fColorG += sampleG * fSampleWeight;
+ fColorB += sampleB * fSampleWeight;
+ fWeight += fSampleWeight;
+ }
+
+ void addBoxSample(FFX_MIN16_F2 fSrcSampleOffsetSq, FFX_MIN16_F2 fOnScreenWeight, FFX_MIN16_F2 sampleR, FFX_MIN16_F2 sampleG, FFX_MIN16_F2 sampleB)
+ {
+ // NOTE: ideally expect here 2x v_fma_mix + 2x v_exp_f32 + 2x v_fma_mix
+ const FFX_MIN16_F2 fBoxSampleWeight = exp(fRectificationCurveBias * fSrcSampleOffsetSq) * fOnScreenWeight;
+
+ FFX_MIN16_F2 weightedSampleR = sampleR * fBoxSampleWeight;
+ FFX_MIN16_F2 weightedSampleG = sampleG * fBoxSampleWeight;
+ FFX_MIN16_F2 weightedSampleB = sampleB * fBoxSampleWeight;
+
+ boxCenterR += weightedSampleR;
+ boxCenterG += weightedSampleG;
+ boxCenterB += weightedSampleB;
+
+ boxVecR += sampleR * weightedSampleR;
+ boxVecG += sampleG * weightedSampleG;
+ boxVecB += sampleB * weightedSampleB;
+
+ fBoxCenterWeight += fBoxSampleWeight;
+ }
+
+ void finalizeUpscaledColor(FFX_PARAMETER_OUT FfxFloat32x4 upscaledColorAndWeight)
+ {
+ upscaledColorAndWeight.r = fColorR.x + fColorR.y;
+ upscaledColorAndWeight.g = fColorG.x + fColorG.y;
+ upscaledColorAndWeight.b = fColorB.x + fColorB.y;
+
+ upscaledColorAndWeight.a = fWeight.x + fWeight.y;
+ }
+
+ void finalizeBox(FFX_PARAMETER_OUT FfxFloat32x2 boxCenterAndVecR,
+ FFX_PARAMETER_OUT FfxFloat32x2 boxCenterAndVecG,
+ FFX_PARAMETER_OUT FfxFloat32x2 boxCenterAndVecB,
+ FFX_PARAMETER_OUT FfxFloat32 boxCenterWeight)
+ {
+ boxCenterAndVecR = FfxFloat32x2(boxCenterR.x + boxCenterR.y, boxVecR.x + boxVecR.y);
+ boxCenterAndVecG = FfxFloat32x2(boxCenterG.x + boxCenterG.y, boxVecG.x + boxVecG.y);
+ boxCenterAndVecB = FfxFloat32x2(boxCenterB.x + boxCenterB.y, boxVecB.x + boxVecB.y);
+
+ boxCenterWeight = fBoxCenterWeight.x + fBoxCenterWeight.y;
+ }
+};
+#endif // #if FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS
+
+void ComputeUpsampledColorAndWeight(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data)
+{
+ // We compute a sliced lanczos filter with 2 lobes (other slices are accumulated temporaly)
+ const FfxFloat32x2 fDstOutputPos = FfxFloat32x2(params.iPxHrPos) + FFX_BROADCAST_FLOAT32X2(0.5f);
+ const FfxFloat32x2 fSrcOutputPos = fDstOutputPos * DownscaleFactor();
+ const FfxInt32x2 iSrcInputPos = FfxInt32x2(floor(fSrcOutputPos));
+ const FfxFloat32x2 fSrcUnjitteredPos = (FfxFloat32x2(iSrcInputPos) + FfxFloat32x2(0.5f, 0.5f)) - Jitter(); // This is the un-jittered position of the sample at offset 0,0
+ const FfxFloat32x2 fBaseSampleOffset = FfxFloat32x2(fSrcUnjitteredPos - fSrcOutputPos);
+
+ FfxInt32x2 offsetTL;
+ offsetTL.x = (fSrcUnjitteredPos.x > fSrcOutputPos.x) ? FfxInt32(-2) : FfxInt32(-1);
+ offsetTL.y = (fSrcUnjitteredPos.y > fSrcOutputPos.y) ? FfxInt32(-2) : FfxInt32(-1);
+
+ //Load samples
+ // If fSrcUnjitteredPos.y > fSrcOutputPos.y, indicates offsetTL.y = -2, sample offset Y will be [-2, 1], clipbox will be rows [1, 3].
+ // Flip row# for sampling offset in this case, so first 0~2 rows in the sampled array can always be used for computing the clipbox.
+ // This reduces branch or cmove on sampled colors, but moving this overhead to sample position / weight calculation time which apply to less values.
+ const FfxBoolean bFlipRow = fSrcUnjitteredPos.y > fSrcOutputPos.y;
+ const FfxBoolean bFlipCol = fSrcUnjitteredPos.x > fSrcOutputPos.x;
+ const FfxFloat32x2 fOffsetTL = FfxFloat32x2(offsetTL);
+
+ const FfxBoolean bIsInitialSample = (params.fAccumulation == 0.0f);
+
+#if FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS
+ // Unroll the loop to load samples on Scarlett to help the shader compiler
+ const FFX_MIN16_F2 fSampleOffsetX02 = __XB_AsHalf(bFlipCol ? __XB_AsUInt(FFX_MIN16_F2( 1, -1)) : __XB_AsUInt(FFX_MIN16_F2(-1, 1)));
+ const FFX_MIN16_F2 fSampleOffsetY02 = __XB_AsHalf(bFlipRow ? __XB_AsUInt(FFX_MIN16_F2( 1, -1)) : __XB_AsUInt(FFX_MIN16_F2(-1, 1)));
+
+ typedef FfxInt32 FfxTexCoordI;
+ typedef FfxInt32x2 FfxTexCoordI2;
+
+ const FfxTexCoordI2 iSrcSamplePosX01 = FfxTexCoordI2(iSrcInputPos.xx) + (bFlipCol ? FfxTexCoordI2( 1, 0) : FfxTexCoordI2(-1, 0));
+ const FfxTexCoordI2 iSrcSamplePosX23 = FfxTexCoordI2(iSrcInputPos.xx) + (bFlipCol ? FfxTexCoordI2(-1, -2) : FfxTexCoordI2( 1, 2));
+
+ const FfxTexCoordI2 iSrcSamplePosY01 = FfxTexCoordI2(iSrcInputPos.yy) + (bFlipRow ? FfxTexCoordI2( 1, 0) : FfxTexCoordI2(-1, 0));
+ const FfxTexCoordI2 iSrcSamplePosY23 = FfxTexCoordI2(iSrcInputPos.yy) + (bFlipRow ? FfxTexCoordI2(-1, -2) : FfxTexCoordI2( 1, 2));
+
+ const FfxTexCoordI2 renderSizeLastTexelCoord = FfxTexCoordI2(RenderSize()) - FfxTexCoordI2(1, 1);
+
+ const FfxTexCoordI2 iSrcSamplePosX01Clamped = FfxTexCoordI2(
+ __XB_Med3_I32(iSrcSamplePosX01.x, 0, renderSizeLastTexelCoord.x),
+ __XB_Med3_I32(iSrcSamplePosX01.y, 0, renderSizeLastTexelCoord.x)
+ );
+
+ const FfxTexCoordI2 iSrcSamplePosX23Clamped = FfxTexCoordI2(
+ __XB_Med3_I32(iSrcSamplePosX23.x, 0, renderSizeLastTexelCoord.x),
+ __XB_Med3_I32(iSrcSamplePosX23.y, 0, renderSizeLastTexelCoord.x)
+ );
+
+ const FfxTexCoordI2 iSrcSamplePosY01Clamped = FfxTexCoordI2(
+ __XB_Med3_I32(iSrcSamplePosY01.x, 0, renderSizeLastTexelCoord.y),
+ __XB_Med3_I32(iSrcSamplePosY01.y, 0, renderSizeLastTexelCoord.y)
+ );
+
+ const FfxTexCoordI2 iSrcSamplePosY23Clamped = FfxTexCoordI2(
+ __XB_Med3_I32(iSrcSamplePosY23.x, 0, renderSizeLastTexelCoord.y),
+ __XB_Med3_I32(iSrcSamplePosY23.y, 0, renderSizeLastTexelCoord.y)
+ );
+
+ FFX_MIN16_F2 TopCornerR, BotCornerR, HorzR, VertR, CenterR;
+ FFX_MIN16_F2 TopCornerG, BotCornerG, HorzG, VertG, CenterG;
+ FFX_MIN16_F2 TopCornerB, BotCornerB, HorzB, VertB, CenterB;
+
+ LoadPreparedColorPairedRgb(TopCornerR, TopCornerG, TopCornerB,
+ FfxTexCoordI2(iSrcSamplePosX01Clamped.x, iSrcSamplePosY01Clamped.x),
+ FfxTexCoordI2(iSrcSamplePosX23Clamped.x, iSrcSamplePosY01Clamped.x)
+ );
+
+ LoadPreparedColorPairedRgb(BotCornerR, BotCornerG, BotCornerB,
+ FfxTexCoordI2(iSrcSamplePosX01Clamped.x, iSrcSamplePosY23Clamped.x),
+ FfxTexCoordI2(iSrcSamplePosX23Clamped.x, iSrcSamplePosY23Clamped.x)
+ );
+
+ LoadPreparedColorPairedRgb(HorzR, HorzG, HorzB,
+ FfxTexCoordI2(iSrcSamplePosX01Clamped.x, iSrcSamplePosY01Clamped.y),
+ FfxTexCoordI2(iSrcSamplePosX23Clamped.x, iSrcSamplePosY01Clamped.y)
+ );
+
+ LoadPreparedColorPairedRgb(VertR, VertG, VertB,
+ FfxTexCoordI2(iSrcSamplePosX01Clamped.y, iSrcSamplePosY01Clamped.x),
+ FfxTexCoordI2(iSrcSamplePosX01Clamped.y, iSrcSamplePosY23Clamped.x)
+ );
+
+ // NOTE: duplicated data
+ LoadPreparedColorPairedRgb(CenterR, CenterG, CenterB,
+ FfxTexCoordI2(iSrcSamplePosX01Clamped.y, iSrcSamplePosY01Clamped.y),
+ FfxTexCoordI2(iSrcSamplePosX01Clamped.y, iSrcSamplePosY01Clamped.y)
+ );
+
+ #if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT
+ if (bIsInitialSample)
+ {
+ TonemapPaired(TopCornerR, TopCornerG, TopCornerB);
+ TonemapPaired(BotCornerR, BotCornerG, BotCornerB);
+ TonemapPaired(HorzR, HorzG, HorzB);
+ TonemapPaired(VertR, VertG, VertB);
+ TonemapPaired(CenterR, CenterG, CenterB);
+ }
+ #endif
+
+ RGBToYCoCgPaired(TopCornerR, TopCornerG, TopCornerB);
+ RGBToYCoCgPaired(BotCornerR, BotCornerG, BotCornerB);
+ RGBToYCoCgPaired(HorzR, HorzG, HorzB);
+ RGBToYCoCgPaired(VertR, VertG, VertB);
+ RGBToYCoCgPaired(CenterR, CenterG, CenterB);
+
+#else
+ FfxFloat32x3 fSamples[9];
+ FfxInt32 iSampleIndex = 0;
+
+ FFX_UNROLL
+ for (FfxInt32 row = 0; row < 3; row++) {
+ FFX_UNROLL
+ for (FfxInt32 col = 0; col < 3; col++) {
+ const FfxInt32x2 iSampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row);
+ const FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + offsetTL + iSampleColRow;
+ const FfxInt32x2 iSampleCoord = ClampLoad(iSrcSamplePos, FfxInt32x2(0, 0), FfxInt32x2(RenderSize()));
+
+ fSamples[iSampleIndex] = LoadPreparedColor(iSampleCoord);
+
+ ++iSampleIndex;
+ }
+ }
+
+#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT
+ if (bIsInitialSample)
+ {
+ for (iSampleIndex = 0; iSampleIndex < 9; ++iSampleIndex)
+ {
+ //YCoCg -> RGB -> Tonemap -> YCoCg (Use RGB tonemapper to avoid color desaturation)
+ fSamples[iSampleIndex] = RGBToYCoCg(Tonemap(YCoCgToRGB(fSamples[iSampleIndex])));
+ }
+ }
+#endif
+
+#endif // #if FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS
+
+ // Identify how much of each upsampled color to be used for this frame
+ const FfxFloat32 fKernelBiasMax = ComputeMaxKernelWeight(params, data);
+ const FfxFloat32 fKernelBiasMin = ffxMax(1.0f, ((1.0f + fKernelBiasMax) * 0.3f));
+
+ const FfxFloat32 fKernelBiasWeight =
+ ffxMin(1.0f - params.fDisocclusion * 0.5f,
+ ffxMin(1.0f - params.fShadingChange,
+ ffxSaturate(data.fHistoryWeight * 5.0f)
+ ));
+
+ const FfxFloat32 fKernelBias = ffxLerp(fKernelBiasMin, fKernelBiasMax, fKernelBiasWeight);
+
+#if FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS
+ // Unroll the loop to load samples on Scarlett to help the shader compiler
+ const bool coordX0OnScreen = iSrcSamplePosX01.x == iSrcSamplePosX01Clamped.x;
+ const bool coordX1OnScreen = iSrcSamplePosX01.y == iSrcSamplePosX01Clamped.y;
+ const bool coordX2OnScreen = iSrcSamplePosX23.x == iSrcSamplePosX23Clamped.x;
+
+ const bool coordY0OnScreen = iSrcSamplePosY01.x == iSrcSamplePosY01Clamped.x;
+ const bool coordY1OnScreen = iSrcSamplePosY01.y == iSrcSamplePosY01Clamped.y;
+ const bool coordY2OnScreen = iSrcSamplePosY23.x == iSrcSamplePosY23Clamped.x;
+
+ const FFX_MIN16_F2 fBaseSampleOffsetHalf = FFX_MIN16_F2(fBaseSampleOffset);
+
+ const FFX_MIN16_F2 fSrcSampleOffsetX_02 = fBaseSampleOffsetHalf.xx + fSampleOffsetX02;
+ const FFX_MIN16_F2 fSrcSampleOffsetY_02 = fBaseSampleOffsetHalf.yy + fSampleOffsetY02;
+
+ const FFX_MIN16_F2 fSrcSampleOffsetXSq_02 = fSrcSampleOffsetX_02 * fSrcSampleOffsetX_02;
+ const FFX_MIN16_F2 fSrcSampleOffsetYSq_02 = fSrcSampleOffsetY_02 * fSrcSampleOffsetY_02;
+ const FFX_MIN16_F2 fSrcSampleOffsetXYSq_11 = fBaseSampleOffsetHalf * fBaseSampleOffsetHalf;
+
+ const FfxFloat32 fRectificationCurveBias = -2.3f;
+ PairedRectificationBoxAndAccumulatedColorAndWeight pairedBox;
+ pairedBox.setKernelBiasAndRectificationCurveBias(fKernelBias, fRectificationCurveBias);
+
+ // init by o o o
+ // o x o
+ // o o o
+ pairedBox.initBox(
+ fSrcSampleOffsetXYSq_11.x + fSrcSampleOffsetXYSq_11.y,
+ Bool2ToFloat16x2(coordX1OnScreen && coordY1OnScreen, false).x,
+ CenterR, CenterG, CenterB
+ );
+
+ // add remaining two samples from 1st row x o x
+ // o * o
+ // o o o
+ pairedBox.addBoxSample(
+ fSrcSampleOffsetXSq_02 + fSrcSampleOffsetYSq_02.xx,
+ Bool2ToFloat16x2(coordX0OnScreen && coordY0OnScreen, coordX2OnScreen && coordY0OnScreen),
+ TopCornerR, TopCornerG, TopCornerB
+ );
+
+ // add two samples from 2nd row * o *
+ // o * o
+ // x o x
+ pairedBox.addBoxSample(
+ fSrcSampleOffsetXSq_02 + fSrcSampleOffsetYSq_02.yy,
+ Bool2ToFloat16x2(coordX0OnScreen && coordY2OnScreen, coordX2OnScreen && coordY2OnScreen),
+ BotCornerR, BotCornerG, BotCornerB
+ );
+
+ // add two samples from 3rd row * o *
+ // x * x
+ // * o *
+ pairedBox.addBoxSample(
+ fSrcSampleOffsetXSq_02 + fSrcSampleOffsetXYSq_11.yy,
+ Bool2ToFloat16x2(coordX0OnScreen && coordY1OnScreen, coordX2OnScreen && coordY1OnScreen),
+ HorzR, HorzG, HorzB
+ );
+
+ // add remaining samples * x *
+ // * * *
+ // * x *
+ pairedBox.addBoxSample(
+ fSrcSampleOffsetXYSq_11.xx + fSrcSampleOffsetYSq_02,
+ Bool2ToFloat16x2(coordX1OnScreen && coordY0OnScreen, coordX1OnScreen && coordY2OnScreen),
+ VertR, VertG, VertB
+ );
+
+ FfxFloat32x2 boxCenterAndVecR, boxCenterAndVecG, boxCenterAndVecB;
+ FfxFloat32 boxCenterWeight;
+ pairedBox.finalizeBox(boxCenterAndVecR, boxCenterAndVecG, boxCenterAndVecB, boxCenterWeight);
+
+ if (!bIsInitialSample)
+ {
+ pairedBox.initUpscaledColor(
+ fSrcSampleOffsetXYSq_11.x + fSrcSampleOffsetXYSq_11.y,
+ Bool2ToFloat16x2(coordX1OnScreen && coordY1OnScreen, false).x,
+ CenterR, CenterG, CenterB
+ );
+
+ // add remaining two samples from 1st row x o x
+ // o * o
+ // o o o
+ pairedBox.addUpscaledColorSample(
+ fSrcSampleOffsetXSq_02 + fSrcSampleOffsetYSq_02.xx,
+ Bool2ToFloat16x2(coordX0OnScreen && coordY0OnScreen, coordX2OnScreen && coordY0OnScreen),
+ TopCornerR, TopCornerG, TopCornerB
+ );
+
+ // add two samples from 2nd row * o *
+ // o * o
+ // x o x
+ pairedBox.addUpscaledColorSample(
+ fSrcSampleOffsetXSq_02 + fSrcSampleOffsetYSq_02.yy,
+ Bool2ToFloat16x2(coordX0OnScreen && coordY2OnScreen, coordX2OnScreen && coordY2OnScreen),
+ BotCornerR, BotCornerG, BotCornerB
+ );
+
+ // add two samples from 3rd row * o *
+ // x * x
+ // * o *
+ pairedBox.addUpscaledColorSample(
+ fSrcSampleOffsetXSq_02 + fSrcSampleOffsetXYSq_11.yy,
+ Bool2ToFloat16x2(coordX0OnScreen && coordY1OnScreen, coordX2OnScreen && coordY1OnScreen),
+ HorzR, HorzG, HorzB
+ );
+
+ // add remaining samples * x *
+ // * * *
+ // * x *
+ pairedBox.addUpscaledColorSample(
+ fSrcSampleOffsetXYSq_11.xx + fSrcSampleOffsetYSq_02,
+ Bool2ToFloat16x2(coordX1OnScreen && coordY0OnScreen, coordX1OnScreen && coordY2OnScreen),
+ VertR, VertG, VertB
+ );
+
+ FfxFloat32x4 upscaledColorAndWeight = 0.0;
+ pairedBox.finalizeUpscaledColor(upscaledColorAndWeight);
+
+ data.fUpsampledColor = FfxFloat32x3(upscaledColorAndWeight.rgb);
+ data.fUpsampledWeight = FfxFloat32(upscaledColorAndWeight.w);
+ }
+
+ FFX_MIN16_F2 aabbMinMaxR = Compute3x3SamplesMinMaxPaired(CenterR, TopCornerR, BotCornerR, HorzR, VertR);
+ FFX_MIN16_F2 aabbMinMaxG = Compute3x3SamplesMinMaxPaired(CenterG, TopCornerG, BotCornerG, HorzG, VertG);
+ FFX_MIN16_F2 aabbMinMaxB = Compute3x3SamplesMinMaxPaired(CenterB, TopCornerB, BotCornerB, HorzB, VertB);
+
+ data.clippingBox.boxCenter = FfxFloat32x3(boxCenterAndVecR.x, boxCenterAndVecG.x, boxCenterAndVecB.x);
+ data.clippingBox.boxVec = FfxFloat32x3(boxCenterAndVecR.y, boxCenterAndVecG.y, boxCenterAndVecB.y);
+ data.clippingBox.aabbMin = FfxFloat32x3(aabbMinMaxR.x, aabbMinMaxG.x, aabbMinMaxB.x);
+ data.clippingBox.aabbMax = FfxFloat32x3(aabbMinMaxR.y, aabbMinMaxG.y, aabbMinMaxB.y);
+ data.clippingBox.fBoxCenterWeight = FfxFloat32(boxCenterWeight);
+#else
+
+ iSampleIndex = 0;
+
+ FFX_UNROLL
+ for (FfxInt32 row = 0; row < 3; row++)
+ {
+ FFX_UNROLL
+ for (FfxInt32 col = 0; col < 3; col++)
+ {
+ const FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row);
+ const FfxFloat32x2 fOffset = fOffsetTL + FfxFloat32x2(sampleColRow);
+ const FfxFloat32x2 fSrcSampleOffset = fBaseSampleOffset + fOffset;
+
+ const FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + FfxInt32x2(offsetTL) + sampleColRow;
+ const FfxFloat32 fOnScreenFactor = FfxFloat32(IsOnScreen(FfxInt32x2(iSrcSamplePos), FfxInt32x2(RenderSize())));
+
+ if (!bIsInitialSample)
+ {
+ const FfxFloat32 fSampleWeight = fOnScreenFactor * FfxFloat32(GetUpsampleLanczosWeight(fSrcSampleOffset, fKernelBias));
+
+ data.fUpsampledColor += fSamples[iSampleIndex] * fSampleWeight;
+ data.fUpsampledWeight += fSampleWeight;
+ }
+
+ // Update rectification box
+ {
+ const FfxFloat32 fRectificationCurveBias = -2.3f;
+ const FfxFloat32 fSrcSampleOffsetSq = dot(fSrcSampleOffset, fSrcSampleOffset);
+ const FfxFloat32 fBoxSampleWeight = exp(fRectificationCurveBias * fSrcSampleOffsetSq) * fOnScreenFactor;
+
+ const FfxBoolean bInitialSample = (row == 0) && (col == 0);
+ RectificationBoxAddSample(bInitialSample, data.clippingBox, fSamples[iSampleIndex], fBoxSampleWeight);
+ }
+ ++iSampleIndex;
+ }
+ }
+
+#endif // #if FFX_FSR3UPSCALER_USE_XBOX_PAIRED_16BIT_MATH_OPTIMIZATIONS
+
+ RectificationBoxComputeVarianceBoxData(data.clippingBox);
+
+ data.fUpsampledWeight *= FfxFloat32(data.fUpsampledWeight > FSR3UPSCALER_EPSILON);
+
+ if (data.fUpsampledWeight > FSR3UPSCALER_EPSILON) {
+ // Normalize for deringing (we need to compare colors)
+ data.fUpsampledColor = data.fUpsampledColor / data.fUpsampledWeight;
+ data.fUpsampledWeight *= fAverageLanczosWeightPerFrame;
+
+ Deringing(data.clippingBox, data.fUpsampledColor);
+ }
+
+ // Initial samples using tonemapped upsampling
+ if (bIsInitialSample) {
+#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT
+ data.fUpsampledColor = RGBToYCoCg(InverseTonemap(YCoCgToRGB(data.clippingBox.boxCenter)));
+#else
+ data.fUpsampledColor = data.clippingBox.boxCenter;
+#endif
+ data.fUpsampledWeight = 1.0f;
+ data.fHistoryWeight = 0.0f;
+ }
+}