193 lines
7.8 KiB
C
Raw Normal View History

// 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_FRAMEINTERPOLATION_H
#define FFX_FRAMEINTERPOLATION_H
struct InterpolationSourceColor
{
FfxFloat32x3 fRaw;
FfxFloat32x3 fLinear;
FfxFloat32 fBilinearWeightSum;
};
InterpolationSourceColor NewInterpolationSourceColor()
{
InterpolationSourceColor c;
c.fRaw = FfxFloat32x3(0.0, 0.0, 0.0);
c.fLinear = FfxFloat32x3(0.0, 0.0, 0.0);
c.fBilinearWeightSum = 0.0;
return c;
}
InterpolationSourceColor SampleTextureBilinear(FfxBoolean isCurrent, FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector, FfxInt32x2 texSize)
{
InterpolationSourceColor result = NewInterpolationSourceColor();
FfxFloat32x2 fReprojectedUv = fUv + fMotionVector;
BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, texSize);
FfxFloat32x3 fColor = FfxFloat32x3(0.0, 0.0, 0.0);
FfxFloat32 fWeightSum = 0.0f;
for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) {
const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex];
const FfxInt32x2 iSamplePos = bilinearInfo.iBasePos + iOffset;
if (IsInRect(iSamplePos, InterpolationRectBase(), InterpolationRectSize()))
{
FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex];
if (isCurrent)
fColor += LoadCurrentBackbuffer(iSamplePos).rgb * fWeight;
else
fColor += LoadPreviousBackbuffer(iSamplePos).rgb * fWeight;
fWeightSum += fWeight;
}
}
//normalize colors
fColor = (fWeightSum != 0.0f) ? fColor / fWeightSum : FfxFloat32x3(0.0f, 0.0f, 0.0f);
result.fRaw = fColor;
result.fLinear = RawRGBToLinear(fColor);
result.fBilinearWeightSum = fWeightSum;
return result;
}
void updateInPaintingWeight(inout FfxFloat32 fInPaintingWeight, FfxFloat32 fFactor)
{
fInPaintingWeight = ffxSaturate(ffxMax(fInPaintingWeight, fFactor));
}
void computeInterpolatedColor(FfxUInt32x2 iPxPos, out FfxFloat32x3 fInterpolatedColor, inout FfxFloat32 fInPaintingWeight)
{
const FfxFloat32x2 fUvInInterpolationRect = (FfxFloat32x2(iPxPos - InterpolationRectBase()) + 0.5f) / InterpolationRectSize();
const FfxFloat32x2 fUvInScreenSpace = (FfxFloat32x2(iPxPos) + 0.5f) / DisplaySize();
const FfxFloat32x2 fLrUvInInterpolationRect = fUvInInterpolationRect * (FfxFloat32x2(RenderSize()) / GetMaxRenderSize());
const FfxFloat32x2 fUvLetterBoxScale = FfxFloat32x2(InterpolationRectSize()) / DisplaySize();
// game MV are top left aligned, the function scales them to render res UV
VectorFieldEntry gameMv;
LoadInpaintedGameFieldMv(fUvInInterpolationRect, gameMv);
// OF is done on the back buffers which already have black bars
VectorFieldEntry ofMv;
SampleOpticalFlowMotionVectorField(fUvInScreenSpace, ofMv);
// Binarize disucclusion factor
FfxFloat32x2 fDisocclusionFactor = FfxFloat32x2(FFX_EQUAL(ffxSaturate(SampleDisocclusionMask(fLrUvInInterpolationRect).xy), FfxFloat32x2(1.0, 1.0)));
InterpolationSourceColor fPrevColorGame = SampleTextureBilinear(false, fUvInScreenSpace, +gameMv.fMotionVector * fUvLetterBoxScale, DisplaySize());
InterpolationSourceColor fCurrColorGame = SampleTextureBilinear(true, fUvInScreenSpace, -gameMv.fMotionVector * fUvLetterBoxScale, DisplaySize());
InterpolationSourceColor fPrevColorOF = SampleTextureBilinear(false, fUvInScreenSpace, +ofMv.fMotionVector * fUvLetterBoxScale, DisplaySize());
InterpolationSourceColor fCurrColorOF = SampleTextureBilinear(true, fUvInScreenSpace, -ofMv.fMotionVector * fUvLetterBoxScale, DisplaySize());
FfxFloat32 fBilinearWeightSum = 0.0f;
FfxFloat32 fDisoccludedFactor = 0.0f;
// Disocclusion logic
{
fDisocclusionFactor.x *= FfxFloat32(!gameMv.bNegOutside);
fDisocclusionFactor.y *= FfxFloat32(!gameMv.bPosOutside);
// Inpaint in bi-directional disocclusion areas
updateInPaintingWeight(fInPaintingWeight, FfxFloat32(length(fDisocclusionFactor) <= FFX_FRAMEINTERPOLATION_EPSILON));
FfxFloat32 t = 0.5f;
t += 0.5f * (1 - (fDisocclusionFactor.x));
t -= 0.5f * (1 - (fDisocclusionFactor.y));
fInterpolatedColor = ffxLerp(fPrevColorGame.fRaw, fCurrColorGame.fRaw, ffxSaturate(t));
fBilinearWeightSum = ffxLerp(fPrevColorGame.fBilinearWeightSum, fCurrColorGame.fBilinearWeightSum, ffxSaturate(t));
fDisoccludedFactor = ffxSaturate(1 - ffxMin(fDisocclusionFactor.x, fDisocclusionFactor.y));
if (fPrevColorGame.fBilinearWeightSum == 0.0f)
{
fInterpolatedColor = fCurrColorGame.fRaw;
fBilinearWeightSum = fCurrColorGame.fBilinearWeightSum;
}
else if (fCurrColorGame.fBilinearWeightSum == 0.0f)
{
fInterpolatedColor = fPrevColorGame.fRaw;
fBilinearWeightSum = fPrevColorGame.fBilinearWeightSum;
}
if (fPrevColorGame.fBilinearWeightSum == 0 && fCurrColorGame.fBilinearWeightSum == 0)
{
fInPaintingWeight = 1.0f;
}
}
{
FfxFloat32 ofT = 0.5f;
if (fPrevColorOF.fBilinearWeightSum > 0 && fCurrColorOF.fBilinearWeightSum > 0)
{
ofT = 0.5f;
}
else if (fPrevColorOF.fBilinearWeightSum > 0)
{
ofT = 0;
} else {
ofT = 1;
}
const FfxFloat32x3 ofColor = ffxLerp(fPrevColorOF.fRaw, fCurrColorOF.fRaw, ofT);
FfxFloat32 fOF_Sim = NormalizedDot3(fPrevColorOF.fRaw, fCurrColorOF.fRaw);
FfxFloat32 fGame_Sim = NormalizedDot3(fPrevColorGame.fRaw, fCurrColorGame.fRaw);
fGame_Sim = ffxLerp(ffxMax(FFX_FRAMEINTERPOLATION_EPSILON, fGame_Sim), 1.0f, ffxSaturate(fDisoccludedFactor));
FfxFloat32 fGameMvBias = ffxPow(ffxSaturate(fGame_Sim / ffxMax(FFX_FRAMEINTERPOLATION_EPSILON, fOF_Sim)), 1.0f);
const FfxFloat32 fFrameIndexFactor = FfxFloat32(FrameIndexSinceLastReset() < 10);
fGameMvBias = ffxLerp(fGameMvBias, 1.0f, fFrameIndexFactor);
fInterpolatedColor = ffxLerp(ofColor, fInterpolatedColor, ffxSaturate(fGameMvBias));
}
}
void computeFrameinterpolation(FfxInt32x2 iPxPos)
{
FfxFloat32x3 fColor = FfxFloat32x3(0, 0, 0);
FfxFloat32 fInPaintingWeight = 0.0f;
if (IsInRect(iPxPos, InterpolationRectBase(), InterpolationRectSize()) == false || FrameIndexSinceLastReset() == 0)
{
// if we just reset or we are out of the interpolation rect, copy the current back buffer and don't interpolate
fColor = LoadCurrentBackbuffer(iPxPos);
}
else
{
computeInterpolatedColor(iPxPos, fColor, fInPaintingWeight);
}
StoreFrameinterpolationOutput(FfxInt32x2(iPxPos), FfxFloat32x4(fColor, fInPaintingWeight));
}
#endif // FFX_FRAMEINTERPOLATION_H