16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 17:29:18 +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,52 @@
# 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(DENOISER_BASE_ARGS
-reflection -deps=gcc -DFFX_GPU=1)
set(DENOISER_PERMUTATION_ARGS
-DFFX_DENOISER_OPTION_INVERTED_DEPTH={0,1})
set(DENOISER_INCLUDE_ARGS
"${FFX_GPU_PATH}"
"${FFX_GPU_PATH}/denoiser")
if (NOT DENOISER_SHADER_EXT)
set(DENOISER_SHADER_EXT *)
endif()
file(GLOB DENOISER_SHADERS
"shaders/denoiser/ffx_denoiser_prepare_shadow_mask_pass.${DENOISER_SHADER_EXT}"
"shaders/denoiser/ffx_denoiser_shadows_tile_classification_pass.${DENOISER_SHADER_EXT}"
"shaders/denoiser/ffx_denoiser_filter_soft_shadows_0_pass.${DENOISER_SHADER_EXT}"
"shaders/denoiser/ffx_denoiser_filter_soft_shadows_1_pass.${DENOISER_SHADER_EXT}"
"shaders/denoiser/ffx_denoiser_filter_soft_shadows_2_pass.${DENOISER_SHADER_EXT}"
"shaders/denoiser/ffx_denoiser_prefilter_reflections_pass.${DENOISER_SHADER_EXT}"
"shaders/denoiser/ffx_denoiser_reproject_reflections_pass.${DENOISER_SHADER_EXT}"
"shaders/denoiser/ffx_denoiser_resolve_temporal_reflections_pass.${DENOISER_SHADER_EXT}")
compile_shaders_with_depfile(
"${FFX_SC_EXECUTABLE}"
"${DENOISER_BASE_ARGS}" "${DENOISER_API_BASE_ARGS}" "${DENOISER_PERMUTATION_ARGS}" "${DENOISER_INCLUDE_ARGS}"
"${DENOISER_SHADERS}" "${FFX_PASS_SHADER_OUTPUT_PATH}" DENOISER_PERMUTATION_OUTPUTS)
add_shader_output("${DENOISER_PERMUTATION_OUTPUTS}")

View File

@@ -0,0 +1,642 @@
// 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_denoiser_resources.h"
#if defined(FFX_GPU)
#include "ffx_core.h"
#ifndef FFX_PREFER_WAVE64
#define FFX_PREFER_WAVE64
#endif // #ifndef FFX_PREFER_WAVE64
#if defined(DENOISER_BIND_CB_DENOISER)
layout (set = 0, binding = DENOISER_BIND_CB_DENOISER, std140) uniform cbDenoiserReflections_t
{
FfxFloat32Mat4 invProjection;
FfxFloat32Mat4 invView;
FfxFloat32Mat4 prevViewProjection;
FfxUInt32x2 renderSize;
FfxFloat32x2 inverseRenderSize;
FfxFloat32x2 motionVectorScale;
FfxFloat32 normalsUnpackMul;
FfxFloat32 normalsUnpackAdd;
FfxBoolean isRoughnessPerceptual;
FfxFloat32 temporalStabilityFactor;
FfxFloat32 roughnessThreshold;
} cbDenoiserReflections;
FfxFloat32Mat4 InvProjection()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.invProjection;
#else
return FfxFloat32Mat4(0.0f);
#endif
}
FfxFloat32Mat4 InvView()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.invView;
#else
return FfxFloat32Mat4(0.0f);
#endif
}
FfxFloat32Mat4 PrevViewProjection()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.prevViewProjection;
#else
return FfxFloat32Mat4(0.0f);
#endif
}
FfxUInt32x2 RenderSize()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.renderSize;
#else
return FfxUInt32x2(0);
#endif
}
FfxFloat32x2 InverseRenderSize()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.inverseRenderSize;
#else
return FfxFloat32x2(0.0f);
#endif
}
FfxFloat32x2 MotionVectorScale()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.motionVectorScale;
#else
return FfxFloat32x2(0.0f);
#endif
}
FfxFloat32 NormalsUnpackMul()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.normalsUnpackMul;
#else
return 0.0f;
#endif
}
FfxFloat32 NormalsUnpackAdd()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.normalsUnpackAdd;
#else
return 0.0f;
#endif
}
FfxBoolean IsRoughnessPerceptual()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.isRoughnessPerceptual;
#else
return false;
#endif
}
FfxFloat32 TemporalStabilityFactor()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.temporalStabilityFactor;
#else
return 0.0f;
#endif
}
FfxFloat32 RoughnessThreshold()
{
#if defined DENOISER_BIND_CB_DENOISER
return cbDenoiserReflections.roughnessThreshold;
#else
return 0.0f;
#endif
}
#endif // #if defined(DENOISER_BIND_CB_DENOISER)
layout (set = 0, binding = 1000) uniform sampler s_LinearSampler;
#if defined DENOISER_BIND_SRV_INPUT_DEPTH_HIERARCHY
layout (set = 0, binding = DENOISER_BIND_SRV_INPUT_DEPTH_HIERARCHY) uniform texture2D r_input_depth_hierarchy;
#endif
#if defined DENOISER_BIND_SRV_INPUT_MOTION_VECTORS
layout (set = 0, binding = DENOISER_BIND_SRV_INPUT_MOTION_VECTORS) uniform texture2D r_input_motion_vectors;
#endif
#if defined DENOISER_BIND_SRV_INPUT_NORMAL
layout (set = 0, binding = DENOISER_BIND_SRV_INPUT_NORMAL) uniform texture2D r_input_normal;
#endif
#if defined DENOISER_BIND_SRV_RADIANCE
layout (set = 0, binding = DENOISER_BIND_SRV_RADIANCE) uniform texture2D r_radiance;
#endif
#if defined DENOISER_BIND_SRV_RADIANCE_HISTORY
layout (set = 0, binding = DENOISER_BIND_SRV_RADIANCE_HISTORY) uniform texture2D r_radiance_history;
#endif
#if defined DENOISER_BIND_SRV_VARIANCE
layout (set = 0, binding = DENOISER_BIND_SRV_VARIANCE) uniform texture2D r_variance;
#endif
#if defined DENOISER_BIND_SRV_SAMPLE_COUNT
layout (set = 0, binding = DENOISER_BIND_SRV_SAMPLE_COUNT) uniform texture2D r_sample_count;
#endif
#if defined DENOISER_BIND_SRV_AVERAGE_RADIANCE
layout (set = 0, binding = DENOISER_BIND_SRV_AVERAGE_RADIANCE) uniform texture2D r_average_radiance;
#endif
#if defined DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS
layout (set = 0, binding = DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS) uniform texture2D r_extracted_roughness;
#endif
#if defined DENOISER_BIND_SRV_DEPTH_HISTORY
layout (set = 0, binding = DENOISER_BIND_SRV_DEPTH_HISTORY) uniform texture2D r_depth_history;
#endif
#if defined DENOISER_BIND_SRV_NORMAL_HISTORY
layout (set = 0, binding = DENOISER_BIND_SRV_NORMAL_HISTORY) uniform texture2D r_normal_history;
#endif
#if defined DENOISER_BIND_SRV_ROUGHNESS_HISTORY
layout (set = 0, binding = DENOISER_BIND_SRV_ROUGHNESS_HISTORY) uniform texture2D r_roughness_history;
#endif
#if defined DENOISER_BIND_SRV_REPROJECTED_RADIANCE
layout (set = 0, binding = DENOISER_BIND_SRV_REPROJECTED_RADIANCE) uniform texture2D r_reprojected_radiance;
#endif
// UAVs
#if defined DENOISER_BIND_UAV_RADIANCE
layout (set = 0, binding = DENOISER_BIND_UAV_RADIANCE, rgba16f) uniform image2D rw_radiance;
#endif
#if defined DENOISER_BIND_UAV_VARIANCE
layout (set = 0, binding = DENOISER_BIND_UAV_VARIANCE, r16f) uniform image2D rw_variance;
#endif
#if defined DENOISER_BIND_UAV_SAMPLE_COUNT
layout (set = 0, binding = DENOISER_BIND_UAV_SAMPLE_COUNT, r16f) uniform image2D rw_sample_count;
#endif
#if defined DENOISER_BIND_UAV_AVERAGE_RADIANCE
layout (set = 0, binding = DENOISER_BIND_UAV_AVERAGE_RADIANCE, r11f_g11f_b10f) uniform image2D rw_average_radiance;
#endif
#if defined DENOISER_BIND_UAV_DENOISER_TILE_LIST
layout (set = 0, binding = DENOISER_BIND_UAV_DENOISER_TILE_LIST, std430) buffer rw_denoiser_tile_list_t
{
FfxUInt32 data[];
} rw_denoiser_tile_list;
#endif
#if defined DENOISER_BIND_UAV_REPROJECTED_RADIANCE
layout (set = 0, binding = DENOISER_BIND_UAV_REPROJECTED_RADIANCE, rgba16f) uniform image2D rw_reprojected_radiance;
#endif
#if FFX_HALF
FfxFloat16x3 FFX_DENOISER_LoadWorldSpaceNormalH(FfxInt32x2 pixel_coordinate)
{
#if defined(DENOISER_BIND_SRV_INPUT_NORMAL)
return normalize(FfxFloat16x3(NormalsUnpackMul() * texelFetch(r_input_normal, pixel_coordinate, 0).xyz + NormalsUnpackAdd()));
#else
return FfxFloat16x3(0.0f);
#endif
}
FfxFloat16x3 LoadRadianceH(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_RADIANCE)
return FfxFloat16x3(texelFetch(r_radiance, coordinate.xy, coordinate.z).xyz);
#else
return FfxFloat16x3(0.0f);
#endif
}
FfxFloat16 LoadVarianceH(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_VARIANCE)
return FfxFloat16(texelFetch(r_variance, coordinate.xy, coordinate.z).x);
#else
return FfxFloat16(0.0f);
#endif
}
#if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
FfxFloat16x3 FFX_DNSR_Reflections_SampleAverageRadiance(FfxFloat32x2 uv)
{
return FfxFloat16x3(textureLod(sampler2D(r_average_radiance, s_LinearSampler), uv, 0.0f).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
#if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
FfxFloat16 FFX_DNSR_Reflections_LoadRoughness(FfxInt32x2 pixel_coordinate)
{
FfxFloat16 rawRoughness = FfxFloat16(texelFetch(r_extracted_roughness, pixel_coordinate, 0).x);
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
void StoreRadianceH(FfxInt32x2 coordinate, FfxFloat16x4 radiance)
{
#if defined (DENOISER_BIND_UAV_RADIANCE)
imageStore(rw_radiance, coordinate, radiance);
#endif
}
void StoreVarianceH(FfxInt32x2 coordinate, FfxFloat16 variance)
{
#if defined (DENOISER_BIND_UAV_VARIANCE)
imageStore(rw_variance, coordinate, FfxFloat16x4(variance));
#endif
}
void FFX_DNSR_Reflections_StorePrefilteredReflections(FfxInt32x2 pixel_coordinate, FfxFloat16x3 radiance, FfxFloat16 variance)
{
StoreRadianceH(pixel_coordinate, radiance.xyzz);
StoreVarianceH(pixel_coordinate, variance.x);
}
void FFX_DNSR_Reflections_StoreTemporalAccumulation(FfxInt32x2 pixel_coordinate, FfxFloat16x3 radiance, FfxFloat16 variance)
{
StoreRadianceH(pixel_coordinate, radiance.xyzz);
StoreVarianceH(pixel_coordinate, variance.x);
}
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_LoadRadianceHistory(FfxInt32x2 pixel_coordinate)
{
return FfxFloat16x3(texelFetch(r_radiance_history, pixel_coordinate, 0).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_SampleRadianceHistory(FfxFloat32x2 uv)
{
return FfxFloat16x3(textureLod(sampler2D(r_radiance_history, s_LinearSampler), uv, 0.0f).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_VARIANCE)
FfxFloat16 FFX_DNSR_Reflections_SampleVarianceHistory(FfxFloat32x2 uv)
{
return FfxFloat16(textureLod(sampler2D(r_variance, s_LinearSampler), uv, 0.0f).x);
}
#endif // #if defined (DENOISER_BIND_SRV_VARIANCE)
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat16 FFX_DNSR_Reflections_SampleNumSamplesHistory(FfxFloat32x2 uv)
{
return FfxFloat16(textureLod(sampler2D(r_sample_count, s_LinearSampler), uv, 0.0f).x);
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
void FFX_DNSR_Reflections_StoreRadianceReprojected(FfxInt32x2 pixel_coordinate, FfxFloat16x3 value)
{
imageStore(rw_reprojected_radiance, pixel_coordinate, FfxFloat16x4(value, 0.0f));
}
#endif // #if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
#if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
void FFX_DNSR_Reflections_StoreAverageRadiance(FfxInt32x2 pixel_coordinate, FfxFloat16x3 value)
{
imageStore(rw_average_radiance, pixel_coordinate, FfxFloat16x4(value, 0.0f));
}
#endif // #if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
FfxFloat16x3 FFX_DNSR_Reflections_LoadWorldSpaceNormal(FfxInt32x2 pixel_coordinate)
{
return FFX_DENOISER_LoadWorldSpaceNormalH(pixel_coordinate);
}
#if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
FfxFloat16 FFX_DNSR_Reflections_SampleRoughnessHistory(FfxFloat32x2 uv)
{
FfxFloat16 rawRoughness = FfxFloat16(textureLod(sampler2D(r_roughness_history, s_LinearSampler), uv, 0.0f).x);
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(FfxInt32x2 pixel_coordinate)
{
return normalize(FfxFloat16x3(NormalsUnpackMul() * texelFetch(r_normal_history, pixel_coordinate.xy, 0).xyz + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(FfxFloat32x2 uv)
{
return normalize(FfxFloat16x3(NormalsUnpackMul() * textureLod(sampler2D(r_normal_history, s_LinearSampler), uv, 0.0f).xyz + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE)
FfxFloat16 FFX_DNSR_Reflections_LoadRayLength(FfxInt32x2 pixel_coordinate)
{
return FfxFloat16(texelFetch(r_radiance, pixel_coordinate, 0).w);
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE)
void FFX_DNSR_Reflections_StoreVariance(FfxInt32x2 pixel_coordinate, FfxFloat16 value)
{
StoreVarianceH(pixel_coordinate, value);
}
#if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
void FFX_DNSR_Reflections_StoreNumSamples(FfxInt32x2 pixel_coordinate, FfxFloat16 value)
{
imageStore(rw_sample_count, pixel_coordinate, FfxFloat16x4(value));
}
#endif // #if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
FfxFloat16x3 FFX_DNSR_Reflections_LoadRadiance(FfxInt32x2 pixel_coordinate)
{
return LoadRadianceH(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat16x3 FFX_DNSR_Reflections_LoadRadianceReprojected(FfxInt32x2 pixel_coordinate)
{
return FfxFloat16x3(texelFetch(r_reprojected_radiance, pixel_coordinate, 0).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat16 FFX_DNSR_Reflections_LoadVariance(FfxInt32x2 pixel_coordinate)
{
return LoadVarianceH(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat16 FFX_DNSR_Reflections_LoadNumSamples(FfxInt32x2 pixel_coordinate)
{
return FfxFloat16(texelFetch(r_sample_count, pixel_coordinate, 0).x);
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#else // FFX_HALF
FfxFloat32x3 LoadRadiance(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_RADIANCE)
return texelFetch(r_radiance, coordinate.xy, coordinate.z).xyz;
#else
return FfxFloat32x3(0.0f);
#endif
}
FfxFloat32 LoadVariance(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_VARIANCE)
return texelFetch(r_variance, coordinate.xy, coordinate.z).x;
#else
return 0.0f;
#endif
}
FfxFloat32x3 FFX_DENOISER_LoadWorldSpaceNormal(FfxInt32x2 pixel_coordinate)
{
#if defined(DENOISER_BIND_SRV_INPUT_NORMAL)
return normalize(NormalsUnpackMul() * texelFetch(r_input_normal, pixel_coordinate, 0).xyz + NormalsUnpackAdd());
#else
return FfxFloat32x3(0.0f);
#endif
}
#if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
FfxFloat32 FFX_DNSR_Reflections_LoadRoughness(FfxInt32x2 pixel_coordinate)
{
FfxFloat32 rawRoughness = FfxFloat32(texelFetch(r_extracted_roughness, pixel_coordinate, 0).x);
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
void StoreRadiance(FfxInt32x2 coordinate, FfxFloat32x4 radiance)
{
#if defined (DENOISER_BIND_UAV_RADIANCE)
imageStore(rw_radiance, coordinate, radiance);
#endif
}
void StoreVariance(FfxInt32x2 coordinate, FfxFloat32 variance)
{
#if defined (DENOISER_BIND_UAV_VARIANCE)
imageStore(rw_variance, coordinate, FfxFloat32x4(variance));
#endif
}
void FFX_DNSR_Reflections_StorePrefilteredReflections(FfxInt32x2 pixel_coordinate, FfxFloat32x3 radiance, FfxFloat32 variance)
{
StoreRadiance(pixel_coordinate, radiance.xyzz);
StoreVariance(pixel_coordinate, variance.x);
}
void FFX_DNSR_Reflections_StoreTemporalAccumulation(FfxInt32x2 pixel_coordinate, FfxFloat32x3 radiance, FfxFloat32 variance)
{
StoreRadiance(pixel_coordinate, radiance.xyzz);
StoreVariance(pixel_coordinate, variance.x);
}
#if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
FfxFloat32x3 FFX_DNSR_Reflections_SampleAverageRadiance(FfxFloat32x2 uv)
{
return FfxFloat32x3(textureLod(sampler2D(r_average_radiance, s_LinearSampler), uv, 0.0f).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_LoadRadianceHistory(FfxInt32x2 pixel_coordinate)
{
return FfxFloat32x3(texelFetch(r_radiance_history, pixel_coordinate, 0).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_SampleRadianceHistory(FfxFloat32x2 uv)
{
return FfxFloat32x3(textureLod(sampler2D(r_radiance_history, s_LinearSampler), uv, 0.0f).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_VARIANCE)
FfxFloat32 FFX_DNSR_Reflections_SampleVarianceHistory(FfxFloat32x2 uv)
{
return FfxFloat32(textureLod(sampler2D(r_variance, s_LinearSampler), uv, 0.0f).x);
}
#endif // #if defined (DENOISER_BIND_SRV_VARIANCE)
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat32 FFX_DNSR_Reflections_SampleNumSamplesHistory(FfxFloat32x2 uv)
{
return FfxFloat32(textureLod(sampler2D(r_sample_count, s_LinearSampler), uv, 0.0f).x);
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
void FFX_DNSR_Reflections_StoreRadianceReprojected(FfxInt32x2 pixel_coordinate, FfxFloat32x3 value)
{
imageStore(rw_reprojected_radiance, pixel_coordinate, FfxFloat32x4(value, 0.0f));
}
#endif // #if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
#if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
void FFX_DNSR_Reflections_StoreAverageRadiance(FfxInt32x2 pixel_coordinate, FfxFloat32x3 value)
{
imageStore(rw_average_radiance, pixel_coordinate, FfxFloat32x4(value, 0.0f));
}
#endif // #if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
FfxFloat32x3 FFX_DNSR_Reflections_LoadWorldSpaceNormal(FfxInt32x2 pixel_coordinate)
{
return FFX_DENOISER_LoadWorldSpaceNormal(pixel_coordinate);
}
#if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
FfxFloat32 FFX_DNSR_Reflections_SampleRoughnessHistory(FfxFloat32x2 uv)
{
FfxFloat32 rawRoughness = FfxFloat32(textureLod(sampler2D(r_roughness_history, s_LinearSampler), uv, 0.0f).x);
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(FfxInt32x2 pixel_coordinate)
{
return normalize(FfxFloat32x3(NormalsUnpackMul() * texelFetch(r_normal_history, pixel_coordinate.xy, 0).xyz + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(FfxFloat32x2 uv)
{
return normalize(FfxFloat32x3(NormalsUnpackMul() * textureLod(sampler2D(r_normal_history, s_LinearSampler), uv, 0.0f).xyz + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE)
FfxFloat32 FFX_DNSR_Reflections_LoadRayLength(FfxInt32x2 pixel_coordinate)
{
return FfxFloat32(texelFetch(r_radiance, pixel_coordinate, 0).w);
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE)
void FFX_DNSR_Reflections_StoreVariance(FfxInt32x2 pixel_coordinate, FfxFloat32 value)
{
StoreVariance(pixel_coordinate, value);
}
#if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
void FFX_DNSR_Reflections_StoreNumSamples(FfxInt32x2 pixel_coordinate, FfxFloat32 value)
{
imageStore(rw_sample_count, pixel_coordinate, FfxFloat32x4(value));
}
#endif // #if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
FfxFloat32x3 FFX_DNSR_Reflections_LoadRadiance(FfxInt32x2 pixel_coordinate)
{
return LoadRadiance(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat32x3 FFX_DNSR_Reflections_LoadRadianceReprojected(FfxInt32x2 pixel_coordinate)
{
return FfxFloat32x3(texelFetch(r_reprojected_radiance, pixel_coordinate, 0).xyz);
}
#endif // #if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat32 FFX_DNSR_Reflections_LoadVariance(FfxInt32x2 pixel_coordinate)
{
return LoadVariance(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat32 FFX_DNSR_Reflections_LoadNumSamples(FfxInt32x2 pixel_coordinate)
{
return FfxFloat32(texelFetch(r_sample_count, pixel_coordinate, 0).x);
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#endif // #if defined(FFX_HALF)
FfxFloat32 FFX_DENOISER_LoadDepth(FfxInt32x2 pixel_coordinate, FfxInt32 mip)
{
#if defined(DENOISER_BIND_SRV_INPUT_DEPTH_HIERARCHY)
return texelFetch(r_input_depth_hierarchy, pixel_coordinate, mip).x;
#else
return 0.0f;
#endif
}
#if defined (DENOISER_BIND_UAV_DENOISER_TILE_LIST)
FfxUInt32 GetDenoiserTile(FfxUInt32 group_id)
{
return rw_denoiser_tile_list.data[group_id];
}
#endif // #if defined (DENOISER_BIND_UAV_DENOISER_TILE_LIST)
#if defined (DENOISER_BIND_SRV_INPUT_MOTION_VECTORS)
FfxFloat32x2 FFX_DNSR_Reflections_LoadMotionVector(FfxInt32x2 pixel_coordinate)
{
return MotionVectorScale() * texelFetch(r_input_motion_vectors, pixel_coordinate, 0).xy;
}
#endif // #if defined (DENOISER_BIND_SRV_INPUT_MOTION_VECTORS)
FfxFloat32 FFX_DNSR_Reflections_LoadDepth(FfxInt32x2 pixel_coordinate)
{
return FFX_DENOISER_LoadDepth(pixel_coordinate, 0);
}
#if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
FfxFloat32 FFX_DNSR_Reflections_LoadDepthHistory(FfxInt32x2 pixel_coordinate)
{
return texelFetch(r_depth_history, pixel_coordinate, 0).x;
}
#endif // #if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
#if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
FfxFloat32 FFX_DNSR_Reflections_SampleDepthHistory(FfxFloat32x2 uv)
{
return textureLod(sampler2D(r_depth_history, s_LinearSampler), uv, 0.0f).x;
}
#endif // #if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,647 @@
// 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_denoiser_resources.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_DENOISER_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex))
#define FFX_DENOISER_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex))
#define FFX_DENOISER_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex))
#if defined(DENOISER_BIND_CB_DENOISER)
cbuffer cbDenoiserReflections : FFX_DENOISER_DECLARE_CB(DENOISER_BIND_CB_DENOISER)
{
FfxFloat32Mat4 invProjection;
FfxFloat32Mat4 invView;
FfxFloat32Mat4 prevViewProjection;
FfxUInt32x2 renderSize;
FfxFloat32x2 inverseRenderSize;
FfxFloat32x2 motionVectorScale;
FfxFloat32 normalsUnpackMul;
FfxFloat32 normalsUnpackAdd;
FfxBoolean isRoughnessPerceptual;
FfxFloat32 temporalStabilityFactor;
FfxFloat32 roughnessThreshold;
#define FFX_DENOISER_CONSTANT_BUFFER_1_SIZE 54 // Number of 32-bit values. This must be kept in sync with the cbDenoiser size.
};
#else
#define invProjection 0
#define invView 0
#define prevViewProjection 0
#define renderSize 0
#define inverseRenderSize 0
#define motionVectorScale 0
#define normalsUnpackMul 0
#define normalsUnpackAdd 0
#define isRoughnessPerceptual 0
#define temporalStabilityFactor 0
#define roughnessThreshold 0
#endif
#if defined(FFX_GPU)
#define FFX_DENOISER_ROOTSIG_STRINGIFY(p) FFX_DENOISER_ROOTSIG_STR(p)
#define FFX_DENOISER_ROOTSIG_STR(p) #p
#define FFX_DENOISER_ROOTSIG [RootSignature("DescriptorTable(UAV(u0, numDescriptors = " FFX_DENOISER_ROOTSIG_STRINGIFY(FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT) ")), " \
"DescriptorTable(SRV(t0, numDescriptors = " FFX_DENOISER_ROOTSIG_STRINGIFY(FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT) ")), " \
"CBV(b0), " \
"StaticSampler(s0, filter = FILTER_MIN_MAG_LINEAR_MIP_POINT, " \
"addressU = TEXTURE_ADDRESS_CLAMP, " \
"addressV = TEXTURE_ADDRESS_CLAMP, " \
"addressW = TEXTURE_ADDRESS_CLAMP, " \
"comparisonFunc = COMPARISON_ALWAYS, " \
"borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK, " \
"maxAnisotropy = 1)" )]
#if defined(FFX_DENOISER_EMBED_ROOTSIG)
#define FFX_DENOISER_EMBED_ROOTSIG_CONTENT FFX_DENOISER_ROOTSIG
#else
#define FFX_DENOISER_EMBED_ROOTSIG_CONTENT
#endif // #if FFX_DENOISER_EMBED_ROOTSIG
#endif // #if defined(FFX_GPU)
SamplerState s_LinearSampler : register(s0);
FfxFloat32Mat4 InvProjection()
{
return invProjection;
}
FfxFloat32Mat4 InvView()
{
return invView;
}
FfxFloat32Mat4 PrevViewProjection()
{
return prevViewProjection;
}
FfxUInt32x2 RenderSize()
{
return renderSize;
}
FfxFloat32x2 InverseRenderSize()
{
return inverseRenderSize;
}
FfxFloat32x2 MotionVectorScale()
{
return motionVectorScale;
}
FfxFloat32 NormalsUnpackMul()
{
return normalsUnpackMul;
}
FfxFloat32 NormalsUnpackAdd()
{
return normalsUnpackAdd;
}
FfxBoolean IsRoughnessPerceptual()
{
return isRoughnessPerceptual;
}
FfxFloat32 TemporalStabilityFactor()
{
return temporalStabilityFactor;
}
FfxFloat32 RoughnessThreshold()
{
return roughnessThreshold;
}
// SRVs
#if defined DENOISER_BIND_SRV_INPUT_DEPTH_HIERARCHY
Texture2D<FfxFloat32> r_input_depth_hierarchy : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_INPUT_DEPTH_HIERARCHY);
#endif
#if defined DENOISER_BIND_SRV_INPUT_MOTION_VECTORS
Texture2D<FfxFloat32x2> r_input_motion_vectors : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_INPUT_MOTION_VECTORS);
#endif
#if defined DENOISER_BIND_SRV_INPUT_NORMAL
Texture2D<FfxFloat32x3> r_input_normal : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_INPUT_NORMAL);
#endif
#if defined DENOISER_BIND_SRV_RADIANCE
Texture2D<FfxFloat32x4> r_radiance : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_RADIANCE);
#endif
#if defined DENOISER_BIND_SRV_RADIANCE_HISTORY
Texture2D<FfxFloat32x4> r_radiance_history : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_RADIANCE_HISTORY);
#endif
#if defined DENOISER_BIND_SRV_VARIANCE
Texture2D<FfxFloat32> r_variance : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_VARIANCE);
#endif
#if defined DENOISER_BIND_SRV_SAMPLE_COUNT
Texture2D<FfxFloat32> r_sample_count : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_SAMPLE_COUNT);
#endif
#if defined DENOISER_BIND_SRV_AVERAGE_RADIANCE
Texture2D<FfxFloat32x3> r_average_radiance : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_AVERAGE_RADIANCE);
#endif
#if defined DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS
Texture2D<FfxFloat32> r_extracted_roughness : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS);
#endif
#if defined DENOISER_BIND_SRV_DEPTH_HISTORY
Texture2D<FfxFloat32> r_depth_history : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_DEPTH_HISTORY);
#endif
#if defined DENOISER_BIND_SRV_NORMAL_HISTORY
Texture2D<FfxFloat32x3> r_normal_history : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_NORMAL_HISTORY);
#endif
#if defined DENOISER_BIND_SRV_ROUGHNESS_HISTORY
Texture2D<FfxFloat32> r_roughness_history : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_ROUGHNESS_HISTORY);
#endif
#if defined DENOISER_BIND_SRV_REPROJECTED_RADIANCE
Texture2D<FfxFloat32x4> r_reprojected_radiance : FFX_DENOISER_DECLARE_SRV(DENOISER_BIND_SRV_REPROJECTED_RADIANCE);
#endif
// UAVs
#if defined DENOISER_BIND_UAV_RADIANCE
RWTexture2D<FfxFloat32x4> rw_radiance : FFX_DENOISER_DECLARE_UAV(DENOISER_BIND_UAV_RADIANCE);
#endif
#if defined DENOISER_BIND_UAV_VARIANCE
RWTexture2D<FfxFloat32> rw_variance : FFX_DENOISER_DECLARE_UAV(DENOISER_BIND_UAV_VARIANCE);
#endif
#if defined DENOISER_BIND_UAV_SAMPLE_COUNT
RWTexture2D<FfxFloat32> rw_sample_count : FFX_DENOISER_DECLARE_UAV(DENOISER_BIND_UAV_SAMPLE_COUNT);
#endif
#if defined DENOISER_BIND_UAV_AVERAGE_RADIANCE
RWTexture2D<FfxFloat32x3> rw_average_radiance : FFX_DENOISER_DECLARE_UAV(DENOISER_BIND_UAV_AVERAGE_RADIANCE);
#endif
#if defined DENOISER_BIND_UAV_DENOISER_TILE_LIST
RWStructuredBuffer<FfxUInt32> rw_denoiser_tile_list : FFX_DENOISER_DECLARE_UAV(DENOISER_BIND_UAV_DENOISER_TILE_LIST);
#endif
#if defined DENOISER_BIND_UAV_REPROJECTED_RADIANCE
RWTexture2D<FfxFloat32x3> rw_reprojected_radiance : FFX_DENOISER_DECLARE_UAV(DENOISER_BIND_UAV_REPROJECTED_RADIANCE);
#endif
#if FFX_HALF
FfxFloat16x3 FFX_DENOISER_LoadWorldSpaceNormalH(FfxInt32x2 pixel_coordinate)
{
#if defined(DENOISER_BIND_SRV_INPUT_NORMAL)
return normalize((FfxFloat16x3)(NormalsUnpackMul() * r_input_normal.Load(FfxInt32x3(pixel_coordinate, 0)) + NormalsUnpackAdd()));
#else
return 0.0f;
#endif // #if defined(DENOISER_BIND_SRV_INPUT_NORMAL)
}
FfxFloat16x3 LoadRadianceH(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_RADIANCE)
return (FfxFloat16x3)r_radiance.Load(coordinate).xyz;
#else
return 0.0f;
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE)
}
FfxFloat16 LoadVarianceH(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_VARIANCE)
return (FfxFloat16)r_variance.Load(coordinate).x;
#else
return 0.0f;
#endif // #if defined (DENOISER_BIND_SRV_VARIANCE)
}
#if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
FfxFloat16x3 FFX_DNSR_Reflections_SampleAverageRadiance(FfxFloat32x2 uv)
{
return (FfxFloat16x3)r_average_radiance.SampleLevel(s_LinearSampler, uv, 0.0f).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
#if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
FfxFloat16 FFX_DNSR_Reflections_LoadRoughness(FfxInt32x2 pixel_coordinate)
{
FfxFloat16 rawRoughness = (FfxFloat16)r_extracted_roughness.Load(FfxInt32x3(pixel_coordinate, 0));
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
void StoreRadianceH(FfxInt32x2 coordinate, FfxFloat16x4 radiance)
{
#if defined (DENOISER_BIND_UAV_RADIANCE)
rw_radiance[coordinate] = radiance;
#endif // #if defined (DENOISER_BIND_UAV_RADIANCE)
}
void StoreVarianceH(FfxInt32x2 coordinate, FfxFloat16 variance)
{
#if defined (DENOISER_BIND_UAV_VARIANCE)
rw_variance[coordinate] = variance;
#endif // #if defined (DENOISER_BIND_UAV_VARIANCE)
}
void FFX_DNSR_Reflections_StorePrefilteredReflections(FfxInt32x2 pixel_coordinate, FfxFloat16x3 radiance, FfxFloat16 variance)
{
StoreRadianceH(pixel_coordinate, radiance.xyzz);
StoreVarianceH(pixel_coordinate, variance.x);
}
void FFX_DNSR_Reflections_StoreTemporalAccumulation(FfxInt32x2 pixel_coordinate, FfxFloat16x3 radiance, FfxFloat16 variance)
{
StoreRadianceH(pixel_coordinate, radiance.xyzz);
StoreVarianceH(pixel_coordinate, variance.x);
}
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_LoadRadianceHistory(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat16x3)r_radiance_history.Load(FfxInt32x3(pixel_coordinate, 0)).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_SampleRadianceHistory(FfxFloat32x2 uv)
{
return (FfxFloat16x3)r_radiance_history.SampleLevel(s_LinearSampler, uv, 0.0f).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_VARIANCE)
FfxFloat16 FFX_DNSR_Reflections_SampleVarianceHistory(FfxFloat32x2 uv)
{
return (FfxFloat16)r_variance.SampleLevel(s_LinearSampler, uv, 0.0f).x;
}
#endif // #if defined (DENOISER_BIND_SRV_VARIANCE)
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat16 FFX_DNSR_Reflections_SampleNumSamplesHistory(FfxFloat32x2 uv)
{
return (FfxFloat16)r_sample_count.SampleLevel(s_LinearSampler, uv, 0.0f).x;
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
void FFX_DNSR_Reflections_StoreRadianceReprojected(FfxInt32x2 pixel_coordinate, FfxFloat16x3 value)
{
rw_reprojected_radiance[pixel_coordinate] = value;
}
#endif // #if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
#if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
void FFX_DNSR_Reflections_StoreAverageRadiance(FfxInt32x2 pixel_coordinate, FfxFloat16x3 value)
{
rw_average_radiance[pixel_coordinate] = value;
}
#endif // #if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
FfxFloat16x3 FFX_DNSR_Reflections_LoadWorldSpaceNormal(FfxInt32x2 pixel_coordinate)
{
return FFX_DENOISER_LoadWorldSpaceNormalH(pixel_coordinate);
}
#if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
FfxFloat16 FFX_DNSR_Reflections_SampleRoughnessHistory(FfxFloat32x2 uv)
{
FfxFloat16 rawRoughness = (FfxFloat16)r_roughness_history.SampleLevel(s_LinearSampler, uv, 0.0f);
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(FfxInt32x2 pixel_coordinate)
{
return normalize((FfxFloat16x3)(NormalsUnpackMul() * r_normal_history.Load(FfxInt32x3(pixel_coordinate, 0)) + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat16x3 FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(FfxFloat32x2 uv)
{
return normalize((FfxFloat16x3)(NormalsUnpackMul() * r_normal_history.SampleLevel(s_LinearSampler, uv, 0.0f) + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE)
FfxFloat16 FFX_DNSR_Reflections_LoadRayLength(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat16)r_radiance.Load(FfxInt32x3(pixel_coordinate, 0)).w;
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE)
void FFX_DNSR_Reflections_StoreVariance(FfxInt32x2 pixel_coordinate, FfxFloat16 value)
{
StoreVarianceH(pixel_coordinate, value);
}
#if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
void FFX_DNSR_Reflections_StoreNumSamples(FfxInt32x2 pixel_coordinate, FfxFloat16 value)
{
rw_sample_count[pixel_coordinate] = value;
}
#endif // #if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
FfxFloat16x3 FFX_DNSR_Reflections_LoadRadiance(FfxInt32x2 pixel_coordinate)
{
return LoadRadianceH(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat16x3 FFX_DNSR_Reflections_LoadRadianceReprojected(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat16x3)r_reprojected_radiance.Load(FfxInt32x3(pixel_coordinate, 0)).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat16 FFX_DNSR_Reflections_LoadVariance(FfxInt32x2 pixel_coordinate)
{
return LoadVarianceH(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat16 FFX_DNSR_Reflections_LoadNumSamples(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat16)r_sample_count.Load(FfxInt32x3(pixel_coordinate, 0)).x;
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#else // FFX_HALF
FfxFloat32x3 LoadRadiance(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_RADIANCE)
return r_radiance.Load(coordinate).xyz;
#else
return 0.0f;
#endif
}
FfxFloat32 LoadVariance(FfxInt32x3 coordinate)
{
#if defined (DENOISER_BIND_SRV_VARIANCE)
return r_variance.Load(coordinate).x;
#else
return 0.0f;
#endif
}
FfxFloat32x3 FFX_DENOISER_LoadWorldSpaceNormal(FfxInt32x2 pixel_coordinate)
{
#if defined(DENOISER_BIND_SRV_INPUT_NORMAL)
return normalize(NormalsUnpackMul() * r_input_normal.Load(FfxInt32x3(pixel_coordinate, 0)) + NormalsUnpackAdd());
#else
return 0.0f;
#endif
}
#if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
FfxFloat32 FFX_DNSR_Reflections_LoadRoughness(FfxInt32x2 pixel_coordinate)
{
FfxFloat32 rawRoughness = (FfxFloat32)r_extracted_roughness.Load(FfxInt32x3(pixel_coordinate, 0));
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_EXTRACTED_ROUGHNESS)
void StoreRadiance(FfxInt32x2 coordinate, FfxFloat32x4 radiance)
{
#if defined (DENOISER_BIND_UAV_RADIANCE)
rw_radiance[coordinate] = radiance;
#endif
}
void StoreVariance(FfxInt32x2 coordinate, FfxFloat32 variance)
{
#if defined (DENOISER_BIND_UAV_VARIANCE)
rw_variance[coordinate] = variance;
#endif
}
void FFX_DNSR_Reflections_StorePrefilteredReflections(FfxInt32x2 pixel_coordinate, FfxFloat32x3 radiance, FfxFloat32 variance)
{
StoreRadiance(pixel_coordinate, radiance.xyzz);
StoreVariance(pixel_coordinate, variance.x);
}
void FFX_DNSR_Reflections_StoreTemporalAccumulation(FfxInt32x2 pixel_coordinate, FfxFloat32x3 radiance, FfxFloat32 variance)
{
StoreRadiance(pixel_coordinate, radiance.xyzz);
StoreVariance(pixel_coordinate, variance.x);
}
#if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
FfxFloat32x3 FFX_DNSR_Reflections_SampleAverageRadiance(FfxFloat32x2 uv)
{
return (FfxFloat32x3)r_average_radiance.SampleLevel(s_LinearSampler, uv, 0.0f).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_AVERAGE_RADIANCE)
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_LoadRadianceHistory(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat32x3)r_radiance_history.Load(FfxInt32x3(pixel_coordinate, 0)).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_SampleRadianceHistory(FfxFloat32x2 uv)
{
return (FfxFloat32x3)r_radiance_history.SampleLevel(s_LinearSampler, uv, 0.0f).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE_HISTORY)
#if defined (DENOISER_BIND_SRV_VARIANCE)
FfxFloat32 FFX_DNSR_Reflections_SampleVarianceHistory(FfxFloat32x2 uv)
{
return (FfxFloat32)r_variance.SampleLevel(s_LinearSampler, uv, 0.0f).x;
}
#endif // #if defined (DENOISER_BIND_SRV_VARIANCE)
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat32 FFX_DNSR_Reflections_SampleNumSamplesHistory(FfxFloat32x2 uv)
{
return (FfxFloat32)r_sample_count.SampleLevel(s_LinearSampler, uv, 0.0f).x;
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
void FFX_DNSR_Reflections_StoreRadianceReprojected(FfxInt32x2 pixel_coordinate, FfxFloat32x3 value)
{
rw_reprojected_radiance[pixel_coordinate] = value;
}
#endif // #if defined (DENOISER_BIND_UAV_REPROJECTED_RADIANCE)
#if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
void FFX_DNSR_Reflections_StoreAverageRadiance(FfxInt32x2 pixel_coordinate, FfxFloat32x3 value)
{
rw_average_radiance[pixel_coordinate] = value;
}
#endif // #if defined (DENOISER_BIND_UAV_AVERAGE_RADIANCE)
FfxFloat32x3 FFX_DNSR_Reflections_LoadWorldSpaceNormal(FfxInt32x2 pixel_coordinate)
{
return FFX_DENOISER_LoadWorldSpaceNormal(pixel_coordinate);
}
#if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
FfxFloat32 FFX_DNSR_Reflections_SampleRoughnessHistory(FfxFloat32x2 uv)
{
FfxFloat32 rawRoughness = (FfxFloat32)r_roughness_history.SampleLevel(s_LinearSampler, uv, 0.0f);
if (IsRoughnessPerceptual())
{
rawRoughness *= rawRoughness;
}
return rawRoughness;
}
#endif // #if defined (DENOISER_BIND_SRV_ROUGHNESS_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(FfxInt32x2 pixel_coordinate)
{
return normalize((FfxFloat32x3)(NormalsUnpackMul() * r_normal_history.Load(FfxInt32x3(pixel_coordinate, 0)) + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
FfxFloat32x3 FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(FfxFloat32x2 uv)
{
return normalize((FfxFloat32x3)(NormalsUnpackMul() * r_normal_history.SampleLevel(s_LinearSampler, uv, 0.0f) + NormalsUnpackAdd()));
}
#endif // #if defined (DENOISER_BIND_SRV_NORMAL_HISTORY)
#if defined (DENOISER_BIND_SRV_RADIANCE)
FfxFloat32 FFX_DNSR_Reflections_LoadRayLength(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat32)r_radiance.Load(FfxInt32x3(pixel_coordinate, 0)).w;
}
#endif // #if defined (DENOISER_BIND_SRV_RADIANCE)
void FFX_DNSR_Reflections_StoreVariance(FfxInt32x2 pixel_coordinate, FfxFloat32 value)
{
StoreVariance(pixel_coordinate, value);
}
#if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
void FFX_DNSR_Reflections_StoreNumSamples(FfxInt32x2 pixel_coordinate, FfxFloat32 value)
{
rw_sample_count[pixel_coordinate] = value;
}
#endif // #if defined (DENOISER_BIND_UAV_SAMPLE_COUNT)
FfxFloat32x3 FFX_DNSR_Reflections_LoadRadiance(FfxInt32x2 pixel_coordinate)
{
return LoadRadiance(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat32x3 FFX_DNSR_Reflections_LoadRadianceReprojected(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat32x3)r_reprojected_radiance.Load(FfxInt32x3(pixel_coordinate, 0)).xyz;
}
#endif // #if defined (DENOISER_BIND_SRV_REPROJECTED_RADIANCE)
FfxFloat32 FFX_DNSR_Reflections_LoadVariance(FfxInt32x2 pixel_coordinate)
{
return LoadVariance(FfxInt32x3(pixel_coordinate, 0));
}
#if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
FfxFloat32 FFX_DNSR_Reflections_LoadNumSamples(FfxInt32x2 pixel_coordinate)
{
return (FfxFloat32)r_sample_count.Load(FfxInt32x3(pixel_coordinate, 0)).x;
}
#endif // #if defined (DENOISER_BIND_SRV_SAMPLE_COUNT)
#endif // #if defined(FFX_HALF)
FfxFloat32 FFX_DENOISER_LoadDepth(FfxInt32x2 pixel_coordinate, FfxInt32 mip)
{
#if defined(DENOISER_BIND_SRV_INPUT_DEPTH_HIERARCHY)
return r_input_depth_hierarchy.Load(FfxInt32x3(pixel_coordinate, mip));
#else
return 0.0f;
#endif
}
#if defined (DENOISER_BIND_UAV_DENOISER_TILE_LIST)
FfxUInt32 GetDenoiserTile(FfxUInt32 group_id)
{
return rw_denoiser_tile_list[group_id];
}
#endif // #if defined (DENOISER_BIND_UAV_DENOISER_TILE_LIST)
#if defined (DENOISER_BIND_SRV_INPUT_MOTION_VECTORS)
FfxFloat32x2 FFX_DNSR_Reflections_LoadMotionVector(FfxInt32x2 pixel_coordinate)
{
return MotionVectorScale() * r_input_motion_vectors.Load(FfxInt32x3(pixel_coordinate, 0));
}
#endif // #if defined (DENOISER_BIND_SRV_INPUT_MOTION_VECTORS)
FfxFloat32 FFX_DNSR_Reflections_LoadDepth(FfxInt32x2 pixel_coordinate)
{
return FFX_DENOISER_LoadDepth(pixel_coordinate, 0);
}
#if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
FfxFloat32 FFX_DNSR_Reflections_LoadDepthHistory(FfxInt32x2 pixel_coordinate)
{
return r_depth_history.Load(FfxInt32x3(pixel_coordinate, 0));
}
#endif // #if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
#if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
FfxFloat32 FFX_DNSR_Reflections_SampleDepthHistory(FfxFloat32x2 uv)
{
return r_depth_history.SampleLevel(s_LinearSampler, uv, 0.0f);
}
#endif // #if defined (DENOISER_BIND_SRV_DEPTH_HISTORY)
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,286 @@
// 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_DNSR_REFLECTIONS_COMMON
#define FFX_DNSR_REFLECTIONS_COMMON
#include "ffx_denoiser_reflections_config.h"
FfxBoolean FFX_DNSR_Reflections_IsGlossyReflection(FfxFloat32 roughness) {
return roughness < RoughnessThreshold();
}
FfxBoolean FFX_DNSR_Reflections_IsMirrorReflection(FfxFloat32 roughness) {
return roughness < 0.0001;
}
// Transforms origin to uv space
// Mat must be able to transform origin from its current space into clip space.
FfxFloat32x3 ProjectPosition(FfxFloat32x3 origin, FfxFloat32Mat4 mat) {
FfxFloat32x4 projected = FFX_MATRIX_MULTIPLY(mat, FfxFloat32x4(origin, 1));
projected.xyz /= projected.w;
projected.xy = 0.5 * projected.xy + 0.5;
projected.y = (1 - projected.y);
return projected.xyz;
}
// Mat must be able to transform origin from texture space to a linear space.
FfxFloat32x3 FFX_DNSR_InvProjectPosition(FfxFloat32x3 coord, FfxFloat32Mat4 mat) {
coord.y = (1 - coord.y);
coord.xy = 2 * coord.xy - 1;
FfxFloat32x4 projected = FFX_MATRIX_MULTIPLY(mat, FfxFloat32x4(coord, 1));
projected.xyz /= projected.w;
return projected.xyz;
}
FfxFloat32 FFX_DNSR_Reflections_GetLinearDepth(FfxFloat32x2 uv, FfxFloat32 depth) {
const FfxFloat32x3 view_space_pos = FFX_DNSR_InvProjectPosition(FfxFloat32x3(uv, depth), InvProjection());
return abs(view_space_pos.z);
}
FfxFloat32x3 FFX_DNSR_Reflections_ScreenSpaceToViewSpace(FfxFloat32x3 screen_uv_coord) {
return FFX_DNSR_InvProjectPosition(screen_uv_coord, InvProjection());
}
FfxFloat32x3 FFX_DNSR_Reflections_WorldSpaceToScreenSpacePrevious(FfxFloat32x3 world_space_pos) {
return ProjectPosition(world_space_pos, PrevViewProjection());
}
FfxFloat32x3 FFX_DNSR_Reflections_ViewSpaceToWorldSpace(FfxFloat32x4 view_space_coord) {
return FFX_MATRIX_MULTIPLY(InvView(), view_space_coord).xyz;
}
// Rounds value to the nearest multiple of 8
FfxUInt32x2 FFX_DNSR_Reflections_RoundUp8(FfxUInt32x2 value) {
FfxUInt32x2 round_down = value & ~7; // 0b111;
return FFX_SELECT((round_down == value), value, value + 8);
}
#if FFX_HALF
FfxFloat16 FFX_DNSR_Reflections_Luminance(FfxFloat16x3 color)
{
return max(FfxFloat16(dot(color, FfxFloat16x3(0.299f, 0.587f, 0.114f))), FfxFloat16(0.001));
}
FfxFloat16 FFX_DNSR_Reflections_ComputeTemporalVariance(FfxFloat16x3 history_radiance, FfxFloat16x3 radiance) {
FfxFloat16 history_luminance = FFX_DNSR_Reflections_Luminance(history_radiance);
FfxFloat16 luminance = FFX_DNSR_Reflections_Luminance(radiance);
FfxFloat16 diff = abs(history_luminance - luminance) / max(max(history_luminance, luminance), FfxFloat16(0.5f));
return diff * diff;
}
FfxUInt32 FFX_DNSR_Reflections_PackFloat16(FfxFloat16x2 v)
{
#if defined(FFX_GLSL)
return ffxPackHalf2x16(FfxFloat32x2(v));
#elif defined(FFX_HLSL)
FfxUInt32x2 p = ffxF32ToF16(FfxFloat32x2(v));
return p.x | (p.y << 16);
#endif
return 0;
}
FfxFloat16x2 FFX_DNSR_Reflections_UnpackFloat16(FfxUInt32 a)
{
#if defined(FFX_GLSL)
return FfxFloat16x2(unpackHalf2x16(a));
#elif defined(FFX_HLSL)
FfxFloat32x2 tmp = f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16));
return FfxFloat16x2(tmp);
#endif
return FfxFloat16x2(0.0f, 0.0f);
}
FfxUInt32x2 FFX_DNSR_Reflections_PackFloat16_4(FfxFloat16x4 v) { return FfxUInt32x2(FFX_DNSR_Reflections_PackFloat16(v.xy), FFX_DNSR_Reflections_PackFloat16(v.zw)); }
FfxFloat16x4 FFX_DNSR_Reflections_UnpackFloat16_4(FfxUInt32x2 a) { return FfxFloat16x4(FFX_DNSR_Reflections_UnpackFloat16(a.x), FFX_DNSR_Reflections_UnpackFloat16(a.y)); }
// From "Temporal Reprojection Anti-Aliasing"
// https://github.com/playdeadgames/temporal
/**********************************************************************
Copyright (c) [2015] [Playdead]
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.
********************************************************************/
FfxFloat16x3 FFX_DNSR_Reflections_ClipAABB(FfxFloat16x3 aabb_min, FfxFloat16x3 aabb_max, FfxFloat16x3 prev_sample) {
// Main idea behind clipping - it prevents clustering when neighbor color space
// is distant from history sample
// Here we find intersection between color vector and aabb color box
// Note: only clips towards aabb center
FfxFloat32x3 aabb_center = 0.5 * (aabb_max + aabb_min);
FfxFloat32x3 extent_clip = 0.5 * (aabb_max - aabb_min) + 0.001;
// Find color vector
FfxFloat32x3 color_vector = prev_sample - aabb_center;
// Transform into clip space
FfxFloat32x3 color_vector_clip = color_vector / extent_clip;
// Find max absolute component
color_vector_clip = abs(color_vector_clip);
FfxFloat16 max_abs_unit = FfxFloat16(max(max(color_vector_clip.x, color_vector_clip.y), color_vector_clip.z));
if (max_abs_unit > 1.0) {
return FfxFloat16x3(aabb_center + color_vector / max_abs_unit); // clip towards color vector
} else {
return FfxFloat16x3(prev_sample); // point is inside aabb
}
}
#ifdef FFX_DNSR_REFLECTIONS_ESTIMATES_LOCAL_NEIGHBORHOOD
# ifndef FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS
# define FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS 4
# endif
FfxFloat16 FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat16 i) {
const FfxFloat16 radius = FfxFloat16(FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS + 1.0f);
return FfxFloat16(exp(-FFX_DNSR_REFLECTIONS_GAUSSIAN_K * (i * i) / (radius * radius)));
}
#endif // FFX_DNSR_REFLECTIONS_ESTIMATES_LOCAL_NEIGHBORHOOD
#else // FFX_HALF
FfxFloat32 FFX_DNSR_Reflections_Luminance(FfxFloat32x3 color)
{
return max(FfxFloat32(dot(color, FfxFloat32x3(0.299f, 0.587f, 0.114f))), FfxFloat32(0.001));
}
FfxFloat32 FFX_DNSR_Reflections_ComputeTemporalVariance(FfxFloat32x3 history_radiance, FfxFloat32x3 radiance) {
FfxFloat32 history_luminance = FFX_DNSR_Reflections_Luminance(history_radiance);
FfxFloat32 luminance = FFX_DNSR_Reflections_Luminance(radiance);
FfxFloat32 diff = abs(history_luminance - luminance) / max(max(history_luminance, luminance), FfxFloat32(0.5f));
return diff * diff;
}
FfxUInt32 FFX_DNSR_Reflections_PackFloat16(FfxFloat32x2 v)
{
#if defined(FFX_GLSL)
return ffxPackHalf2x16(v);
#elif defined(FFX_HLSL)
FfxUInt32x2 p = ffxF32ToF16(v);
return p.x | (p.y << 16);
#endif
return 0;
}
FfxFloat32x2 FFX_DNSR_Reflections_UnpackFloat16(FfxUInt32 a)
{
#if defined(FFX_GLSL)
return unpackHalf2x16(a);
#elif defined(FFX_HLSL)
FfxFloat32x2 tmp = f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16));
return tmp;
#endif
return FfxFloat32x2(0.0f, 0.0f);
}
FfxUInt32x2 FFX_DNSR_Reflections_PackFloat16_4(FfxFloat32x4 v) { return FfxUInt32x2(FFX_DNSR_Reflections_PackFloat16(v.xy), FFX_DNSR_Reflections_PackFloat16(v.zw)); }
FfxFloat32x4 FFX_DNSR_Reflections_UnpackFloat16_4(FfxUInt32x2 a) { return FfxFloat32x4(FFX_DNSR_Reflections_UnpackFloat16(a.x), FFX_DNSR_Reflections_UnpackFloat16(a.y)); }
// From "Temporal Reprojection Anti-Aliasing"
// https://github.com/playdeadgames/temporal
/**********************************************************************
Copyright (c) [2015] [Playdead]
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 FFX_DNSR_Reflections_ClipAABB(FfxFloat32x3 aabb_min, FfxFloat32x3 aabb_max, FfxFloat32x3 prev_sample) {
// Main idea behind clipping - it prevents clustering when neighbor color space
// is distant from history sample
// Here we find intersection between color vector and aabb color box
// Note: only clips towards aabb center
FfxFloat32x3 aabb_center = 0.5 * (aabb_max + aabb_min);
FfxFloat32x3 extent_clip = 0.5 * (aabb_max - aabb_min) + 0.001;
// Find color vector
FfxFloat32x3 color_vector = prev_sample - aabb_center;
// Transform into clip space
FfxFloat32x3 color_vector_clip = color_vector / extent_clip;
// Find max absolute component
color_vector_clip = abs(color_vector_clip);
FfxFloat32 max_abs_unit = FfxFloat32(max(max(color_vector_clip.x, color_vector_clip.y), color_vector_clip.z));
if (max_abs_unit > 1.0) {
return FfxFloat32x3(aabb_center + color_vector / max_abs_unit); // clip towards color vector
} else {
return FfxFloat32x3(prev_sample); // point is inside aabb
}
}
#ifdef FFX_DNSR_REFLECTIONS_ESTIMATES_LOCAL_NEIGHBORHOOD
# ifndef FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS
# define FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS 4
# endif
FfxFloat32 FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat32 i) {
const FfxFloat32 radius = FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS + 1.0;
return exp(-FFX_DNSR_REFLECTIONS_GAUSSIAN_K * (i * i) / (radius * radius));
}
#endif // FFX_DNSR_REFLECTIONS_ESTIMATES_LOCAL_NEIGHBORHOOD
#endif // FFX_HALF
#endif // FFX_DNSR_REFLECTIONS_COMMON

View File

@@ -0,0 +1,41 @@
// 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_DNSR_REFLECTIONS_CONFIG
#define FFX_DNSR_REFLECTIONS_CONFIG
#define FFX_DNSR_REFLECTIONS_GAUSSIAN_K 3.0
#define FFX_DNSR_REFLECTIONS_RADIANCE_WEIGHT_BIAS 0.6
#define FFX_DNSR_REFLECTIONS_RADIANCE_WEIGHT_VARIANCE_K 0.1
#define FFX_DNSR_REFLECTIONS_AVG_RADIANCE_LUMINANCE_WEIGHT 0.3
#define FFX_DNSR_REFLECTIONS_PREFILTER_VARIANCE_WEIGHT 4.4
#define FFX_DNSR_REFLECTIONS_REPROJECT_SURFACE_DISCARD_VARIANCE_WEIGHT 1.5
#define FFX_DNSR_REFLECTIONS_PREFILTER_VARIANCE_BIAS 0.1
#define FFX_DNSR_REFLECTIONS_PREFILTER_NORMAL_SIGMA 512.0
#define FFX_DNSR_REFLECTIONS_PREFILTER_DEPTH_SIGMA 4.0
#define FFX_DNSR_REFLECTIONS_DISOCCLUSION_NORMAL_WEIGHT 1.4
#define FFX_DNSR_REFLECTIONS_DISOCCLUSION_DEPTH_WEIGHT 1.0
#define FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD 0.9
#define FFX_DNSR_REFLECTIONS_REPROJECTION_NORMAL_SIMILARITY_THRESHOLD 0.9999
#define FFX_DNSR_REFLECTIONS_SAMPLES_FOR_ROUGHNESS(r) (1.0 - exp(-r * 100.0))
#endif // FFX_DNSR_REFLECTIONS_CONFIG

View File

@@ -0,0 +1,340 @@
// 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_DNSR_REFLECTIONS_PREFILTER
#define FFX_DNSR_REFLECTIONS_PREFILTER
#include "ffx_denoiser_reflections_common.h"
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_0[16][16];
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_1[16][16];
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_2[16][16];
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_3[16][16];
FFX_GROUPSHARED FfxFloat32 g_ffx_dnsr_shared_depth[16][16];
#if FFX_HALF
struct FFX_DNSR_Reflections_NeighborhoodSample {
FfxFloat16x3 radiance;
FfxFloat16 variance;
FfxFloat16x3 normal;
FfxFloat32 depth;
};
FFX_DNSR_Reflections_NeighborhoodSample FFX_DNSR_Reflections_LoadFromGroupSharedMemory(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
FfxFloat16x4 unpacked_radiance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance);
FfxUInt32x2 packed_normal_variance = FfxUInt32x2(g_ffx_dnsr_shared_2[idx.y][idx.x], g_ffx_dnsr_shared_3[idx.y][idx.x]);
FfxFloat16x4 unpacked_normal_variance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_normal_variance);
FFX_DNSR_Reflections_NeighborhoodSample neighborSample;
neighborSample.radiance = unpacked_radiance.xyz;
neighborSample.normal = unpacked_normal_variance.xyz;
neighborSample.variance = unpacked_normal_variance.w;
neighborSample.depth = g_ffx_dnsr_shared_depth[idx.y][idx.x];
return neighborSample;
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat16x3 radiance, FfxFloat16 variance, FfxFloat16x3 normal, FfxFloat32 depth) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.zz);
g_ffx_dnsr_shared_2[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(normal.xy);
g_ffx_dnsr_shared_3[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(FfxFloat16x2(normal.z, variance));
g_ffx_dnsr_shared_depth[group_thread_id.y][group_thread_id.x] = depth;
}
void FFX_DNSR_Reflections_LoadNeighborhood(
FfxInt32x2 pixel_coordinate,
FFX_PARAMETER_OUT FfxFloat16x3 radiance,
FFX_PARAMETER_OUT FfxFloat16 variance,
FFX_PARAMETER_OUT FfxFloat16x3 normal,
FFX_PARAMETER_OUT FfxFloat32 depth,
FfxInt32x2 screen_size) {
radiance = LoadRadianceH(FfxInt32x3(pixel_coordinate, 0));
variance = LoadVarianceH(FfxInt32x3(pixel_coordinate, 0));
normal = FFX_DENOISER_LoadWorldSpaceNormalH(pixel_coordinate);
FfxFloat32x2 uv = (pixel_coordinate.xy + (0.5f).xx) / FfxFloat32x2(screen_size.xy);
depth = FFX_DNSR_Reflections_GetLinearDepth(uv, FFX_DENOISER_LoadDepth(pixel_coordinate, 0));
}
void FFX_DNSR_Reflections_InitializeGroupSharedMemory(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxInt32x2 screen_size) {
// Load 16x16 region into shared memory using 4 8x8 blocks.
FfxInt32x2 offset[4] = {FfxInt32x2(0, 0), FfxInt32x2(8, 0), FfxInt32x2(0, 8), FfxInt32x2(8, 8)};
// Intermediate storage registers to cache the result of all loads
FfxFloat16x3 radiance[4];
FfxFloat16 variance[4];
FfxFloat16x3 normal[4];
FfxFloat32 depth[4];
// Start in the upper left corner of the 16x16 region.
dispatch_thread_id -= 4;
// First store all loads in registers
for (FfxInt32 i = 0; i < 4; ++i) {
FFX_DNSR_Reflections_LoadNeighborhood(dispatch_thread_id + offset[i], radiance[i], variance[i], normal[i], depth[i], screen_size);
}
// Then move all registers to groupshared memory
for (FfxInt32 j = 0; j < 4; ++j) {
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id + offset[j], radiance[j], variance[j], normal[j], depth[j]); // X
}
}
FfxFloat16 FFX_DNSR_Reflections_GetEdgeStoppingNormalWeight(FfxFloat16x3 normal_p, FfxFloat16x3 normal_q) {
return FfxFloat16(pow(max(dot(normal_p, normal_q), FfxFloat16(0.0f)), FFX_DNSR_REFLECTIONS_PREFILTER_NORMAL_SIGMA));
}
FfxFloat16 FFX_DNSR_Reflections_GetEdgeStoppingDepthWeight(FfxFloat32 center_depth, FfxFloat32 neighbor_depth) {
return FfxFloat16(exp(-abs(center_depth - neighbor_depth) * center_depth * FFX_DNSR_REFLECTIONS_PREFILTER_DEPTH_SIGMA));
}
FfxFloat16 FFX_DNSR_Reflections_GetRadianceWeight(FfxFloat16x3 center_radiance, FfxFloat16x3 neighbor_radiance, FfxFloat16 variance) {
return FfxFloat16(max(exp(-(FFX_DNSR_REFLECTIONS_RADIANCE_WEIGHT_BIAS + variance * FFX_DNSR_REFLECTIONS_RADIANCE_WEIGHT_VARIANCE_K)
* length(center_radiance - neighbor_radiance.xyz))
, 1.0e-2));
}
void FFX_DNSR_Reflections_Resolve(FfxInt32x2 group_thread_id, FfxFloat16x3 avg_radiance, FFX_DNSR_Reflections_NeighborhoodSample center,
out FfxFloat16x3 resolved_radiance, out FfxFloat16 resolved_variance) {
// Initial weight is important to remove fireflies.
// That removes quite a bit of energy but makes everything much more stable.
FfxFloat16 accumulated_weight = FFX_DNSR_Reflections_GetRadianceWeight(avg_radiance, center.radiance.xyz, center.variance);
FfxFloat16x3 accumulated_radiance = center.radiance.xyz * accumulated_weight;
FfxFloat16 accumulated_variance = center.variance * accumulated_weight * accumulated_weight;
// First 15 numbers of Halton(2,3) streteched to [-3,3]. Skipping the center, as we already have that in center_radiance and center_variance.
const FfxUInt32 sample_count = 15;
const FfxInt32x2 sample_offsets[] = {FfxInt32x2(0, 1), FfxInt32x2(-2, 1), FfxInt32x2(2, -3), FfxInt32x2(-3, 0), FfxInt32x2(1, 2), FfxInt32x2(-1, -2), FfxInt32x2(3, 0), FfxInt32x2(-3, 3),
FfxInt32x2(0, -3), FfxInt32x2(-1, -1), FfxInt32x2(2, 1), FfxInt32x2(-2, -2), FfxInt32x2(1, 0), FfxInt32x2(0, 2), FfxInt32x2(3, -1)};
FfxFloat16 variance_weight = FfxFloat16(max(FFX_DNSR_REFLECTIONS_PREFILTER_VARIANCE_BIAS,
1.0 - exp(-(center.variance * FFX_DNSR_REFLECTIONS_PREFILTER_VARIANCE_WEIGHT))
));
for (FfxInt32 i = 0; i < sample_count; ++i) {
FfxInt32x2 new_idx = group_thread_id + sample_offsets[i];
FFX_DNSR_Reflections_NeighborhoodSample neighbor = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(new_idx);
FfxFloat16 weight = FfxFloat16(1.0f);
weight *= FFX_DNSR_Reflections_GetEdgeStoppingNormalWeight(center.normal, neighbor.normal);
weight *= FFX_DNSR_Reflections_GetEdgeStoppingDepthWeight(center.depth, neighbor.depth);
weight *= FFX_DNSR_Reflections_GetRadianceWeight(avg_radiance, neighbor.radiance.xyz, center.variance);
weight *= variance_weight;
// Accumulate all contributions.
accumulated_weight += weight;
accumulated_radiance += weight * neighbor.radiance.xyz;
accumulated_variance += weight * weight * neighbor.variance;
}
accumulated_radiance /= accumulated_weight;
accumulated_variance /= (accumulated_weight * accumulated_weight);
resolved_radiance = accumulated_radiance;
resolved_variance = accumulated_variance;
}
void FFX_DNSR_Reflections_Prefilter(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxUInt32x2 screen_size) {
FfxFloat16 center_roughness = FFX_DNSR_Reflections_LoadRoughness(dispatch_thread_id);
FFX_DNSR_Reflections_InitializeGroupSharedMemory(dispatch_thread_id, group_thread_id, FfxInt32x2(screen_size));
FFX_GROUP_MEMORY_BARRIER;
group_thread_id += 4; // Center threads in groupshared memory
FFX_DNSR_Reflections_NeighborhoodSample center = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(group_thread_id);
FfxFloat16x3 resolved_radiance = center.radiance;
FfxFloat16 resolved_variance = center.variance;
// Check if we have to denoise or if a simple copy is enough
bool needs_denoiser = center.variance > 0.0 && FFX_DNSR_Reflections_IsGlossyReflection(center_roughness) && !FFX_DNSR_Reflections_IsMirrorReflection(center_roughness);
if (needs_denoiser) {
FfxFloat32x2 uv8 = (FfxFloat32x2(dispatch_thread_id.xy) + (0.5).xx) / FFX_DNSR_Reflections_RoundUp8(screen_size);
FfxFloat16x3 avg_radiance = FFX_DNSR_Reflections_SampleAverageRadiance(uv8);
FFX_DNSR_Reflections_Resolve(group_thread_id, avg_radiance, center, resolved_radiance, resolved_variance);
}
FFX_DNSR_Reflections_StorePrefilteredReflections(dispatch_thread_id, resolved_radiance, resolved_variance);
}
#else // FFX_HALF
struct FFX_DNSR_Reflections_NeighborhoodSample {
FfxFloat32x3 radiance;
FfxFloat32 variance;
FfxFloat32x3 normal;
FfxFloat32 depth;
};
FFX_DNSR_Reflections_NeighborhoodSample FFX_DNSR_Reflections_LoadFromGroupSharedMemory(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
FfxFloat32x4 unpacked_radiance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance);
FfxUInt32x2 packed_normal_variance = FfxUInt32x2(g_ffx_dnsr_shared_2[idx.y][idx.x], g_ffx_dnsr_shared_3[idx.y][idx.x]);
FfxFloat32x4 unpacked_normal_variance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_normal_variance);
FFX_DNSR_Reflections_NeighborhoodSample neighborSample;
neighborSample.radiance = unpacked_radiance.xyz;
neighborSample.normal = unpacked_normal_variance.xyz;
neighborSample.variance = unpacked_normal_variance.w;
neighborSample.depth = g_ffx_dnsr_shared_depth[idx.y][idx.x];
return neighborSample;
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat32x3 radiance, FfxFloat32 variance, FfxFloat32x3 normal, FfxFloat32 depth) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.zz);
g_ffx_dnsr_shared_2[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(normal.xy);
g_ffx_dnsr_shared_3[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(FfxFloat32x2(normal.z, variance));
g_ffx_dnsr_shared_depth[group_thread_id.y][group_thread_id.x] = depth;
}
void FFX_DNSR_Reflections_LoadNeighborhood(
FfxInt32x2 pixel_coordinate,
FFX_PARAMETER_OUT FfxFloat32x3 radiance,
FFX_PARAMETER_OUT FfxFloat32 variance,
FFX_PARAMETER_OUT FfxFloat32x3 normal,
FFX_PARAMETER_OUT FfxFloat32 depth,
FfxInt32x2 screen_size) {
radiance = LoadRadiance(FfxInt32x3(pixel_coordinate, 0));
variance = LoadVariance(FfxInt32x3(pixel_coordinate, 0));
normal = FFX_DENOISER_LoadWorldSpaceNormal(pixel_coordinate);
FfxFloat32x2 uv = (pixel_coordinate.xy + (0.5f).xx) / FfxFloat32x2(screen_size.xy);
depth = FFX_DNSR_Reflections_GetLinearDepth(uv, FFX_DENOISER_LoadDepth(pixel_coordinate, 0));
}
void FFX_DNSR_Reflections_InitializeGroupSharedMemory(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxInt32x2 screen_size) {
// Load 16x16 region into shared memory using 4 8x8 blocks.
FfxInt32x2 offset[4] = {FfxInt32x2(0, 0), FfxInt32x2(8, 0), FfxInt32x2(0, 8), FfxInt32x2(8, 8)};
// Intermediate storage registers to cache the result of all loads
FfxFloat32x3 radiance[4];
FfxFloat32 variance[4];
FfxFloat32x3 normal[4];
FfxFloat32 depth[4];
// Start in the upper left corner of the 16x16 region.
dispatch_thread_id -= 4;
// First store all loads in registers
for (FfxInt32 i = 0; i < 4; ++i) {
FFX_DNSR_Reflections_LoadNeighborhood(dispatch_thread_id + offset[i], radiance[i], variance[i], normal[i], depth[i], screen_size);
}
// Then move all registers to groupshared memory
for (FfxInt32 j = 0; j < 4; ++j) {
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id + offset[j], radiance[j], variance[j], normal[j], depth[j]); // X
}
}
FfxFloat32 FFX_DNSR_Reflections_GetEdgeStoppingNormalWeight(FfxFloat32x3 normal_p, FfxFloat32x3 normal_q) {
return FfxFloat32(pow(max(dot(normal_p, normal_q), FfxFloat32(0.0f)), FFX_DNSR_REFLECTIONS_PREFILTER_NORMAL_SIGMA));
}
FfxFloat32 FFX_DNSR_Reflections_GetEdgeStoppingDepthWeight(FfxFloat32 center_depth, FfxFloat32 neighbor_depth) {
return FfxFloat32(exp(-abs(center_depth - neighbor_depth) * center_depth * FFX_DNSR_REFLECTIONS_PREFILTER_DEPTH_SIGMA));
}
FfxFloat32 FFX_DNSR_Reflections_GetRadianceWeight(FfxFloat32x3 center_radiance, FfxFloat32x3 neighbor_radiance, FfxFloat32 variance) {
return FfxFloat32(max(exp(-(FFX_DNSR_REFLECTIONS_RADIANCE_WEIGHT_BIAS + variance * FFX_DNSR_REFLECTIONS_RADIANCE_WEIGHT_VARIANCE_K)
* length(center_radiance - neighbor_radiance.xyz))
, 1.0e-2));
}
void FFX_DNSR_Reflections_Resolve(FfxInt32x2 group_thread_id, FfxFloat32x3 avg_radiance, FFX_DNSR_Reflections_NeighborhoodSample center,
out FfxFloat32x3 resolved_radiance, out FfxFloat32 resolved_variance) {
// Initial weight is important to remove fireflies.
// That removes quite a bit of energy but makes everything much more stable.
FfxFloat32 accumulated_weight = FFX_DNSR_Reflections_GetRadianceWeight(avg_radiance, center.radiance.xyz, center.variance);
FfxFloat32x3 accumulated_radiance = center.radiance.xyz * accumulated_weight;
FfxFloat32 accumulated_variance = center.variance * accumulated_weight * accumulated_weight;
// First 15 numbers of Halton(2,3) streteched to [-3,3]. Skipping the center, as we already have that in center_radiance and center_variance.
const FfxUInt32 sample_count = 15;
const FfxInt32x2 sample_offsets[] = {FfxInt32x2(0, 1), FfxInt32x2(-2, 1), FfxInt32x2(2, -3), FfxInt32x2(-3, 0), FfxInt32x2(1, 2), FfxInt32x2(-1, -2), FfxInt32x2(3, 0), FfxInt32x2(-3, 3),
FfxInt32x2(0, -3), FfxInt32x2(-1, -1), FfxInt32x2(2, 1), FfxInt32x2(-2, -2), FfxInt32x2(1, 0), FfxInt32x2(0, 2), FfxInt32x2(3, -1)};
FfxFloat32 variance_weight = FfxFloat32(max(FFX_DNSR_REFLECTIONS_PREFILTER_VARIANCE_BIAS,
1.0 - exp(-(center.variance * FFX_DNSR_REFLECTIONS_PREFILTER_VARIANCE_WEIGHT))
));
for (FfxInt32 i = 0; i < sample_count; ++i) {
FfxInt32x2 new_idx = group_thread_id + sample_offsets[i];
FFX_DNSR_Reflections_NeighborhoodSample neighbor = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(new_idx);
FfxFloat32 weight = FfxFloat32(1.0f);
weight *= FFX_DNSR_Reflections_GetEdgeStoppingNormalWeight(center.normal, neighbor.normal);
weight *= FFX_DNSR_Reflections_GetEdgeStoppingDepthWeight(center.depth, neighbor.depth);
weight *= FFX_DNSR_Reflections_GetRadianceWeight(avg_radiance, neighbor.radiance.xyz, center.variance);
weight *= variance_weight;
// Accumulate all contributions.
accumulated_weight += weight;
accumulated_radiance += weight * neighbor.radiance.xyz;
accumulated_variance += weight * weight * neighbor.variance;
}
accumulated_radiance /= accumulated_weight;
accumulated_variance /= (accumulated_weight * accumulated_weight);
resolved_radiance = accumulated_radiance;
resolved_variance = accumulated_variance;
}
void FFX_DNSR_Reflections_Prefilter(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxUInt32x2 screen_size) {
FfxFloat32 center_roughness = FFX_DNSR_Reflections_LoadRoughness(dispatch_thread_id);
FFX_DNSR_Reflections_InitializeGroupSharedMemory(dispatch_thread_id, group_thread_id, FfxInt32x2(screen_size));
FFX_GROUP_MEMORY_BARRIER;
group_thread_id += 4; // Center threads in groupshared memory
FFX_DNSR_Reflections_NeighborhoodSample center = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(group_thread_id);
FfxFloat32x3 resolved_radiance = center.radiance;
FfxFloat32 resolved_variance = center.variance;
// Check if we have to denoise or if a simple copy is enough
bool needs_denoiser = center.variance > 0.0 && FFX_DNSR_Reflections_IsGlossyReflection(center_roughness) && !FFX_DNSR_Reflections_IsMirrorReflection(center_roughness);
if (needs_denoiser) {
FfxFloat32x2 uv8 = (FfxFloat32x2(dispatch_thread_id.xy) + (0.5).xx) / FFX_DNSR_Reflections_RoundUp8(screen_size);
FfxFloat32x3 avg_radiance = FFX_DNSR_Reflections_SampleAverageRadiance(uv8);
FFX_DNSR_Reflections_Resolve(group_thread_id, avg_radiance, center, resolved_radiance, resolved_variance);
}
FFX_DNSR_Reflections_StorePrefilteredReflections(dispatch_thread_id, resolved_radiance, resolved_variance);
}
#endif // FFX_HALF
void Prefilter(FfxUInt32 group_index, FfxUInt32 group_id, FfxInt32x2 group_thread_id) {
FfxUInt32 packed_coords = GetDenoiserTile(group_id);
FfxInt32x2 dispatch_thread_id = FfxInt32x2(packed_coords & 0xffffu, (packed_coords >> 16) & 0xffffu) + group_thread_id;
FfxInt32x2 dispatch_group_id = dispatch_thread_id / 8;
FfxInt32x2 remapped_group_thread_id = FfxInt32x2(ffxRemapForWaveReduction(group_index));
FfxInt32x2 remapped_dispatch_thread_id = FfxInt32x2(dispatch_group_id * 8 + remapped_group_thread_id);
FFX_DNSR_Reflections_Prefilter(remapped_dispatch_thread_id, remapped_group_thread_id, RenderSize());
}
#else
void Prefilter(FfxUInt32 group_index, FfxUInt32 group_id, FfxInt32x2 group_thread_id) {}
#endif // FFX_DNSR_REFLECTIONS_PREFILTER

View File

@@ -0,0 +1,722 @@
// 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_DNSR_REFLECTIONS_REPROJECT
#define FFX_DNSR_REFLECTIONS_REPROJECT
#define FFX_DNSR_REFLECTIONS_ESTIMATES_LOCAL_NEIGHBORHOOD
#include "ffx_denoiser_reflections_common.h"
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_0[16][16];
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_1[16][16];
#if FFX_HALF
struct FFX_DNSR_Reflections_NeighborhoodSample {
FfxFloat16x3 radiance;
};
FFX_DNSR_Reflections_NeighborhoodSample FFX_DNSR_Reflections_LoadFromGroupSharedMemory(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
FfxFloat16x4 unpacked_radiance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance);
FFX_DNSR_Reflections_NeighborhoodSample neighborSample;
neighborSample.radiance = unpacked_radiance.xyz;
return neighborSample;
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat16x3 radiance) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.zz);
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat16x4 radiance_variance) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance_variance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance_variance.zw);
}
void FFX_DNSR_Reflections_InitializeGroupSharedMemory(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxInt32x2 screen_size) {
// Load 16x16 region into shared memory using 4 8x8 blocks.
FfxInt32x2 offset[4] = {FfxInt32x2(0, 0), FfxInt32x2(8, 0), FfxInt32x2(0, 8), FfxInt32x2(8, 8)};
// Intermediate storage registers to cache the result of all loads
FfxFloat16x3 radiance[4];
// Start in the upper left corner of the 16x16 region.
dispatch_thread_id -= 4;
// First store all loads in registers
for (FfxInt32 i = 0; i < 4; ++i) {
radiance[i] = FFX_DNSR_Reflections_LoadRadiance(dispatch_thread_id + offset[i]);
}
// Then move all registers to FFX_GROUPSHARED memory
for (FfxInt32 j = 0; j < 4; ++j) {
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id + offset[j], radiance[j]); // X
}
}
FfxFloat16x4 FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
return FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance);
}
FfxFloat16 FFX_DNSR_Reflections_GetLuminanceWeight(FfxFloat16x3 val) {
FfxFloat16 luma = FFX_DNSR_Reflections_Luminance(val.xyz);
FfxFloat16 weight = FfxFloat16(max(exp(-luma * FFX_DNSR_REFLECTIONS_AVG_RADIANCE_LUMINANCE_WEIGHT), 1.0e-2));
return weight;
}
FfxFloat32x2 FFX_DNSR_Reflections_GetSurfaceReprojection(FfxInt32x2 dispatch_thread_id, FfxFloat32x2 uv, FfxFloat32x2 motion_vector) {
// Reflector position reprojection
FfxFloat32x2 history_uv = uv + motion_vector;
return history_uv;
}
FfxFloat32x2 FFX_DNSR_Reflections_GetHitPositionReprojection(FfxInt32x2 dispatch_thread_id, FfxFloat32x2 uv, FfxFloat32 reflected_ray_length) {
FfxFloat32 z = FFX_DNSR_Reflections_LoadDepth(dispatch_thread_id);
FfxFloat32x3 view_space_ray = FFX_DNSR_Reflections_ScreenSpaceToViewSpace(FfxFloat32x3(uv, z));
// We start out with reconstructing the ray length in view space.
// This includes the portion from the camera to the reflecting surface as well as the portion from the surface to the hit position.
FfxFloat32 surface_depth = length(view_space_ray);
FfxFloat32 ray_length = surface_depth + reflected_ray_length;
// We then perform a parallax correction by shooting a ray
// of the same length "straight through" the reflecting surface
// and reprojecting the tip of that ray to the previous frame.
view_space_ray /= surface_depth; // == normalize(view_space_ray)
view_space_ray *= ray_length;
FfxFloat32x3 world_hit_position =
FFX_DNSR_Reflections_ViewSpaceToWorldSpace(FfxFloat32x4(view_space_ray, 1)); // This is the "fake" hit position if we would follow the ray straight through the surface.
FfxFloat32x3 prev_hit_position = FFX_DNSR_Reflections_WorldSpaceToScreenSpacePrevious(world_hit_position);
FfxFloat32x2 history_uv = prev_hit_position.xy;
return history_uv;
}
FfxFloat16 FFX_DNSR_Reflections_GetDisocclusionFactor(FfxFloat16x3 normal, FfxFloat16x3 history_normal, FfxFloat32 linear_depth, FfxFloat32 history_linear_depth) {
FfxFloat16 factor = FfxFloat16(1.0f //
* exp(-abs(1.0f - max(FfxFloat16(0.0f), dot(normal, history_normal))) * FFX_DNSR_REFLECTIONS_DISOCCLUSION_NORMAL_WEIGHT) //
* exp(-abs(history_linear_depth - linear_depth) / linear_depth * FFX_DNSR_REFLECTIONS_DISOCCLUSION_DEPTH_WEIGHT));
return factor;
}
struct FFX_DNSR_Reflections_Moments {
FfxFloat16x3 mean;
FfxFloat16x3 variance;
};
FFX_DNSR_Reflections_Moments FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(FfxInt32x2 group_thread_id) {
FFX_DNSR_Reflections_Moments estimate;
estimate.mean = FfxFloat16x3(0.0f, 0.0f, 0.0f);
estimate.variance = FfxFloat16x3(0.0f, 0.0f, 0.0f);
FfxFloat16 accumulated_weight = FfxFloat16(0.0f);
for (FfxInt32 j = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; j <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++j) {
for (FfxInt32 i = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; i <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++i) {
FfxInt32x2 new_idx = group_thread_id + FfxInt32x2(i, j);
FfxFloat16x3 radiance = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(new_idx).radiance;
FfxFloat16 weight = FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat16(i)) * FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat16(j));
accumulated_weight += weight;
estimate.mean += radiance * weight;
estimate.variance += radiance * radiance * weight;
}
}
estimate.mean /= accumulated_weight;
estimate.variance /= accumulated_weight;
estimate.variance = abs(estimate.variance - estimate.mean * estimate.mean);
return estimate;
}
FfxFloat32 dot2(FfxFloat32x3 a) { return dot(a, a); }
void FFX_DNSR_Reflections_PickReprojection(FfxInt32x2 dispatch_thread_id, //
FfxInt32x2 group_thread_id, //
FfxUInt32x2 screen_size, //
FfxFloat16 roughness, //
FfxFloat16 ray_length, //
out FfxFloat16 disocclusion_factor, //
out FfxFloat32x2 reprojection_uv, //
out FfxFloat16x3 reprojection) {
FFX_DNSR_Reflections_Moments local_neighborhood = FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(group_thread_id);
FfxFloat32x2 uv = FfxFloat32x2(dispatch_thread_id.x + 0.5, dispatch_thread_id.y + 0.5) / screen_size;
FfxFloat16x3 normal = FFX_DNSR_Reflections_LoadWorldSpaceNormal(dispatch_thread_id);
FfxFloat16x3 history_normal;
FfxFloat32 history_linear_depth;
{
const FfxFloat32x2 motion_vector = FFX_DNSR_Reflections_LoadMotionVector(dispatch_thread_id);
const FfxFloat32x2 surface_reprojection_uv = FFX_DNSR_Reflections_GetSurfaceReprojection(dispatch_thread_id, uv, motion_vector);
const FfxFloat32x2 hit_reprojection_uv = FFX_DNSR_Reflections_GetHitPositionReprojection(dispatch_thread_id, uv, ray_length);
const FfxFloat16x3 surface_normal = FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(surface_reprojection_uv);
const FfxFloat16x3 hit_normal = FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(hit_reprojection_uv);
const FfxFloat16x3 surface_history = FFX_DNSR_Reflections_SampleRadianceHistory(surface_reprojection_uv);
const FfxFloat16x3 hit_history = FFX_DNSR_Reflections_SampleRadianceHistory(hit_reprojection_uv);
const FfxFloat32 hit_normal_similarity = dot(normalize(FfxFloat32x3(hit_normal)), normalize(FfxFloat32x3(normal)));
const FfxFloat32 surface_normal_similarity = dot(normalize(FfxFloat32x3(surface_normal)), normalize(FfxFloat32x3(normal)));
const FfxFloat16 hit_roughness = FFX_DNSR_Reflections_SampleRoughnessHistory(hit_reprojection_uv);
const FfxFloat16 surface_roughness = FFX_DNSR_Reflections_SampleRoughnessHistory(surface_reprojection_uv);
// Choose reprojection uv based on similarity to the local neighborhood.
if (hit_normal_similarity > FFX_DNSR_REFLECTIONS_REPROJECTION_NORMAL_SIMILARITY_THRESHOLD // Candidate for mirror reflection parallax
&& hit_normal_similarity + 1.0e-3 > surface_normal_similarity //
&& abs(hit_roughness - roughness) < abs(surface_roughness - roughness) + 1.0e-3 //
) {
history_normal = hit_normal;
FfxFloat32 hit_history_depth = FFX_DNSR_Reflections_SampleDepthHistory(hit_reprojection_uv);
FfxFloat32 hit_history_linear_depth = FFX_DNSR_Reflections_GetLinearDepth(hit_reprojection_uv, hit_history_depth);
history_linear_depth = hit_history_linear_depth;
reprojection_uv = hit_reprojection_uv;
reprojection = hit_history;
} else {
// Reject surface reprojection based on simple distance
if (dot2(surface_history - local_neighborhood.mean) <
FFX_DNSR_REFLECTIONS_REPROJECT_SURFACE_DISCARD_VARIANCE_WEIGHT * length(local_neighborhood.variance)) {
history_normal = surface_normal;
FfxFloat32 surface_history_depth = FFX_DNSR_Reflections_SampleDepthHistory(surface_reprojection_uv);
FfxFloat32 surface_history_linear_depth = FFX_DNSR_Reflections_GetLinearDepth(surface_reprojection_uv, surface_history_depth);
history_linear_depth = surface_history_linear_depth;
reprojection_uv = surface_reprojection_uv;
reprojection = surface_history;
} else {
disocclusion_factor = FfxFloat16(0.0f);
return;
}
}
}
FfxFloat32 depth = FFX_DNSR_Reflections_LoadDepth(dispatch_thread_id);
FfxFloat32 linear_depth = FFX_DNSR_Reflections_GetLinearDepth(uv, depth);
// Determine disocclusion factor based on history
disocclusion_factor = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, history_normal, linear_depth, history_linear_depth);
if (disocclusion_factor > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) // Early out, good enough
return;
// Try to find the closest sample in the vicinity if we are not convinced of a disocclusion
if (disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) {
FfxFloat32x2 closest_uv = reprojection_uv;
FfxFloat32x2 dudv = 1.0 / FfxFloat32x2(screen_size);
const FfxInt32 search_radius = 1;
for (FfxInt32 y = -search_radius; y <= search_radius; y++) {
for (FfxInt32 x = -search_radius; x <= search_radius; x++) {
FfxFloat32x2 uv = reprojection_uv + FfxFloat32x2(x, y) * dudv;
FfxFloat16x3 history_normal = FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(uv);
FfxFloat32 history_depth = FFX_DNSR_Reflections_SampleDepthHistory(uv);
FfxFloat32 history_linear_depth = FFX_DNSR_Reflections_GetLinearDepth(uv, history_depth);
FfxFloat16 weight = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, history_normal, linear_depth, history_linear_depth);
if (weight > disocclusion_factor) {
disocclusion_factor = weight;
closest_uv = uv;
reprojection_uv = closest_uv;
}
}
}
reprojection = FFX_DNSR_Reflections_SampleRadianceHistory(reprojection_uv);
}
// Rare slow path - triggered only on the edges.
// Try to get rid of potential leaks at bilinear interpolation level.
if (disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) {
// If we've got a discarded history, try to construct a better sample out of 2x2 interpolation neighborhood
// Helps quite a bit on the edges in movement
FfxFloat32 uvx = ffxFract(FfxFloat32(screen_size.x) * reprojection_uv.x + 0.5);
FfxFloat32 uvy = ffxFract(FfxFloat32(screen_size.y) * reprojection_uv.y + 0.5);
FfxInt32x2 reproject_texel_coords = FfxInt32x2(screen_size * reprojection_uv - 0.5);
FfxFloat16x3 reprojection00 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(0, 0));
FfxFloat16x3 reprojection10 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(1, 0));
FfxFloat16x3 reprojection01 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(0, 1));
FfxFloat16x3 reprojection11 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(1, 1));
FfxFloat16x3 normal00 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(0, 0));
FfxFloat16x3 normal10 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(1, 0));
FfxFloat16x3 normal01 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(0, 1));
FfxFloat16x3 normal11 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(1, 1));
FfxFloat32 depth00 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(0, 0)));
FfxFloat32 depth10 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(1, 0)));
FfxFloat32 depth01 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(0, 1)));
FfxFloat32 depth11 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(1, 1)));
FfxFloat16x4 w = FfxFloat16x4(1.0f, 1.0f, 1.0f, 1.0f);
// Initialize with occlusion weights
w.x = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal00, linear_depth, depth00) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat16(1.0f) : FfxFloat16(0.0f);
w.y = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal10, linear_depth, depth10) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat16(1.0f) : FfxFloat16(0.0f);
w.z = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal01, linear_depth, depth01) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat16(1.0f) : FfxFloat16(0.0f);
w.w = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal11, linear_depth, depth11) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat16(1.0f) : FfxFloat16(0.0f);
// And then mix in bilinear weights
w.x = FfxFloat16(w.x * (1.0 - uvx) * (1.0 - uvy));
w.y = FfxFloat16(w.y * (uvx) * (1.0 - uvy));
w.z = FfxFloat16(w.z * (1.0 - uvx) * (uvy));
w.w = FfxFloat16(w.w * (uvx) * (uvy));
FfxFloat16 ws = max(w.x + w.y + w.z + w.w, FfxFloat16(1.0e-3));
// normalize
w /= ws;
FfxFloat16x3 history_normal;
FfxFloat32 history_linear_depth;
reprojection = reprojection00 * w.x + reprojection10 * w.y + reprojection01 * w.z + reprojection11 * w.w;
history_linear_depth = depth00 * w.x + depth10 * w.y + depth01 * w.z + depth11 * w.w;
history_normal = normal00 * w.x + normal10 * w.y + normal01 * w.z + normal11 * w.w;
disocclusion_factor = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, history_normal, linear_depth, history_linear_depth);
}
disocclusion_factor = disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD ? FfxFloat16(0.0f) : disocclusion_factor;
}
void FFX_DNSR_Reflections_Reproject(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxUInt32x2 screen_size, FfxFloat32 temporal_stability_factor, FfxInt32 max_samples) {
FFX_DNSR_Reflections_InitializeGroupSharedMemory(dispatch_thread_id, group_thread_id, FfxInt32x2(screen_size));
FFX_GROUP_MEMORY_BARRIER;
group_thread_id += 4; // Center threads in FFX_GROUPSHARED memory
FfxFloat16 variance = FfxFloat16(1.0f);
FfxFloat16 num_samples = FfxFloat16(0.0f);
FfxFloat16 roughness = FFX_DNSR_Reflections_LoadRoughness(dispatch_thread_id);
FfxFloat32x3 normal = FFX_DNSR_Reflections_LoadWorldSpaceNormal(dispatch_thread_id);
FfxFloat16x3 radiance = FFX_DNSR_Reflections_LoadRadiance(dispatch_thread_id);
const FfxFloat16 ray_length = FFX_DNSR_Reflections_LoadRayLength(dispatch_thread_id);
if (FFX_DNSR_Reflections_IsGlossyReflection(roughness)) {
FfxFloat16 disocclusion_factor;
FfxFloat32x2 reprojection_uv;
FfxFloat16x3 reprojection;
FFX_DNSR_Reflections_PickReprojection(/*in*/ dispatch_thread_id,
/* in */ group_thread_id,
/* in */ screen_size,
/* in */ roughness,
/* in */ ray_length,
/* out */ disocclusion_factor,
/* out */ reprojection_uv,
/* out */ reprojection);
if ( (reprojection_uv.x > 0.0f) && (reprojection_uv.y > 0.0f) && (reprojection_uv.x < 1.0f) && (reprojection_uv.y< 1.0f)) {
FfxFloat16 prev_variance = FFX_DNSR_Reflections_SampleVarianceHistory(reprojection_uv);
num_samples = FFX_DNSR_Reflections_SampleNumSamplesHistory(reprojection_uv) * disocclusion_factor;
FfxFloat16 s_max_samples = FfxFloat16(max(8.0f, FfxFloat16(max_samples) * FFX_DNSR_REFLECTIONS_SAMPLES_FOR_ROUGHNESS(roughness)));
num_samples = min(s_max_samples, num_samples + FfxFloat16(1.0f));
FfxFloat16 new_variance = FFX_DNSR_Reflections_ComputeTemporalVariance(radiance.xyz, reprojection.xyz);
if (disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) {
FFX_DNSR_Reflections_StoreRadianceReprojected(dispatch_thread_id, FfxFloat16x3(0.0f, 0.0f, 0.0f));
FFX_DNSR_Reflections_StoreVariance(dispatch_thread_id, FfxFloat16(1.0f));
FFX_DNSR_Reflections_StoreNumSamples(dispatch_thread_id, FfxFloat16(1.0f));
} else {
FfxFloat16 variance_mix = ffxLerp(new_variance, prev_variance, FfxFloat16(1.0f / num_samples));
FFX_DNSR_Reflections_StoreRadianceReprojected(dispatch_thread_id, reprojection);
FFX_DNSR_Reflections_StoreVariance(dispatch_thread_id, variance_mix);
FFX_DNSR_Reflections_StoreNumSamples(dispatch_thread_id, num_samples);
// Mix in reprojection for radiance mip computation
radiance = ffxLerp(radiance, reprojection, FfxFloat16(0.3f));
}
} else {
FFX_DNSR_Reflections_StoreRadianceReprojected(dispatch_thread_id, FfxFloat16x3(0.0f, 0.0f, 0.0f));
FFX_DNSR_Reflections_StoreVariance(dispatch_thread_id, FfxFloat16(1.0f));
FFX_DNSR_Reflections_StoreNumSamples(dispatch_thread_id, FfxFloat16(1.0f));
}
}
// Downsample 8x8 -> 1 radiance using FFX_GROUPSHARED memory
// Initialize FFX_GROUPSHARED array for downsampling
FfxFloat16 weight = FFX_DNSR_Reflections_GetLuminanceWeight(radiance.xyz);
radiance.xyz *= weight;
if ( (dispatch_thread_id.x >= screen_size.x) || (dispatch_thread_id.y >= screen_size.y) || any(isinf(radiance)) || any(isnan(radiance)) || (weight > FfxFloat16(1.0e3))) {
radiance = FfxFloat16x3(0.0f, 0.0f, 0.0f);
weight = FfxFloat16(0.0f);
}
group_thread_id -= 4; // Center threads in FFX_GROUPSHARED memory
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id, FfxFloat16x4(radiance.xyz, weight));
FFX_GROUP_MEMORY_BARRIER;
for (FfxInt32 i = 2; i <= 8; i = i * 2) {
FfxInt32 ox = group_thread_id.x * i;
FfxInt32 oy = group_thread_id.y * i;
FfxInt32 ix = group_thread_id.x * i + i / 2;
FfxInt32 iy = group_thread_id.y * i + i / 2;
if (ix < 8 && iy < 8) {
FfxFloat16x4 rad_weight00 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ox, oy));
FfxFloat16x4 rad_weight10 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ox, iy));
FfxFloat16x4 rad_weight01 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ix, oy));
FfxFloat16x4 rad_weight11 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ix, iy));
FfxFloat16x4 sum = rad_weight00 + rad_weight01 + rad_weight10 + rad_weight11;
FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2(ox, oy), sum);
}
FFX_GROUP_MEMORY_BARRIER;
}
if ((group_thread_id.x == 0) && (group_thread_id.y == 0)) {
FfxFloat16x4 sum = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(0, 0));
FfxFloat16 weight_acc = max(sum.w, FfxFloat16(1.0e-3));
FfxFloat32x3 radiance_avg = sum.xyz / weight_acc;
FFX_DNSR_Reflections_StoreAverageRadiance(dispatch_thread_id.xy / 8, FfxFloat16x3(radiance_avg));
}
}
#else // #if FFX_HALF
struct FFX_DNSR_Reflections_NeighborhoodSample {
FfxFloat32x3 radiance;
};
FFX_DNSR_Reflections_NeighborhoodSample FFX_DNSR_Reflections_LoadFromGroupSharedMemory(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
FfxFloat32x4 unpacked_radiance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance);
FFX_DNSR_Reflections_NeighborhoodSample neighborSample;
neighborSample.radiance = unpacked_radiance.xyz;
return neighborSample;
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat32x3 radiance) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.zz);
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat32x4 radiance_variance) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance_variance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance_variance.zw);
}
void FFX_DNSR_Reflections_InitializeGroupSharedMemory(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxInt32x2 screen_size) {
// Load 16x16 region into shared memory using 4 8x8 blocks.
FfxInt32x2 offset[4] = {FfxInt32x2(0, 0), FfxInt32x2(8, 0), FfxInt32x2(0, 8), FfxInt32x2(8, 8)};
// Intermediate storage registers to cache the result of all loads
FfxFloat32x3 radiance[4];
// Start in the upper left corner of the 16x16 region.
dispatch_thread_id -= 4;
// First store all loads in registers
for (FfxInt32 i = 0; i < 4; ++i) {
radiance[i] = FFX_DNSR_Reflections_LoadRadiance(dispatch_thread_id + offset[i]);
}
// Then move all registers to FFX_GROUPSHARED memory
for (FfxInt32 j = 0; j < 4; ++j) {
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id + offset[j], radiance[j]); // X
}
}
FfxFloat32x4 FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
return FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance);
}
FfxFloat32 FFX_DNSR_Reflections_GetLuminanceWeight(FfxFloat32x3 val) {
FfxFloat32 luma = FFX_DNSR_Reflections_Luminance(val.xyz);
FfxFloat32 weight = FfxFloat32(max(exp(-luma * FFX_DNSR_REFLECTIONS_AVG_RADIANCE_LUMINANCE_WEIGHT), 1.0e-2));
return weight;
}
FfxFloat32x2 FFX_DNSR_Reflections_GetSurfaceReprojection(FfxInt32x2 dispatch_thread_id, FfxFloat32x2 uv, FfxFloat32x2 motion_vector) {
// Reflector position reprojection
FfxFloat32x2 history_uv = uv + motion_vector;
return history_uv;
}
FfxFloat32x2 FFX_DNSR_Reflections_GetHitPositionReprojection(FfxInt32x2 dispatch_thread_id, FfxFloat32x2 uv, FfxFloat32 reflected_ray_length) {
FfxFloat32 z = FFX_DNSR_Reflections_LoadDepth(dispatch_thread_id);
FfxFloat32x3 view_space_ray = FFX_DNSR_Reflections_ScreenSpaceToViewSpace(FfxFloat32x3(uv, z));
// We start out with reconstructing the ray length in view space.
// This includes the portion from the camera to the reflecting surface as well as the portion from the surface to the hit position.
FfxFloat32 surface_depth = length(view_space_ray);
FfxFloat32 ray_length = surface_depth + reflected_ray_length;
// We then perform a parallax correction by shooting a ray
// of the same length "straight through" the reflecting surface
// and reprojecting the tip of that ray to the previous frame.
view_space_ray /= surface_depth; // == normalize(view_space_ray)
view_space_ray *= ray_length;
FfxFloat32x3 world_hit_position =
FFX_DNSR_Reflections_ViewSpaceToWorldSpace(FfxFloat32x4(view_space_ray, 1)); // This is the "fake" hit position if we would follow the ray straight through the surface.
FfxFloat32x3 prev_hit_position = FFX_DNSR_Reflections_WorldSpaceToScreenSpacePrevious(world_hit_position);
FfxFloat32x2 history_uv = prev_hit_position.xy;
return history_uv;
}
FfxFloat32 FFX_DNSR_Reflections_GetDisocclusionFactor(FfxFloat32x3 normal, FfxFloat32x3 history_normal, FfxFloat32 linear_depth, FfxFloat32 history_linear_depth) {
FfxFloat32 factor = FfxFloat32(1.0f //
* exp(-abs(1.0f - max(FfxFloat32(0.0f), dot(normal, history_normal))) * FFX_DNSR_REFLECTIONS_DISOCCLUSION_NORMAL_WEIGHT) //
* exp(-abs(history_linear_depth - linear_depth) / linear_depth * FFX_DNSR_REFLECTIONS_DISOCCLUSION_DEPTH_WEIGHT));
return factor;
}
struct FFX_DNSR_Reflections_Moments {
FfxFloat32x3 mean;
FfxFloat32x3 variance;
};
FFX_DNSR_Reflections_Moments FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(FfxInt32x2 group_thread_id) {
FFX_DNSR_Reflections_Moments estimate;
estimate.mean = FfxFloat32x3(0.0f, 0.0f, 0.0f);
estimate.variance = FfxFloat32x3(0.0f, 0.0f, 0.0f);
FfxFloat32 accumulated_weight = FfxFloat32(0.0f);
for (FfxInt32 j = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; j <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++j) {
for (FfxInt32 i = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; i <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++i) {
FfxInt32x2 new_idx = group_thread_id + FfxInt32x2(i, j);
FfxFloat32x3 radiance = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(new_idx).radiance;
FfxFloat32 weight = FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat32(i)) * FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat32(j));
accumulated_weight += weight;
estimate.mean += radiance * weight;
estimate.variance += radiance * radiance * weight;
}
}
estimate.mean /= accumulated_weight;
estimate.variance /= accumulated_weight;
estimate.variance = abs(estimate.variance - estimate.mean * estimate.mean);
return estimate;
}
FfxFloat32 dot2(FfxFloat32x3 a) { return dot(a, a); }
void FFX_DNSR_Reflections_PickReprojection(FfxInt32x2 dispatch_thread_id, //
FfxInt32x2 group_thread_id, //
FfxUInt32x2 screen_size, //
FfxFloat32 roughness, //
FfxFloat32 ray_length, //
out FfxFloat32 disocclusion_factor, //
out FfxFloat32x2 reprojection_uv, //
out FfxFloat32x3 reprojection) {
FFX_DNSR_Reflections_Moments local_neighborhood = FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(group_thread_id);
FfxFloat32x2 uv = FfxFloat32x2(dispatch_thread_id.x + 0.5, dispatch_thread_id.y + 0.5) / screen_size;
FfxFloat32x3 normal = FFX_DNSR_Reflections_LoadWorldSpaceNormal(dispatch_thread_id);
FfxFloat32x3 history_normal;
FfxFloat32 history_linear_depth;
{
const FfxFloat32x2 motion_vector = FFX_DNSR_Reflections_LoadMotionVector(dispatch_thread_id);
const FfxFloat32x2 surface_reprojection_uv = FFX_DNSR_Reflections_GetSurfaceReprojection(dispatch_thread_id, uv, motion_vector);
const FfxFloat32x2 hit_reprojection_uv = FFX_DNSR_Reflections_GetHitPositionReprojection(dispatch_thread_id, uv, ray_length);
const FfxFloat32x3 surface_normal = FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(surface_reprojection_uv);
const FfxFloat32x3 hit_normal = FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(hit_reprojection_uv);
const FfxFloat32x3 surface_history = FFX_DNSR_Reflections_SampleRadianceHistory(surface_reprojection_uv);
const FfxFloat32x3 hit_history = FFX_DNSR_Reflections_SampleRadianceHistory(hit_reprojection_uv);
const FfxFloat32 hit_normal_similarity = dot(normalize(FfxFloat32x3(hit_normal)), normalize(FfxFloat32x3(normal)));
const FfxFloat32 surface_normal_similarity = dot(normalize(FfxFloat32x3(surface_normal)), normalize(FfxFloat32x3(normal)));
const FfxFloat32 hit_roughness = FFX_DNSR_Reflections_SampleRoughnessHistory(hit_reprojection_uv);
const FfxFloat32 surface_roughness = FFX_DNSR_Reflections_SampleRoughnessHistory(surface_reprojection_uv);
// Choose reprojection uv based on similarity to the local neighborhood.
if (hit_normal_similarity > FFX_DNSR_REFLECTIONS_REPROJECTION_NORMAL_SIMILARITY_THRESHOLD // Candidate for mirror reflection parallax
&& hit_normal_similarity + 1.0e-3 > surface_normal_similarity //
&& abs(hit_roughness - roughness) < abs(surface_roughness - roughness) + 1.0e-3 //
) {
history_normal = hit_normal;
FfxFloat32 hit_history_depth = FFX_DNSR_Reflections_SampleDepthHistory(hit_reprojection_uv);
FfxFloat32 hit_history_linear_depth = FFX_DNSR_Reflections_GetLinearDepth(hit_reprojection_uv, hit_history_depth);
history_linear_depth = hit_history_linear_depth;
reprojection_uv = hit_reprojection_uv;
reprojection = hit_history;
} else {
// Reject surface reprojection based on simple distance
if (dot2(surface_history - local_neighborhood.mean) <
FFX_DNSR_REFLECTIONS_REPROJECT_SURFACE_DISCARD_VARIANCE_WEIGHT * length(local_neighborhood.variance)) {
history_normal = surface_normal;
FfxFloat32 surface_history_depth = FFX_DNSR_Reflections_SampleDepthHistory(surface_reprojection_uv);
FfxFloat32 surface_history_linear_depth = FFX_DNSR_Reflections_GetLinearDepth(surface_reprojection_uv, surface_history_depth);
history_linear_depth = surface_history_linear_depth;
reprojection_uv = surface_reprojection_uv;
reprojection = surface_history;
} else {
disocclusion_factor = FfxFloat32(0.0f);
return;
}
}
}
FfxFloat32 depth = FFX_DNSR_Reflections_LoadDepth(dispatch_thread_id);
FfxFloat32 linear_depth = FFX_DNSR_Reflections_GetLinearDepth(uv, depth);
// Determine disocclusion factor based on history
disocclusion_factor = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, history_normal, linear_depth, history_linear_depth);
if (disocclusion_factor > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) // Early out, good enough
return;
// Try to find the closest sample in the vicinity if we are not convinced of a disocclusion
if (disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) {
FfxFloat32x2 closest_uv = reprojection_uv;
FfxFloat32x2 dudv = 1.0 / FfxFloat32x2(screen_size);
const FfxInt32 search_radius = 1;
for (FfxInt32 y = -search_radius; y <= search_radius; y++) {
for (FfxInt32 x = -search_radius; x <= search_radius; x++) {
FfxFloat32x2 uv = reprojection_uv + FfxFloat32x2(x, y) * dudv;
FfxFloat32x3 history_normal = FFX_DNSR_Reflections_SampleWorldSpaceNormalHistory(uv);
FfxFloat32 history_depth = FFX_DNSR_Reflections_SampleDepthHistory(uv);
FfxFloat32 history_linear_depth = FFX_DNSR_Reflections_GetLinearDepth(uv, history_depth);
FfxFloat32 weight = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, history_normal, linear_depth, history_linear_depth);
if (weight > disocclusion_factor) {
disocclusion_factor = weight;
closest_uv = uv;
reprojection_uv = closest_uv;
}
}
}
reprojection = FFX_DNSR_Reflections_SampleRadianceHistory(reprojection_uv);
}
// Rare slow path - triggered only on the edges.
// Try to get rid of potential leaks at bilinear interpolation level.
if (disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) {
// If we've got a discarded history, try to construct a better sample out of 2x2 interpolation neighborhood
// Helps quite a bit on the edges in movement
FfxFloat32 uvx = ffxFract(FfxFloat32(screen_size.x) * reprojection_uv.x + 0.5);
FfxFloat32 uvy = ffxFract(FfxFloat32(screen_size.y) * reprojection_uv.y + 0.5);
FfxInt32x2 reproject_texel_coords = FfxInt32x2(screen_size * reprojection_uv - 0.5);
FfxFloat32x3 reprojection00 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(0, 0));
FfxFloat32x3 reprojection10 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(1, 0));
FfxFloat32x3 reprojection01 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(0, 1));
FfxFloat32x3 reprojection11 = FFX_DNSR_Reflections_LoadRadianceHistory(reproject_texel_coords + FfxInt32x2(1, 1));
FfxFloat32x3 normal00 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(0, 0));
FfxFloat32x3 normal10 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(1, 0));
FfxFloat32x3 normal01 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(0, 1));
FfxFloat32x3 normal11 = FFX_DNSR_Reflections_LoadWorldSpaceNormalHistory(reproject_texel_coords + FfxInt32x2(1, 1));
FfxFloat32 depth00 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(0, 0)));
FfxFloat32 depth10 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(1, 0)));
FfxFloat32 depth01 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(0, 1)));
FfxFloat32 depth11 = FFX_DNSR_Reflections_GetLinearDepth(reprojection_uv, FFX_DNSR_Reflections_LoadDepthHistory(reproject_texel_coords + FfxInt32x2(1, 1)));
FfxFloat32x4 w = FfxFloat32x4(1.0f, 1.0f, 1.0f, 1.0f);
// Initialize with occlusion weights
w.x = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal00, linear_depth, depth00) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat32(1.0f) : FfxFloat32(0.0f);
w.y = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal10, linear_depth, depth10) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat32(1.0f) : FfxFloat32(0.0f);
w.z = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal01, linear_depth, depth01) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat32(1.0f) : FfxFloat32(0.0f);
w.w = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, normal11, linear_depth, depth11) > FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD / 2.0f ? FfxFloat32(1.0f) : FfxFloat32(0.0f);
// And then mix in bilinear weights
w.x = FfxFloat32(w.x * (1.0 - uvx) * (1.0 - uvy));
w.y = FfxFloat32(w.y * (uvx) * (1.0 - uvy));
w.z = FfxFloat32(w.z * (1.0 - uvx) * (uvy));
w.w = FfxFloat32(w.w * (uvx) * (uvy));
FfxFloat32 ws = max(w.x + w.y + w.z + w.w, FfxFloat32(1.0e-3));
// normalize
w /= ws;
FfxFloat32x3 history_normal;
FfxFloat32 history_linear_depth;
reprojection = reprojection00 * w.x + reprojection10 * w.y + reprojection01 * w.z + reprojection11 * w.w;
history_linear_depth = depth00 * w.x + depth10 * w.y + depth01 * w.z + depth11 * w.w;
history_normal = normal00 * w.x + normal10 * w.y + normal01 * w.z + normal11 * w.w;
disocclusion_factor = FFX_DNSR_Reflections_GetDisocclusionFactor(normal, history_normal, linear_depth, history_linear_depth);
}
disocclusion_factor = disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD ? FfxFloat32(0.0f) : disocclusion_factor;
}
void FFX_DNSR_Reflections_Reproject(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxUInt32x2 screen_size, FfxFloat32 temporal_stability_factor, FfxInt32 max_samples) {
FFX_DNSR_Reflections_InitializeGroupSharedMemory(dispatch_thread_id, group_thread_id, FfxInt32x2(screen_size));
FFX_GROUP_MEMORY_BARRIER;
group_thread_id += 4; // Center threads in FFX_GROUPSHARED memory
FfxFloat32 variance = FfxFloat32(1.0f);
FfxFloat32 num_samples = FfxFloat32(0.0f);
FfxFloat32 roughness = FFX_DNSR_Reflections_LoadRoughness(dispatch_thread_id);
FfxFloat32x3 normal = FFX_DNSR_Reflections_LoadWorldSpaceNormal(dispatch_thread_id);
FfxFloat32x3 radiance = FFX_DNSR_Reflections_LoadRadiance(dispatch_thread_id);
const FfxFloat32 ray_length = FFX_DNSR_Reflections_LoadRayLength(dispatch_thread_id);
if (FFX_DNSR_Reflections_IsGlossyReflection(roughness)) {
FfxFloat32 disocclusion_factor;
FfxFloat32x2 reprojection_uv;
FfxFloat32x3 reprojection;
FFX_DNSR_Reflections_PickReprojection(/*in*/ dispatch_thread_id,
/* in */ group_thread_id,
/* in */ screen_size,
/* in */ roughness,
/* in */ ray_length,
/* out */ disocclusion_factor,
/* out */ reprojection_uv,
/* out */ reprojection);
if ( (reprojection_uv.x > 0.0f) && (reprojection_uv.y > 0.0f) && (reprojection_uv.x < 1.0f) && (reprojection_uv.y< 1.0f)) {
FfxFloat32 prev_variance = FFX_DNSR_Reflections_SampleVarianceHistory(reprojection_uv);
num_samples = FFX_DNSR_Reflections_SampleNumSamplesHistory(reprojection_uv) * disocclusion_factor;
FfxFloat32 s_max_samples = FfxFloat32(max(8.0f, max_samples * FFX_DNSR_REFLECTIONS_SAMPLES_FOR_ROUGHNESS(roughness)));
num_samples = min(s_max_samples, num_samples + FfxFloat32(1.0f));
FfxFloat32 new_variance = FFX_DNSR_Reflections_ComputeTemporalVariance(radiance.xyz, reprojection.xyz);
if (disocclusion_factor < FFX_DNSR_REFLECTIONS_DISOCCLUSION_THRESHOLD) {
FFX_DNSR_Reflections_StoreRadianceReprojected(dispatch_thread_id, FfxFloat32x3(0.0f, 0.0f, 0.0f));
FFX_DNSR_Reflections_StoreVariance(dispatch_thread_id, FfxFloat32(1.0f));
FFX_DNSR_Reflections_StoreNumSamples(dispatch_thread_id, FfxFloat32(1.0f));
} else {
FfxFloat32 variance_mix = ffxLerp(new_variance, prev_variance, FfxFloat32(1.0f / num_samples));
FFX_DNSR_Reflections_StoreRadianceReprojected(dispatch_thread_id, reprojection);
FFX_DNSR_Reflections_StoreVariance(dispatch_thread_id, variance_mix);
FFX_DNSR_Reflections_StoreNumSamples(dispatch_thread_id, num_samples);
// Mix in reprojection for radiance mip computation
radiance = ffxLerp(radiance, reprojection, FfxFloat32(0.3f));
}
} else {
FFX_DNSR_Reflections_StoreRadianceReprojected(dispatch_thread_id, FfxFloat32x3(0.0f, 0.0f, 0.0f));
FFX_DNSR_Reflections_StoreVariance(dispatch_thread_id, FfxFloat32(1.0f));
FFX_DNSR_Reflections_StoreNumSamples(dispatch_thread_id, FfxFloat32(1.0f));
}
}
// Downsample 8x8 -> 1 radiance using FFX_GROUPSHARED memory
// Initialize FFX_GROUPSHARED array for downsampling
FfxFloat32 weight = FFX_DNSR_Reflections_GetLuminanceWeight(radiance.xyz);
radiance.xyz *= weight;
if ( (dispatch_thread_id.x >= screen_size.x) || (dispatch_thread_id.y >= screen_size.y) || any(isinf(radiance)) || any(isnan(radiance)) || (weight > FfxFloat32(1.0e3))) {
radiance = FfxFloat32x3(0.0f, 0.0f, 0.0f);
weight = FfxFloat32(0.0f);
}
group_thread_id -= 4; // Center threads in FFX_GROUPSHARED memory
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id, FfxFloat32x4(radiance.xyz, weight));
FFX_GROUP_MEMORY_BARRIER;
for (FfxInt32 i = 2; i <= 8; i = i * 2) {
FfxInt32 ox = group_thread_id.x * i;
FfxInt32 oy = group_thread_id.y * i;
FfxInt32 ix = group_thread_id.x * i + i / 2;
FfxInt32 iy = group_thread_id.y * i + i / 2;
if (ix < 8 && iy < 8) {
FfxFloat32x4 rad_weight00 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ox, oy));
FfxFloat32x4 rad_weight10 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ox, iy));
FfxFloat32x4 rad_weight01 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ix, oy));
FfxFloat32x4 rad_weight11 = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(ix, iy));
FfxFloat32x4 sum = rad_weight00 + rad_weight01 + rad_weight10 + rad_weight11;
FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2(ox, oy), sum);
}
FFX_GROUP_MEMORY_BARRIER;
}
if ((group_thread_id.x == 0) && (group_thread_id.y == 0)) {
FfxFloat32x4 sum = FFX_DNSR_Reflections_LoadFromGroupSharedMemoryRaw(FfxInt32x2(0, 0));
FfxFloat32 weight_acc = max(sum.w, FfxFloat32(1.0e-3));
FfxFloat32x3 radiance_avg = sum.xyz / weight_acc;
FFX_DNSR_Reflections_StoreAverageRadiance(dispatch_thread_id.xy / 8, FfxFloat32x3(radiance_avg));
}
}
#endif // #if FFX_HALF
void Reproject(FfxUInt32 group_index, FfxUInt32 group_id, FfxUInt32x2 group_thread_id) {
FfxUInt32 packed_coords = GetDenoiserTile(group_id);
FfxInt32x2 dispatch_thread_id = FfxInt32x2(packed_coords & 0xffffu, (packed_coords >> 16) & 0xffffu) + FfxInt32x2(group_thread_id);
FfxInt32x2 dispatch_group_id = dispatch_thread_id / 8;
FfxInt32x2 remapped_group_thread_id = FfxInt32x2(ffxRemapForWaveReduction(group_index));
FfxInt32x2 remapped_dispatch_thread_id = dispatch_group_id * 8 + remapped_group_thread_id;
FFX_DNSR_Reflections_Reproject(remapped_dispatch_thread_id, remapped_group_thread_id, RenderSize(), TemporalStabilityFactor(), 32);
}
#endif // FFX_DNSR_REFLECTIONS_REPROJECT

View File

@@ -0,0 +1,298 @@
// 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_DNSR_REFLECTIONS_RESOLVE_TEMPORAL
#define FFX_DNSR_REFLECTIONS_RESOLVE_TEMPORAL
#define RADIANCE_THRESHOLD 0.0001
#define FFX_DNSR_REFLECTIONS_ESTIMATES_LOCAL_NEIGHBORHOOD
#include "ffx_denoiser_reflections_common.h"
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_0[16][16];
FFX_GROUPSHARED FfxUInt32 g_ffx_dnsr_shared_1[16][16];
#if FFX_HALF
struct FFX_DNSR_Reflections_NeighborhoodSample {
FfxFloat16x3 radiance;
};
FFX_DNSR_Reflections_NeighborhoodSample FFX_DNSR_Reflections_LoadFromGroupSharedMemory(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
FfxFloat16x3 unpacked_radiance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance).xyz;
FFX_DNSR_Reflections_NeighborhoodSample neighborSample;
neighborSample.radiance = unpacked_radiance;
return neighborSample;
}
struct FFX_DNSR_Reflections_Moments {
FfxFloat16x3 mean;
FfxFloat16x3 variance;
};
FFX_DNSR_Reflections_Moments FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(FfxInt32x2 group_thread_id) {
FFX_DNSR_Reflections_Moments estimate;
estimate.mean = FfxFloat16x3(0.0f, 0.0f, 0.0f);
estimate.variance = FfxFloat16x3(0.0f, 0.0f, 0.0f);
FfxFloat16 accumulated_weight = FfxFloat16(0.0f);
for (FfxInt32 j = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; j <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++j) {
for (FfxInt32 i = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; i <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++i) {
FfxInt32x2 new_idx = group_thread_id + FfxInt32x2(i, j);
FfxFloat16x3 radiance = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(new_idx).radiance;
FfxFloat16 weight = FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat16(i)) * FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat16(j));
accumulated_weight += weight;
estimate.mean += radiance * weight;
estimate.variance += radiance * radiance * weight;
}
}
estimate.mean /= accumulated_weight;
estimate.variance /= accumulated_weight;
estimate.variance = abs(estimate.variance - estimate.mean * estimate.mean);
return estimate;
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat16x3 radiance) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.zz);
}
void FFX_DNSR_Reflections_LoadNeighborhood(FfxInt32x2 pixel_coordinate, out FfxFloat16x3 radiance) { radiance = FFX_DNSR_Reflections_LoadRadiance(pixel_coordinate); }
void FFX_DNSR_Reflections_InitializeGroupSharedMemory(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxInt32x2 screen_size) {
// Load 16x16 region into shared memory using 4 8x8 blocks.
FfxInt32x2 offset[4] = {FfxInt32x2(0, 0), FfxInt32x2(8, 0), FfxInt32x2(0, 8), FfxInt32x2(8, 8)};
// Intermediate storage registers to cache the result of all loads
FfxFloat16x3 radiance[4];
// Start in the upper left corner of the 16x16 region.
dispatch_thread_id -= 4;
// First store all loads in registers
for (FfxInt32 i = 0; i < 4; ++i) {
FFX_DNSR_Reflections_LoadNeighborhood(dispatch_thread_id + offset[i], radiance[i]);
}
// Then move all registers to FFX_GROUPSHARED memory
for (FfxInt32 j = 0; j < 4; ++j) {
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id + offset[j], radiance[j]);
}
}
void FFX_DNSR_Reflections_ResolveTemporal(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxUInt32x2 screen_size, FfxFloat32x2 inv_screen_size, FfxFloat32 history_clip_weight) {
FFX_DNSR_Reflections_InitializeGroupSharedMemory(dispatch_thread_id, group_thread_id, FfxInt32x2(screen_size));
FFX_GROUP_MEMORY_BARRIER;
group_thread_id += 4; // Center threads in FFX_GROUPSHARED memory
FfxFloat16x3 radiance = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(group_thread_id).radiance;
FfxFloat32 radianceSum = radiance.x + radiance.y + radiance.z;
if (radianceSum < RADIANCE_THRESHOLD)
{
FFX_DNSR_Reflections_StoreTemporalAccumulation(dispatch_thread_id, FfxFloat16x3(0.0f, 0.0f, 0.0f), FfxFloat16(0.0f));
return;
}
FFX_DNSR_Reflections_NeighborhoodSample center = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(group_thread_id);
FfxFloat16x3 new_signal = center.radiance;
FfxFloat16 roughness = FFX_DNSR_Reflections_LoadRoughness(dispatch_thread_id);
FfxFloat16 new_variance = FFX_DNSR_Reflections_LoadVariance(dispatch_thread_id);
if (FFX_DNSR_Reflections_IsGlossyReflection(roughness)) {
FfxFloat16 num_samples = FFX_DNSR_Reflections_LoadNumSamples(dispatch_thread_id);
FfxFloat32x2 uv8 = (FfxFloat32x2(dispatch_thread_id.xy) + (0.5).xx) / FFX_DNSR_Reflections_RoundUp8(screen_size);
FfxFloat16x3 avg_radiance = FFX_DNSR_Reflections_SampleAverageRadiance(uv8);
FfxFloat16x3 old_signal = FFX_DNSR_Reflections_LoadRadianceReprojected(dispatch_thread_id);
FFX_DNSR_Reflections_Moments local_neighborhood = FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(group_thread_id);
// Clip history based on the curren local statistics
FfxFloat16x3 color_std = FfxFloat16x3((sqrt(local_neighborhood.variance.xyz) + length(local_neighborhood.mean.xyz - avg_radiance)) * history_clip_weight * 1.4f);
local_neighborhood.mean.xyz = ffxLerp(local_neighborhood.mean.xyz, avg_radiance, FfxFloat16x3(0.2f, 0.2f, 0.2f));
FfxFloat16x3 radiance_min = local_neighborhood.mean.xyz - color_std;
FfxFloat16x3 radiance_max = local_neighborhood.mean.xyz + color_std;
FfxFloat16x3 clipped_old_signal = FFX_DNSR_Reflections_ClipAABB(radiance_min, radiance_max, old_signal.xyz);
FfxFloat16 accumulation_speed = FfxFloat16(1.0f) / max(num_samples, FfxFloat16(1.0f));
FfxFloat16 weight = (FfxFloat16(1.0f) - accumulation_speed);
// Blend with average for small sample count
new_signal.xyz = ffxLerp(new_signal.xyz, avg_radiance, FfxFloat16(1.0f) / max(num_samples + FfxFloat16(1.0f), FfxFloat16(1.0f)));
// Clip outliers
{
FfxFloat16x3 radiance_min = avg_radiance.xyz - color_std * FfxFloat16x3(1.0f, 1.0f, 1.0f);
FfxFloat16x3 radiance_max = avg_radiance.xyz + color_std * FfxFloat16x3(1.0f, 1.0f, 1.0f);
new_signal.xyz = FFX_DNSR_Reflections_ClipAABB(radiance_min, radiance_max, new_signal.xyz);
}
// Blend with history
#ifdef FFX_GLSL
new_signal = (FfxFloat16(1.0) - weight) * new_signal + weight * clipped_old_signal;
#else
new_signal = ffxLerp(new_signal, clipped_old_signal, weight);
#endif
new_variance = ffxLerp(FFX_DNSR_Reflections_ComputeTemporalVariance(new_signal.xyz, clipped_old_signal.xyz), new_variance, weight);
if (any(isinf(new_signal)) || any(isnan(new_signal)) || isinf(new_variance) || isnan(new_variance)) {
new_signal = FfxFloat16x3(0.0f, 0.0f, 0.0f);
new_variance = FfxFloat16(0.0f);
}
}
FFX_DNSR_Reflections_StoreTemporalAccumulation(dispatch_thread_id, new_signal, new_variance);
}
#else // #if FFX_HALF
struct FFX_DNSR_Reflections_NeighborhoodSample {
FfxFloat32x3 radiance;
};
FFX_DNSR_Reflections_NeighborhoodSample FFX_DNSR_Reflections_LoadFromGroupSharedMemory(FfxInt32x2 idx) {
FfxUInt32x2 packed_radiance = FfxUInt32x2(g_ffx_dnsr_shared_0[idx.y][idx.x], g_ffx_dnsr_shared_1[idx.y][idx.x]);
FfxFloat32x3 unpacked_radiance = FFX_DNSR_Reflections_UnpackFloat16_4(packed_radiance).xyz;
FFX_DNSR_Reflections_NeighborhoodSample neighborSample;
neighborSample.radiance = unpacked_radiance;
return neighborSample;
}
struct FFX_DNSR_Reflections_Moments {
FfxFloat32x3 mean;
FfxFloat32x3 variance;
};
FFX_DNSR_Reflections_Moments FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(FfxInt32x2 group_thread_id) {
FFX_DNSR_Reflections_Moments estimate;
estimate.mean = FfxFloat32x3(0.0f, 0.0f, 0.0f);
estimate.variance = FfxFloat32x3(0.0f, 0.0f, 0.0f);
FfxFloat32 accumulated_weight = FfxFloat32(0.0f);
for (FfxInt32 j = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; j <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++j) {
for (FfxInt32 i = -FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; i <= FFX_DNSR_REFLECTIONS_LOCAL_NEIGHBORHOOD_RADIUS; ++i) {
FfxInt32x2 new_idx = group_thread_id + FfxInt32x2(i, j);
FfxFloat32x3 radiance = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(new_idx).radiance;
FfxFloat32 weight = FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat32(i)) * FFX_DNSR_Reflections_LocalNeighborhoodKernelWeight(FfxFloat32(j));
accumulated_weight += weight;
estimate.mean += radiance * weight;
estimate.variance += radiance * radiance * weight;
}
}
estimate.mean /= accumulated_weight;
estimate.variance /= accumulated_weight;
estimate.variance = abs(estimate.variance - estimate.mean * estimate.mean);
return estimate;
}
void FFX_DNSR_Reflections_StoreInGroupSharedMemory(FfxInt32x2 group_thread_id, FfxFloat32x3 radiance) {
g_ffx_dnsr_shared_0[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.xy);
g_ffx_dnsr_shared_1[group_thread_id.y][group_thread_id.x] = FFX_DNSR_Reflections_PackFloat16(radiance.zz);
}
void FFX_DNSR_Reflections_LoadNeighborhood(FfxInt32x2 pixel_coordinate, out FfxFloat32x3 radiance) { radiance = FFX_DNSR_Reflections_LoadRadiance(pixel_coordinate); }
void FFX_DNSR_Reflections_InitializeGroupSharedMemory(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxInt32x2 screen_size) {
// Load 16x16 region into shared memory using 4 8x8 blocks.
FfxInt32x2 offset[4] = {FfxInt32x2(0, 0), FfxInt32x2(8, 0), FfxInt32x2(0, 8), FfxInt32x2(8, 8)};
// Intermediate storage registers to cache the result of all loads
FfxFloat32x3 radiance[4];
// Start in the upper left corner of the 16x16 region.
dispatch_thread_id -= 4;
// First store all loads in registers
for (FfxInt32 i = 0; i < 4; ++i) {
FFX_DNSR_Reflections_LoadNeighborhood(dispatch_thread_id + offset[i], radiance[i]);
}
// Then move all registers to FFX_GROUPSHARED memory
for (FfxInt32 j = 0; j < 4; ++j) {
FFX_DNSR_Reflections_StoreInGroupSharedMemory(group_thread_id + offset[j], radiance[j]);
}
}
void FFX_DNSR_Reflections_ResolveTemporal(FfxInt32x2 dispatch_thread_id, FfxInt32x2 group_thread_id, FfxUInt32x2 screen_size, FfxFloat32x2 inv_screen_size, FfxFloat32 history_clip_weight) {
FFX_DNSR_Reflections_InitializeGroupSharedMemory(dispatch_thread_id, group_thread_id, FfxInt32x2(screen_size));
FFX_GROUP_MEMORY_BARRIER;
group_thread_id += 4; // Center threads in FFX_GROUPSHARED memory
FfxFloat32x3 radiance = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(group_thread_id).radiance;
FfxFloat32 radianceSum = radiance.x + radiance.y + radiance.z;
if (radianceSum < RADIANCE_THRESHOLD)
{
FFX_DNSR_Reflections_StoreTemporalAccumulation(dispatch_thread_id, FfxFloat32x3(0.0f, 0.0f, 0.0f), FfxFloat32(0.0f));
return;
}
FFX_DNSR_Reflections_NeighborhoodSample center = FFX_DNSR_Reflections_LoadFromGroupSharedMemory(group_thread_id);
FfxFloat32x3 new_signal = center.radiance;
FfxFloat32 roughness = FFX_DNSR_Reflections_LoadRoughness(dispatch_thread_id);
FfxFloat32 new_variance = FFX_DNSR_Reflections_LoadVariance(dispatch_thread_id);
if (FFX_DNSR_Reflections_IsGlossyReflection(roughness)) {
FfxFloat32 num_samples = FFX_DNSR_Reflections_LoadNumSamples(dispatch_thread_id);
FfxFloat32x2 uv8 = (FfxFloat32x2(dispatch_thread_id.xy) + (0.5).xx) / FFX_DNSR_Reflections_RoundUp8(screen_size);
FfxFloat32x3 avg_radiance = FFX_DNSR_Reflections_SampleAverageRadiance(uv8);
FfxFloat32x3 old_signal = FFX_DNSR_Reflections_LoadRadianceReprojected(dispatch_thread_id);
FFX_DNSR_Reflections_Moments local_neighborhood = FFX_DNSR_Reflections_EstimateLocalNeighborhoodInGroup(group_thread_id);
// Clip history based on the curren local statistics
FfxFloat32x3 color_std = FfxFloat32x3((sqrt(local_neighborhood.variance.xyz) + length(local_neighborhood.mean.xyz - avg_radiance)) * history_clip_weight * 1.4f);
local_neighborhood.mean.xyz = ffxLerp(local_neighborhood.mean.xyz, avg_radiance, FfxFloat32x3(0.2f, 0.2f, 0.2f));
FfxFloat32x3 radiance_min = local_neighborhood.mean.xyz - color_std;
FfxFloat32x3 radiance_max = local_neighborhood.mean.xyz + color_std;
FfxFloat32x3 clipped_old_signal = FFX_DNSR_Reflections_ClipAABB(radiance_min, radiance_max, old_signal.xyz);
FfxFloat32 accumulation_speed = FfxFloat32(1.0f) / max(num_samples, FfxFloat32(1.0f));
FfxFloat32 weight = (FfxFloat32(1.0f) - accumulation_speed);
// Blend with average for small sample count
new_signal.xyz = ffxLerp(new_signal.xyz, avg_radiance, FfxFloat32(1.0f) / max(num_samples + FfxFloat32(1.0f), FfxFloat32(1.0f)));
// Clip outliers
{
FfxFloat32x3 radiance_min = avg_radiance.xyz - color_std * FfxFloat32x3(1.0f, 1.0f, 1.0f);
FfxFloat32x3 radiance_max = avg_radiance.xyz + color_std * FfxFloat32x3(1.0f, 1.0f, 1.0f);
new_signal.xyz = FFX_DNSR_Reflections_ClipAABB(radiance_min, radiance_max, new_signal.xyz);
}
// Blend with history
new_signal = ffxLerp(new_signal, clipped_old_signal, weight);
new_variance = ffxLerp(FFX_DNSR_Reflections_ComputeTemporalVariance(new_signal.xyz, clipped_old_signal.xyz), new_variance, weight);
if (any(isinf(new_signal)) || any(isnan(new_signal)) || isinf(new_variance) || isnan(new_variance)) {
new_signal = FfxFloat32x3(0.0f, 0.0f, 0.0f);
new_variance = FfxFloat32(0.0f);
}
}
FFX_DNSR_Reflections_StoreTemporalAccumulation(dispatch_thread_id, new_signal, new_variance);
}
#endif // #if FFX_HALF
void ResolveTemporal(FfxUInt32 group_index, FfxUInt32 group_id, FfxUInt32x2 group_thread_id) {
FfxUInt32 packed_coords = GetDenoiserTile(group_id);
FfxInt32x2 dispatch_thread_id = FfxInt32x2(packed_coords & 0xffffu, (packed_coords >> 16) & 0xffffu) + FfxInt32x2(group_thread_id);
FfxInt32x2 dispatch_group_id = dispatch_thread_id / 8;
FfxInt32x2 remapped_group_thread_id = FfxInt32x2(ffxRemapForWaveReduction(group_index));
FfxInt32x2 remapped_dispatch_thread_id = dispatch_group_id * 8 + remapped_group_thread_id;
FFX_DNSR_Reflections_ResolveTemporal(remapped_dispatch_thread_id, remapped_group_thread_id, RenderSize(), InverseRenderSize(), TemporalStabilityFactor());
}
#endif // FFX_DNSR_REFLECTIONS_RESOLVE_TEMPORAL

View File

@@ -0,0 +1,94 @@
// 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_SHADOWS_DENOISER_RESOURCES_H
#define FFX_SHADOWS_DENOISER_RESOURCES_H
#if defined(FFX_CPU) || defined(FFX_GPU)
#define FFX_DENOISER_RESOURCE_IDENTIFIER_NULL 0
#define FFX_DENOISER_RESOURCE_IDENTIFIER_HIT_MASK_RESULTS 1
#define FFX_DENOISER_RESOURCE_IDENTIFIER_TILE_BUFFER 2
#define FFX_DENOISER_RESOURCE_IDENTIFIER_SHADOW_MASK 3
#define FFX_DENOISER_RESOURCE_IDENTIFIER_RAYTRACER_RESULT 4
#define FFX_DENOISER_RESOURCE_IDENTIFIER_DEPTH 5
#define FFX_DENOISER_RESOURCE_IDENTIFIER_VELOCITY 6
#define FFX_DENOISER_RESOURCE_IDENTIFIER_NORMAL 7
#define FFX_DENOISER_RESOURCE_IDENTIFIER_NORMAL_FP16 8 //same resource as NORMAL
#define FFX_DENOISER_RESOURCE_IDENTIFIER_PREVIOUS_DEPTH 9
#define FFX_DENOISER_RESOURCE_IDENTIFIER_TILE_META_DATA 10
#define FFX_DENOISER_RESOURCE_IDENTIFIER_MOMENTS0 11
#define FFX_DENOISER_RESOURCE_IDENTIFIER_MOMENTS1 12
#define FFX_DENOISER_RESOURCE_IDENTIFIER_PREVIOUS_MOMENTS 13 //same resource as MOMENT0 & MOMENT1
#define FFX_DENOISER_RESOURCE_IDENTIFIER_CURRENT_MOMENTS 14 //same resource as MOMENT0 & MOMENT1
#define FFX_DENOISER_RESOURCE_IDENTIFIER_SCRATCH0 15
#define FFX_DENOISER_RESOURCE_IDENTIFIER_SCRATCH1 16
#define FFX_DENOISER_RESOURCE_IDENTIFIER_HISTORY 17 //same resource as SCRATCH1
#define FFX_DENOISER_RESOURCE_IDENTIFIER_REPROJECTION_RESULTS 18 //same resource as SCRATCH0
#define FFX_DENOISER_RESOURCE_IDENTIFIER_FILTER_INPUT 19 //same resource as SCRATCH0 & SCRATCH1
#define FFX_DENOISER_RESOURCE_IDENTIFIER_FILTER_OUTPUT 20
#define FFX_DENOISER_RESOURCE_IDENTIFIER_INPUT_DEPTH_HIERARCHY 21
#define FFX_DENOISER_RESOURCE_IDENTIFIER_INPUT_MOTION_VECTORS 22
#define FFX_DENOISER_RESOURCE_IDENTIFIER_INPUT_NORMAL 23
#define FFX_DENOISER_RESOURCE_IDENTIFIER_OUTPUT 24
#define FFX_DENOISER_RESOURCE_IDENTIFIER_RADIANCE 25
#define FFX_DENOISER_RESOURCE_IDENTIFIER_RADIANCE_HISTORY 26
#define FFX_DENOISER_RESOURCE_IDENTIFIER_VARIANCE 27
#define FFX_DENOISER_RESOURCE_IDENTIFIER_SAMPLE_COUNT 28
#define FFX_DENOISER_RESOURCE_IDENTIFIER_AVERAGE_RADIANCE 29
#define FFX_DENOISER_RESOURCE_IDENTIFIER_DENOISER_TILE_LIST 10
#define FFX_DENOISER_RESOURCE_IDENTIFIER_EXTRACTED_ROUGHNESS 31
#define FFX_DENOISER_RESOURCE_IDENTIFIER_DEPTH_HISTORY 32
#define FFX_DENOISER_RESOURCE_IDENTIFIER_NORMAL_HISTORY 33
#define FFX_DENOISER_RESOURCE_IDENTIFIER_ROUGHNESS_HISTORY 34
#define FFX_DENOISER_RESOURCE_IDENTIFIER_RADIANCE_0 35
#define FFX_DENOISER_RESOURCE_IDENTIFIER_RADIANCE_1 36
#define FFX_DENOISER_RESOURCE_IDENTIFIER_VARIANCE_0 37
#define FFX_DENOISER_RESOURCE_IDENTIFIER_VARIANCE_1 38
#define FFX_DENOISER_RESOURCE_IDENTIFIER_SAMPLE_COUNT_0 39
#define FFX_DENOISER_RESOURCE_IDENTIFIER_SAMPLE_COUNT_1 40
#define FFX_DENOISER_RESOURCE_IDENTIFIER_AVERAGE_RADIANCE_0 41
#define FFX_DENOISER_RESOURCE_IDENTIFIER_AVERAGE_RADIANCE_1 42
#define FFX_DENOISER_RESOURCE_IDENTIFIER_REPROJECTED_RADIANCE 43
#define FFX_DENOISER_RESOURCE_IDENTIFIER_INDIRECT_ARGS 44
#define FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT 45
// CBV resource definitions
#define FFX_DENOISER_SHADOWS_CONSTANTBUFFER_IDENTIFIER_DENOISER_SHADOWS0 0
#define FFX_DENOISER_SHADOWS_CONSTANTBUFFER_IDENTIFIER_DENOISER_SHADOWS1 1
#define FFX_DENOISER_SHADOWS_CONSTANTBUFFER_IDENTIFIER_DENOISER_SHADOWS2 2
#define FFX_DENOISER_SHADOWS_CONSTANTBUFFER_IDENTIFIER_DENOISER_SHADOWS_COUNT 3
// CBV resource definitions
#define FFX_DENOISER_REFLECTIONS_CONSTANTBUFFER_IDENTIFIER 0
#define FFX_DENOISER_REFLECTIONS_CONSTANTBUFFER_IDENTIFIER_COUNT 1
#endif // #if defined(FFX_CPU) || defined(FFX_GPU)
#endif // FFX_SHADOWS_DENOISER_RESOURCES_H

View File

@@ -0,0 +1,378 @@
// 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_denoiser_resources.h"
#if defined(FFX_GPU)
#include "ffx_core.h"
#if defined(DENOISER_SHADOWS_BIND_CB0_DENOISER_SHADOWS)
layout (set = 0, binding = DENOISER_SHADOWS_BIND_CB0_DENOISER_SHADOWS, std140) uniform cb0DenoiserShadows_t
{
FfxInt32x2 iBufferDimensions;
} cb0DenoiserShadows;
#endif
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
layout (set = 0, binding = DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS, std140) uniform cb1DenoiserShadows_t
{
FfxFloat32x3 fEye;
FfxInt32 iFirstFrame;
FfxInt32x2 iBufferDimensions;
FfxFloat32x2 fInvBufferDimensions;
FfxFloat32x2 fMotionVectorScale;
FfxFloat32x2 normalsUnpackMul_unpackAdd;
FfxFloat32Mat4 fProjectionInverse;
FfxFloat32Mat4 fReprojectionMatrix;
FfxFloat32Mat4 fViewProjectionInverse;
} cb1DenoiserShadows;
#endif
#if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
layout (set = 0, binding = DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS, std140) uniform cb2DenoiserShadows_t
{
FfxFloat32Mat4 fProjectionInverse;
FfxFloat32x2 fInvBufferDimensions;
FfxFloat32x2 normalsUnpackMul_unpackAdd;
FfxInt32x2 iBufferDimensions;
FfxFloat32 fDepthSimilaritySigma;
} cb2DenoiserShadows;
#endif
FfxInt32x2 BufferDimensions()
{
#if defined(DENOISER_SHADOWS_BIND_CB0_DENOISER_SHADOWS)
return cb0DenoiserShadows.iBufferDimensions;
#elif defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
return cb1DenoiserShadows.iBufferDimensions;
#elif defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
return cb2DenoiserShadows.iBufferDimensions;
#endif
return FfxInt32x2(0, 0);
}
FfxFloat32x2 MotionVectorScale()
{
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
return cb1DenoiserShadows.fMotionVectorScale;
#endif
return FfxFloat32x2(0, 0);
}
// SRVs
#if defined DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS)
uniform utexture2D r_hit_mask_results;
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_DEPTH
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_DEPTH)
uniform texture2D r_depth;
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_VELOCITY
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_VELOCITY)
uniform texture2D r_velocity;
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_NORMAL
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_NORMAL)
uniform texture2D r_normal;
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_HISTORY
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_HISTORY)
uniform texture2D r_history;
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH)
uniform texture2D r_previous_depth;
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS)
uniform texture2D r_previous_moments;
#endif
#if FFX_HALF
#if defined DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT
layout (set = 0, binding = DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT)
uniform texture2D r_filter_input;
#endif
#endif
// UAV declarations
#if defined DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK
layout (set = 0, binding = DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK, std430) buffer rw_shadow_mask_t
{
FfxUInt32 data[];
} rw_shadow_mask;
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT
layout (set = 0, binding = DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT, std430) buffer rw_raytracer_result_t
{
FfxUInt32 data[];
} rw_raytracer_result;
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_TILE_METADATA
layout (set = 0, binding = DENOISER_SHADOWS_BIND_UAV_TILE_METADATA, std430) buffer rw_tile_metadata_t
{
FfxUInt32 data[];
} rw_tile_metadata;
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS
layout (set = 0, binding = DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS, rg32f)
uniform image2D rw_reprojection_results;
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS
layout (set = 0, binding = DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS, rgba32f)
uniform image2D rw_current_moments;
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_HISTORY
layout (set = 0, binding = DENOISER_SHADOWS_BIND_UAV_HISTORY, rg32f)
uniform image2D rw_history;
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT
layout (set = 0, binding = DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT) writeonly
uniform image2D rw_filter_output;
#endif
#define TILE_SIZE_X 8
#define TILE_SIZE_Y 4
FfxUInt32 LaneIdToBitShift(FfxUInt32x2 localID)
{
return localID.y * TILE_SIZE_X + localID.x;
}
FfxBoolean WaveMaskToBool(FfxUInt32 mask, FfxUInt32x2 localID)
{
return FfxBoolean((FfxUInt32(1) << LaneIdToBitShift(localID.xy)) & mask);
}
#if defined(DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS)
FfxBoolean HitsLight(FfxUInt32x2 did, FfxUInt32x2 gtid, FfxUInt32x2 gid)
{
FfxUInt32 mask = texelFetch(r_hit_mask_results, ivec2(gid), 0).r;
return !WaveMaskToBool(mask, gtid);
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS)
FfxFloat32 NormalsUnpackMul()
{
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
return cb1DenoiserShadows.normalsUnpackMul_unpackAdd[0];
#endif
#if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
return cb2DenoiserShadows.normalsUnpackMul_unpackAdd[0];
#endif
return 0;
}
FfxFloat32 NormalsUnpackAdd()
{
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
return cb1DenoiserShadows.normalsUnpackMul_unpackAdd[1];
#endif
#if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
return cb2DenoiserShadows.normalsUnpackMul_unpackAdd[1];
#endif
return 0;
}
#if defined(DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK)
void StoreShadowMask(FfxUInt32 offset, FfxUInt32 value)
{
rw_shadow_mask.data[offset] = value;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32Mat4 ViewProjectionInverse()
{
return cb1DenoiserShadows.fViewProjectionInverse;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32Mat4 ReprojectionMatrix()
{
return cb1DenoiserShadows.fReprojectionMatrix;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32Mat4 ProjectionInverse()
{
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
return cb1DenoiserShadows.fProjectionInverse;
#endif
#if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
return cb2DenoiserShadows.fProjectionInverse;
#else
return FfxFloat32Mat4(0);
#endif
}
FfxFloat32x2 InvBufferDimensions()
{
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
return cb1DenoiserShadows.fInvBufferDimensions;
#elif defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
return cb2DenoiserShadows.fInvBufferDimensions;
#else
return FfxFloat32x2(0, 0);
#endif
}
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxInt32 IsFirstFrame()
{
return cb1DenoiserShadows.iFirstFrame;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32x3 Eye()
{
return cb1DenoiserShadows.fEye;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32 LoadDepth(FfxInt32x2 p)
{
#if defined(DENOISER_SHADOWS_BIND_SRV_DEPTH)
return texelFetch(r_depth, ivec2(p), 0).x;
#else
return 0.f;
#endif
}
#if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH)
FfxFloat32 LoadPreviousDepth(FfxInt32x2 p)
{
return texelFetch(r_previous_depth, ivec2(p), 0).x;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH)
#if defined(DENOISER_SHADOWS_BIND_SRV_NORMAL)
FfxFloat32x3 LoadNormals(FfxUInt32x2 p)
{
FfxFloat32x3 normal = texelFetch(r_normal, ivec2(p), 0).xyz;
normal = normal * NormalsUnpackMul().xxx + NormalsUnpackAdd().xxx;
return normalize(normal);
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_NORMAL)
#if defined(DENOISER_SHADOWS_BIND_SRV_VELOCITY)
FfxFloat32x2 LoadVelocity(FfxInt32x2 p)
{
FfxFloat32x2 velocity = texelFetch(r_velocity, ivec2(p), 0).rg;
return velocity * MotionVectorScale();
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_VELOCITY)
#if defined(DENOISER_SHADOWS_BIND_SRV_HISTORY)
layout (set = 0, binding = 1000) uniform sampler s_trilinerClamp;
FfxFloat32 LoadHistory(FfxFloat32x2 p)
{
return FfxFloat32(textureLod(sampler2D(r_history, s_trilinerClamp), p, 0.0f).x);
}
#endif
#if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS)
FfxFloat32x3 LoadPreviousMomentsBuffer(FfxInt32x2 p)
{
return texelFetch(r_previous_moments, ivec2(p), 0).xyz;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS)
#if defined(DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT)
FfxUInt32 LoadRaytracedShadowMask(FfxUInt32 p)
{
return rw_raytracer_result.data[p];
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT)
#if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
void StoreMetadata(FfxUInt32 p, FfxUInt32 val)
{
rw_tile_metadata.data[p] = val;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
#if defined(DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS)
void StoreMoments(FfxUInt32x2 p, FfxFloat32x3 val)
{
imageStore(rw_current_moments, ivec2(p), FfxFloat32x4(val, 0));
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS)
#if defined(DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS)
void StoreReprojectionResults(FfxUInt32x2 p, FfxFloat32x2 val)
{
imageStore(rw_reprojection_results, ivec2(p), FfxFloat32x4(val, 0, 0));
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS)
#if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
FfxFloat32 DepthSimilaritySigma()
{
return cb2DenoiserShadows.fDepthSimilaritySigma;
}
#endif
#if FFX_HALF
#if defined(DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT)
FfxFloat16x2 LoadFilterInput(FfxUInt32x2 p)
{
return FfxFloat16x2(texelFetch(r_filter_input, ivec2(p), 0).xy);
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT)
#endif // #if FFX_HALF
FfxBoolean IsShadowReciever(FfxUInt32x2 p)
{
FfxFloat32 depth = LoadDepth(FfxInt32x2(p));
return (depth > 0.0f) && (depth < 1.0f);
}
#if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
FfxUInt32 LoadTileMetaData(FfxUInt32 p)
{
return rw_tile_metadata.data[p];
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
void StoreHistory(FfxUInt32x2 p, FfxFloat32x2 val)
{
#if defined(DENOISER_SHADOWS_BIND_UAV_HISTORY)
imageStore(rw_history, ivec2(p), FfxFloat32x4(val, 0, 0));
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_HISTORY)
}
void StoreFilterOutput(FfxUInt32x2 p, FfxFloat32 val)
{
#if defined(DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT)
imageStore(rw_filter_output, ivec2(p), FfxFloat32x4(val, 0, 0, 0));
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT)
}
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,392 @@
// 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_denoiser_resources.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 FFX_DECLARE_SRV_REGISTER(regIndex) t##regIndex
#define FFX_DECLARE_UAV_REGISTER(regIndex) u##regIndex
#define FFX_DECLARE_CB_REGISTER(regIndex) b##regIndex
#define FFX_DENOISER_SHADOWS_DECLARE_SRV(regIndex) register(FFX_DECLARE_SRV_REGISTER(regIndex))
#define FFX_DENOISER_SHADOWS_DECLARE_UAV(regIndex) register(FFX_DECLARE_UAV_REGISTER(regIndex))
#define FFX_DENOISER_SHADOWS_DECLARE_CB(regIndex) register(FFX_DECLARE_CB_REGISTER(regIndex))
#if defined(DENOISER_SHADOWS_BIND_CB0_DENOISER_SHADOWS)
cbuffer cb0DenoiserShadows : FFX_DENOISER_SHADOWS_DECLARE_CB(DENOISER_SHADOWS_BIND_CB0_DENOISER_SHADOWS)
{
FfxInt32x2 iBufferDimensions;
#define FFX_DENOISER_SHADOWS_CONSTANT_BUFFER_0_SIZE 2
}
#endif
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
cbuffer cb1DenoiserShadows : FFX_DENOISER_SHADOWS_DECLARE_CB(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
{
FfxFloat32x3 fEye;
FfxInt32 iFirstFrame;
FfxInt32x2 iBufferDimensions;
FfxFloat32x2 fInvBufferDimensions;
FfxFloat32x2 fMotionVectorScale;
FfxFloat32x2 normalsUnpackMul_unpackAdd;
FfxFloat32Mat4 fProjectionInverse;
FfxFloat32Mat4 fReprojectionMatrix;
FfxFloat32Mat4 fViewProjectionInverse;
#define FFX_DENOISER_SHADOWS_CONSTANT_BUFFER_1_SIZE 56
}
#endif
#if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
cbuffer cb2DenoiserShadows : FFX_DENOISER_SHADOWS_DECLARE_CB(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
{
FfxFloat32Mat4 fProjectionInverse;
FfxFloat32x2 fInvBufferDimensions;
FfxFloat32x2 normalsUnpackMul_unpackAdd;
FfxInt32x2 iBufferDimensions;
FfxFloat32 fDepthSimilaritySigma;
#define FFX_DENOISER_SHADOWS_CONSTANT_BUFFER_2_SIZE 24
}
#endif
#if defined(FFX_GPU)
#define FFX_DENOISER_SHADOWS_ROOTSIG_STRINGIFY(p) FFX_DENOISER_SHADOWS_ROOTSIG_STR(p)
#define FFX_DENOISER_SHADOWS_ROOTSIG_STR(p) #p
#define FFX_DENOISER_SHADOWS_PREPARE_SHADOW_MASK_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_DENOISER_SHADOWS_ROOTSIG_STRINGIFY(FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT) ")), " \
"DescriptorTable(SRV(t0, numDescriptors = " FFX_DENOISER_SHADOWS_ROOTSIG_STRINGIFY(FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT) ")), " \
"CBV(b0)")]
#define FFX_DENOISER_SHADOWS_TILE_CLASSIFICATION_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_DENOISER_SHADOWS_ROOTSIG_STRINGIFY(FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT) ")), " \
"DescriptorTable(SRV(t0, numDescriptors = " FFX_DENOISER_SHADOWS_ROOTSIG_STRINGIFY(FFX_DENOISER_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, " \
"MinLOD = 0, " \
"MaxLOD = 3.402823466e+38f, " \
"mipLODBias = 0, " \
"comparisonFunc = COMPARISON_LESS_EQUAL, " \
"maxAnisotropy = 16, " \
"borderColor = STATIC_BORDER_COLOR_OPAQUE_WHITE)")]
#define FFX_DENOISER_SHADOWS_FILTER_SOFT_SHADOWS_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_DENOISER_SHADOWS_ROOTSIG_STRINGIFY(FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT) ")), " \
"DescriptorTable(SRV(t0, numDescriptors = " FFX_DENOISER_SHADOWS_ROOTSIG_STRINGIFY(FFX_DENOISER_RESOURCE_IDENTIFIER_COUNT) ")), " \
"CBV(b0)")]
#if defined(FFX_DENOISER_SHADOWS_EMBED_ROOTSIG)
#define FFX_DENOISER_SHADOWS_EMBED_PREPARE_SHADOW_MASK_ROOTSIG_CONTENT FFX_DENOISER_SHADOWS_PREPARE_SHADOW_MASK_ROOTSIG
#define FFX_DENOISER_SHADOWS_EMBED_TILE_CLASSIFICATION_ROOTSIG_CONTENT FFX_DENOISER_SHADOWS_TILE_CLASSIFICATION_ROOTSIG
#define FFX_DENOISER_SHADOWS_EMBED_FILTER_SOFT_SHADOWS_ROOTSIG_CONTENT FFX_DENOISER_SHADOWS_FILTER_SOFT_SHADOWS_ROOTSIG
#else
#define FFX_DENOISER_SHADOWS_EMBED_PREPARE_SHADOW_MASK_ROOTSIG_CONTENT
#define FFX_DENOISER_SHADOWS_EMBED_TILE_CLASSIFICATION_ROOTSIG_CONTENT
#define FFX_DENOISER_SHADOWS_EMBED_FILTER_SOFT_SHADOWS_ROOTSIG_CONTENT
#endif // #if FFX_DENOISER_SHADOWS_EMBED_ROOTSIG
#endif // #if defined(FFX_GPU)
// Sampler
#if defined(DENOISER_SHADOWS_BIND_SRV_HISTORY)
SamplerState s_trilinerClamp : register(s0);
#endif
// SRVs
#if defined DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS
Texture2D<FfxUInt32> r_hit_mask_results : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS);
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_DEPTH
Texture2D<FfxFloat32> r_depth : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_DEPTH);
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_VELOCITY
Texture2D<FfxFloat32x2> r_velocity : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_VELOCITY);
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_NORMAL
Texture2D<FfxFloat32x3> r_normal : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_NORMAL);
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_HISTORY
Texture2D<FfxFloat32x2> r_history : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_HISTORY);
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH
Texture2D<FfxFloat32> r_previous_depth : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH);
#endif
#if defined DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS
Texture2D<FfxFloat32x3> r_previous_moments : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS);
#endif
#if FFX_HALF
#if defined DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT
Texture2D<FfxFloat16x2> r_filter_input : FFX_DENOISER_SHADOWS_DECLARE_SRV(DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT);
#endif
#endif
// UAV declarations
#if defined DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK
RWStructuredBuffer<FfxUInt32> rw_shadow_mask : FFX_DENOISER_SHADOWS_DECLARE_UAV(DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK);
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT
RWStructuredBuffer<FfxUInt32> rw_raytracer_result : FFX_DENOISER_SHADOWS_DECLARE_UAV(DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT);
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_TILE_METADATA
RWStructuredBuffer<FfxUInt32> rw_tile_metadata : FFX_DENOISER_SHADOWS_DECLARE_UAV(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA);
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS
RWTexture2D<FfxFloat32x2> rw_reprojection_results : FFX_DENOISER_SHADOWS_DECLARE_UAV(DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS);
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS
RWTexture2D<FfxFloat32x3> rw_current_moments : FFX_DENOISER_SHADOWS_DECLARE_UAV(DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS);
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_HISTORY
RWTexture2D<FfxFloat32x2> rw_history : FFX_DENOISER_SHADOWS_DECLARE_UAV(DENOISER_SHADOWS_BIND_UAV_HISTORY);
#endif
#if defined DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT
RWTexture2D<unorm FfxFloat32x4> rw_filter_output : FFX_DENOISER_SHADOWS_DECLARE_UAV(DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT);
#endif
#define TILE_SIZE_X 8
#define TILE_SIZE_Y 4
FfxUInt32 LaneIdToBitShift(FfxUInt32x2 localID)
{
return localID.y * TILE_SIZE_X + localID.x;
}
FfxBoolean WaveMaskToBool(FfxUInt32 mask, FfxUInt32x2 localID)
{
return (1 << LaneIdToBitShift(localID.xy)) & mask;
}
#if defined(DENOISER_SHADOWS_BIND_CB0_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
FfxInt32x2 BufferDimensions()
{
return iBufferDimensions;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB0_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS)
FfxBoolean HitsLight(FfxUInt32x2 did, FfxUInt32x2 gtid, FfxUInt32x2 gid)
{
return !WaveMaskToBool(r_hit_mask_results[gid], gtid);
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_INPUT_HIT_MASK_RESULTS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
FfxFloat32 NormalsUnpackMul()
{
return normalsUnpackMul_unpackAdd[0];
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
FfxFloat32 NormalsUnpackAdd()
{
return normalsUnpackMul_unpackAdd[1];
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK)
void StoreShadowMask(FfxUInt32 offset, FfxUInt32 value)
{
rw_shadow_mask[offset] = value;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_SHADOW_MASK)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32Mat4 ViewProjectionInverse()
{
return fViewProjectionInverse;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32Mat4 ReprojectionMatrix()
{
return fReprojectionMatrix;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
FfxFloat32Mat4 ProjectionInverse()
{
return fProjectionInverse;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
FfxFloat32x2 InvBufferDimensions()
{
return fInvBufferDimensions;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS) || defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32x2 MotionVectorScale()
{
return fMotionVectorScale;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxInt32 IsFirstFrame()
{
return iFirstFrame;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
#if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32x3 Eye()
{
return fEye;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB1_DENOISER_SHADOWS)
FfxFloat32 LoadDepth(FfxInt32x2 p)
{
#if defined(DENOISER_SHADOWS_BIND_SRV_DEPTH)
return r_depth.Load(FfxInt32x3(p, 0)).x;
#else
return 0.f;
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_DEPTH)
}
#if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH)
FfxFloat32 LoadPreviousDepth(FfxInt32x2 p)
{
return r_previous_depth.Load(FfxInt32x3(p, 0)).x;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_DEPTH)
#if defined(DENOISER_SHADOWS_BIND_SRV_NORMAL)
FfxFloat32x3 LoadNormals(FfxInt32x2 p)
{
FfxFloat32x3 normal = r_normal.Load(FfxInt32x3(p, 0)).xyz;
normal = normal * NormalsUnpackMul().xxx + NormalsUnpackAdd().xxx;
return normalize(normal);
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_NORMAL)
#if defined(DENOISER_SHADOWS_BIND_SRV_VELOCITY)
FfxFloat32x2 LoadVelocity(FfxInt32x2 p)
{
FfxFloat32x2 velocity = r_velocity.Load(FfxInt32x3(p, 0)).rg;
return velocity * MotionVectorScale();
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_VELOCITY)
#if defined(DENOISER_SHADOWS_BIND_SRV_HISTORY)
FfxFloat32 LoadHistory(FfxFloat32x2 p)
{
return r_history.SampleLevel(s_trilinerClamp, p, 0).x;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_HISTORY)
#if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS)
FfxFloat32x3 LoadPreviousMomentsBuffer(FfxInt32x2 p)
{
return r_previous_moments.Load(FfxInt32x3(p, 0)).xyz;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_PREVIOUS_MOMENTS)
#if defined(DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT)
FfxUInt32 LoadRaytracedShadowMask(FfxUInt32 p)
{
return rw_raytracer_result[p];
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_RAYTRACER_RESULT)
#if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
void StoreMetadata(FfxUInt32 p, FfxUInt32 val)
{
rw_tile_metadata[p] = val;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
#if defined(DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS)
void StoreMoments(FfxUInt32x2 p, FfxFloat32x3 val)
{
rw_current_moments[p] = val;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_CURRENT_MOMENTS)
#if defined(DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS)
void StoreReprojectionResults(FfxUInt32x2 p, FfxFloat32x2 val)
{
rw_reprojection_results[p] = val;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_REPROJECTION_RESULTS)
#if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
FfxFloat32 DepthSimilaritySigma()
{
return fDepthSimilaritySigma;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_CB2_DENOISER_SHADOWS)
#if FFX_HALF
#if defined(DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT)
FfxFloat16x2 LoadFilterInput(FfxUInt32x2 p)
{
return (FfxFloat16x2)r_filter_input.Load(FfxInt32x3(p, 0)).xy;
}
#endif // #if defined(DENOISER_SHADOWS_BIND_SRV_FILTER_INPUT)
#endif
FfxBoolean IsShadowReciever(FfxUInt32x2 p)
{
FfxFloat32 depth = LoadDepth(p);
return (depth > 0.0f) && (depth < 1.0f);
}
#if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
FfxUInt32 LoadTileMetaData(FfxUInt32 p)
{
return rw_tile_metadata[p];
}
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_TILE_METADATA)
void StoreHistory(FfxUInt32x2 p, FfxFloat32x2 val)
{
#if defined(DENOISER_SHADOWS_BIND_UAV_HISTORY)
rw_history[p] = val;
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_HISTORY)
}
void StoreFilterOutput(FfxUInt32x2 p, FfxFloat32 val)
{
#if defined(DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT)
rw_filter_output[p].x = val;
#endif // #if defined(DENOISER_SHADOWS_BIND_UAV_FILTER_OUTPUT)
}
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,331 @@
// 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_DNSR_SHADOWS_FILTER_HLSL
#define FFX_DNSR_SHADOWS_FILTER_HLSL
#include "ffx_denoiser_shadows_util.h"
FFX_GROUPSHARED FfxUInt32 g_FFX_DNSR_Shadows_shared_input[16][16];
FFX_GROUPSHARED FfxFloat32 g_FFX_DNSR_Shadows_shared_depth[16][16];
FFX_GROUPSHARED FfxUInt32 g_FFX_DNSR_Shadows_shared_normals_xy[16][16];
FFX_GROUPSHARED FfxUInt32 g_FFX_DNSR_Shadows_shared_normals_zw[16][16];
#if FFX_HALF
FfxUInt32 FFX_DNSR_Shadows_PackFloat16(FfxFloat16x2 v)
{
return ffxPackHalf2x16(FfxFloat32x2(v));
}
FfxFloat16x2 FFX_DNSR_Shadows_UnpackFloat16(FfxUInt32 a)
{
return ffxUnpackF16(a);
}
FfxFloat16x2 FFX_DNSR_Shadows_LoadInputFromGroupSharedMemory(FfxInt32x2 idx)
{
return FFX_DNSR_Shadows_UnpackFloat16(g_FFX_DNSR_Shadows_shared_input[idx.y][idx.x]);
}
#endif
FfxFloat32 FFX_DNSR_Shadows_LoadDepthFromGroupSharedMemory(FfxInt32x2 idx)
{
return g_FFX_DNSR_Shadows_shared_depth[idx.y][idx.x];
}
#if FFX_HALF
FfxFloat16x3 FFX_DNSR_Shadows_LoadNormalsFromGroupSharedMemory(FfxInt32x2 idx)
{
FfxFloat16x3 normals;
normals.xy = FFX_DNSR_Shadows_UnpackFloat16(g_FFX_DNSR_Shadows_shared_normals_xy[idx.y][idx.x]);
normals.z = FFX_DNSR_Shadows_UnpackFloat16(g_FFX_DNSR_Shadows_shared_normals_zw[idx.y][idx.x]).x;
return normals;
}
void FFX_DNSR_Shadows_StoreInGroupSharedMemory(FfxInt32x2 idx, FfxFloat16x3 normals, FfxFloat16x2 inp, FfxFloat32 depth)
{
g_FFX_DNSR_Shadows_shared_input[idx.y][idx.x] = FFX_DNSR_Shadows_PackFloat16(inp);
g_FFX_DNSR_Shadows_shared_depth[idx.y][idx.x] = depth;
g_FFX_DNSR_Shadows_shared_normals_xy[idx.y][idx.x] = FFX_DNSR_Shadows_PackFloat16(normals.xy);
g_FFX_DNSR_Shadows_shared_normals_zw[idx.y][idx.x] = FFX_DNSR_Shadows_PackFloat16(FfxFloat16x2(normals.z, 0));
}
void FFX_DNSR_Shadows_LoadWithOffset(FfxInt32x2 did, FfxInt32x2 offset, out FfxFloat16x3 normals, out FfxFloat16x2 inp, out FfxFloat32 depth)
{
did += offset;
const FfxInt32x2 p = clamp(did, FfxInt32x2(0, 0), BufferDimensions() - 1);
normals = FfxFloat16x3(LoadNormals(p));
inp = LoadFilterInput(p);
depth = LoadDepth(p);
}
void FFX_DNSR_Shadows_StoreWithOffset(FfxInt32x2 gtid, FfxInt32x2 offset, FfxFloat16x3 normals, FfxFloat16x2 inp, FfxFloat32 depth)
{
gtid += offset;
FFX_DNSR_Shadows_StoreInGroupSharedMemory(gtid, normals, inp, depth);
}
void FFX_DNSR_Shadows_InitializeGroupSharedMemory(FfxInt32x2 did, FfxInt32x2 gtid)
{
FfxInt32x2 offset_0 = FfxInt32x2(0, 0);
FfxInt32x2 offset_1 = FfxInt32x2(8, 0);
FfxInt32x2 offset_2 = FfxInt32x2(0, 8);
FfxInt32x2 offset_3 = FfxInt32x2(8, 8);
FfxFloat16x3 normals_0;
FfxFloat16x2 input_0;
FfxFloat32 depth_0;
FfxFloat16x3 normals_1;
FfxFloat16x2 input_1;
FfxFloat32 depth_1;
FfxFloat16x3 normals_2;
FfxFloat16x2 input_2;
FfxFloat32 depth_2;
FfxFloat16x3 normals_3;
FfxFloat16x2 input_3;
FfxFloat32 depth_3;
/// XA
/// BC
did -= 4;
FFX_DNSR_Shadows_LoadWithOffset(did, offset_0, normals_0, input_0, depth_0); // X
FFX_DNSR_Shadows_LoadWithOffset(did, offset_1, normals_1, input_1, depth_1); // A
FFX_DNSR_Shadows_LoadWithOffset(did, offset_2, normals_2, input_2, depth_2); // B
FFX_DNSR_Shadows_LoadWithOffset(did, offset_3, normals_3, input_3, depth_3); // C
FFX_DNSR_Shadows_StoreWithOffset(gtid, offset_0, normals_0, input_0, depth_0); // X
FFX_DNSR_Shadows_StoreWithOffset(gtid, offset_1, normals_1, input_1, depth_1); // A
FFX_DNSR_Shadows_StoreWithOffset(gtid, offset_2, normals_2, input_2, depth_2); // B
FFX_DNSR_Shadows_StoreWithOffset(gtid, offset_3, normals_3, input_3, depth_3); // C
}
#else
// Not used, defined to make sure f32 path compiles
FfxUInt32 FFX_DNSR_Shadows_PackFloat16(FfxFloat32x2 v){return 0;}
FfxFloat32x2 FFX_DNSR_Shadows_UnpackFloat16(FfxUInt32 a){return FfxFloat32x2(0,0);}
FfxFloat32x2 FFX_DNSR_Shadows_LoadInputFromGroupSharedMemory(FfxInt32x2 idx){return FfxFloat32x2(0,0);}
FfxFloat32x3 FFX_DNSR_Shadows_LoadNormalsFromGroupSharedMemory(FfxInt32x2 idx){return FfxFloat32x3(0,0,0);}
void FFX_DNSR_Shadows_StoreInGroupSharedMemory(FfxInt32x2 idx, FfxFloat32x3 normals, FfxFloat32x2 inp, FfxFloat32 depth){}
void FFX_DNSR_Shadows_LoadWithOffset(FfxInt32x2 did, FfxInt32x2 offset, out FfxFloat32x3 normals, out FfxFloat32x2 inp, out FfxFloat32 depth){depth = 0;}
void FFX_DNSR_Shadows_StoreWithOffset(FfxInt32x2 gtid, FfxInt32x2 offset, FfxFloat32x3 normals, FfxFloat32x2 inp, FfxFloat32 depth){}
void FFX_DNSR_Shadows_InitializeGroupSharedMemory(FfxInt32x2 did, FfxInt32x2 gtid){}
#endif
FfxFloat32 FFX_DNSR_Shadows_GetShadowSimilarity(FfxFloat32 x1, FfxFloat32 x2, FfxFloat32 sigma)
{
return exp(-abs(x1 - x2) / sigma);
}
FfxFloat32 FFX_DNSR_Shadows_GetDepthSimilarity(FfxFloat32 x1, FfxFloat32 x2, FfxFloat32 sigma)
{
return exp(-abs(x1 - x2) / sigma);
}
FfxFloat32 FFX_DNSR_Shadows_GetNormalSimilarity(FfxFloat32x3 x1, FfxFloat32x3 x2)
{
return ffxPow(ffxSaturate(dot(x1, x2)), 32.0f);
}
FfxFloat32 FFX_DNSR_Shadows_GetLinearDepth(FfxUInt32x2 did, FfxFloat32 depth)
{
const FfxFloat32x2 uv = (did + 0.5f) * InvBufferDimensions();
const FfxFloat32x2 ndc = 2.0f * FfxFloat32x2(uv.x, 1.0f - uv.y) - 1.0f;
FfxFloat32x4 projected = FFX_MATRIX_MULTIPLY(ProjectionInverse(), FfxFloat32x4(ndc, depth, 1));
return abs(projected.z / projected.w);
}
FfxFloat32 FFX_DNSR_Shadows_FetchFilteredVarianceFromGroupSharedMemory(FfxInt32x2 pos)
{
const FfxInt32 k = 1;
FfxFloat32 variance = 0.0f;
const FfxFloat32 kernel[2][2] =
{
{ 1.0f / 4.0f, 1.0f / 8.0f },
{ 1.0f / 8.0f, 1.0f / 16.0f }
};
for (FfxInt32 y = -k; y <= k; ++y)
{
for (FfxInt32 x = -k; x <= k; ++x)
{
const FfxFloat32 w = kernel[abs(x)][abs(y)];
variance += w * FFX_DNSR_Shadows_LoadInputFromGroupSharedMemory(pos + FfxInt32x2(x, y)).y;
}
}
return variance;
}
void FFX_DNSR_Shadows_DenoiseFromGroupSharedMemory(FfxUInt32x2 did, FfxUInt32x2 gtid, inout FfxFloat32 weight_sum, inout FfxFloat32x2 shadow_sum, FfxFloat32 depth, FfxUInt32 stepsize)
{
// Load our center sample
const FfxFloat32x2 shadow_center = FFX_DNSR_Shadows_LoadInputFromGroupSharedMemory(FfxInt32x2(gtid));
const FfxFloat32x3 normal_center = FFX_DNSR_Shadows_LoadNormalsFromGroupSharedMemory(FfxInt32x2(gtid));
weight_sum = 1.0f;
shadow_sum = shadow_center;
const FfxFloat32 variance = FFX_DNSR_Shadows_FetchFilteredVarianceFromGroupSharedMemory(FfxInt32x2(gtid));
const FfxFloat32 std_deviation = sqrt(max(variance + 1e-9f, 0.0f));
const FfxFloat32 depth_center = FFX_DNSR_Shadows_GetLinearDepth(did, depth); // linearize the depth value
// Iterate filter kernel
const FfxInt32 k = 1;
const FfxFloat32 kernel[3] = { 1.0f, 2.0f / 3.0f, 1.0f / 6.0f };
for (FfxInt32 y = -k; y <= k; ++y)
{
for (FfxInt32 x = -k; x <= k; ++x)
{
// Should we process this sample?
const FfxInt32x2 step = FfxInt32x2(x, y) * FfxInt32x2(stepsize, stepsize);
const FfxInt32x2 gtid_idx = FfxInt32x2(gtid) + step;
const FfxInt32x2 did_idx = FfxInt32x2(did) + step;
FfxFloat32 depth_neigh = FFX_DNSR_Shadows_LoadDepthFromGroupSharedMemory(gtid_idx);
FfxFloat32x3 normal_neigh = FFX_DNSR_Shadows_LoadNormalsFromGroupSharedMemory(gtid_idx);
FfxFloat32x2 shadow_neigh = FFX_DNSR_Shadows_LoadInputFromGroupSharedMemory(gtid_idx);
FfxFloat32 sky_pixel_multiplier = ((x == 0 && y == 0) || depth_neigh >= 1.0f || depth_neigh <= 0.0f) ? 0 : 1; // Zero weight for sky pixels
// Fetch our filtering values
depth_neigh = FFX_DNSR_Shadows_GetLinearDepth(did_idx, depth_neigh);
// Evaluate the edge-stopping function
FfxFloat32 w = kernel[abs(x)] * kernel[abs(y)]; // kernel weight
w *= FFX_DNSR_Shadows_GetShadowSimilarity(shadow_center.x, shadow_neigh.x, std_deviation);
w *= FFX_DNSR_Shadows_GetDepthSimilarity(depth_center, depth_neigh, DepthSimilaritySigma());
w *= FFX_DNSR_Shadows_GetNormalSimilarity(normal_center, normal_neigh);
w *= sky_pixel_multiplier;
// Accumulate the filtered sample
shadow_sum += FfxFloat32x2(w, w * w) * shadow_neigh;
weight_sum += w;
}
}
}
FfxFloat32x2 FFX_DNSR_Shadows_ApplyFilterWithPrecache(FfxUInt32x2 did, FfxUInt32x2 gtid, FfxUInt32 stepsize)
{
FfxFloat32 weight_sum = 1.0;
FfxFloat32x2 shadow_sum = FfxFloat32x2(0, 0);
FFX_DNSR_Shadows_InitializeGroupSharedMemory(FfxInt32x2(did), FfxInt32x2(gtid));
FfxBoolean needs_denoiser = IsShadowReciever(did);
FFX_GROUP_MEMORY_BARRIER;
if (needs_denoiser)
{
FfxFloat32 depth = LoadDepth(FfxInt32x2(did));
gtid += 4; // Center threads in groupshared memory
FFX_DNSR_Shadows_DenoiseFromGroupSharedMemory(did, gtid, weight_sum, shadow_sum, depth, stepsize);
}
FfxFloat32 mean = shadow_sum.x / weight_sum;
FfxFloat32 variance = shadow_sum.y / (weight_sum * weight_sum);
return FfxFloat32x2(mean, variance);
}
void FFX_DNSR_Shadows_ReadTileMetaData(FfxUInt32x2 gid, out FfxBoolean is_cleared, out FfxBoolean all_in_light)
{
FfxUInt32 meta_data = LoadTileMetaData(gid.y * FFX_DNSR_Shadows_RoundedDivide(BufferDimensions().x, 8) + gid.x);
is_cleared = FfxBoolean(meta_data & FfxUInt32(TILE_META_DATA_CLEAR_MASK));
all_in_light = FfxBoolean(meta_data & FfxUInt32(TILE_META_DATA_LIGHT_MASK));
}
FfxFloat32x2 FFX_DNSR_Shadows_FilterSoftShadowsPass(FfxUInt32x2 gid, FfxUInt32x2 gtid, FfxUInt32x2 did, out FfxBoolean bWriteResults, const FfxUInt32 pass, const FfxUInt32 stepsize)
{
FfxBoolean is_cleared;
FfxBoolean all_in_light;
FFX_DNSR_Shadows_ReadTileMetaData(gid, is_cleared, all_in_light);
bWriteResults = FFX_FALSE;
FfxFloat32x2 results = FfxFloat32x2(0, 0);
if (is_cleared)
{
if (pass != 1)
{
results.x = all_in_light ? 1.0 : 0.0;
bWriteResults = FFX_TRUE;
}
}
else
{
results = FFX_DNSR_Shadows_ApplyFilterWithPrecache(did, gtid, stepsize);
bWriteResults = FFX_TRUE;
}
return results;
}
void DenoiserShadowsFilterPass0(FfxUInt32x2 gid, FfxUInt32x2 gtid, FfxUInt32x2 did)
{
const uint PASS_INDEX = 0;
const uint STEP_SIZE = 1;
FfxBoolean bWriteOutput = FFX_FALSE;
const FfxFloat32x2 results = FFX_DNSR_Shadows_FilterSoftShadowsPass(gid, gtid, did, bWriteOutput, PASS_INDEX, STEP_SIZE);
if (bWriteOutput)
{
StoreHistory(did, results);
}
}
void DenoiserShadowsFilterPass1(FfxUInt32x2 gid, FfxUInt32x2 gtid, FfxUInt32x2 did)
{
const uint PASS_INDEX = 1;
const uint STEP_SIZE = 2;
FfxBoolean bWriteOutput = FFX_FALSE;
const FfxFloat32x2 results = FFX_DNSR_Shadows_FilterSoftShadowsPass(gid, gtid, did, bWriteOutput, PASS_INDEX, STEP_SIZE);
if (bWriteOutput)
{
StoreHistory(did, results);
}
}
void DenoiserShadowsFilterPass2(FfxUInt32x2 gid, FfxUInt32x2 gtid, FfxUInt32x2 did)
{
const uint PASS_INDEX = 2;
const uint STEP_SIZE = 4;
FfxBoolean bWriteOutput = FFX_FALSE;
const FfxFloat32x2 results = FFX_DNSR_Shadows_FilterSoftShadowsPass(gid, gtid, did, bWriteOutput, PASS_INDEX, STEP_SIZE);
// Recover some of the contrast lost during denoising
const FfxFloat32 shadow_remap = max(1.2f - results.y, 1.0f);
const FfxFloat32 mean = pow(abs(results.x), shadow_remap);
if (bWriteOutput)
{
StoreFilterOutput(did, mean);
}
}
#endif

View File

@@ -0,0 +1,53 @@
// 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_DNSR_SHADOWS_PREPARESHADOWMASK_HLSL
#define FFX_DNSR_SHADOWS_PREPARESHADOWMASK_HLSL
#include "ffx_denoiser_shadows_util.h"
void FFX_DNSR_Shadows_CopyResult(FfxUInt32x2 gtid, FfxUInt32x2 gid)
{
const FfxUInt32x2 did = gid * FfxUInt32x2(8, 4) + gtid;
const FfxUInt32 linear_tile_index = FFX_DNSR_Shadows_LinearTileIndex(gid, BufferDimensions().x);
const FfxBoolean hit_light = HitsLight(did, gtid, gid);
const FfxUInt32 lane_mask = hit_light ? FFX_DNSR_Shadows_GetBitMaskFromPixelPosition(did) : 0;
StoreShadowMask(linear_tile_index, ffxWaveOr(lane_mask));
}
void FFX_DNSR_Shadows_PrepareShadowMask(FfxUInt32x2 gtid, FfxUInt32x2 gid)
{
gid *= 4;
FfxUInt32x2 tile_dimensions = (BufferDimensions() + FfxUInt32x2(7, 3)) / FfxUInt32x2(8, 4);
for (FfxInt32 i = 0; i < 4; ++i)
{
for (FfxInt32 j = 0; j < 4; ++j)
{
FfxUInt32x2 tile_id = FfxUInt32x2(gid.x + i, gid.y + j);
tile_id = clamp(tile_id, FfxUInt32x2(0, 0), tile_dimensions - FfxUInt32x2(1,1));
FFX_DNSR_Shadows_CopyResult(gtid, tile_id);
}
}
}
#endif

View File

@@ -0,0 +1,430 @@
// 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_DNSR_SHADOWS_TILECLASSIFICATION_HLSL
#define FFX_DNSR_SHADOWS_TILECLASSIFICATION_HLSL
#include "ffx_denoiser_shadows_util.h"
FFX_GROUPSHARED FfxInt32 g_FFX_DNSR_Shadows_false_count;
FfxBoolean FFX_DNSR_Shadows_ThreadGroupAllTrue(FfxBoolean val)
{
const FfxUInt32 lane_count_in_thread_group = 64;
if (ffxWaveLaneCount() == lane_count_in_thread_group)
{
return ffxWaveAllTrue(val);
}
else
{
FFX_GROUP_MEMORY_BARRIER;
g_FFX_DNSR_Shadows_false_count = 0;
FFX_GROUP_MEMORY_BARRIER;
if (!val) g_FFX_DNSR_Shadows_false_count = 1;
FFX_GROUP_MEMORY_BARRIER;
return g_FFX_DNSR_Shadows_false_count == 0;
}
}
void FFX_DNSR_Shadows_SearchSpatialRegion(FfxUInt32x2 gid, out FfxBoolean all_in_light, out FfxBoolean all_in_shadow)
{
// The spatial passes can reach a total region of 1+2+4 = 7x7 around each block.
// The masks are 8x4, so we need a larger vertical stride
// Visualization - each x represents a 4x4 block, xx is one entire 8x4 mask as read from the raytracer result
// Same for yy, these are the ones we are working on right now
// xx xx xx
// xx xx xx
// xx yy xx <-- yy here is the base_tile below
// xx yy xx
// xx xx xx
// xx xx xx
// All of this should result in scalar ops
FfxUInt32x2 base_tile = FFX_DNSR_Shadows_GetTileIndexFromPixelPosition(gid * FfxInt32x2(8, 8));
// Load the entire region of masks in a scalar fashion
FfxUInt32 combined_or_mask = 0;
FfxUInt32 combined_and_mask = 0xFFFFFFFF;
for (FfxInt32 j = -2; j <= 3; ++j)
{
for (FfxInt32 i = -1; i <= 1; ++i)
{
FfxInt32x2 tile_index = FfxInt32x2(base_tile) + FfxInt32x2(i, j);
tile_index = clamp(tile_index, FfxInt32x2(0,0), FfxInt32x2(FFX_DNSR_Shadows_RoundedDivide(BufferDimensions().x, 8), FFX_DNSR_Shadows_RoundedDivide(BufferDimensions().y, 4)) - 1);
const FfxUInt32 linear_tile_index = FFX_DNSR_Shadows_LinearTileIndex(tile_index, BufferDimensions().x);
const FfxUInt32 shadow_mask = LoadRaytracedShadowMask(linear_tile_index);
combined_or_mask = combined_or_mask | shadow_mask;
combined_and_mask = combined_and_mask & shadow_mask;
}
}
all_in_light = combined_and_mask == 0xFFFFFFFFu;
all_in_shadow = combined_or_mask == 0u;
}
FfxFloat32 FFX_DNSR_Shadows_GetLinearDepth(FfxUInt32x2 did, FfxFloat32 depth)
{
const FfxFloat32x2 uv = (did + 0.5f) * InvBufferDimensions();
const FfxFloat32x2 ndc = 2.0f * FfxFloat32x2(uv.x, 1.0f - uv.y) - 1.0f;
FfxFloat32x4 projected = FFX_MATRIX_MULTIPLY(ProjectionInverse(), FfxFloat32x4(ndc, depth, 1));
return abs(projected.z / projected.w);
}
FfxBoolean FFX_DNSR_Shadows_IsDisoccluded(FfxUInt32x2 did, FfxFloat32 depth, FfxFloat32x2 velocity)
{
const FfxInt32x2 dims = BufferDimensions();
const FfxFloat32x2 texel_size = InvBufferDimensions();
const FfxFloat32x2 uv = (did + 0.5f) * texel_size;
const FfxFloat32x2 ndc = (2.0f * uv - 1.0f) * FfxFloat32x2(1.0f, -1.0f);
const FfxFloat32x2 previous_uv = uv + velocity;
FfxBoolean is_disoccluded = FFX_TRUE;
if (all(FFX_GREATER_THAN(previous_uv, FfxFloat32x2(0,0))) && all(FFX_LESS_THAN(previous_uv, FfxFloat32x2(1,1))))
{
// Read the center values
FfxFloat32x3 normal = LoadNormals(did);
FfxFloat32x4 clip_space = FFX_MATRIX_MULTIPLY(ReprojectionMatrix(), FfxFloat32x4(ndc, depth, 1.0f));
clip_space.z /= clip_space.w; // perspective divide
// How aligned with the view vector? (the more Z aligned, the higher the depth errors)
const FfxFloat32x4 homogeneous = FFX_MATRIX_MULTIPLY(ViewProjectionInverse(), FfxFloat32x4(ndc, depth, 1.0f));
const FfxFloat32x3 world_position = FfxFloat32x3(homogeneous.xyz / homogeneous.w); // perspective divide
const FfxFloat32x3 view_direction = normalize(Eye().xyz - world_position);
FfxFloat32 z_alignment = 1.0f - dot(view_direction, normal);
z_alignment = pow(z_alignment, 8);
// Calculate the depth difference
FfxFloat32 linear_depth = FFX_DNSR_Shadows_GetLinearDepth(did, clip_space.z); // get linear depth
FfxInt32x2 idx = FfxInt32x2(previous_uv * FfxFloat32x2(dims));
const FfxFloat32 previous_depth = FFX_DNSR_Shadows_GetLinearDepth(idx, LoadPreviousDepth(idx));
const FfxFloat32 depth_difference = abs(previous_depth - linear_depth) / linear_depth;
// Resolve into the disocclusion mask
const FfxFloat32 depth_tolerance = ffxLerp(1e-2f, 1e-1f, z_alignment);
is_disoccluded = depth_difference >= depth_tolerance;
}
return is_disoccluded;
}
FfxFloat32x2 FFX_DNSR_Shadows_GetClosestVelocity(FfxInt32x2 did, FfxFloat32 depth)
{
FfxFloat32x2 closest_velocity = LoadVelocity(did);
FfxFloat32 closest_depth = depth;
FfxFloat32 new_depth = ffxQuadReadX(closest_depth);
FfxFloat32x2 new_velocity = ffxQuadReadX(closest_velocity);
#if FFX_DENOISER_OPTION_INVERTED_DEPTH
if (new_depth > closest_depth)
#else
if (new_depth < closest_depth)
#endif
{
closest_depth = new_depth;
closest_velocity = new_velocity;
}
new_depth = ffxQuadReadY(closest_depth);
new_velocity = ffxQuadReadY(closest_velocity);
#if FFX_DENOISER_OPTION_INVERTED_DEPTH
if (new_depth > closest_depth)
#else
if (new_depth < closest_depth)
#endif
{
closest_depth = new_depth;
closest_velocity = new_velocity;
}
return closest_velocity;
}
#define KERNEL_RADIUS 8
FfxFloat32 FFX_DNSR_Shadows_KernelWeight(FfxFloat32 i)
{
#define KERNEL_WEIGHT(i) (exp(-3.0 * FfxFloat32(i * i) / ((KERNEL_RADIUS + 1.0) * (KERNEL_RADIUS + 1.0))))
// Statically initialize kernel_weights_sum
FfxFloat32 kernel_weights_sum = 0;
kernel_weights_sum += KERNEL_WEIGHT(0);
for (FfxInt32 c = 1; c <= KERNEL_RADIUS; ++c)
{
kernel_weights_sum += 2 * KERNEL_WEIGHT(c); // Add other half of the kernel to the sum
}
FfxFloat32 inv_kernel_weights_sum = ffxReciprocal(kernel_weights_sum);
// The only runtime code in this function
return KERNEL_WEIGHT(i) * inv_kernel_weights_sum;
}
void FFX_DNSR_Shadows_AccumulateMoments(FfxFloat32 value, FfxFloat32 weight, inout FfxFloat32 moments)
{
// We get value from the horizontal neighborhood calculations. Thus, it's both mean and variance due to using one sample per pixel
moments += value * weight;
}
// The horizontal part of a 17x17 local neighborhood kernel
FfxFloat32 FFX_DNSR_Shadows_HorizontalNeighborhood(FfxInt32x2 did)
{
const FfxInt32x2 base_did = did;
// Prevent vertical out of bounds access
if ((base_did.y < 0) || (base_did.y >= BufferDimensions().y)) return 0;
const FfxUInt32x2 tile_index = FFX_DNSR_Shadows_GetTileIndexFromPixelPosition(base_did);
const FfxUInt32 linear_tile_index = FFX_DNSR_Shadows_LinearTileIndex(tile_index, BufferDimensions().x);
const FfxUInt32 left_tile_index = linear_tile_index - 1;
const FfxUInt32 center_tile_index = linear_tile_index;
const FfxUInt32 right_tile_index = linear_tile_index + 1;
FfxBoolean is_first_tile_in_row = tile_index.x == 0;
FfxBoolean is_last_tile_in_row = tile_index.x == (FFX_DNSR_Shadows_RoundedDivide(BufferDimensions().x, 8) - 1);
FfxUInt32 left_tile = 0;
if (!is_first_tile_in_row) left_tile = LoadRaytracedShadowMask(left_tile_index);
FfxUInt32 center_tile = LoadRaytracedShadowMask(center_tile_index);
FfxUInt32 right_tile = 0;
if (!is_last_tile_in_row) right_tile = LoadRaytracedShadowMask(right_tile_index);
// Construct a single FfxUInt32 with the lowest 17bits containing the horizontal part of the local neighborhood.
// First extract the 8 bits of our row in each of the neighboring tiles
const FfxUInt32 row_base_index = (did.y % 4) * 8;
const FfxUInt32 left = (left_tile >> row_base_index) & 0xFF;
const FfxUInt32 center = (center_tile >> row_base_index) & 0xFF;
const FfxUInt32 right = (right_tile >> row_base_index) & 0xFF;
// Combine them into a single mask containting [left, center, right] from least significant to most significant bit
FfxUInt32 neighborhood = left | (center << 8) | (right << 16);
// Make sure our pixel is at bit position 9 to get the highest contribution from the filter kernel
const FfxUInt32 bit_index_in_row = (did.x % 8);
neighborhood = neighborhood >> bit_index_in_row; // Shift out bits to the right, so the center bit ends up at bit 9.
FfxFloat32 moment = 0.0; // For one sample per pixel this is both, mean and variance
// First 8 bits up to the center pixel
FfxUInt32 mask;
FfxInt32 i;
for (i = 0; i < 8; ++i)
{
mask = 1u << i;
moment += FfxBoolean(mask & neighborhood) ? FFX_DNSR_Shadows_KernelWeight(8 - i) : 0;
}
// Center pixel
mask = 1u << 8;
moment += FfxBoolean(mask & neighborhood) ? FFX_DNSR_Shadows_KernelWeight(0) : 0;
// Last 8 bits
for (i = 1; i <= 8; ++i)
{
mask = 1u << (8 + i);
moment += FfxBoolean(mask & neighborhood) ? FFX_DNSR_Shadows_KernelWeight(i) : 0;
}
return moment;
}
FFX_GROUPSHARED FfxFloat32 g_FFX_DNSR_Shadows_neighborhood[8][24];
FfxFloat32 FFX_DNSR_Shadows_ComputeLocalNeighborhood(FfxInt32x2 did, FfxInt32x2 gtid)
{
FfxFloat32 local_neighborhood = 0;
FfxFloat32 upper = FFX_DNSR_Shadows_HorizontalNeighborhood(FfxInt32x2(did.x, did.y - 8));
FfxFloat32 center = FFX_DNSR_Shadows_HorizontalNeighborhood(FfxInt32x2(did.x, did.y));
FfxFloat32 lower = FFX_DNSR_Shadows_HorizontalNeighborhood(FfxInt32x2(did.x, did.y + 8));
g_FFX_DNSR_Shadows_neighborhood[gtid.x][gtid.y] = upper;
g_FFX_DNSR_Shadows_neighborhood[gtid.x][gtid.y + 8] = center;
g_FFX_DNSR_Shadows_neighborhood[gtid.x][gtid.y + 16] = lower;
FFX_GROUP_MEMORY_BARRIER;
// First combine the own values.
// KERNEL_RADIUS pixels up is own upper and KERNEL_RADIUS pixels down is own lower value
FFX_DNSR_Shadows_AccumulateMoments(center, FFX_DNSR_Shadows_KernelWeight(0), local_neighborhood);
FFX_DNSR_Shadows_AccumulateMoments(upper, FFX_DNSR_Shadows_KernelWeight(KERNEL_RADIUS), local_neighborhood);
FFX_DNSR_Shadows_AccumulateMoments(lower, FFX_DNSR_Shadows_KernelWeight(KERNEL_RADIUS), local_neighborhood);
// Then read the neighboring values.
for (FfxInt32 i = 1; i < KERNEL_RADIUS; ++i)
{
FfxFloat32 upper_value = g_FFX_DNSR_Shadows_neighborhood[gtid.x][8 + gtid.y - i];
FfxFloat32 lower_value = g_FFX_DNSR_Shadows_neighborhood[gtid.x][8 + gtid.y + i];
FfxFloat32 weight = FFX_DNSR_Shadows_KernelWeight(i);
FFX_DNSR_Shadows_AccumulateMoments(upper_value, weight, local_neighborhood);
FFX_DNSR_Shadows_AccumulateMoments(lower_value, weight, local_neighborhood);
}
return local_neighborhood;
}
void FFX_DNSR_Shadows_WriteTileMetaData(FfxUInt32x2 gid, FfxUInt32x2 gtid, FfxBoolean is_cleared, FfxBoolean all_in_light)
{
if (all(FFX_EQUAL(gtid, FfxUInt32x2(0,0))))
{
FfxUInt32 light_mask = all_in_light ? TILE_META_DATA_LIGHT_MASK : 0;
FfxUInt32 clear_mask = is_cleared ? TILE_META_DATA_CLEAR_MASK : 0;
FfxUInt32 mask = FfxUInt32(light_mask | clear_mask);
StoreMetadata(gid.y * FFX_DNSR_Shadows_RoundedDivide(BufferDimensions().x, 8) + gid.x, mask);
}
}
void FFX_DNSR_Shadows_ClearTargets(FfxUInt32x2 did, FfxUInt32x2 gtid, FfxUInt32x2 gid, FfxFloat32 shadow_value, FfxBoolean is_shadow_receiver, FfxBoolean all_in_light)
{
FFX_DNSR_Shadows_WriteTileMetaData(gid, gtid, FFX_TRUE, all_in_light);
StoreReprojectionResults(did, FfxFloat32x2(shadow_value, 0)); // mean, variance
FfxFloat32 temporal_sample_count = is_shadow_receiver ? 1 : 0;
StoreMoments(did, FfxFloat32x3(shadow_value, 0, temporal_sample_count));// mean, variance, temporal sample count
}
void FFX_DNSR_Shadows_TileClassification(FfxUInt32 group_index, FfxUInt32x2 gid)
{
FfxUInt32x2 gtid = ffxRemapForWaveReduction(group_index); // Make sure we can use the QuadReadAcross intrinsics to access a 2x2 region.
FfxUInt32x2 did = gid * 8 + gtid;
FfxBoolean is_shadow_receiver = IsShadowReciever(did);
FfxBoolean skip_sky = FFX_DNSR_Shadows_ThreadGroupAllTrue(!is_shadow_receiver);
if (skip_sky)
{
// We have to set all resources of the tile we skipped to sensible values as neighboring active denoiser tiles might want to read them.
FFX_DNSR_Shadows_ClearTargets(did, gtid, gid, 0, is_shadow_receiver, FFX_FALSE);
return;
}
FfxBoolean all_in_light = FFX_FALSE;
FfxBoolean all_in_shadow = FFX_FALSE;
FFX_DNSR_Shadows_SearchSpatialRegion(gid, all_in_light, all_in_shadow);
FfxFloat32 shadow_value = all_in_light ? 1 : 0; // Either all_in_light or all_in_shadow must be true, otherwise we would not skip the tile.
FfxBoolean can_skip = all_in_light || all_in_shadow;
// We have to append the entire tile if there is a single lane that we can't skip
FfxBoolean skip_tile = FFX_DNSR_Shadows_ThreadGroupAllTrue(can_skip);
if (skip_tile)
{
// We have to set all resources of the tile we skipped to sensible values as neighboring active denoiser tiles might want to read them.
FFX_DNSR_Shadows_ClearTargets(did, gtid, gid, shadow_value, is_shadow_receiver, all_in_light);
return;
}
FFX_DNSR_Shadows_WriteTileMetaData(gid, gtid, FFX_FALSE, FFX_FALSE);
FfxFloat32 depth = LoadDepth(FfxInt32x2(did));
const FfxFloat32x2 velocity = FFX_DNSR_Shadows_GetClosestVelocity(FfxInt32x2(did), depth); // Must happen before we deactivate lanes
const FfxFloat32 local_neighborhood = FFX_DNSR_Shadows_ComputeLocalNeighborhood(FfxInt32x2(did), FfxInt32x2(gtid));
const FfxFloat32x2 texel_size = InvBufferDimensions();
const FfxFloat32x2 uv = (did.xy + 0.5f) * texel_size;
const FfxFloat32x2 history_uv = uv + velocity;
const FfxInt32x2 history_pos = FfxInt32x2(history_uv * BufferDimensions());
const FfxUInt32x2 tile_index = FFX_DNSR_Shadows_GetTileIndexFromPixelPosition(FfxInt32x2(did));
const FfxUInt32 linear_tile_index = FFX_DNSR_Shadows_LinearTileIndex(tile_index, BufferDimensions().x);
const FfxUInt32 shadow_tile = LoadRaytracedShadowMask(linear_tile_index);
FfxFloat32x3 moments_current = FfxFloat32x3(0,0,0);
FfxFloat32 variance = 0;
FfxFloat32 shadow_clamped = 0;
if (is_shadow_receiver) // do not process sky pixels
{
FfxBoolean hit_light = FfxBoolean(shadow_tile & FFX_DNSR_Shadows_GetBitMaskFromPixelPosition(did));
const FfxFloat32 shadow_current = hit_light ? 1.0 : 0.0;
// Perform moments and variance calculations
{
FfxBoolean is_disoccluded = FFX_DNSR_Shadows_IsDisoccluded(did, depth, velocity);
const FfxFloat32x3 previous_moments = is_disoccluded ? FfxFloat32x3(0.0f, 0.0f, 0.0f) // Can't trust previous moments on disocclusion
: LoadPreviousMomentsBuffer(history_pos);
const FfxFloat32 old_m = previous_moments.x;
const FfxFloat32 old_s = previous_moments.y;
const FfxFloat32 sample_count = previous_moments.z + 1.0f;
const FfxFloat32 new_m = old_m + (shadow_current - old_m) / sample_count;
const FfxFloat32 new_s = old_s + (shadow_current - old_m) * (shadow_current - new_m);
variance = (sample_count > 1.0f ? new_s / (sample_count - 1.0f) : 1.0f);
moments_current = FfxFloat32x3(new_m, new_s, sample_count);
}
// Retrieve local neighborhood and reproject
{
FfxFloat32 mean = local_neighborhood;
FfxFloat32 spatial_variance = local_neighborhood;
spatial_variance = max(spatial_variance - mean * mean, 0.0f);
// Compute the clamping bounding box
const FfxFloat32 std_deviation = sqrt(spatial_variance);
const FfxFloat32 nmin = mean - 0.5f * std_deviation;
const FfxFloat32 nmax = mean + 0.5f * std_deviation;
// Clamp reprojected sample to local neighborhood
FfxFloat32 shadow_previous = shadow_current;
if (IsFirstFrame() == 0)
{
shadow_previous = LoadHistory(history_uv);
}
shadow_clamped = clamp(shadow_previous, nmin, nmax);
// Reduce history weighting
const FfxFloat32 sigma = 20.0f;
const FfxFloat32 temporal_discontinuity = (shadow_previous - mean) / max(0.5f * std_deviation, 0.001f);
const FfxFloat32 sample_counter_damper = exp(-temporal_discontinuity * temporal_discontinuity / sigma);
moments_current.z *= sample_counter_damper;
// Boost variance on first frames
if (moments_current.z < 16.0f)
{
const FfxFloat32 variance_boost = max(16.0f - moments_current.z, 1.0f);
variance = max(variance, spatial_variance);
variance *= variance_boost;
}
}
// Perform the temporal blend
const FfxFloat32 history_weight = sqrt(max(8.0f - moments_current.z, 0.0f) / 8.0f);
shadow_clamped = ffxLerp(shadow_clamped, shadow_current, ffxLerp(0.05f, 1.0f, history_weight));
}
// Output the results of the temporal pass
StoreReprojectionResults(did.xy, FfxFloat32x2(shadow_clamped, variance));
StoreMoments(did.xy, moments_current);
}
#endif

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_DNSR_SHADOWS_UTILS_HLSL
#define FFX_DNSR_SHADOWS_UTILS_HLSL
FfxUInt32 FFX_DNSR_Shadows_RoundedDivide(FfxUInt32 value, FfxUInt32 divisor)
{
return (value + divisor - 1) / divisor;
}
FfxUInt32x2 FFX_DNSR_Shadows_GetTileIndexFromPixelPosition(FfxUInt32x2 pixel_pos)
{
return FfxUInt32x2(pixel_pos.x / 8, pixel_pos.y / 4);
}
FfxUInt32 FFX_DNSR_Shadows_LinearTileIndex(FfxUInt32x2 tile_index, FfxUInt32 screen_width)
{
return tile_index.y * FFX_DNSR_Shadows_RoundedDivide(screen_width, 8) + tile_index.x;
}
FfxUInt32 FFX_DNSR_Shadows_GetBitMaskFromPixelPosition(FfxUInt32x2 pixel_pos)
{
FfxUInt32 lane_index = (pixel_pos.y % 4) * 8 + (pixel_pos.x % 8);
return (1u << lane_index);
}
#define TILE_META_DATA_CLEAR_MASK 1
#define TILE_META_DATA_LIGHT_MASK 2
#endif