16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 15:09:19 +02:00

Some renaming

This commit is contained in:
2025-04-15 01:32:56 +02:00
parent 12d6a1578d
commit a39d972126
864 changed files with 29 additions and 34 deletions

View File

@@ -0,0 +1,51 @@
# 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.
set(DOF_BASE_ARGS
-reflection -deps=gcc -DFFX_GPU=1)
set(DOF_PERMUTATION_ARGS
-DFFX_DOF_OPTION_MAX_RING_MERGE_LOG={0,1}
-DFFX_DOF_OPTION_COMBINE_IN_PLACE={0,1}
-DFFX_DOF_OPTION_REVERSE_DEPTH={0,1})
set(DOF_INCLUDE_ARGS
"${FFX_GPU_PATH}"
"${FFX_GPU_PATH}/dof")
if (NOT DOF_SHADER_EXT)
set(DOF_SHADER_EXT *)
endif()
file(GLOB DOF_SHADERS
"shaders/dof/ffx_dof_downsample_depth_pass.${DOF_SHADER_EXT}"
"shaders/dof/ffx_dof_downsample_color_pass.${DOF_SHADER_EXT}"
"shaders/dof/ffx_dof_dilate_pass.${DOF_SHADER_EXT}"
"shaders/dof/ffx_dof_blur_pass.${DOF_SHADER_EXT}"
"shaders/dof/ffx_dof_composite_pass.${DOF_SHADER_EXT}")
compile_shaders_with_depfile(
"${FFX_SC_EXECUTABLE}"
"${DOF_BASE_ARGS}" "${DOF_API_BASE_ARGS}" "${DOF_PERMUTATION_ARGS}" "${DOF_INCLUDE_ARGS}"
"${DOF_SHADERS}" "${FFX_PASS_SHADER_OUTPUT_PATH}" DOF_PERMUTATION_OUTPUTS)
add_shader_output("${DOF_PERMUTATION_OUTPUTS}")

View File

@@ -0,0 +1,714 @@
// 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_core.h"
#include "ffx_dof_common.h"
// Factor applied to a distance value before checking that it is in range of the blur kernel.
FFX_STATIC const FfxFloat32 FFX_DOF_RANGE_TOLERANCE_FACTOR = 0.98;
// Accumulators for one ring. Used for ring occlusion.
struct FfxDofBucket
{
FfxFloat32x4 color; // rgb=color sum, a=weight sum
FfxFloat32 ringCovg; // radius of the ring coverage (average of tileCoc/coc with some clamping)
FfxFloat32 radius; // radius of the ring center
FfxUInt32 sampleCount; // number of samples counted
};
// One sample of the input and related variables
struct FfxDofSample
{
FfxFloat32 coc; // signed circle of confusion in pixels. negative values are far-field.
FfxFloat32x3 color; // color value of the sample
};
// Helper struct to contain all input variables
struct FfxDofInputState
{
FfxUInt32x2 imageSize; // input pixel size (half res)
FfxFloat32x2 pxCoord; // pixel coordinates of the kernel center
FfxFloat32 tileCoc; // coc value bilinearly interpolated from the tile map
FfxFloat32 centerCoc; // signed coc value at the kernel center
FfxFloat32 ringGap; // undersampling factor. ringGap * nRings = tileCoc.
FfxUInt32 mipLevel; // mip level to use based on coc and MAX_RINGS
FfxBoolean nearField; // whether the center pixel is in the near field
FfxUInt32 nSamples, // number of actual samples taken
nRings; // number of rings to sample (<= MAX_RINGS)
FfxFloat32 covgFactor, covgBias; // coverage parameters
};
// Helper struct to contain accumulation variables
struct FfxDofAccumulators
{
FfxDofBucket prevBucket, currBucket;
FfxFloat32x4 ringColor;
FfxFloat32 ringCovg;
FfxFloat32x4 nearColor, fillColor;
};
// Merges currBucket into prevBucket. Opacity is ratio of hit/total samples in current ring.
void FfxDofMergeBuckets(inout FfxDofAccumulators acc, FfxFloat32 opacity)
{
// averaging
FfxFloat32 prevRC = ffxSaturate(acc.prevBucket.ringCovg / acc.prevBucket.sampleCount);
FfxFloat32 currRC = ffxSaturate(acc.currBucket.ringCovg / acc.currBucket.sampleCount);
// occlusion term is calculated as the ratio of the area of intersection of both buckets
// (being viewed as rings with a radius (centered on the samples) and ring width (=avg coverage))
// divided by the area of the previous bucket ring.
FfxFloat32 prevOuter = ffxSaturate(acc.prevBucket.radius + prevRC);
FfxFloat32 prevInner = (acc.prevBucket.radius - prevRC);
FfxFloat32 currOuter = ffxSaturate(acc.currBucket.radius + currRC);
FfxFloat32 currInner = (acc.currBucket.radius - currRC);
// intersection is between min(outer) and max(inner)
FfxFloat32 insOuter = min(prevOuter, currOuter);
FfxFloat32 insInner = max(prevInner, currInner);
// intersection area formula.
// ffxSaturate here fixes edge case where prev area = 0 -> ffxSaturate(0/0)=ffxSaturate(nan) = 0
// The value does not matter in that case, since the previous values will be all zero, but it must be finite.
FfxFloat32 occlusion = insOuter < insInner ? 0 : ffxSaturate((insOuter * insOuter - sign(insInner) * insInner * insInner) / (prevOuter * prevOuter - sign(prevInner) * prevInner * prevInner));
FfxFloat32 factor = 1 - opacity * occlusion;
acc.prevBucket.color = acc.prevBucket.color * factor + acc.currBucket.color;
// select new radius so that (roughly) covers both rings, so in the middle of the combined ring
FfxFloat32 newRadius = 0.5 * (max(prevOuter, currOuter) + min(prevInner, currInner));
// the new coverage should then be the difference between the radius and either bound
FfxFloat32 newCovg = 0.5 * (max(prevOuter, currOuter) - min(prevInner, currInner));
acc.prevBucket.sampleCount = FfxUInt32(acc.prevBucket.sampleCount * factor) + acc.currBucket.sampleCount;
acc.prevBucket.ringCovg = acc.prevBucket.sampleCount * newCovg;
acc.prevBucket.radius = newRadius;
}
FfxFloat32 FfxDofWeight(FfxDofInputState ins, FfxFloat32 coc)
{
// Weight is inverse coc area (1 / pi*r^2). Use pi~=4 for perf reasons.
// Saturate to avoid explosion of weight close to zero. If coc < 0.5, the coc is contained
// within this pixel and the weight should be 1.
FfxFloat32 invRad = 1.0 / coc;
return ffxSaturate(invRad * invRad / 4);
}
FfxFloat32 FfxDofCoverage(FfxDofInputState ins, FfxFloat32 coc)
{
// Coverage is essentially the radius of the sample's projection to the lens aperture.
// The radius is normalized to the tile CoC and kernel diameter in samples.
// Add a small bias to account for gaps between sample rings.
// Clamped to avoid infinity near zero.
return ffxSaturate(ins.covgFactor / coc + ins.covgBias);
}
#ifdef FFX_DOF_CUSTOM_SAMPLES
// declarations only
// *MUST DEFINE* own struct SampleStreamState ! See below #else for example.
/// Callback for custom blur kernels. Gets the sample offset and advances the iterator.
/// @param state Mutable state for the sample stream.
/// @return The sample location in normalized uv coordinates. The default implementation approximates a circle using a combination of rotation and translation in an affine transform.
/// @ingroup FfxGPUDof
FfxFloat32x2 FfxDofAdvanceSampleStream(inout SampleStreamState state);
/// Callback for custom blur kernels. Gets the number of samples in a ring and initalizes iteration state.
/// @param state Mutable state for the sample stream.
/// @param ins Input parameters for the blur kernel. Contains the full number of rings.
/// @param ri Index of the current ring. If rings are being merged, this is the center of the indices.
/// @param mergeRingsCount The number of rings being merged. 1 if the current ring is not merged with any other.
/// @return The number of samples in the ring, assuming no merging. This is divided by the mergeRingsCount to get the actual number of samples.
/// @ingroup FfxGPUDof
FfxUInt32 FfxDofInitSampleStream(out SampleStreamState state, FfxDofInputState ins, FfxFloat32 ri, FfxUInt32 mergeRingsCount);
#else
struct SampleStreamState {
// represents an affine 2d transform to go from one sample position to the next.
FfxFloat32 cosTheta; // top-left and bottom-right element of rotation matrix
FfxFloat32 sinThetaXAspect; // bottom-left element of rotation matrix
FfxFloat32 mSinThetaXrAspect; // top-right element of rotation matrix
FfxFloat32x2 translation; // additive part of affine transform
FfxFloat32x2 position; // next sample position
};
FfxFloat32x2 FfxDofAdvanceSampleStream(inout SampleStreamState state)
{
FfxFloat32x2 pos = state.position;
// affine transformation.
FfxFloat32 x = state.cosTheta * pos.x + state.mSinThetaXrAspect * pos.y;
FfxFloat32 y = state.sinThetaXAspect * pos.x + state.cosTheta * pos.y;
state.position = FfxFloat32x2(x, y) + state.translation;
return pos;
}
FfxUInt32 FfxDofInitSampleStream(out SampleStreamState state, FfxDofInputState ins, FfxFloat32 ri, FfxUInt32 merge)
{
#if FFX_DOF_MAX_RING_MERGE > 1
FfxUInt32 n = FfxUInt32(6.25 * (ins.nRings - ri)); // approx. pi/asin(1/(2*(nR-ri)))
#else
// using fixed-point version of above allows scalar alu to do the same operation. Equivalent if merge == 1 (=> ri is integer).
FfxUInt32 n = (27 * FfxUInt32(ins.nRings - ri)) >> 2;
#endif
FfxFloat32 r = ins.tileCoc * ffxReciprocal(ins.nRings) * FfxFloat32(ins.nRings - ri);
state.position = InputSizeHalfRcp() * (ins.pxCoord + FfxFloat32x2(r, 0));
FfxFloat32 theta = 6.2831853 * ffxReciprocal(n) * merge;
FfxFloat32 s = sin(theta), c = cos(theta);
FfxFloat32x2 aspect = InputSizeHalf() * InputSizeHalfRcp().yx;
state.cosTheta = c;
state.sinThetaXAspect = s * aspect.x;
state.mSinThetaXrAspect = -s * aspect.y;
state.translation = InputSizeHalfRcp() * (ins.pxCoord - ins.pxCoord.x * FfxFloat32x2(c, s) - ins.pxCoord.y * FfxFloat32x2(-s, c));
return n;
}
#endif // #ifdef FFX_DOF_CUSTOM_SAMPLES
struct FfxDofRingParams {
FfxFloat32 distance; // distance to center in pixels / radius of ring
FfxFloat32 bucketBorder; // (far field) border between curr/prev bucket
FfxFloat32 rangeThreshNear; // threshold for in-range determination in near field
FfxFloat32 rangeThreshFar; // same for far field
FfxFloat32 rangeThreshFill; // threshold for main or fallback fill selection
FfxFloat32 fillQuality; // bg-fill contribution quality
};
FfxDofSample FfxDofFetchSample(FfxDofInputState ins, inout SampleStreamState streamState, FfxUInt32 mipBias)
{
FfxFloat32x2 samplePos = FfxDofAdvanceSampleStream(streamState);
FfxUInt32 mipLevel = ins.mipLevel + mipBias;
FfxFloat32x4 texval = FfxDofSampleInput(samplePos, mipLevel);
FfxDofSample result;
result.coc = texval.a;
result.color = texval.rgb;
return result;
}
void FfxDofProcessNearSample(FfxDofInputState ins, FfxDofSample s, inout FfxDofAccumulators acc, FfxDofRingParams ring)
{
// feather the range slightly (1px)
FfxFloat32 inRangeWeight = ffxSaturate(s.coc - ring.rangeThreshNear);
FfxFloat32 weight = FfxDofWeight(ins, abs(s.coc));
// fill background behind center using farther away samples.
if (ins.nearField)
{
// Try to reject same-surface samples using using a slope of 1px radius per px distance.
// But use the rejected pixels if no others are available.
FfxFloat32 rejectionWeight = (s.coc < ring.rangeThreshFill) ? 1.0 : (1.0/2048.0);
// prefer nearest (in image space) samples
// Contribution decreases quadratically with sample distance.
acc.fillColor += FfxFloat32x4(s.color, 1) * weight * ring.fillQuality * rejectionWeight;
}
acc.nearColor += FfxFloat32x4(s.color, 1) * weight * inRangeWeight;
}
void FfxDofProcessFarSample(FfxDofInputState ins, FfxDofSample s, inout FfxDofAccumulators acc, FfxDofRingParams ring)
{
FfxFloat32 clampedFarCoc = max(0, -s.coc);
FfxFloat32 inRangeWeight = ffxSaturate(-s.coc - ring.rangeThreshFar);
FfxFloat32 covg = FfxDofCoverage(ins, abs(s.coc));
FfxFloat32x4 color = FfxFloat32x4(s.color, 1) * FfxDofWeight(ins, abs(s.coc)) * inRangeWeight;
if (-s.coc >= ring.bucketBorder)
{
acc.prevBucket.ringCovg += covg;
acc.prevBucket.color += color;
acc.prevBucket.sampleCount++;
}
else
{
acc.currBucket.ringCovg += covg;
acc.currBucket.color += color;
acc.currBucket.sampleCount++;
}
}
void FfxDofProcessNearFar(FfxDofInputState ins, inout FfxDofAccumulators acc)
{
// base case: both near and far-field are processed.
// scan outside-in for far-field occlusion
for (FfxUInt32 ri = 0; ri < ins.nRings; ri++)
{
acc.currBucket.color = FFX_BROADCAST_FLOAT32X4(0);
acc.currBucket.ringCovg = 0;
acc.currBucket.radius = ffxReciprocal(ins.nRings) * FfxFloat32(ins.nRings - ri);
acc.currBucket.sampleCount = 0;
SampleStreamState streamState;
FfxUInt32 ringSamples = FfxDofInitSampleStream(streamState, ins, ri, 1);
FfxDofRingParams ring;
ring.distance = ins.tileCoc * ffxReciprocal(ins.nRings) * FfxFloat32(ins.nRings - ri);
ring.bucketBorder = (ins.nRings - 1 - ri + 2.5) * ins.tileCoc * ffxReciprocal(0.5 + ins.nRings);
ring.rangeThreshNear = max(0, ring.distance - ins.ringGap - 0.5);
ring.rangeThreshFar = max(0, ring.distance - ins.ringGap);
ring.rangeThreshFill = ins.centerCoc - ring.distance;
ring.fillQuality = (1 / ring.distance) * (1 / ring.distance);
// partially unrolled loop
const FfxUInt32 UNROLL_CNT = 2;
FfxUInt32 iunr = 0;
for (iunr = 0; iunr + UNROLL_CNT <= ringSamples; iunr += UNROLL_CNT)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < UNROLL_CNT; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, 0);
FfxDofProcessFarSample(ins, s, acc, ring);
FfxDofProcessNearSample(ins, s, acc, ring);
}
}
for (FfxUInt32 i = iunr; i < ringSamples; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, 0);
FfxDofProcessFarSample(ins, s, acc, ring);
FfxDofProcessNearSample(ins, s, acc, ring);
}
FfxFloat32 opacity = FfxFloat32(acc.currBucket.sampleCount) / FfxFloat32(ringSamples);
FfxDofMergeBuckets(acc, opacity);
}
}
void FfxDofProcessNearOnly(FfxDofInputState ins, inout FfxDofAccumulators acc)
{
// variant with the assumption that all samples are near field
for (FfxUInt32 ri = 0; ri < ins.nRings; ri++)
{
SampleStreamState streamState;
FfxUInt32 ringSamples = FfxDofInitSampleStream(streamState, ins, ri, 1);
FfxDofRingParams ring;
ring.distance = ins.tileCoc * ffxReciprocal(ins.nRings) * FfxFloat32(ins.nRings - ri);
ring.rangeThreshNear = max(0, ring.distance - ins.ringGap - 0.5);
ring.rangeThreshFill = ins.centerCoc - ring.distance;
ring.fillQuality = (1 / ring.distance) * (1 / ring.distance);
// partially unrolled loop
const FfxUInt32 UNROLL_CNT = 4;
FfxUInt32 iunr = 0;
for (iunr = 0; iunr + UNROLL_CNT <= ringSamples; iunr += UNROLL_CNT)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < UNROLL_CNT; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, 0);
FfxDofProcessNearSample(ins, s, acc, ring);
}
}
for (FfxUInt32 i = iunr; i < ringSamples; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, 0);
FfxDofProcessNearSample(ins, s, acc, ring);
}
}
}
void FfxDofProcessFarOnly(FfxDofInputState ins, inout FfxDofAccumulators acc)
{
// variant with the assumption that all samples are far field
// scan outside-in for far-field occlusion
for (FfxUInt32 ri = 0; ri < ins.nRings; ri++)
{
acc.currBucket.color = FFX_BROADCAST_FLOAT32X4(0);
acc.currBucket.ringCovg = 0;
acc.currBucket.radius = ffxReciprocal(ins.nRings) * FfxFloat32(ins.nRings - ri);
acc.currBucket.sampleCount = 0;
SampleStreamState streamState;
FfxUInt32 ringSamples = FfxDofInitSampleStream(streamState, ins, ri, 1);
FfxDofRingParams ring;
ring.distance = ins.tileCoc * ffxReciprocal(ins.nRings) * FfxFloat32(ins.nRings - ri);
ring.bucketBorder = (ins.nRings - 1 - ri + 2.5) * ins.tileCoc * ffxReciprocal(0.5 + ins.nRings);
ring.rangeThreshFar = max(0, ring.distance - ins.ringGap);
// partially unrolled loop
const FfxUInt32 UNROLL_CNT = 3;
FfxUInt32 iunr = 0;
for (iunr = 0; iunr + UNROLL_CNT <= ringSamples; iunr += UNROLL_CNT)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < UNROLL_CNT; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, 0);
FfxDofProcessFarSample(ins, s, acc, ring);
}
}
for (FfxUInt32 i = iunr; i < ringSamples; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, 0);
FfxDofProcessFarSample(ins, s, acc, ring);
}
FfxFloat32 opacity = FfxFloat32(acc.currBucket.sampleCount) / FfxFloat32(ringSamples);
FfxDofMergeBuckets(acc, opacity);
}
}
void FfxDofProcessNearColorOnly(FfxDofInputState ins, inout FfxDofAccumulators acc)
{
// variant with the assumption that all samples are near and equally weighed
for (FfxUInt32 ri = 0; ri < ins.nRings;)
{
// merge inner rings if possible
FfxUInt32 merge = min(min(1 << ri, FFX_DOF_MAX_RING_MERGE), ins.nRings - ri);
FfxFloat32 rif = ri + 0.5 * merge - 0.5;
FfxUInt32 weight = merge * merge;
SampleStreamState streamState;
FfxUInt32 ringSamples = FfxDofInitSampleStream(streamState, ins, rif, merge) / merge;
FfxUInt32 mipBias = 2 * FfxUInt32(log2(merge));
FfxFloat32 sampleDist = ins.tileCoc * ffxReciprocal(ins.nRings) * (FfxFloat32(ins.nRings) - rif);
FfxHalfOpt rangeThresh = FfxHalfOpt(max(0, sampleDist - ins.ringGap - 0.5));
FfxHalfOpt3 nearColorAcc = FfxHalfOpt3(0, 0, 0);
FfxHalfOpt weightSum = FfxHalfOpt(0);
// We presume that all samples are in range
// partially unrolled loop (x6)
const FfxUInt32 UNROLL_CNT = 6;
FfxUInt32 iunr = 0;
for (iunr = 0; iunr + UNROLL_CNT <= ringSamples; iunr += UNROLL_CNT)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < UNROLL_CNT; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
FfxHalfOpt rangeWeight = ffxSaturate(FfxHalfOpt(s.coc) - rangeThresh);
nearColorAcc += FfxHalfOpt3(s.color) * rangeWeight;
weightSum += rangeWeight;
}
}
for (FfxUInt32 i = iunr; i < ringSamples; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
FfxHalfOpt rangeWeight = ffxSaturate(FfxHalfOpt(s.coc) - rangeThresh);
nearColorAcc += FfxHalfOpt3(s.color) * rangeWeight;
weightSum += rangeWeight;
}
acc.nearColor.rgb += nearColorAcc * FfxHalfOpt(weight);
acc.nearColor.a += weightSum * FfxHalfOpt(weight);
ri += merge;
}
}
void FfxDofProcessFarColorOnly(FfxDofInputState ins, inout FfxDofAccumulators acc)
{
FfxFloat32 nSamples = 0;
FfxUInt32 ri = 0;
// peel first iteration
if (ri < ins.nRings)
{
// merge inner rings if possible
FfxUInt32 merge = min(min(1 << ri, FFX_DOF_MAX_RING_MERGE), ins.nRings - ri);
FfxFloat32 rif = ri + 0.5 * merge - 0.5;
FfxUInt32 weight = merge * merge;
SampleStreamState streamState;
FfxUInt32 ringSamples = FfxDofInitSampleStream(streamState, ins, rif, merge) / merge;
FfxUInt32 mipBias = 2 * FfxUInt32(log2(merge));
FfxFloat32 sampleDist = ins.tileCoc * ffxReciprocal(ins.nRings) * (FfxFloat32(ins.nRings) - rif);
FfxHalfOpt rangeThresh = FfxHalfOpt(max(0, sampleDist - ins.ringGap));
FfxHalfOpt3 colorAcc = FfxHalfOpt3(0, 0, 0);
FfxHalfOpt weightSum = FfxHalfOpt(0);
// partially unrolled loop (x8, then x4)
FfxUInt32 iunr = 0;
for (; iunr + 8 <= ringSamples; iunr += 8)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < 8; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
FfxHalfOpt rangeWeight = ffxSaturate(FfxHalfOpt(-s.coc) - rangeThresh);
colorAcc += FfxHalfOpt3(s.color) * rangeWeight;
weightSum += rangeWeight;
}
}
for (; iunr + 4 <= ringSamples; iunr += 4)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < 4; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
FfxHalfOpt rangeWeight = ffxSaturate(FfxHalfOpt(-s.coc) - rangeThresh);
colorAcc += FfxHalfOpt3(s.color) * rangeWeight;
weightSum += rangeWeight;
}
}
for (FfxUInt32 i = iunr; i < ringSamples; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
FfxHalfOpt rangeWeight = ffxSaturate(FfxHalfOpt(-s.coc) - rangeThresh);
colorAcc += FfxHalfOpt3(s.color) * rangeWeight;
weightSum += rangeWeight;
}
acc.prevBucket.color.rgb += colorAcc * FfxHalfOpt(weight);
nSamples += weightSum * FfxHalfOpt(weight);
ri += merge;
}
while (ri < ins.nRings)
{
// merge inner rings if possible
FfxUInt32 merge = min(min(1 << ri, FFX_DOF_MAX_RING_MERGE), ins.nRings - ri);
FfxFloat32 rif = ri + 0.5 * merge - 0.5;
FfxUInt32 weight = merge * merge;
SampleStreamState streamState;
FfxUInt32 ringSamples = FfxDofInitSampleStream(streamState, ins, rif, merge) / merge;
FfxUInt32 mipBias = 2 * FfxUInt32(log2(merge));
FfxHalfOpt3 colorAcc = FfxHalfOpt3(0, 0, 0);
// partially unrolled loop (x12, then x4)
FfxUInt32 iunr = 0;
for (; iunr + 12 <= ringSamples; iunr += 12)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < 12; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
// Max difference between sample coc and tile coc is 0.5px (see PrepareTile).
// Max radius of any ring after first (ri>0) is 1 less than tile coc.
// rangeWeight = 1
colorAcc += FfxHalfOpt3(s.color);
}
}
for (; iunr + 4 <= ringSamples; iunr += 4)
{
FFX_DOF_UNROLL
for (FfxUInt32 i = 0; i < 4; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
colorAcc += FfxHalfOpt3(s.color);
}
}
for (FfxUInt32 i = iunr; i < ringSamples; i++)
{
FfxDofSample s = FfxDofFetchSample(ins, streamState, mipBias);
colorAcc += FfxHalfOpt3(s.color);
}
acc.prevBucket.color.rgb += colorAcc * FfxHalfOpt(weight);
nSamples += ringSamples * weight;
ri += merge;
}
acc.prevBucket.color.a = nSamples;
acc.prevBucket.ringCovg = nSamples;
acc.prevBucket.sampleCount = FfxUInt32(nSamples);
}
struct FfxDofTileClass
{
FfxBoolean colorOnly, needsNear, needsFar;
};
// prepare values for the tile. Return classification.
FfxDofTileClass FfxDofPrepareTile(FfxUInt32x2 id, out FfxDofInputState ins)
{
FfxDofTileClass tileClass;
FfxFloat32x2 dilatedCocSigned = FfxDofSampleDilatedRadius(id);
FfxFloat32x2 dilatedCoc = abs(dilatedCocSigned);
FfxFloat32 tileRad = max(dilatedCoc.x, dilatedCoc.y);
// check if copying the tile is good enough
#if FFX_HLSL
if (WaveActiveAllTrue(tileRad < 0.5))
#elif FFX_GLSL
if (subgroupAll(tileRad < 0.5))
#endif
{
tileClass.needsNear = false;
tileClass.needsFar = false;
tileClass.colorOnly = false;
return tileClass;
}
FfxFloat32 idealRingCount = // kernel radius in pixels -> one sample per pixel
#if FFX_HLSL
WaveActiveMax(ceil(tileRad));
#elif FFX_GLSL
subgroupMax(ceil(tileRad));
#endif
ins.nRings = FfxUInt32(idealRingCount);
ins.mipLevel = 0;
if (idealRingCount > MaxRings())
{
ins.nRings = MaxRings();
// use a higher mip to cover the missing rings.
// for every factor 2 decrease of the rings, increase mips by 1.
ins.mipLevel = FfxUInt32(log2(idealRingCount * ffxReciprocal(MaxRings())));
}
// Gap = number of pixels between rings that are not sampled.
ins.ringGap = idealRingCount / ins.nRings - 1.0;
ins.tileCoc = tileRad;
FfxFloat32x2 texcoord = FfxFloat32x2(id) + FfxFloat32x2(0.25, 0.25); // shift to center of top-left pixel in quad
// Add noise to reduce banding (if too noisy, this could be disabled)
{
/* hash22 adapted from https://www.shadertoy.com/view/4djSRW
Copyright (c)2014 David Hoskins.
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.*/
FfxFloat32x3 p3 = ffxFract(texcoord.xyx * FfxFloat32x3(0.1031, 0.1030, 0.0973));
p3 += dot(p3, p3.yzx + 33.33);
texcoord += ffxFract((p3.xx+p3.yz)*p3.zy) * 0.5 - 0.25;
}
ins.pxCoord = texcoord;
FfxFloat32 centerCoc = FfxDofLoadInput(id).a;
ins.nearField = centerCoc > 0;
ins.centerCoc = abs(centerCoc);
ins.nSamples = 0;
#ifdef FFX_DOF_CUSTOM_SAMPLES
for (FfxUInt32 ri = 0; ri < ins.nRings; ri++)
{
SampleStreamState streamState;
ins.nSamples += FfxDofInitSampleStream(streamState, ins, ri, 1);
}
#else
// due to rounding this will likely over-approximate, but that should be okay.
ins.nSamples = FfxUInt32(6.25 * 0.5 * (ins.nRings * (ins.nRings + 1)));
#endif
// read first lane to force using a scalar register.
ins.covgFactor = ffxWaveReadLaneFirst(0.5 * ffxReciprocal(ins.nRings) * ins.tileCoc);
ins.covgBias = ffxWaveReadLaneFirst(0.5 * ffxReciprocal(ins.nRings));
#if FFX_HLSL
// simple check: is there even any near/far that we would sample?
// See ProcessNearSample: No relevant code is executed if the center is not near and no sample is near
// (use the CoC of the dilated tile minimum depth as the proxy for any sample)
tileClass.needsNear = WaveActiveAnyTrue(dilatedCocSigned.x > -1);
// See ProcessFarSample: All weights will be 0 if no sample is in the far field.
// (use the CoC of dilated tile max depth as proxy)
tileClass.needsFar = WaveActiveAnyTrue(dilatedCocSigned.y < 1);
tileClass.colorOnly = WaveActiveAllTrue(dilatedCocSigned.x - dilatedCocSigned.y < 0.5);
#elif FFX_GLSL
// as above
tileClass.needsNear = subgroupAny(dilatedCocSigned.x > -1);
tileClass.needsFar = subgroupAny(dilatedCocSigned.y < 1);
tileClass.colorOnly = subgroupAll(dilatedCocSigned.x - dilatedCocSigned.y < 0.5);
#endif
return tileClass;
}
/// Blur pass entry point. Runs in 8x8x1 thread groups and computes transient near and far outputs.
///
/// @param pixel Coordinate of the pixel (SV_DispatchThreadID)
/// @param halfImageSize Resolution of the source image (half resolution) in pixels
/// @ingroup FfxGPUDof
void FfxDofBlur(FfxUInt32x2 pixel, FfxUInt32x2 halfImageSize)
{
FfxDofResetMaxTileRadius();
FfxDofInputState ins;
FfxDofTileClass tileClass = FfxDofPrepareTile(pixel, ins);
ins.imageSize = halfImageSize;
// initialize accumulators
FfxDofAccumulators acc;
acc.prevBucket.color = FfxFloat32x4(0, 0, 0, 0);
acc.prevBucket.ringCovg = 0;
acc.prevBucket.radius = 0;
acc.prevBucket.sampleCount = 0;
FfxFloat32 centerWeight = FfxDofWeight(ins, ins.centerCoc);
FfxFloat32 centerCovg = FfxDofCoverage(ins, ins.centerCoc);
FfxFloat32x4 centerColor = FfxFloat32x4(FfxDofLoadInput(pixel).rgb, 1);
acc.nearColor = FfxFloat32x4(0, 0, 0, 0);
acc.fillColor = FfxFloat32x4(0, 0, 0, 0);
if (ins.nearField)
{
// for near field: adjust center weight to cover everything beyond innermost ring
// radius of innermost ring:
FfxFloat32 innerRingRad = max(1, ins.tileCoc * ffxReciprocal(ins.nRings) - pow(2, ins.mipLevel));
FfxFloat32 nearCenterWeight = innerRingRad * innerRingRad;
// if center radius < 1px, split the center color between near and fill
FfxFloat32 nearPart = ffxSaturate(ins.centerCoc), fillPart = 1 - nearPart;
acc.nearColor = centerColor * centerWeight * nearCenterWeight * nearPart;
acc.fillColor = centerColor * fillPart;
}
if (tileClass.needsNear && tileClass.needsFar)
{
FfxDofProcessNearFar(ins, acc);
}
else if (tileClass.needsNear)
{
if (tileClass.colorOnly)
FfxDofProcessNearColorOnly(ins, acc);
else
FfxDofProcessNearOnly(ins, acc);
}
else if (tileClass.needsFar)
{
if (tileClass.colorOnly)
FfxDofProcessFarColorOnly(ins, acc);
else
FfxDofProcessFarOnly(ins, acc);
}
else // if (!tileClass.needsFar && !tileClass.needsNear)
{
FfxDofStoreFar(pixel, FfxHalfOpt4(FfxDofLoadInput(pixel).rgb, 1));
FfxDofStoreNear(pixel, FfxHalfOpt4(0, 0, 0, 0));
return;
}
// process center
acc.currBucket.ringCovg = ins.nearField ? 0 : centerCovg;
acc.currBucket.color = ins.nearField ? FfxFloat32x4(0, 0, 0, 0) : centerColor * centerWeight;
acc.currBucket.radius = 0;
acc.currBucket.sampleCount = 1;
FfxDofMergeBuckets(acc, 1);
if (ins.nearField)
{
acc.prevBucket.color += acc.fillColor;
}
FfxFloat32 fgOpacity = (!tileClass.needsFar && tileClass.colorOnly) ? 1.0 :
ffxSaturate(acc.nearColor.a / (FfxDofWeight(ins, ins.tileCoc) * ins.nSamples));
FfxFloat32x4 ffTarget = acc.prevBucket.color / acc.prevBucket.color.a;
FfxFloat32x4 ffOutput = !any(isnan(ffTarget)) ? ffTarget : FfxFloat32x4(0, 0, 0, 0);
FfxFloat32x3 nfTarget = acc.nearColor.rgb / acc.nearColor.a;
FfxFloat32x4 nfOutput = FfxFloat32x4(!any(isnan(nfTarget)) ? nfTarget : FfxFloat32x3(0, 0, 0), fgOpacity);
FfxDofStoreFar(pixel, FfxHalfOpt4(ffOutput));
FfxDofStoreNear(pixel, FfxHalfOpt4(nfOutput));
}

View File

@@ -0,0 +1,282 @@
// 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_dof_resources.h"
#include "ffx_core.h"
#if defined(FFX_GPU)
#ifndef FFX_PREFER_WAVE64
#define FFX_PREFER_WAVE64
#endif // #ifndef FFX_PREFER_WAVE64
#if defined(FFX_DOF_BIND_CB_DOF)
layout (set = 0, binding = FFX_DOF_BIND_CB_DOF, std140) uniform cbDOF_t
{
FfxFloat32 cocScale;
FfxFloat32 cocBias;
FfxUInt32x2 inputSizeHalf;
FfxUInt32x2 inputSize;
FfxFloat32x2 inputSizeHalfRcp;
FfxFloat32 cocLimit;
FfxUInt32 maxRings;
#define FFX_DOF_CONSTANT_BUFFER_1_SIZE 10 // Number of 32-bit values. This must be kept in sync with the cbDOF size.
} cbDOF;
#endif
FfxFloat32 CocScale()
{
return cbDOF.cocScale;
}
FfxFloat32 CocBias()
{
return cbDOF.cocBias;
}
FfxUInt32x2 InputSizeHalf()
{
return cbDOF.inputSizeHalf;
}
FfxUInt32x2 InputSize()
{
return cbDOF.inputSize;
}
FfxFloat32x2 InputSizeHalfRcp()
{
return cbDOF.inputSizeHalfRcp;
}
FfxFloat32 CocLimit()
{
return cbDOF.cocLimit;
}
FfxUInt32 MaxRings()
{
return cbDOF.maxRings;
}
layout (set = 0, binding = 1000)
uniform sampler LinearSampler;
layout (set = 0, binding = 1001)
uniform sampler PointSampler;
#if FFX_HALF
#define FFX_DOF_HALF_OPT_LAYOUT rgba16f
#else // #if FFX_HALF
#define FFX_DOF_HALF_OPT_LAYOUT rgba32f
#endif // #if FFX_HALF #else
// SRVs
#if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
layout (set = 0, binding = FFX_DOF_BIND_SRV_INPUT_DEPTH)
uniform texture2D r_input_depth;
#endif
#if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
layout (set = 0, binding = FFX_DOF_BIND_SRV_INPUT_COLOR)
uniform texture2D r_input_color;
#endif
#if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
layout (set = 0, binding = FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
uniform texture2D r_internal_bilat_color;
#endif
#if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
layout (set = 0, binding = FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
uniform texture2D r_internal_dilated_radius;
#endif
// UAVs
#if defined(FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR)
layout (set = 0, binding = FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR, FFX_DOF_HALF_OPT_LAYOUT)
uniform image2D rw_internal_bilat_color[4];
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
layout (set = 0, binding = FFX_DOF_BIND_UAV_INTERNAL_RADIUS, rg32f)
uniform image2D rw_internal_radius;
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS)
layout (set = 0, binding = FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS, rg32f)
uniform image2D rw_internal_dilated_radius;
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
layout (set = 0, binding = FFX_DOF_BIND_UAV_INTERNAL_NEAR, FFX_DOF_HALF_OPT_LAYOUT)
uniform image2D rw_internal_near;
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
layout (set = 0, binding = FFX_DOF_BIND_UAV_INTERNAL_FAR, FFX_DOF_HALF_OPT_LAYOUT)
uniform image2D rw_internal_far;
#endif
#if defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)
layout (set = 0, binding = FFX_DOF_BIND_UAV_OUTPUT_COLOR, rgba32f)
uniform image2D rw_output_color;
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
layout (set = 0, binding = FFX_DOF_BIND_UAV_INTERNAL_GLOBALS, std430)
coherent buffer DofGlobalVars_t { uint maxTileRad; } rw_internal_globals;
#endif
#include "ffx_dof_common.h"
#if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
FfxHalfOpt4 FfxDofLoadSource(FfxInt32x2 tex)
{
return FfxHalfOpt4(texelFetch(sampler2D(r_input_color, LinearSampler), tex, 0));
}
#endif // #if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR)
void FfxDofStoreBilatMip(FfxUInt32 mip, FfxInt32x2 tex, FfxHalfOpt4 value)
{
imageStore(rw_internal_bilat_color[mip], tex, value);
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR)
#if defined(FFX_DOF_BIND_CB_DOF)
FfxFloat32 FfxDofGetCoc(FfxFloat32 depth)
{
// clamped for perf reasons
return clamp(CocScale() * depth + CocBias(), -CocLimit(), CocLimit());
}
#endif // #if defined(FFX_DOF_BIND_CB_DOF)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS)
void FfxDofStoreDilatedRadius(FfxUInt32x2 coord, FfxFloat32x2 dilatedMinMax)
{
imageStore(rw_internal_dilated_radius, ivec2(coord), vec4(dilatedMinMax, 0, 0));
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS)
#if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
#if defined(FFX_DOF_BIND_CB_DOF)
FfxFloat32x2 FfxDofSampleDilatedRadius(FfxUInt32x2 coord)
{
return textureLod(sampler2D(r_internal_dilated_radius, LinearSampler), vec2(coord) * InputSizeHalfRcp(), 0).rg;
}
#endif // #if defined(FFX_DOF_BIND_CB_DOF)
FfxFloat32x2 FfxDofLoadDilatedRadius(FfxUInt32x2 coord)
{
return texelFetch(sampler2D(r_internal_dilated_radius, LinearSampler), ivec2(coord), 0).rg;
}
#endif // #if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
#if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
FfxHalfOpt4 FfxDofLoadInput(FfxUInt32x2 coord)
{
return FfxHalfOpt4(texelFetch(sampler2D(r_internal_bilat_color, LinearSampler), ivec2(coord), 0));
}
#if defined(FFX_DOF_BIND_CB_DOF)
FfxHalfOpt4 FfxDofSampleInput(FfxFloat32x2 coord, FfxUInt32 mip)
{
return FfxHalfOpt4(textureLod(sampler2D(r_internal_bilat_color, PointSampler), coord, mip));
}
#endif // #if defined(FFX_DOF_BIND_CB_DOF)
#endif // #if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
#if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
FfxFloat32x4 FfxDofGatherDepth(FfxFloat32x2 coord)
{
return textureGather(sampler2D(r_input_depth, LinearSampler), coord);
}
FfxFloat32 FfxDofLoadFullDepth(FfxUInt32x2 coord)
{
return texelFetch(sampler2D(r_input_depth, LinearSampler), ivec2(coord), 0).r;
}
#endif // #if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
void FfxDofStoreNear(FfxUInt32x2 coord, FfxHalfOpt4 color)
{
imageStore(rw_internal_near, ivec2(coord), color);
}
FfxHalfOpt4 FfxDofLoadNear(FfxUInt32x2 coord)
{
return FfxHalfOpt4(imageLoad(rw_internal_near, ivec2(coord)));
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
void FfxDofStoreFar(FfxUInt32x2 coord, FfxHalfOpt4 color)
{
imageStore(rw_internal_far, ivec2(coord), color);
}
FfxHalfOpt4 FfxDofLoadFar(FfxUInt32x2 coord)
{
return FfxHalfOpt4(imageLoad(rw_internal_far, ivec2(coord)));
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
void FfxDofAccumMaxTileRadius(FfxUInt32 radius)
{
atomicMax(rw_internal_globals.maxTileRad, radius);
}
FfxUInt32 FfxDofGetMaxTileRadius()
{
return rw_internal_globals.maxTileRad;
}
void FfxDofResetMaxTileRadius()
{
rw_internal_globals.maxTileRad = 0;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
void FfxDofStoreTileRadius(FfxUInt32x2 tile, FfxFloat32x2 radius)
{
imageStore(rw_internal_radius, ivec2(tile), vec4(radius, 0, 0));
}
FfxFloat32x2 FfxDofLoadTileRadius(FfxUInt32x2 tile)
{
return imageLoad(rw_internal_radius, ivec2(tile)).rg;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
#if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
FfxFloat32x4 FfxDofLoadFullInput(FfxUInt32x2 coord)
{
return texelFetch(sampler2D(r_input_color, LinearSampler), ivec2(coord), 0);
}
#elif (defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)) && FFX_DOF_OPTION_COMBINE_IN_PLACE
FfxFloat32x4 FfxDofLoadFullInput(FfxUInt32x2 coord)
{
return imageLoad(rw_output_color, ivec2(coord));
}
#endif // #if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
#if defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)
void FfxDofStoreOutput(FfxUInt32x2 coord, FfxFloat32x4 color)
{
imageStore(rw_output_color, ivec2(coord), color);
}
#endif // #if defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,322 @@
// 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_dof_resources.h"
#include "ffx_core.h"
#include "ffx_dof_common.h"
#if defined(FFX_GPU)
#ifdef __hlsl_dx_compiler
#pragma dxc diagnostic push
#pragma dxc diagnostic ignored "-Wambig-lit-shift"
#endif //__hlsl_dx_compiler
#include "ffx_core.h"
#ifdef __hlsl_dx_compiler
#pragma dxc diagnostic pop
#endif //__hlsl_dx_compiler
#ifndef FFX_PREFER_WAVE64
#define FFX_PREFER_WAVE64
#endif // #ifndef FFX_PREFER_WAVE64
#pragma warning(disable: 3205) // conversion from larger type to smaller
#define DECLARE_SRV_REGISTER(regIndex) t##regIndex
#define DECLARE_UAV_REGISTER(regIndex) u##regIndex
#define DECLARE_CB_REGISTER(regIndex) b##regIndex
#define FFX_DOF_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex))
#define FFX_DOF_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex))
#define FFX_DOF_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex))
#if defined(FFX_DOF_BIND_CB_DOF)
cbuffer cbDOF : FFX_DOF_DECLARE_CB(FFX_DOF_BIND_CB_DOF)
{
FfxFloat32 cocScale;
FfxFloat32 cocBias;
FfxUInt32x2 inputSizeHalf;
FfxUInt32x2 inputSize;
FfxFloat32x2 inputSizeHalfRcp;
FfxFloat32 cocLimit;
FfxUInt32 maxRings;
#define FFX_DOF_CONSTANT_BUFFER_1_SIZE 10 // Number of 32-bit values. This must be kept in sync with the cbDOF size.
};
#else
#define cocScale 0
#define cocBias 0
#define inputSizeHalf 0
#define inputSize 0
#define inputSizeHalfRcp 0
#define cocLimit 0
#define maxRings 0
#endif
#define FFX_DOF_ROOTSIG_STRINGIFY(p) FFX_DOF_ROOTSIG_STR(p)
#define FFX_DOF_ROOTSIG_STR(p) #p
#define FFX_DOF_ROOTSIG [RootSignature("DescriptorTable(UAV(u0, numDescriptors = " FFX_DOF_ROOTSIG_STRINGIFY(FFX_DOF_RESOURCE_IDENTIFIER_COUNT) ")), " \
"DescriptorTable(SRV(t0, numDescriptors = " FFX_DOF_ROOTSIG_STRINGIFY(FFX_DOF_RESOURCE_IDENTIFIER_COUNT) ")), " \
"CBV(b0), " \
"StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_LINEAR, " \
"addressU = TEXTURE_ADDRESS_CLAMP, " \
"addressV = TEXTURE_ADDRESS_CLAMP, " \
"addressW = TEXTURE_ADDRESS_CLAMP, " \
"comparisonFunc = COMPARISON_NEVER, " \
"borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \
"StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_POINT, " \
"addressU = TEXTURE_ADDRESS_CLAMP, " \
"addressV = TEXTURE_ADDRESS_CLAMP, " \
"addressW = TEXTURE_ADDRESS_CLAMP, " \
"comparisonFunc = COMPARISON_NEVER, " \
"borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )]
#if defined(FFX_DOF_EMBED_ROOTSIG)
#define FFX_DOF_EMBED_ROOTSIG_CONTENT FFX_DOF_ROOTSIG
#else
#define FFX_DOF_EMBED_ROOTSIG_CONTENT
#endif // #if FFX_SPD_EMBED_ROOTSIG
FfxFloat32 CocScale()
{
return cocScale;
}
FfxFloat32 CocBias()
{
return cocBias;
}
FfxUInt32x2 InputSizeHalf()
{
return inputSizeHalf;
}
FfxUInt32x2 InputSize()
{
return inputSize;
}
FfxFloat32x2 InputSizeHalfRcp()
{
return inputSizeHalfRcp;
}
FfxFloat32 CocLimit()
{
return cocLimit;
}
FfxUInt32 MaxRings()
{
return maxRings;
}
SamplerState LinearSampler : register(s0);
SamplerState PointSampler : register(s1);
// SRVs
#if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
Texture2D<FfxFloat32> r_input_depth : FFX_DOF_DECLARE_SRV(FFX_DOF_BIND_SRV_INPUT_DEPTH);
#endif
#if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
Texture2D<FfxFloat32x4> r_input_color : FFX_DOF_DECLARE_SRV(FFX_DOF_BIND_SRV_INPUT_COLOR);
#endif
#if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
Texture2D<FfxHalfOpt4> r_internal_bilat_color : FFX_DOF_DECLARE_SRV(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR);
#endif
#if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
Texture2D<FfxFloat32x2> r_internal_dilated_radius : FFX_DOF_DECLARE_SRV(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS);
#endif
// UAVs
#if defined(FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR)
RWTexture2D<FfxHalfOpt4> rw_internal_bilat_color[4] : FFX_DOF_DECLARE_UAV(FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR);
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
RWTexture2D<FfxFloat32x2> rw_internal_radius : FFX_DOF_DECLARE_UAV(FFX_DOF_BIND_UAV_INTERNAL_RADIUS);
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS)
RWTexture2D<FfxFloat32x2> rw_internal_dilated_radius : FFX_DOF_DECLARE_UAV(FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS);
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
RWTexture2D<FfxHalfOpt4> rw_internal_near : FFX_DOF_DECLARE_UAV(FFX_DOF_BIND_UAV_INTERNAL_NEAR);
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
RWTexture2D<FfxHalfOpt4> rw_internal_far : FFX_DOF_DECLARE_UAV(FFX_DOF_BIND_UAV_INTERNAL_FAR);
#endif
#if defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)
RWTexture2D<FfxFloat32x4> rw_output_color : FFX_DOF_DECLARE_UAV(FFX_DOF_BIND_UAV_OUTPUT_COLOR);
#endif
#if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
struct DofGlobalVars { uint maxTileRad; };
globallycoherent RWStructuredBuffer<DofGlobalVars> rw_internal_globals : FFX_DOF_DECLARE_UAV(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS);
#endif
#if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
FfxHalfOpt4 FfxDofLoadSource(FfxInt32x2 tex)
{
return FfxHalfOpt4(r_input_color[tex]);
}
#endif // #if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR)
void FfxDofStoreBilatMip(FfxUInt32 mip, FfxInt32x2 tex, FfxHalfOpt4 value)
{
rw_internal_bilat_color[mip][tex] = value;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_BILAT_COLOR)
FfxFloat32 FfxDofGetCoc(FfxFloat32 depth)
{
// clamped for perf reasons
return clamp(CocScale() * depth + CocBias(), -CocLimit(), CocLimit());
}
#if defined(FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS)
void FfxDofStoreDilatedRadius(FfxUInt32x2 coord, FfxFloat32x2 dilatedMinMax)
{
rw_internal_dilated_radius[coord] = dilatedMinMax;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_DILATED_RADIUS)
#if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
FfxFloat32x2 FfxDofSampleDilatedRadius(FfxUInt32x2 coord)
{
return r_internal_dilated_radius.SampleLevel(LinearSampler, float2(coord) * InputSizeHalfRcp(), 0);
}
#endif // #if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
#if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
FfxFloat32x2 FfxDofLoadDilatedRadius(FfxUInt32x2 coord)
{
return r_internal_dilated_radius[coord];
}
#endif // #if defined(FFX_DOF_BIND_SRV_INTERNAL_DILATED_RADIUS)
#if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
FfxHalfOpt4 FfxDofLoadInput(FfxUInt32x2 coord)
{
return r_internal_bilat_color[coord];
}
#endif // #if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
#if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
FfxHalfOpt4 FfxDofSampleInput(FfxFloat32x2 coord, FfxUInt32 mip)
{
return r_internal_bilat_color.SampleLevel(PointSampler, coord, mip);
}
#endif // #if defined(FFX_DOF_BIND_SRV_INTERNAL_BILAT_COLOR)
#if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
FfxFloat32x4 FfxDofGatherDepth(FfxFloat32x2 coord)
{
return r_input_depth.GatherRed(LinearSampler, coord);
}
#endif // #if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
#if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
FfxFloat32 FfxDofLoadFullDepth(FfxUInt32x2 coord)
{
return r_input_depth[coord];
}
#endif // #if defined(FFX_DOF_BIND_SRV_INPUT_DEPTH)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
void FfxDofStoreNear(FfxUInt32x2 coord, FfxHalfOpt4 color)
{
rw_internal_near[coord] = color;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
FfxHalfOpt4 FfxDofLoadNear(FfxUInt32x2 coord)
{
return rw_internal_near[coord];
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_NEAR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
void FfxDofStoreFar(FfxUInt32x2 coord, FfxHalfOpt4 color)
{
rw_internal_far[coord] = color;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
FfxHalfOpt4 FfxDofLoadFar(FfxUInt32x2 coord)
{
return rw_internal_far[coord];
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_FAR)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
void FfxDofAccumMaxTileRadius(FfxUInt32 radius)
{
uint dummy;
InterlockedMax(rw_internal_globals[0].maxTileRad, radius, dummy);
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
FfxUInt32 FfxDofGetMaxTileRadius()
{
return rw_internal_globals[0].maxTileRad;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
void FfxDofResetMaxTileRadius()
{
rw_internal_globals[0].maxTileRad = 0;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_GLOBALS)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
void FfxDofStoreTileRadius(FfxUInt32x2 tile, FfxFloat32x2 radius)
{
rw_internal_radius[tile] = radius;
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
#if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
FfxFloat32x2 FfxDofLoadTileRadius(FfxUInt32x2 tile)
{
return rw_internal_radius[tile];
}
#endif // #if defined(FFX_DOF_BIND_UAV_INTERNAL_RADIUS)
FfxFloat32x4 FfxDofLoadFullInput(FfxUInt32x2 coord)
{
#if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
return r_input_color[coord];
#elif (defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)) && FFX_DOF_OPTION_COMBINE_IN_PLACE
return rw_output_color[coord];
#endif // #if defined(FFX_DOF_BIND_SRV_INPUT_COLOR)
return 0;
}
#if defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)
void FfxDofStoreOutput(FfxUInt32x2 coord, FfxFloat32x4 color)
{
rw_output_color[coord] = color;
}
#endif // #if defined(FFX_DOF_BIND_UAV_OUTPUT_COLOR)
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,142 @@
// 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_DOF_COMMON_H
#define FFX_DOF_COMMON_H
/// @defgroup FfxGPUDof FidelityFX DOF
/// FidelityFX Depth of Field GPU documentation
///
/// @ingroup FfxGPUEffects
// Constants - these are fixed magic numbers. Changes likely require changing code.
/// The width/height of tiles storing per-tile min and max CoC. The blur pass needs to run
/// at a compatible tile size, so that the assumption that min/max CoC is uniform for all threads in
/// a thread group holds.
///
/// @ingroup FfxGPUDof
FFX_STATIC const FfxUInt32 FFX_DOF_DEPTH_TILE_SIZE = 8;
// Default settings
#ifndef FFX_DOF_OPTION_MAX_MIP
/// The number of mips that should be generated in the color downsample pass
///
/// @ingroup FfxGPUDof
#define FFX_DOF_OPTION_MAX_MIP 4
#endif
#ifndef FFX_DOF_OPTION_MAX_RING_MERGE_LOG
/// The base-2 logarithm of the number of blur kernel rings that may be merged, if possible.
///
/// Setting this to zero disables ring merging.
///
/// Values above 1 are technically valid, but are known to cause a visible quality drop.
///
/// @ingroup FfxGPUDof
#define FFX_DOF_OPTION_MAX_RING_MERGE_LOG 1
#endif
#define FFX_DOF_MAX_RING_MERGE (1 << FFX_DOF_OPTION_MAX_RING_MERGE_LOG)
// optional 16-bit types
#if FFX_HLSL
#if FFX_HALF
typedef FfxFloat16 FfxHalfOpt;
typedef FfxFloat16x2 FfxHalfOpt2;
typedef FfxFloat16x3 FfxHalfOpt3;
typedef FfxFloat16x4 FfxHalfOpt4;
#else
typedef FfxFloat32 FfxHalfOpt;
typedef FfxFloat32x2 FfxHalfOpt2;
typedef FfxFloat32x3 FfxHalfOpt3;
typedef FfxFloat32x4 FfxHalfOpt4;
#endif
#elif FFX_GLSL
#if FFX_HALF
#define FfxHalfOpt FfxFloat16
#define FfxHalfOpt2 FfxFloat16x2
#define FfxHalfOpt3 FfxFloat16x3
#define FfxHalfOpt4 FfxFloat16x4
#else
#define FfxHalfOpt FfxFloat32
#define FfxHalfOpt2 FfxFloat32x2
#define FfxHalfOpt3 FfxFloat32x3
#define FfxHalfOpt4 FfxFloat32x4
#endif
#endif
// unroll macros with count argument
#if FFX_HLSL
#define FFX_DOF_UNROLL_N(_n) [unroll(_n)]
#define FFX_DOF_UNROLL [unroll]
#elif FFX_GLSL // #if FFX_HLSL
#define FFX_DOF_UNROLL_N(_n)
// enable unrolling loops
#extension GL_EXT_control_flow_attributes : enable
#if GL_EXT_control_flow_attributes
#define FFX_DOF_UNROLL [[unroll]]
#else
#define FFX_DOF_UNROLL
#endif // #if GL_EXT_control_flow_attributes
#endif // #if FFX_HLSL #elif FFX_GLSL
// ReadLaneFirst
#if FFX_HLSL
#define ffxWaveReadLaneFirst WaveReadLaneFirst
#elif FFX_GLSL
#define ffxWaveReadLaneFirst subgroupBroadcastFirst
#endif // #if FFX_GLSL
// Callback declarations
FfxFloat32 FfxDofGetCoc(FfxFloat32 depth); // returns circle of confusion radius in pixels (at half-res). Sign indicates near-field (positive) or far-field (negative).
void FfxDofStoreDilatedRadius(FfxUInt32x2 coord, FfxFloat32x2 dilatedMinMax);
FfxFloat32x2 FfxDofSampleDilatedRadius(FfxUInt32x2 coord); // returns the dilated min and max depth for a pixel coordinate. Should be bilinearly interpolated!
FfxFloat32x2 FfxDofLoadDilatedRadius(FfxUInt32x2 coord); // returns the dilated depth for a *tile* coordinate without interpolation
FfxHalfOpt4 FfxDofLoadInput(FfxUInt32x2 coord); // returns input rgb and (in alpha) CoC in pixels from the most detailed mip (which is half-res)
FfxHalfOpt4 FfxDofSampleInput(FfxFloat32x2 coord, FfxUInt32 mip); // returns input rgb and (in alpha) CoC in pixels.
FfxFloat32x4 FfxDofGatherDepth(FfxFloat32x2 coord); // returns 4 input depth values in a quad, must use sampler with coordinate clamping
void FfxDofStoreNear(FfxUInt32x2 coord, FfxHalfOpt4 color); // stores the near color output from the blur pass
void FfxDofStoreFar(FfxUInt32x2 coord, FfxHalfOpt4 color); // stores the far color output from the blur pass
FfxHalfOpt4 FfxDofLoadNear(FfxUInt32x2 coord); // loads the near color value
FfxHalfOpt4 FfxDofLoadFar(FfxUInt32x2 coord); // loads the far color value
void FfxDofAccumMaxTileRadius(FfxUInt32 radius); // stores the global maximum dilation radius using an atomic max operation
FfxUInt32 FfxDofGetMaxTileRadius(); // gets the global maximum dilation radius
void FfxDofResetMaxTileRadius(); // sets max dilation radius back to zero
void FfxDofStoreTileRadius(FfxUInt32x2 tile, FfxFloat32x2 radius); // stores the min/max signed radius for a tile in the tile map
FfxFloat32x2 FfxDofLoadTileRadius(FfxUInt32x2 tile); // loads the min/max depth for a tile
FfxFloat32x4 FfxDofLoadFullInput(FfxUInt32x2 coord); // returns full resultion input rgba (alpha is ignored)
FfxFloat32 FfxDofLoadFullDepth(FfxUInt32x2 coord); // returns full resolution depth
void FfxDofStoreOutput(FfxUInt32x2 coord, FfxFloat32x4 color); // writes the final output
// Common helper functions
FfxUInt32x2 FfxDofPxRadToTiles(FfxFloat32x2 rPx)
{
return FfxUInt32x2(ceil(rPx / (FFX_DOF_DEPTH_TILE_SIZE / 2))) + 1; // +1 to handle diagonals & bilinear
}
FfxFloat32x2 FfxDofGetCoc2(FfxFloat32x2 depths)
{
return FfxFloat32x2(FfxDofGetCoc(depths.x), FfxDofGetCoc(depths.y));
}
#endif // #ifndef FFX_DOF_COMMON_H

View File

@@ -0,0 +1,629 @@
// 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_core.h"
#include "ffx_dof_common.h"
#if FFX_HALF
FfxFloat16 FfxMed9(FfxFloat16 a, FfxFloat16 b, FfxFloat16 c,
FfxFloat16 d, FfxFloat16 e, FfxFloat16 f,
FfxFloat16 g, FfxFloat16 h, FfxFloat16 i)
{
// TODO (perf): compiler does not use med3 at all for these
FfxFloat16 hi_lo = ffxMax3Half(ffxMin3Half(a, b, c), ffxMin3Half(d, e, f), ffxMin3Half(g, h, i));
FfxFloat16 mi_mi = ffxMed3Half(ffxMed3Half(a, b, c), ffxMed3Half(d, e, f), ffxMed3Half(g, h, i));
FfxFloat16 lo_hi = ffxMin3Half(ffxMax3Half(a, b, c), ffxMax3Half(d, e, f), ffxMax3Half(g, h, i));
return ffxMed3Half(hi_lo, mi_mi, lo_hi);
}
#endif
FfxFloat32 FfxMed9(FfxFloat32 a, FfxFloat32 b, FfxFloat32 c,
FfxFloat32 d, FfxFloat32 e, FfxFloat32 f,
FfxFloat32 g, FfxFloat32 h, FfxFloat32 i)
{
// TODO (perf): compiler does not use med3 at all for these
FfxFloat32 hi_lo = ffxMax3(ffxMin3(a, b, c), ffxMin3(d, e, f), ffxMin3(g, h, i));
FfxFloat32 mi_mi = ffxMed3(ffxMed3(a, b, c), ffxMed3(d, e, f), ffxMed3(g, h, i));
FfxFloat32 lo_hi = ffxMin3(ffxMax3(a, b, c), ffxMax3(d, e, f), ffxMax3(g, h, i));
return ffxMed3(hi_lo, mi_mi, lo_hi);
}
FfxFloat32 FfxCubicSpline(FfxFloat32 x)
{
// evaluate cubic spline -2x^3 + 3x^2
return x * x * (3 - 2 * x);
}
FFX_STATIC const FfxUInt32 FFX_DOF_COMBINE_TILE_SIZE = 8;
FFX_STATIC const FfxUInt32 FFX_DOF_COMBINE_ROW_PITCH = FFX_DOF_COMBINE_TILE_SIZE + 3; // Add +2 for 3x3 filter margin, +1 on one side for bilinear filter.
FFX_STATIC const FfxUInt32 FFX_DOF_COMBINE_AREA = FFX_DOF_COMBINE_ROW_PITCH * FFX_DOF_COMBINE_ROW_PITCH;
#if FFX_HALF
FFX_GROUPSHARED FfxUInt32 FfxDofLDSLuma[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxUInt32 FfxDofLDSNearRG[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxUInt32 FfxDofLDSNearBA[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxUInt32 FfxDofLDSFarRG[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxUInt32 FfxDofLDSFarBA[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxUInt32 FfxDofLDSFullColorRG[18*18];
FFX_GROUPSHARED FfxFloat16 FfxDofLDSFullColorB[18*18];
#else // #if FFX_HALF
FFX_GROUPSHARED FfxFloat32 FfxDofLDSNearLuma[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFarLuma[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSNearR[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSNearG[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSNearB[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSNearA[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFarR[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFarG[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFarB[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFarA[FFX_DOF_COMBINE_AREA];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFullColorR[18*18];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFullColorG[18*18];
FFX_GROUPSHARED FfxFloat32 FfxDofLDSFullColorB[18*18];
#endif // #if FFX_HALF #else
#if FFX_HALF
FfxFloat16x4 FfxDofGetIntermediateNearColor(FfxUInt32 idx)
{
FfxFloat16x2 rg = FFX_UINT32_TO_FLOAT16X2(FfxDofLDSNearRG[idx]);
FfxFloat16x2 ba = FFX_UINT32_TO_FLOAT16X2(FfxDofLDSNearBA[idx]);
return FfxFloat16x4(rg, ba);
}
FfxFloat16x4 FfxDofGetIntermediateFarColor(FfxUInt32 idx)
{
FfxFloat16x2 rg = FFX_UINT32_TO_FLOAT16X2(FfxDofLDSFarRG[idx]);
FfxFloat16x2 ba = FFX_UINT32_TO_FLOAT16X2(FfxDofLDSFarBA[idx]);
return FfxFloat16x4(rg, ba);
}
FfxFloat16x3 FfxDofGetIntFullColor(FfxUInt32 idx)
{
FfxFloat16x2 rg = FFX_UINT32_TO_FLOAT16X2(FfxDofLDSFullColorRG[idx]);
FfxFloat16 b = FfxDofLDSFullColorB[idx];
return FfxFloat16x3(rg, b);
}
void FfxDofSetIntNearLuma(FfxUInt32 idx, FfxFloat16 luma)
{
FfxFloat16x2 unpacked = FFX_UINT32_TO_FLOAT16X2(FfxDofLDSLuma[idx]);
unpacked.x = luma;
FfxDofLDSLuma[idx] = FFX_FLOAT16X2_TO_UINT32(unpacked);
}
void FfxDofSetIntFarLuma(FfxUInt32 idx, FfxFloat16 luma)
{
FfxFloat16x2 unpacked = FFX_UINT32_TO_FLOAT16X2(FfxDofLDSLuma[idx]);
unpacked.y = luma;
FfxDofLDSLuma[idx] = FFX_FLOAT16X2_TO_UINT32(unpacked);
}
void FfxDofSetIntermediateNearColor(FfxUInt32 idx, FfxFloat16x4 col)
{
FfxDofLDSNearRG[idx] = FFX_FLOAT16X2_TO_UINT32(col.rg);
FfxDofLDSNearBA[idx] = FFX_FLOAT16X2_TO_UINT32(col.ba);
}
void FfxDofSetIntermediateFarColor(FfxUInt32 idx, FfxFloat16x4 col)
{
FfxDofLDSFarRG[idx] = FFX_FLOAT16X2_TO_UINT32(col.rg);
FfxDofLDSFarBA[idx] = FFX_FLOAT16X2_TO_UINT32(col.ba);
}
void FfxDofSetIntFullColor(FfxUInt32 idx, FfxFloat16x3 col)
{
FfxDofLDSFullColorRG[idx] = FFX_FLOAT16X2_TO_UINT32(col.rg);
FfxDofLDSFullColorB[idx] = col.b;
}
FfxFloat16 FfxDofGetIntermediateNearAlpha(FfxUInt32 idx, FfxUInt32 offX, FfxUInt32 offY)
{
return FfxDofGetIntermediateNearColor(idx + offX + FFX_DOF_COMBINE_ROW_PITCH * offY).a;
}
FfxFloat16 FfxDofGetIntermediateFarAlpha(FfxUInt32 idx)
{
return FfxDofGetIntermediateFarColor(idx).a;
}
#else // #if FFX_HALF
FfxFloat32x4 FfxDofGetIntermediateNearColor(FfxUInt32 idx)
{
return FfxFloat32x4(FfxDofLDSNearR[idx], FfxDofLDSNearG[idx], FfxDofLDSNearB[idx], FfxDofLDSNearA[idx]);
}
FfxFloat32x4 FfxDofGetIntermediateFarColor(FfxUInt32 idx)
{
return FfxFloat32x4(FfxDofLDSFarR[idx], FfxDofLDSFarG[idx], FfxDofLDSFarB[idx], FfxDofLDSFarA[idx]);
}
FfxFloat32x3 FfxDofGetIntFullColor(FfxUInt32 idx)
{
return FfxFloat32x3(FfxDofLDSFullColorR[idx], FfxDofLDSFullColorG[idx], FfxDofLDSFullColorB[idx]);
}
void FfxDofSetIntNearLuma(FfxUInt32 idx, FfxFloat32 luma)
{
FfxDofLDSNearLuma[idx] = luma;
}
void FfxDofSetIntFarLuma(FfxUInt32 idx, FfxFloat32 luma)
{
FfxDofLDSFarLuma[idx] = luma;
}
void FfxDofSetIntermediateNearColor(FfxUInt32 idx, FfxFloat32x4 col)
{
FfxDofLDSNearR[idx] = col.r;
FfxDofLDSNearG[idx] = col.g;
FfxDofLDSNearB[idx] = col.b;
FfxDofLDSNearA[idx] = col.a;
}
void FfxDofSetIntermediateFarColor(FfxUInt32 idx, FfxFloat32x4 col)
{
FfxDofLDSFarR[idx] = col.r;
FfxDofLDSFarG[idx] = col.g;
FfxDofLDSFarB[idx] = col.b;
FfxDofLDSFarA[idx] = col.a;
}
void FfxDofSetIntFullColor(FfxUInt32 idx, FfxFloat32x3 col)
{
FfxDofLDSFullColorR[idx] = col.r;
FfxDofLDSFullColorG[idx] = col.g;
FfxDofLDSFullColorB[idx] = col.b;
}
FfxFloat32 FfxDofGetIntermediateNearAlpha(FfxUInt32 idx, FfxUInt32 offX, FfxUInt32 offY)
{
return FfxDofGetIntermediateNearColor(idx + offX + FFX_DOF_COMBINE_ROW_PITCH * offY).a;
}
FfxFloat32 FfxDofGetIntermediateFarAlpha(FfxUInt32 idx)
{
return FfxDofGetIntermediateFarColor(idx).a;
}
#endif // #if FFX_HALF #else
FfxHalfOpt3 FfxDofBlur3x3(FfxUInt32 baseIdx)
{
// kernel coefficients based on coverage of a circle in a 3x3 grid
const FfxHalfOpt CORNER = FfxHalfOpt(0.5453);
const FfxHalfOpt SIDE = FfxHalfOpt(0.9717);
// accumulate convolution
const FfxHalfOpt weights_sum = FfxHalfOpt(1) + FfxHalfOpt(4) * CORNER + FfxHalfOpt(4) * SIDE;
FfxHalfOpt3 sum = FfxDofGetIntFullColor(baseIdx + 19)
+ CORNER * (FfxDofGetIntFullColor(baseIdx) + FfxDofGetIntFullColor(baseIdx + 2) + FfxDofGetIntFullColor(baseIdx + 36) + FfxDofGetIntFullColor(baseIdx + 38))
+ SIDE * (FfxDofGetIntFullColor(baseIdx + 1) + FfxDofGetIntFullColor(baseIdx + 18) + FfxDofGetIntFullColor(baseIdx + 20) + FfxDofGetIntFullColor(baseIdx + 37));
return sum / weights_sum;
}
FfxHalfOpt4 FfxDofFinalCombineColors(FfxUInt32x2 coord, FfxUInt32x2 relCoord, FfxHalfOpt4 bg, FfxHalfOpt4 fg, FfxHalfOpt minFgW)
{
FfxFloat32 d = FfxDofLoadFullDepth(coord);
FfxUInt32 baseIdx = relCoord.x + 18 * relCoord.y;
FfxHalfOpt3 full = FfxHalfOpt3(FfxDofGetIntFullColor(baseIdx + 19));
FfxHalfOpt3 fixBlurred = FfxHalfOpt3(FfxDofBlur3x3(baseIdx));
// expand background around edges
if (bg.a > FfxHalfOpt(0)) bg.rgb /= bg.a;
// if any FG sample has zero weight, the interpolation is invalid.
if (minFgW == FfxHalfOpt(0)) fg.a = FfxHalfOpt(0);
FfxHalfOpt c = FfxHalfOpt(2) * FfxHalfOpt(abs(FfxDofGetCoc(d))); // double it for full-res pixels
FfxHalfOpt c1 = ffxSaturate(c - FfxHalfOpt(0.5)); // lerp factor for full vs. fixed 1.5px blur
FfxHalfOpt c2 = ffxSaturate(c - FfxHalfOpt(1.5)); // lerp factor for prev vs. quarter res
if (bg.a == FfxHalfOpt(0)) c2 = FfxHalfOpt(0);
FfxHalfOpt3 combinedColor = ffxLerp(full, fixBlurred, c1);
combinedColor = ffxLerp(combinedColor, bg.rgb, c2);
combinedColor = ffxLerp(combinedColor, fg.rgb, FfxHalfOpt(FfxCubicSpline(fg.a)));
return FfxHalfOpt4(combinedColor, 1);
}
#if FFX_HALF
FfxFloat16 FfxDofGetLDSNearLuma(FfxUInt32 idx, FfxUInt32 offX, FfxUInt32 offY)
{
return FFX_UINT32_TO_FLOAT16X2(FfxDofLDSLuma[idx + offX + FFX_DOF_COMBINE_ROW_PITCH * offY]).x;
}
FfxFloat16 FfxDofGetLDSFarLuma(FfxUInt32 idx, FfxUInt32 offX, FfxUInt32 offY)
{
return FFX_UINT32_TO_FLOAT16X2(FfxDofLDSLuma[idx + offX + FFX_DOF_COMBINE_ROW_PITCH * offY]).y;
}
#else // #if FFX_HALF
FfxFloat32 FfxDofGetLDSNearLuma(FfxUInt32 idx, FfxUInt32 offX, FfxUInt32 offY)
{
return FfxDofLDSNearLuma[idx + offX + FFX_DOF_COMBINE_ROW_PITCH * offY];
}
FfxFloat32 FfxDofGetLDSFarLuma(FfxUInt32 idx, FfxUInt32 offX, FfxUInt32 offY)
{
return FfxDofLDSFarLuma[idx + offX + FFX_DOF_COMBINE_ROW_PITCH * offY];
}
#endif // #if FFX_HALF #else
FfxHalfOpt4 FfxDofFilterFF(FfxUInt32 baseIdx)
{
// get the median of the surrounding 3x3 area of luma values
FfxHalfOpt med_luma = FfxMed9(
FfxDofGetLDSFarLuma(baseIdx, 0, 0), FfxDofGetLDSFarLuma(baseIdx, 1, 0), FfxDofGetLDSFarLuma(baseIdx, 2, 0),
FfxDofGetLDSFarLuma(baseIdx, 0, 1), FfxDofGetLDSFarLuma(baseIdx, 1, 1), FfxDofGetLDSFarLuma(baseIdx, 2, 1),
FfxDofGetLDSFarLuma(baseIdx, 0, 2), FfxDofGetLDSFarLuma(baseIdx, 1, 2), FfxDofGetLDSFarLuma(baseIdx, 2, 2));
FfxUInt32 idx = baseIdx + FFX_DOF_COMBINE_ROW_PITCH + 1;
FfxHalfOpt3 col = FfxDofGetIntermediateFarColor(idx).rgb;
FfxHalfOpt lumaFactor = clamp(med_luma / FfxDofGetLDSFarLuma(idx, 0, 0), FfxHalfOpt(0), FfxHalfOpt(2));
// corner fix: if color pixel is on a corner (has 5 black pixels as neighbor), don't reduce color.
if (med_luma == FfxHalfOpt(0)) lumaFactor = FfxHalfOpt(1);
return FfxHalfOpt4(col * lumaFactor, FfxDofGetIntermediateFarAlpha(idx));
}
FfxHalfOpt4 FfxDofFilterNF(FfxUInt32 baseIdx)
{
// Get 3x3 median luma
FfxHalfOpt med_luma = FfxMed9(
FfxDofGetLDSNearLuma(baseIdx, 0, 0), FfxDofGetLDSNearLuma(baseIdx, 1, 0), FfxDofGetLDSNearLuma(baseIdx, 2, 0),
FfxDofGetLDSNearLuma(baseIdx, 0, 1), FfxDofGetLDSNearLuma(baseIdx, 1, 1), FfxDofGetLDSNearLuma(baseIdx, 2, 1),
FfxDofGetLDSNearLuma(baseIdx, 0, 2), FfxDofGetLDSNearLuma(baseIdx, 1, 2), FfxDofGetLDSNearLuma(baseIdx, 2, 2));
FfxHalfOpt avg_alpha = FfxHalfOpt(ffxReciprocal(9.0)) * (
FfxDofGetIntermediateNearAlpha(baseIdx, 0, 0) + FfxDofGetIntermediateNearAlpha(baseIdx, 1, 0) + FfxDofGetIntermediateNearAlpha(baseIdx, 2, 0) +
FfxDofGetIntermediateNearAlpha(baseIdx, 0, 1) + FfxDofGetIntermediateNearAlpha(baseIdx, 1, 1) + FfxDofGetIntermediateNearAlpha(baseIdx, 2, 1) +
FfxDofGetIntermediateNearAlpha(baseIdx, 0, 2) + FfxDofGetIntermediateNearAlpha(baseIdx, 1, 2) + FfxDofGetIntermediateNearAlpha(baseIdx, 2, 2));
FfxUInt32 idx = baseIdx + FFX_DOF_COMBINE_ROW_PITCH + 1;
if (FfxDofGetIntermediateNearAlpha(idx, 0, 0) < 0.01)
{
// center has zero weight, grab one of the corner colors
FfxUInt32 maxIdx = baseIdx;
if (FfxDofGetLDSNearLuma(baseIdx, 2, 0) > FfxDofGetLDSNearLuma(maxIdx, 0, 0)) maxIdx = baseIdx + 2;
if (FfxDofGetLDSNearLuma(baseIdx, 0, 2) > FfxDofGetLDSNearLuma(maxIdx, 0, 0)) maxIdx = baseIdx + 2 * FFX_DOF_COMBINE_ROW_PITCH;
if (FfxDofGetLDSNearLuma(baseIdx, 2, 2) > FfxDofGetLDSNearLuma(maxIdx, 0, 0)) maxIdx = baseIdx + 2 * FFX_DOF_COMBINE_ROW_PITCH + 2;
idx = maxIdx;
}
FfxHalfOpt3 col = FfxDofGetIntermediateNearColor(idx).rgb;
FfxHalfOpt lumaFactor = med_luma > FfxHalfOpt(0) ? clamp(med_luma / FfxDofGetLDSNearLuma(idx, 0, 0), FfxHalfOpt(0), FfxHalfOpt(2)) : FfxHalfOpt(1.0);
return FfxHalfOpt4(col.rgb * lumaFactor, avg_alpha);
}
FfxFloat32x2 FfxDofGetTileRadius(FfxUInt32x2 group)
{
// need to read 4 values
FfxUInt32x2 tile = group * 2;
FfxFloat32x2 a = FfxDofLoadDilatedRadius(tile);
FfxFloat32x2 b = FfxDofLoadDilatedRadius(tile + FfxUInt32x2(0, 1));
FfxFloat32x2 c = FfxDofLoadDilatedRadius(tile + FfxUInt32x2(1, 0));
FfxFloat32x2 d = FfxDofLoadDilatedRadius(tile + FfxUInt32x2(1, 1));
FfxFloat32 near = max(a.x, ffxMax3(b.x, c.x, d.x));
FfxFloat32 far = min(a.y, ffxMin3(b.y, c.y, d.y));
return FfxFloat32x2(near, far);
}
void FfxDofCombineSharpOnly(FfxUInt32x2 group, FfxUInt32x2 thread)
{
#if !defined(FFX_DOF_OPTION_COMBINE_IN_PLACE) || !FFX_DOF_OPTION_COMBINE_IN_PLACE
FfxUInt32x2 base = 16 * group;
FfxDofStoreOutput(base + thread + FfxUInt32x2(0, 0), FfxDofLoadFullInput(base + thread + FfxUInt32x2(0, 0)));
FfxDofStoreOutput(base + thread + FfxUInt32x2(8, 0), FfxDofLoadFullInput(base + thread + FfxUInt32x2(8, 0)));
FfxDofStoreOutput(base + thread + FfxUInt32x2(0, 8), FfxDofLoadFullInput(base + thread + FfxUInt32x2(0, 8)));
FfxDofStoreOutput(base + thread + FfxUInt32x2(8, 8), FfxDofLoadFullInput(base + thread + FfxUInt32x2(8, 8)));
#endif
}
void FfxDofFetchFullColor(FfxUInt32x2 gid, FfxUInt32 gix, FfxUInt32x2 imageSize)
{
FFX_DOF_UNROLL
for (FfxUInt32 iter = 0; iter < 6; iter++)
{
FfxUInt32 iFetch = (gix + iter * 64) % (18 * 18);
FfxInt32x2 coord = FfxInt32x2(gid * 16) + FfxInt32x2(iFetch % 18 - 1, iFetch / 18 - 1);
coord = clamp(coord, FfxInt32x2(0, 0), FfxInt32x2(imageSize) - FfxInt32x2(1, 1));
FfxHalfOpt3 color = FfxHalfOpt3(FfxDofLoadFullInput(coord).rgb);
FfxDofSetIntFullColor(iFetch, color);
}
}
void FfxDofSwizQuad(inout FfxUInt32x2 a, inout FfxUInt32x2 b, inout FfxUInt32x2 c, inout FfxUInt32x2 d)
{
// Input: four color values in a quad.
// Re-orders the output to a swizzled format for better store throughput.
// This maps from one quad per lane, stored in four separate registers to
// four 16x2 regions (one per register).
// This is done in two steps. First, permute the values among the lanes
// using WaveReadLaneAt. Second, swap values between registers.
// This only works for lane counts >= 32, do nothing otherwise for compatibility
#if FFX_HLSL
if (WaveGetLaneCount() < 32) return;
FfxUInt32 lane = WaveGetLaneIndex();
// index for A, switch bits around 43210 -> 10432.
FfxUInt32 idxA = ((lane & 3) << 3) + (lane >> 2);
// Adding 8/16/24 for B/C/D makes each variable offset from the previous by one slot.
a = WaveReadLaneAt(a, (lane & ~31) + (idxA + 0) % 32);
b = WaveReadLaneAt(b, (lane & ~31) + (idxA + 8) % 32);
c = WaveReadLaneAt(c, (lane & ~31) + (idxA + 16) % 32);
d = WaveReadLaneAt(d, (lane & ~31) + (idxA + 24) % 32);
#elif FFX_GLSL
if (gl_SubgroupSize < 32) return;
FfxUInt32 lane = gl_SubgroupInvocationID;
FfxUInt32 idxA = ((lane & 3) << 3) + (lane >> 2);
a = subgroupShuffle(a, (lane & ~31) + (idxA + 0) % 32);
b = subgroupShuffle(b, (lane & ~31) + (idxA + 8) % 32);
c = subgroupShuffle(c, (lane & ~31) + (idxA + 16) % 32);
d = subgroupShuffle(d, (lane & ~31) + (idxA + 24) % 32);
#endif
// Now, for each lane, a/b/c/d contain one value from each of the four 16x2 lines.
// And each group of 4 lanes have values from the same quads.
// We just need to shuffle between abcd, so that each set of 4 lanes contains one quad per variable.
// General idea: rotate by (lane % 4) variables.
if ((lane & 1) != 0)
{
// rotate A->B->C->D->A
FfxUInt32x2 tmp = d;
d = c;
c = b;
b = a;
a = tmp;
}
if ((lane & 2) != 0)
{
// swap A<->C and B<->D
FfxUInt32x2 tmp = a;
a = c;
c = tmp;
tmp = b;
b = d;
d = tmp;
}
}
#if FFX_HALF
void FfxDofSwizQuad(inout FfxFloat16x4 a, inout FfxFloat16x4 b, inout FfxFloat16x4 c, inout FfxFloat16x4 d)
{
// Same as above.
FfxUInt32x2 packed_a = FFX_FLOAT16X4_TO_UINT32X2(a);
FfxUInt32x2 packed_b = FFX_FLOAT16X4_TO_UINT32X2(b);
FfxUInt32x2 packed_c = FFX_FLOAT16X4_TO_UINT32X2(c);
FfxUInt32x2 packed_d = FFX_FLOAT16X4_TO_UINT32X2(d);
FfxDofSwizQuad(packed_a, packed_b, packed_c, packed_d);
a = FFX_UINT32X2_TO_FLOAT16X4(packed_a);
b = FFX_UINT32X2_TO_FLOAT16X4(packed_b);
c = FFX_UINT32X2_TO_FLOAT16X4(packed_c);
d = FFX_UINT32X2_TO_FLOAT16X4(packed_d);
}
#else // #if FFX_HALF
void FfxDofSwizQuad(inout FfxFloat32x4 a, inout FfxFloat32x4 b, inout FfxFloat32x4 c, inout FfxFloat32x4 d)
{
// Same as above.
FfxUInt32x2 a0 = ffxAsUInt32(a.xy);
FfxUInt32x2 a1 = ffxAsUInt32(a.zw);
FfxUInt32x2 b0 = ffxAsUInt32(b.xy);
FfxUInt32x2 b1 = ffxAsUInt32(b.zw);
FfxUInt32x2 c0 = ffxAsUInt32(c.xy);
FfxUInt32x2 c1 = ffxAsUInt32(c.zw);
FfxUInt32x2 d0 = ffxAsUInt32(d.xy);
FfxUInt32x2 d1 = ffxAsUInt32(d.zw);
FfxDofSwizQuad(a0, b0, c0, d0);
FfxDofSwizQuad(a1, b1, c1, d1);
a = FfxFloat32x4(ffxAsFloat(a0), ffxAsFloat(a1));
b = FfxFloat32x4(ffxAsFloat(b0), ffxAsFloat(b1));
c = FfxFloat32x4(ffxAsFloat(c0), ffxAsFloat(c1));
d = FfxFloat32x4(ffxAsFloat(d0), ffxAsFloat(d1));
}
#endif // #if FFX_HALF #else
void FfxDofCombineFarOnly(FfxUInt32x2 id, FfxUInt32x2 gtID, FfxUInt32x2 gid, FfxUInt32 gix, FfxUInt32x2 imageSize)
{
// TODO: Is this the best configuration for fetching?
FFX_DOF_UNROLL_N(2)
for (FfxUInt32 iter = 0; iter < 2; iter++)
{
// HACK: with the modulo, we will re-fetch some pixels, but might be better than waiting twice (latency-wise)
// which is what the compiler would do if it had to possibly branch
FfxUInt32 iFetch = (gix + iter * 64) % FFX_DOF_COMBINE_AREA;
FfxInt32x2 coord = FfxInt32x2(gid * FFX_DOF_COMBINE_TILE_SIZE) + FfxInt32x2(iFetch % FFX_DOF_COMBINE_ROW_PITCH - 1, iFetch / FFX_DOF_COMBINE_ROW_PITCH - 1);
coord = clamp(coord, FfxInt32x2(0, 0), FfxInt32x2(imageSize - 1));
FfxHalfOpt4 ffColor = FfxHalfOpt4(FfxDofLoadFar(coord));
// calculate and store luma for later median calculation
FfxHalfOpt ffLuma = FfxHalfOpt(0.2126) * ffColor.r + FfxHalfOpt(0.7152) * ffColor.g + FfxHalfOpt(0.0722) * ffColor.b;
FfxDofSetIntFarLuma(iFetch, ffLuma);
FfxDofSetIntermediateFarColor(iFetch, ffColor);
}
FFX_GROUP_MEMORY_BARRIER;
const FfxUInt32 baseIdx = gtID.x + gtID.y * FFX_DOF_COMBINE_ROW_PITCH;
// one extra round of filtering needs to be done around the edge, this index maps to that.
// TODO: This is ugly and possibly slow
const FfxUInt32 baseIdx2 = (FFX_DOF_COMBINE_TILE_SIZE + FFX_DOF_COMBINE_ROW_PITCH * gix + (gix / (FFX_DOF_COMBINE_TILE_SIZE + 1)) * ((gix - FFX_DOF_COMBINE_TILE_SIZE) * (-FFX_DOF_COMBINE_ROW_PITCH + 1) - (FFX_DOF_COMBINE_TILE_SIZE + 1))) % FFX_DOF_COMBINE_AREA;
FfxHalfOpt4 ffColor = FfxHalfOpt4(0, 0, 0, 0), ffColor2 = FfxHalfOpt4(0, 0, 0, 0);
// far-field post-filter
ffColor = FfxDofFilterFF(baseIdx);
ffColor2 = gix < (2 * FFX_DOF_COMBINE_TILE_SIZE + 1) ? FfxDofFilterFF(baseIdx2) : FfxHalfOpt4(0, 0, 0, 0);
FFX_GROUP_MEMORY_BARRIER;
// write out colors for interpolation
FfxDofSetIntermediateFarColor(baseIdx, ffColor);
if (gix < (2 * FFX_DOF_COMBINE_TILE_SIZE + 1))
{
FfxDofSetIntermediateFarColor(baseIdx2, ffColor2);
}
FFX_GROUP_MEMORY_BARRIER;
// upscaling
FfxHalfOpt4 ffTR = FfxHalfOpt4(0, 0, 0, 0), ffBL = FfxHalfOpt4(0, 0, 0, 0), ffBR = FfxHalfOpt4(0, 0, 0, 0);
ffTR = FfxHalfOpt(0.5) * ffColor + FfxHalfOpt(0.5) * FfxDofGetIntermediateFarColor(baseIdx + 1);
ffBL = FfxHalfOpt(0.5) * ffColor + FfxHalfOpt(0.5) * FfxDofGetIntermediateFarColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH);
ffBR = FfxHalfOpt(0.5) * ffTR + FfxHalfOpt(0.25) * FfxDofGetIntermediateFarColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH) + FfxHalfOpt(0.25) * FfxDofGetIntermediateFarColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH + 1);
// top-left pixel
FfxUInt32x2 coord = 2 * id;
FfxUInt32x2 relCoord = 2 * gtID;
FfxUInt32x2 coordA = coord;
FfxHalfOpt4 colA = FfxDofFinalCombineColors(coord, relCoord, ffColor, FfxHalfOpt4(0, 0, 0, 0), FfxHalfOpt(0));
// top-right
coord.x++;
relCoord.x++;
FfxUInt32x2 coordB = coord;
FfxHalfOpt4 colB = FfxDofFinalCombineColors(coord, relCoord, ffTR, FfxHalfOpt4(0, 0, 0, 0), FfxHalfOpt(0));
// bottom-right
coord.y++;
relCoord.y++;
FfxUInt32x2 coordC = coord;
FfxHalfOpt4 colC = FfxDofFinalCombineColors(coord, relCoord, ffBR, FfxHalfOpt4(0, 0, 0, 0), FfxHalfOpt(0));
// bottom-left
coord.x--;
relCoord.x--;
FfxUInt32x2 coordD = coord;
FfxHalfOpt4 colD = FfxDofFinalCombineColors(coord, relCoord, ffBL, FfxHalfOpt4(0, 0, 0, 0), FfxHalfOpt(0));
// TODO: Navi3 should make swizzling unnecessary because it supports write-combining clauses
FfxDofSwizQuad(colA, colB, colC, colD);
FfxDofSwizQuad(coordA, coordB, coordC, coordD);
FfxDofStoreOutput(coordA, colA);
FfxDofStoreOutput(coordB, colB);
FfxDofStoreOutput(coordC, colC);
FfxDofStoreOutput(coordD, colD);
}
void FfxDofCombineAll(FfxUInt32x2 id, FfxUInt32x2 gtID, FfxUInt32x2 gid, FfxUInt32 gix, FfxUInt32x2 imageSize)
{
// TODO: Is this the best configuration for fetching?
FFX_DOF_UNROLL_N(2)
for (FfxUInt32 iter = 0; iter < 2; iter++)
{
// HACK: with the modulo, we will re-fetch some pixels, but might be better than waiting twice (latency-wise)
// which is what the compiler would do if it had to possibly branch
FfxUInt32 iFetch = (gix + iter * 64) % FFX_DOF_COMBINE_AREA;
FfxInt32x2 coord = FfxInt32x2(gid * FFX_DOF_COMBINE_TILE_SIZE) + FfxInt32x2(iFetch % FFX_DOF_COMBINE_ROW_PITCH - 1, iFetch / FFX_DOF_COMBINE_ROW_PITCH - 1);
coord = clamp(coord, FfxInt32x2(0, 0), FfxInt32x2(imageSize - 1));
FfxHalfOpt4 ffColor = FfxHalfOpt4(FfxDofLoadFar(coord));
FfxHalfOpt4 nfColor = FfxHalfOpt4(FfxDofLoadNear(coord));
// calculate and store luma for later median calculation
FfxHalfOpt ffLuma = FfxHalfOpt(0.2126) * ffColor.r + FfxHalfOpt(0.7152) * ffColor.g + FfxHalfOpt(0.0722) * ffColor.b;
FfxHalfOpt nfLuma = FfxHalfOpt(0.2126) * nfColor.r + FfxHalfOpt(0.7152) * nfColor.g + FfxHalfOpt(0.0722) * nfColor.b;
FfxDofSetIntFarLuma(iFetch, ffLuma);
FfxDofSetIntNearLuma(iFetch, nfLuma);
FfxDofSetIntermediateFarColor(iFetch, ffColor);
FfxDofSetIntermediateNearColor(iFetch, nfColor);
}
FFX_GROUP_MEMORY_BARRIER;
const FfxUInt32 baseIdx = gtID.x + gtID.y * FFX_DOF_COMBINE_ROW_PITCH;
// one extra round of filtering needs to be done around the edge, this index maps to that.
// TODO: same as above, ugly and slow.
const FfxUInt32 baseIdx2 = (FFX_DOF_COMBINE_TILE_SIZE + FFX_DOF_COMBINE_ROW_PITCH * gix + (gix / (FFX_DOF_COMBINE_TILE_SIZE + 1)) * ((gix - FFX_DOF_COMBINE_TILE_SIZE) * (-FFX_DOF_COMBINE_ROW_PITCH + 1) - (FFX_DOF_COMBINE_TILE_SIZE + 1))) % FFX_DOF_COMBINE_AREA;
FfxHalfOpt4 ffColor = FfxHalfOpt4(0, 0, 0, 0), ffColor2 = FfxHalfOpt4(0, 0, 0, 0), nfColor = FfxHalfOpt4(0, 0, 0, 0), nfColor2 = FfxHalfOpt4(0, 0, 0, 0);
// far-field post-filter
ffColor = FfxDofFilterFF(baseIdx);
ffColor2 = gix < (2 * FFX_DOF_COMBINE_TILE_SIZE + 1) ? FfxDofFilterFF(baseIdx2) : FfxHalfOpt4(0, 0, 0, 0);
// near-field post-filter
nfColor = FfxDofFilterNF(baseIdx);
nfColor2 = gix < (2 * FFX_DOF_COMBINE_TILE_SIZE + 1) ? FfxDofFilterNF(baseIdx2) : FfxHalfOpt4(0, 0, 0, 0);
FFX_GROUP_MEMORY_BARRIER;
// write out colors for interpolation
FfxDofSetIntermediateNearColor(baseIdx, nfColor);
FfxDofSetIntermediateFarColor(baseIdx, ffColor);
if (gix < (2 * FFX_DOF_COMBINE_TILE_SIZE + 1))
{
FfxDofSetIntermediateNearColor(baseIdx2, nfColor2);
FfxDofSetIntermediateFarColor(baseIdx2, ffColor2);
}
FFX_GROUP_MEMORY_BARRIER;
// if any FG sample has zero weight, the interpolation is invalid.
// take the min and invalidate if zero (see CombineColors)
FfxHalfOpt fgMinW = min(nfColor.a, FfxHalfOpt(ffxMin3(FfxDofGetIntermediateNearAlpha(baseIdx, 1, 0), FfxDofGetIntermediateNearAlpha(baseIdx, 0, 1), FfxDofGetIntermediateNearAlpha(baseIdx, 1, 1))));
// upscaling
FfxHalfOpt4 nfTR = FfxHalfOpt4(0, 0, 0, 0), nfBL = FfxHalfOpt4(0, 0, 0, 0), nfBR = FfxHalfOpt4(0, 0, 0, 0);
nfTR = FfxHalfOpt(0.5) * nfColor + FfxHalfOpt(0.5) * FfxDofGetIntermediateNearColor(baseIdx + 1);
nfBL = FfxHalfOpt(0.5) * nfColor + FfxHalfOpt(0.5) * FfxDofGetIntermediateNearColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH);
nfBR = FfxHalfOpt(0.5) * nfTR + FfxHalfOpt(0.25) * FfxDofGetIntermediateNearColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH) + FfxHalfOpt(0.25) * FfxDofGetIntermediateNearColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH + 1);
FfxHalfOpt4 ffTR = FfxHalfOpt4(0, 0, 0, 0), ffBL = FfxHalfOpt4(0, 0, 0, 0), ffBR = FfxHalfOpt4(0, 0, 0, 0);
ffTR = FfxHalfOpt(0.5) * ffColor + FfxHalfOpt(0.5) * FfxDofGetIntermediateFarColor(baseIdx + 1);
ffBL = FfxHalfOpt(0.5) * ffColor + FfxHalfOpt(0.5) * FfxDofGetIntermediateFarColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH);
ffBR = FfxHalfOpt(0.5) * ffTR + FfxHalfOpt(0.25) * FfxDofGetIntermediateFarColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH) + FfxHalfOpt(0.25) * FfxDofGetIntermediateFarColor(baseIdx + FFX_DOF_COMBINE_ROW_PITCH + 1);
// top-left pixel
FfxUInt32x2 coord = 2 * id;
FfxUInt32x2 relCoord = 2 * gtID;
FfxUInt32x2 coordA = coord;
FfxHalfOpt4 colA = FfxDofFinalCombineColors(coord, relCoord, ffColor, nfColor, fgMinW);
// top-right
coord.x += 1;
relCoord.x += 1;
FfxUInt32x2 coordB = coord;
FfxHalfOpt4 colB = FfxDofFinalCombineColors(coord, relCoord, ffTR, nfTR, fgMinW);
// bottom-right
coord.y++;
relCoord.y++;
FfxUInt32x2 coordC = coord;
FfxHalfOpt4 colC = FfxDofFinalCombineColors(coord, relCoord, ffBR, nfBR, fgMinW);
// bottom-left
coord.x--;
relCoord.x--;
FfxUInt32x2 coordD = coord;
FfxHalfOpt4 colD = FfxDofFinalCombineColors(coord, relCoord, ffBL, nfBL, fgMinW);
FfxDofSwizQuad(colA, colB, colC, colD);
FfxDofSwizQuad(coordA, coordB, coordC, coordD);
FfxDofStoreOutput(coordA, colA);
FfxDofStoreOutput(coordB, colB);
FfxDofStoreOutput(coordC, colC);
FfxDofStoreOutput(coordD, colD);
}
/// Entry point. Meant to run in 8x8 threads and writes 16x16 output pixels.
///
/// @param threadID SV_DispatchThreadID.xy
/// @param groupThreadID SV_GroupThreadID.xy
/// @param group SV_GroupID.xy
/// @param index SV_GroupIndex
/// @param halfImageSize Pixel size of the input (half resolution)
/// @param fullImageSize Pixel size of the output (full resolution)
/// @ingroup FfxGPUDof
void FfxDofCombineHalfRes(FfxUInt32x2 threadID, FfxUInt32x2 groupThreadID, FfxUInt32x2 group, FfxUInt32 index, FfxUInt32x2 halfImageSize, FfxUInt32x2 fullImageSize)
{
// classify tile
FfxFloat32x2 tileCoc = FfxDofGetTileRadius(group);
FfxBoolean nearNeeded = tileCoc.x > -1.025; // halved due to resolution change, then: 2px = threshold in main pass + small inaccuracy bias
FfxBoolean allSharp = max(abs(tileCoc.x), abs(tileCoc.y)) < 0.25;
if (allSharp)
{
FfxDofCombineSharpOnly(group, groupThreadID);
}
else if (!nearNeeded)
{
FfxDofFetchFullColor(group, index, fullImageSize);
FfxDofCombineFarOnly(threadID, groupThreadID, group, index, halfImageSize);
}
else
{
FfxDofFetchFullColor(group, index, fullImageSize);
FfxDofCombineAll(threadID, groupThreadID, group, index, halfImageSize);
}
}

View File

@@ -0,0 +1,101 @@
// 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_core.h"
#include "ffx_dof_common.h"
// helper function for dilating depth values circularly
void FfxDofDilateStep(inout FfxFloat32 cMin, inout FfxFloat32 cMax, FfxInt32 x, FfxInt32 y, FfxInt32 r, FfxUInt32x2 size, FfxUInt32x2 dtID)
{
if (x * x + y * y <= r * r)
{
// inside the circle
FfxInt32x2 tileID = FfxInt32x2(dtID) + FfxInt32x2(x, y);
if (tileID.x >= 0 && tileID.y >= 0 && tileID.x < size.x && tileID.y < size.y)
{
FfxFloat32x2 localCocRange = FfxDofLoadTileRadius(tileID);
FfxUInt32x2 tileRad = FfxDofPxRadToTiles(abs(localCocRange));
// seperately for min/max: check radius in range to spread and update
if (x * x + y * y < tileRad.x * tileRad.x)
{
// using max for cMin and min for cMax, since cMin actually refers to the CoC for the minimal view depth
// NOT the minimum signed CoC value
cMin = max(cMin, localCocRange.x);
}
if (x * x + y * y < tileRad.y * tileRad.y)
{
cMax = min(cMax, localCocRange.y);
}
}
}
}
/// Entry point for the dilate pass.
///
/// @param tile Coordinate of the tile to run on (SV_DispatchThreadID)
/// @param imageSize Resolution of the depth image (full resolution)
/// @ingroup FfxGPUDof
void FfxDofDilate(FfxUInt32x2 tile, FfxUInt32x2 imageSize)
{
// dilate scatter-as-gather using global max radius
// reject out-of-bounds on border
FfxUInt32x2 size = FfxUInt32x2(ceil(FfxFloat32x2(imageSize) / FfxFloat32(FFX_DOF_DEPTH_TILE_SIZE)));
// get CoC and depth
FfxInt32 rMax = FfxInt32(FfxDofGetMaxTileRadius());
FfxFloat32x2 cocMinMax = FfxDofLoadTileRadius(tile);
FfxFloat32x2 absCocMinMax = abs(cocMinMax);
FfxFloat32 cMin = cocMinMax.x;
FfxFloat32 cMax = cocMinMax.y;
// Very extremes of the kernel done explicity. Expanding the square (loop below) to this radius would
// waste time with a lot of failed radius checks.
if (rMax > 0)
{
FfxDofDilateStep(cMin, cMax, -rMax, 0, rMax, size, tile);
FfxDofDilateStep(cMin, cMax, rMax, 0, rMax, size, tile);
FfxDofDilateStep(cMin, cMax, 0, -rMax, rMax, size, tile);
FfxDofDilateStep(cMin, cMax, 0, rMax, rMax, size, tile);
}
// Gather the rest as a square shape. Likely faster than trying to make a circle.
for (FfxInt32 x = -rMax + 1; x < rMax; x++)
{
for (FfxInt32 y = -rMax + 1; y < rMax; y++)
{
// Zero offset is the starting point, don't need to handle again.
if (x == 0 && y == 0) continue;
FfxDofDilateStep(cMin, cMax, x, y, rMax, size, tile);
}
}
// If center tile is in-focus enough: ignore far-field dilation (it is occluded).
if (absCocMinMax.x < 0.5 && absCocMinMax.y < 0.5)
{
cMax = cocMinMax.y;
}
// store min and max
FfxDofStoreDilatedRadius(tile, FfxFloat32x2(cMin, cMax));
}

View File

@@ -0,0 +1,176 @@
// 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_core.h"
#ifndef FFX_DOF_OPTION_MAX_MIP
#define FFX_DOF_OPTION_MAX_MIP FFX_DOF_INTERNAL_BILAT_MIP_COUNT
#endif
#if FFX_HALF
#define FFX_SPD_PACKED_ONLY
FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16];
FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16];
FfxFloat16x4 SpdLoadSourceImageH(FfxInt32x2 tex, FfxUInt32 slice)
{
FfxFloat16 d = FfxFloat16(FfxDofLoadFullDepth(tex));
FfxFloat16 c = FfxFloat16(FfxDofGetCoc(d));
return FfxFloat16x4(FfxDofLoadSource(tex).rgb, c);
}
FfxFloat16x4 SpdLoadH(FfxInt32x2 tex, FfxUInt32 slice)
{
return FfxFloat16x4(0, 0, 0, 0);
}
void SpdStoreH(FfxInt32x2 pix, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice)
{
FfxDofStoreBilatMip(mip, pix, value);
}
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;
}
/// Bilateral downsampling function, half-precision version
/// @ingroup FfxGPUDof
FfxFloat16x4 FfxDofDownsampleQuadH(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3)
{
FfxFloat16 c0 = v0.a;
FfxFloat16 c1 = v1.a;
FfxFloat16 c2 = v2.a;
FfxFloat16 c3 = v3.a;
FfxFloat16 w1 = ffxSaturate(FfxFloat16(1.0) - abs(c0 - c1));
FfxFloat16 w2 = ffxSaturate(FfxFloat16(1.0) - abs(c0 - c2));
FfxFloat16 w3 = ffxSaturate(FfxFloat16(1.0) - abs(c0 - c3));
FfxFloat16x3 color = v0.rgb + w1 * v1.rgb + w2 * v2.rgb + w3 * v3.rgb;
FfxFloat16 weights = FfxFloat16(1.0) + w1 + w2 + w3;
return FfxFloat16x4(color / weights, c0);
}
FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3)
{
return FfxDofDownsampleQuadH(v0, v1, v2, v3);
}
#else // #if FFX_HALF
#define FFX_SPD_NO_WAVE_OPERATIONS 1
FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16];
FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16];
FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16];
FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16];
FfxFloat32x4 SpdLoadSourceImage(FfxInt32x2 tex, FfxUInt32 slice)
{
FfxFloat32 d = FfxDofLoadFullDepth(tex);
FfxFloat32 c = FfxDofGetCoc(d);
return FfxFloat32x4(FfxDofLoadSource(tex).rgb, c);
}
FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice)
{
return FfxFloat32x4(0, 0, 0, 0);
}
void SpdStore(FfxInt32x2 pix, FfxFloat32x4 value, FfxUInt32 mip, FfxUInt32 slice)
{
FfxDofStoreBilatMip(mip, pix, value);
}
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.r;
spdIntermediateG[x][y] = value.g;
spdIntermediateB[x][y] = value.b;
spdIntermediateA[x][y] = value.a;
}
/// Bilateral downsampling function, full-precision version
/// @ingroup FfxGPUDof
FfxFloat32x4 FfxDofDownsampleQuad(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3)
{
FfxFloat32 c0 = v0.a;
FfxFloat32 c1 = v1.a;
FfxFloat32 c2 = v2.a;
FfxFloat32 c3 = v3.a;
FfxFloat32 w1 = ffxSaturate(1.0 - abs(c0 - c1));
FfxFloat32 w2 = ffxSaturate(1.0 - abs(c0 - c2));
FfxFloat32 w3 = ffxSaturate(1.0 - abs(c0 - c3));
FfxFloat32x3 color = v0.rgb + w1 * v1.rgb + w2 * v2.rgb + w3 * v3.rgb;
FfxFloat32 weights = 1.0 + w1 + w2 + w3;
return FfxFloat32x4(color / weights, c0);
}
FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3)
{
return FfxDofDownsampleQuad(v0, v1, v2, v3);
}
#endif // #if FFX_HALF #else
// we only generate 4 mips, so these functions are not needed and never called.
void SpdIncreaseAtomicCounter(FfxUInt32 slice)
{
/*nothing*/
}
FfxUInt32 SpdGetAtomicCounter()
{
return 0;
}
void SpdResetAtomicCounter(FfxUInt32 slice)
{
/*nothing*/
}
#include "spd/ffx_spd.h"
/// Entry point for the downsample color pass. Uses SPD internally.
///
/// @param LocalThreadId Thread index in thread group (SV_GroupIndex)
/// @param WorkGroupId Coordinate of the tile (SV_GroupID.xy)
/// @ingroup FfxGPUDof
void DownsampleColor(FfxUInt32 LocalThreadId, FfxUInt32x2 WorkGroupId)
{
#if FFX_HALF
SpdDownsampleH(WorkGroupId, LocalThreadId, FfxUInt32(FFX_DOF_OPTION_MAX_MIP), FfxUInt32(0), FfxUInt32(0));
#else
SpdDownsample(WorkGroupId, LocalThreadId, FfxUInt32(FFX_DOF_OPTION_MAX_MIP), FfxUInt32(0), FfxUInt32(0));
#endif
}

View File

@@ -0,0 +1,77 @@
// 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_core.h"
#include "ffx_dof_common.h"
FfxUInt32x2 FfxDofCocRadInTiles(FfxFloat32x2 zMinMax)
{
FfxFloat32x2 rPx = abs(FfxDofGetCoc2(zMinMax));
return FfxDofPxRadToTiles(rPx);
}
FfxUInt32 FfxDofMaxCocInTiles(FfxFloat32x2 zMinMax)
{
FfxUInt32x2 rTiles = FfxDofCocRadInTiles(zMinMax);
return max(rTiles.x, rTiles.y);
}
/// Entry point for depth downsample function. SPD is not used for this,
/// since we only need one specific downsampled resolution.
///
/// @param tile coordinate of the tile to run on (SV_DispatchThreadID)
/// @param imageSize Size of the depth image (full resolution)
/// @ingroup FfxGPUDof
void DownsampleDepth(FfxUInt32x2 tile, FfxUInt32x2 imageSize)
{
FfxFloat32 minD = 1.0;
FfxFloat32 maxD = 0.0;
const FfxUInt32x2 coordBase = tile * FFX_DOF_DEPTH_TILE_SIZE;
const FfxFloat32x2 rcpImageSize = ffxReciprocal(FfxFloat32x2(imageSize));
for (FfxUInt32 yy = 0; yy < FFX_DOF_DEPTH_TILE_SIZE; yy += 2)
{
for (FfxUInt32 xx = 0; xx < FFX_DOF_DEPTH_TILE_SIZE; xx += 2)
{
FfxUInt32x2 coordInt = coordBase + FfxUInt32x2(xx, yy);
FfxFloat32x2 coord = ffxSaturate(FfxFloat32x2(coordInt) * rcpImageSize);
FfxFloat32x4 d = FfxDofGatherDepth(coord);
FfxFloat32 lo = min(min(min(d.x, d.y), d.z), d.w);
FfxFloat32 hi = max(max(max(d.x, d.y), d.z), d.w);
minD = min(minD, lo);
maxD = max(maxD, hi);
}
}
#if FFX_DOF_OPTION_REVERSE_DEPTH
// if Z-buffer is reversed, the nearest Z is the max.
FfxFloat32x2 nearFarDepth = FfxFloat32x2(maxD, minD);
#else
FfxFloat32x2 nearFarDepth = FfxFloat32x2(minD, maxD);
#endif
FfxFloat32x2 coc = FfxDofGetCoc2(nearFarDepth);
FfxUInt32 rTiles = FfxDofMaxCocInTiles(nearFarDepth);
FfxDofAccumMaxTileRadius(rTiles);
FfxDofStoreTileRadius(tile, coc);
}

View File

@@ -0,0 +1,50 @@
// 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_DOF_RESOURCES_H
#define FFX_DOF_RESOURCES_H
#if defined(FFX_CPU) || defined(FFX_GPU)
#define FFX_DOF_RESOURCE_IDENTIFIER_NULL 0
#define FFX_DOF_RESOURCE_IDENTIFIER_INPUT_DEPTH 1
#define FFX_DOF_RESOURCE_IDENTIFIER_INPUT_COLOR 2
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_BILAT_COLOR 3 // same as MIP0
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_BILAT_COLOR_MIP0 3
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_BILAT_COLOR_MIP1 4
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_BILAT_COLOR_MIP2 5
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_BILAT_COLOR_MIP3 6
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_RADIUS 7
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_DILATED_RADIUS 8
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_NEAR 9
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_FAR 10
#define FFX_DOF_RESOURCE_IDENTIFIER_OUTPUT_COLOR 11
#define FFX_DOF_RESOURCE_IDENTIFIER_INTERNAL_GLOBALS 12
#define FFX_DOF_RESOURCE_IDENTIFIER_COUNT 13
#define FFX_DOF_CONSTANTBUFFER_IDENTIFIER_DOF 0
#define FFX_DOF_INTERNAL_BILAT_MIP_COUNT 4
#endif // #if defined(FFX_CPU) || defined(FFX_GPU)
#endif //!defined( FFX_DOF_RESOURCES_H )