You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

445 lines
16 KiB

// 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_FRAMEINTERPOLATION_COMMON_H)
#define FFX_FRAMEINTERPOLATION_COMMON_H
#define FFX_FRAMEINTERPOLATION_DISPATCH_DRAW_DEBUG_TEAR_LINES (1 << 0)
#define FFX_FRAMEINTERPOLATION_DISPATCH_DRAW_DEBUG_RESET_INDICATORS (1 << 1)
#define FFX_FRAMEINTERPOLATION_DISPATCH_DRAW_DEBUG_VIEW (1 << 2)
FFX_STATIC const FfxFloat32 FFX_FRAMEINTERPOLATION_EPSILON = 1e-03f;
FFX_STATIC const FfxFloat32 FFX_FRAMEINTERPOLATION_FLT_MAX = 3.402823466e+38f;
FFX_STATIC const FfxFloat32 FFX_FRAMEINTERPOLATION_FLT_MIN = 1.175494351e-38f;
FFX_STATIC const FfxFloat32 fReconstructedDepthBilinearWeightThreshold = FFX_FRAMEINTERPOLATION_EPSILON;
FfxFloat32 RGBToLuma(FfxFloat32x3 fLinearRgb)
{
return dot(fLinearRgb, FfxFloat32x3(0.2126f, 0.7152f, 0.0722f));
}
FfxFloat32 LinearRec2020ToLuminance(FfxFloat32x3 linearRec2020RGB)
{
FfxFloat32 fY = 0.2627 * linearRec2020RGB.x + 0.678 * linearRec2020RGB.y + 0.0593 * linearRec2020RGB.z;
return fY;
}
FfxFloat32x3 ffxscRGBToLinear(FfxFloat32x3 value, FfxFloat32 minLuminance, FfxFloat32 maxLuminance)
{
FfxFloat32x3 p = value - ffxBroadcast3(minLuminance / 80.0f);
return p / ffxBroadcast3((maxLuminance - minLuminance) / 80.0f);
}
FfxFloat32x3 RawRGBToLinear(FfxFloat32x3 fRawRgb)
{
FfxFloat32x3 fLinearRgb;
switch (BackBufferTransferFunction())
{
case 0:
fLinearRgb = ffxLinearFromSrgb(fRawRgb);
break;
case 1:
fLinearRgb = ffxLinearFromPQ(fRawRgb) * (10000.0f / MaxLuminance());
break;
case 2:
fLinearRgb = ffxscRGBToLinear(fRawRgb, MinLuminance(), MaxLuminance());
break;
}
return fLinearRgb;
}
FfxFloat32 RawRGBToLuminance(FfxFloat32x3 fRawRgb)
{
FfxFloat32 fLuminance = 0.0f;
switch (BackBufferTransferFunction())
{
case 0:
fLuminance = RGBToLuma(RawRGBToLinear(fRawRgb));
break;
case 1:
fLuminance = LinearRec2020ToLuminance(RawRGBToLinear(fRawRgb));
break;
case 2:
fLuminance = RGBToLuma(RawRGBToLinear(fRawRgb));
break;
}
return fLuminance;
}
FfxFloat32 RawRGBToPerceivedLuma(FfxFloat32x3 fRawRgb)
{
FfxFloat32 fLuminance = RawRGBToLuminance(fRawRgb);
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;
}
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;
}
#if defined(FFX_FRAMEINTERPOLATION_BIND_CB_FRAMEINTERPOLATION)
FfxFloat32 ConvertFromDeviceDepthToViewSpace(FfxFloat32 fDeviceDepth)
{
const FfxFloat32x4 deviceToViewDepth = DeviceToViewSpaceTransformFactors();
return deviceToViewDepth[1] / (fDeviceDepth - deviceToViewDepth[0]);
}
FfxFloat32x2 ComputeNdc(FfxFloat32x2 fPxPos, FfxInt32x2 iSize)
{
return fPxPos / FfxFloat32x2(iSize) * FfxFloat32x2(2.0f, -2.0f) + FfxFloat32x2(-1.0f, 1.0f);
}
FfxFloat32x3 GetViewSpacePosition(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth)
{
const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors();
const FfxFloat32 Z = ConvertFromDeviceDepthToViewSpace(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);
}
#endif
FfxBoolean IsOnScreen(FfxInt32x2 pos, FfxInt32x2 size)
{
return all(FFX_LESS_THAN(FfxUInt32x2(pos), FfxUInt32x2(size)));
}
FfxBoolean IsUvInside(FfxFloat32x2 fUv)
{
return (fUv.x > 0.0f && fUv.x < 1.0f) && (fUv.y > 0.0f && fUv.y < 1.0f);
}
FfxBoolean IsInRect(FfxInt32x2 pos, FfxInt32x2 iRectCorner, FfxInt32x2 iRectSize)
{
return (pos.x >= iRectCorner.x && pos.x < (iRectSize.x + iRectCorner.x) && pos.y >= iRectCorner.y && pos.y < (iRectSize.y + iRectCorner.y));
}
FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1)
{
const FfxFloat32 m = ffxMax(v0, v1);
return m != 0 ? ffxMin(v0, v1) / m : 0;
}
FfxFloat32 NormalizedDot3(const FfxFloat32x3 v0, const FfxFloat32x3 v1)
{
FfxFloat32 fMaxLength = ffxMax(length(v0), length(v1));
return fMaxLength > 0.0f ? dot(v0 / fMaxLength, v1 / fMaxLength) : 1.0f;
}
FfxFloat32 NormalizedDot2(const FfxFloat32x2 v0, const FfxFloat32x2 v1)
{
FfxFloat32 fMaxLength = ffxMax(length(v0), length(v1));
return fMaxLength > 0.0f ? dot(v0 / fMaxLength, v1 / fMaxLength) : 1.0f;
}
FfxFloat32 CalculateStaticContentFactor(FfxFloat32x3 fCurrentInterpolationSource, FfxFloat32x3 fPresentColor)
{
const FfxFloat32x3 fFactor = ffxSaturate(FfxFloat32x3(
ffxSaturate((1.0f - MinDividedByMax(fCurrentInterpolationSource.r, fPresentColor.r)) / 0.1f),
ffxSaturate((1.0f - MinDividedByMax(fCurrentInterpolationSource.g, fPresentColor.g)) / 0.1f),
ffxSaturate((1.0f - MinDividedByMax(fCurrentInterpolationSource.b, fPresentColor.b)) / 0.1f)
));
return max(fFactor.x, max(fFactor.y, fFactor.z));
}
//
// MOTION VECTOR FIELD
//
FFX_STATIC const FfxUInt32 MOTION_VECTOR_FIELD_ENTRY_BIT_COUNT = 32;
// Make sure all bit counts add up to MOTION_VECTOR_FIELD_ENTRY_BIT_COUNT
FFX_STATIC const FfxUInt32 MOTION_VECTOR_FIELD_VECTOR_COEFFICIENT_BIT_COUNT = 16;
FFX_STATIC const FfxUInt32 MOTION_VECTOR_FIELD_PRIORITY_LOW_BIT_COUNT = 5;
FFX_STATIC const FfxUInt32 MOTION_VECTOR_FIELD_PRIORITY_HIGH_BIT_COUNT = 10;
FFX_STATIC const FfxUInt32 MOTION_VECTOR_PRIMARY_VECTOR_INDICATION_BIT_COUNT = 1;
FFX_STATIC const FfxUInt32 MOTION_VECTOR_FIELD_PRIMARY_VECTOR_INDICATION_BIT = (1U << (MOTION_VECTOR_FIELD_ENTRY_BIT_COUNT - 1));
FFX_STATIC const FfxUInt32 PRIORITY_LOW_MAX = (1U << MOTION_VECTOR_FIELD_PRIORITY_LOW_BIT_COUNT) - 1;
FFX_STATIC const FfxUInt32 PRIORITY_HIGH_MAX = (1U << MOTION_VECTOR_FIELD_PRIORITY_HIGH_BIT_COUNT) - 1;
FFX_STATIC const FfxUInt32 PRIORITY_LOW_OFFSET = MOTION_VECTOR_FIELD_VECTOR_COEFFICIENT_BIT_COUNT;
FFX_STATIC const FfxUInt32 PRIORITY_HIGH_OFFSET = PRIORITY_LOW_OFFSET + MOTION_VECTOR_FIELD_PRIORITY_LOW_BIT_COUNT;
FFX_STATIC const FfxUInt32 PRIMARY_VECTOR_INDICATION_OFFSET = PRIORITY_HIGH_OFFSET + MOTION_VECTOR_FIELD_PRIORITY_HIGH_BIT_COUNT;
struct VectorFieldEntry
{
FfxFloat32x2 fMotionVector;
FfxFloat32 uHighPriorityFactor;
FfxFloat32 uLowPriorityFactor;
FfxBoolean bValid;
FfxBoolean bPrimary;
FfxBoolean bSecondary;
FfxBoolean bInPainted;
FfxFloat32 fVelocity;
FfxBoolean bNegOutside;
FfxBoolean bPosOutside;
};
VectorFieldEntry NewVectorFieldEntry()
{
VectorFieldEntry vfe;
vfe.fMotionVector = FfxFloat32x2(0.0, 0.0);
vfe.uHighPriorityFactor = 0.0;
vfe.uLowPriorityFactor = 0.0;
vfe.bValid = false;
vfe.bPrimary = false;
vfe.bSecondary = false;
vfe.bInPainted = false;
vfe.fVelocity = 0.0;
vfe.bNegOutside = false;
vfe.bPosOutside = false;
return vfe;
}
FfxBoolean PackedVectorFieldEntryIsPrimary(FfxUInt32 packedEntry)
{
return ((packedEntry & MOTION_VECTOR_FIELD_PRIMARY_VECTOR_INDICATION_BIT) != 0);
}
FfxUInt32x2 PackVectorFieldEntries(FfxBoolean bIsPrimary, FfxUInt32 uHighPriorityFactor, FfxUInt32 uLowPriorityFactor, FfxFloat32x2 fMotionVector)
{
const FfxUInt32 uPriority =
(FfxUInt32(bIsPrimary) * MOTION_VECTOR_FIELD_PRIMARY_VECTOR_INDICATION_BIT)
| ((uHighPriorityFactor & PRIORITY_HIGH_MAX) << PRIORITY_HIGH_OFFSET)
| ((uLowPriorityFactor & PRIORITY_LOW_MAX) << PRIORITY_LOW_OFFSET);
FfxUInt32 packedX = uPriority | ffxF32ToF16(fMotionVector.x);
FfxUInt32 packedY = uPriority | ffxF32ToF16(fMotionVector.y);
return FfxUInt32x2(packedX, packedY);
}
void UnpackVectorFieldEntries(FfxUInt32x2 packed, out VectorFieldEntry vfElement)
{
vfElement.uHighPriorityFactor = FfxFloat32((packed.x >> PRIORITY_HIGH_OFFSET) & PRIORITY_HIGH_MAX) / PRIORITY_HIGH_MAX;
vfElement.uLowPriorityFactor = FfxFloat32((packed.x >> PRIORITY_LOW_OFFSET) & PRIORITY_LOW_MAX) / PRIORITY_LOW_MAX;
vfElement.bPrimary = PackedVectorFieldEntryIsPrimary(packed.x);
vfElement.bValid = (vfElement.uHighPriorityFactor > 0.0f);
vfElement.bSecondary = vfElement.bValid && !vfElement.bPrimary;
// Reverse priority factor for secondary vectors
if (vfElement.bSecondary)
{
vfElement.uHighPriorityFactor = 1.0f - vfElement.uHighPriorityFactor;
}
vfElement.fMotionVector.x = ffxUnpackF32(packed.x).x;
vfElement.fMotionVector.y = ffxUnpackF32(packed.y).x;
vfElement.bInPainted = false;
}
//
// MOTION VECTOR FIELD
//
#if defined(FFX_FRAMEINTERPOLATION_BIND_SRV_INPAINTING_PYRAMID)
FfxFloat32x4 ComputeMvInpaintingLevel(FfxFloat32x2 fUv, const FfxInt32 iMipLevel, const FfxInt32x2 iTexSize)
{
BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fUv, iTexSize);
FfxFloat32 fSum = 0.0f;
FfxFloat32x4 fColor = FfxFloat32x4(0.0, 0.0, 0.0, 0.0);
fColor.z = 0;
const FfxFloat32 fMaxPriorityFactor = 1.0f;
for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++)
{
const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex];
const FfxInt32x2 iSamplePos = bilinearInfo.iBasePos + iOffset;
if (IsOnScreen(iSamplePos, iTexSize))
{
FfxFloat32x4 fSample = LoadInpaintingPyramid(iMipLevel, iSamplePos);
const FfxFloat32 fPriorityFactor = fSample.z;
const FfxFloat32 fValidMvFactor = FfxFloat32(fSample.z > 0);
const FfxFloat32 fSampleWeight = bilinearInfo.fWeights[iSampleIndex] * fValidMvFactor * fPriorityFactor;
fSum += fSampleWeight;
fColor += fSample * fSampleWeight;
}
}
fColor /= (fSum > 0.0f) ? fSum : 1.0f;
return fColor;
}
#if defined(FFX_FRAMEINTERPOLATION_BIND_CB_FRAMEINTERPOLATION) && \
defined(FFX_FRAMEINTERPOLATION_BIND_SRV_GAME_MOTION_VECTOR_FIELD_X) && \
defined(FFX_FRAMEINTERPOLATION_BIND_SRV_GAME_MOTION_VECTOR_FIELD_Y)
void LoadInpaintedGameFieldMv(FfxFloat32x2 fUv, out VectorFieldEntry vfElement)
{
FfxInt32x2 iPxSample = FfxInt32x2(fUv * RenderSize());
FfxUInt32x2 packedGameFieldMv = LoadGameFieldMv(iPxSample);
UnpackVectorFieldEntries(packedGameFieldMv, vfElement);
if (!vfElement.bValid)
{
//FfxFloat32x2 fUv = (FfxFloat32x2(iPxSample) + 0.5f) / RenderSize();
FfxInt32x2 iTexSize = RenderSize();
FfxFloat32x4 fInPaintedVector = FfxFloat32x4(0.0, 0.0, 0.0, 0.0);
for (FfxInt32 iMipLevel = 0; iMipLevel < 11 && (fInPaintedVector.w == 0.0f); iMipLevel++)
{
iTexSize /= 2;
fInPaintedVector = ComputeMvInpaintingLevel(fUv, iMipLevel, iTexSize);
}
vfElement.fMotionVector = fInPaintedVector.xy;
vfElement.uHighPriorityFactor = fInPaintedVector.z;
vfElement.uLowPriorityFactor = fInPaintedVector.w;
vfElement.bInPainted = true;
}
vfElement.bNegOutside = !IsUvInside(fUv - vfElement.fMotionVector);
vfElement.bPosOutside = !IsUvInside(fUv + vfElement.fMotionVector);
vfElement.fVelocity = length(vfElement.fMotionVector);
}
#endif
#endif
#if defined(FFX_FRAMEINTERPOLATION_BIND_SRV_OPTICAL_FLOW_MOTION_VECTOR_FIELD_X) && \
defined(FFX_FRAMEINTERPOLATION_BIND_SRV_OPTICAL_FLOW_MOTION_VECTOR_FIELD_Y) && \
defined(FFX_FRAMEINTERPOLATION_BIND_CB_FRAMEINTERPOLATION)
void SampleOpticalFlowMotionVectorField(FfxFloat32x2 fUv, out VectorFieldEntry vfElement)
{
const FfxFloat32 scaleFactor = 1.0f;
BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fUv, FfxInt32x2(GetOpticalFlowSize2() * scaleFactor));
vfElement = NewVectorFieldEntry();
FfxFloat32 fWeightSum = 0.0f;
for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++)
{
const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex];
const FfxInt32x2 iSamplePos = bilinearInfo.iBasePos + iOffset;
if (IsOnScreen(iSamplePos, FfxInt32x2(GetOpticalFlowSize2() * scaleFactor)))
{
const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex];
VectorFieldEntry fOfVectorSample = NewVectorFieldEntry();
FfxInt32x2 packedOpticalFlowMv = FfxInt32x2(LoadOpticalFlowFieldMv(iSamplePos));
UnpackVectorFieldEntries(packedOpticalFlowMv, fOfVectorSample);
vfElement.fMotionVector += fOfVectorSample.fMotionVector * fWeight;
vfElement.uHighPriorityFactor += fOfVectorSample.uHighPriorityFactor * fWeight;
vfElement.uLowPriorityFactor += fOfVectorSample.uLowPriorityFactor * fWeight;
fWeightSum += fWeight;
}
}
if (fWeightSum > 0.0f)
{
vfElement.fMotionVector /= fWeightSum;
vfElement.uHighPriorityFactor /= fWeightSum;
vfElement.uLowPriorityFactor /= fWeightSum;
}
vfElement.bNegOutside = !IsUvInside(fUv - vfElement.fMotionVector);
vfElement.bPosOutside = !IsUvInside(fUv + vfElement.fMotionVector);
vfElement.fVelocity = length(vfElement.fMotionVector);
}
#endif
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(FFX_TONEMAP_EPSILON, 1.f - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx;
}
FfxInt32x2 ComputeHrPosFromLrPos(FfxInt32x2 iPxLrPos)
{
FfxFloat32x2 fSrcJitteredPos = FfxFloat32x2(iPxLrPos) + 0.5f - Jitter();
FfxFloat32x2 fLrPosInHr = (fSrcJitteredPos / RenderSize()) * DisplaySize();
FfxInt32x2 iPxHrPos = FfxInt32x2(floor(fLrPosInHr));
return iPxHrPos;
}
#if FFX_HALF
FFX_MIN16_I2 ComputeHrPosFromLrPos(FFX_MIN16_I2 iPxLrPos)
{
FFX_MIN16_F2 fSrcJitteredPos = FFX_MIN16_F2(iPxLrPos) + FFX_MIN16_F(0.5f) - FFX_MIN16_F2(Jitter());
FFX_MIN16_F2 fLrPosInHr = (fSrcJitteredPos / FFX_MIN16_F2(RenderSize())) * FFX_MIN16_F2(DisplaySize());
FFX_MIN16_I2 iPxHrPos = FFX_MIN16_I2(floor(fLrPosInHr));
return iPxHrPos;
}
#endif
#endif //!defined(FFX_FRAMEINTERPOLATION_COMMON_H)