Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.cpp
2025-05-18 13:04:45 +08:00

3106 lines
120 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ScreenSpaceDenoise.cpp: Denoise in screen space.
=============================================================================*/
#include "ScreenSpaceDenoise.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "LightSceneProxy.h"
#include "StaticBoundShaderState.h"
#include "SceneUtils.h"
#include "PostProcess/SceneRenderTargets.h"
#include "SceneRenderTargetParameters.h"
#include "ScenePrivate.h"
#include "ClearQuad.h"
#include "PipelineStateCache.h"
#include "SceneTextureParameters.h"
#include "SystemTextures.h"
#include "Lumen/Lumen.h"
// ---------------------------------------------------- Cvars
static TAutoConsoleVariable<int32> CVarShadowReconstructionSampleCount(
TEXT("r.Shadow.Denoiser.ReconstructionSamples"), 8,
TEXT("Maximum number of samples for the reconstruction pass (default = 16)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarShadowPreConvolutionCount(
TEXT("r.Shadow.Denoiser.PreConvolution"), 1,
TEXT("Number of pre-convolution passes (default = 1)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarShadowTemporalAccumulation(
TEXT("r.Shadow.Denoiser.TemporalAccumulation"), 1,
TEXT(""),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarShadowHistoryConvolutionSampleCount(
TEXT("r.Shadow.Denoiser.HistoryConvolutionSamples"), 1,
TEXT("Number of samples to use to convolve the history over time."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarUseReflectionDenoiser(
TEXT("r.Reflections.Denoiser"),
2,
TEXT("Choose the denoising algorithm.\n")
TEXT(" 0: Disabled;\n")
TEXT(" 1: Forces the default denoiser of the renderer;\n")
TEXT(" 2: GScreenSpaceDenoiser which may be overriden by a third party plugin (default)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarReflectionReconstructionSampleCount(
TEXT("r.Reflections.Denoiser.ReconstructionSamples"), 8,
TEXT("Maximum number of samples for the reconstruction pass (default = 8)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarReflectionPreConvolutionCount(
TEXT("r.Reflections.Denoiser.PreConvolution"), 1,
TEXT("Number of pre-convolution passes (default = 1)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarReflectionTemporalAccumulation(
TEXT("r.Reflections.Denoiser.TemporalAccumulation"), 1,
TEXT("Accumulates the samples over multiple frames."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarAOReconstructionSampleCount(
TEXT("r.AmbientOcclusion.Denoiser.ReconstructionSamples"), 16,
TEXT("Maximum number of samples for the reconstruction pass (default = 16)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarAOPreConvolutionCount(
TEXT("r.AmbientOcclusion.Denoiser.PreConvolution"), 2,
TEXT("Number of pre-convolution passes (default = 1)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarAOKernelSpreadFactor(
TEXT("r.AmbientOcclusion.Denoiser.KernelSpreadFactor"), 4,
TEXT("Spread factor of the preconvolution passes."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarAOTemporalAccumulation(
TEXT("r.AmbientOcclusion.Denoiser.TemporalAccumulation"), 1,
TEXT("Accumulates the samples over multiple frames."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarAOHistoryConvolutionSampleCount(
TEXT("r.AmbientOcclusion.Denoiser.HistoryConvolution.SampleCount"), 1,
TEXT("Number of samples to use for history post filter (default = 16)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarAOHistoryConvolutionKernelSpreadFactor(
TEXT("r.AmbientOcclusion.Denoiser.HistoryConvolution.KernelSpreadFactor"), 7,
TEXT("Multiplication factor applied on the kernel sample offset (default = 7)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarGIReconstructionSampleCount(
TEXT("r.GlobalIllumination.Denoiser.ReconstructionSamples"), 16,
TEXT("Maximum number of samples for the reconstruction pass (default = 16)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarGIPreConvolutionCount(
TEXT("r.GlobalIllumination.Denoiser.PreConvolution"), 1,
TEXT("Number of pre-convolution passes (default = 1)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarGITemporalAccumulation(
TEXT("r.GlobalIllumination.Denoiser.TemporalAccumulation"), 1,
TEXT("Accumulates the samples over multiple frames."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarGIHistoryConvolutionSampleCount(
TEXT("r.GlobalIllumination.Denoiser.HistoryConvolution.SampleCount"), 1,
TEXT("Number of samples to use for history post filter (default = 1)."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarGIHistoryConvolutionKernelSpreadFactor(
TEXT("r.GlobalIllumination.Denoiser.HistoryConvolution.KernelSpreadFactor"), 3,
TEXT("Multiplication factor applied on the kernel sample offset (default=3)."),
ECVF_RenderThreadSafe);
/** The maximum number of mip level supported in the denoiser. */
// TODO(Denoiser): jump to 3 because bufefr size already have a size multiple of 4.
static const int32 kMaxMipLevel = 2;
/** Maximum number of sample per pixel supported in the stackowiak sample set. */
static const int32 kStackowiakMaxSampleCountPerSet = 56;
/** The maximum number of buffers. */
static const int32 kMaxBufferProcessingCount = kMaxDenoiserBufferProcessingCount;
/** Number of texture to store compressed metadata. */
static const int32 kCompressedMetadataTextures = 2;
static_assert(IScreenSpaceDenoiser::kMaxBatchSize <= kMaxBufferProcessingCount, "Can't batch more signal than there is internal buffer in the denoiser.");
// ---------------------------------------------------- Globals
const IScreenSpaceDenoiser* GScreenSpaceDenoiser = nullptr;
DECLARE_GPU_STAT(ReflectionsDenoiser);
DECLARE_GPU_STAT(ShadowsDenoiser);
DECLARE_GPU_STAT(AmbientOcclusionDenoiser);
DECLARE_GPU_STAT(DiffuseIndirectDenoiser);
namespace
{
// ---------------------------------------------------- Enums
/** Layout for compressed meta data. */
enum class ECompressedMetadataLayout
{
// The signal denoiser use directly depth buffer and gbuffer.
Disabled,
// Compress scene depth and world space normal into same render target.
DepthAndNormal,
// Compress scene depth and view space normal into same render target The advantage of having the normal
// in the view space is to use much faster ScreenToView than ScreenToTranslatedWorld. But doesn't
// support history bilateral rejection.
DepthAndViewNormal,
// Scene depth and shading model ID are in separate render target.
FedDepthAndShadingModelID,
MAX,
};
/** Different signals to denoise. */
enum class ESignalProcessing
{
// Denoise a shadow mask.
ShadowVisibilityMask,
// Denoise one lighting harmonic when denoising multiple light's penumbra.
PolychromaticPenumbraHarmonic,
// Denoise first bounce specular.
Reflections,
// Denoise ambient occlusion.
AmbientOcclusion,
// Denoise first bounce diffuse and ambient occlusion.
DiffuseAndAmbientOcclusion,
// Denoise first bounce diffuse as sperical harmonic
DiffuseSphericalHarmonic,
// Denoise SSGI.
ScreenSpaceDiffuseIndirect,
// Denoise diffuse indirect hierarchy.
IndirectProbeHierarchy,
MAX,
};
// ---------------------------------------------------- Simple functions
static bool IsSupportedLightType(ELightComponentType LightType)
{
return LightType == LightType_Point || LightType == LightType_Directional || LightType == LightType_Rect || LightType == LightType_Spot;
}
/** Returns whether a signal processing is supported by the constant pixel density pass layout. */
static bool UsesConstantPixelDensityPassLayout(ESignalProcessing SignalProcessing)
{
return (
SignalProcessing == ESignalProcessing::ShadowVisibilityMask ||
SignalProcessing == ESignalProcessing::PolychromaticPenumbraHarmonic ||
SignalProcessing == ESignalProcessing::Reflections ||
SignalProcessing == ESignalProcessing::AmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseSphericalHarmonic ||
SignalProcessing == ESignalProcessing::ScreenSpaceDiffuseIndirect ||
SignalProcessing == ESignalProcessing::IndirectProbeHierarchy);
}
/** Returns whether a signal processing support upscaling. */
static bool SignalSupportsUpscaling(ESignalProcessing SignalProcessing)
{
return (
SignalProcessing == ESignalProcessing::Reflections ||
SignalProcessing == ESignalProcessing::AmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion);
}
/** Returns whether a signal processing uses an injestion pass. */
static bool SignalUsesInjestion(ESignalProcessing SignalProcessing)
{
return (
SignalProcessing == ESignalProcessing::ShadowVisibilityMask);
}
/** Returns whether a signal processing uses a reduction pass before the reconstruction. */
static bool SignalUsesReduction(ESignalProcessing SignalProcessing)
{
return false; //SignalProcessing == ESignalProcessing::DiffuseSphericalHarmonic;
}
/** Returns whether a signal processing uses an additional pre convolution pass. */
static bool SignalUsesPreConvolution(ESignalProcessing SignalProcessing)
{
return
SignalProcessing == ESignalProcessing::ShadowVisibilityMask ||
SignalProcessing == ESignalProcessing::Reflections ||
SignalProcessing == ESignalProcessing::AmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion;
}
/** Returns whether a signal processing uses a history rejection pre convolution pass. */
static bool SignalUsesRejectionPreConvolution(ESignalProcessing SignalProcessing)
{
return (
//SignalProcessing == ESignalProcessing::ShadowVisibilityMask ||
//SignalProcessing == ESignalProcessing::Reflections ||
SignalProcessing == ESignalProcessing::AmbientOcclusion);
}
/** Returns whether a signal processing uses a convolution pass after temporal accumulation pass. */
static bool SignalUsesPostConvolution(ESignalProcessing SignalProcessing)
{
return (
SignalProcessing == ESignalProcessing::ShadowVisibilityMask ||
SignalProcessing == ESignalProcessing::AmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion);
}
/** Returns whether a signal processing uses a history rejection pre convolution pass. */
static bool SignalUsesFinalConvolution(ESignalProcessing SignalProcessing)
{
return (
SignalProcessing == ESignalProcessing::ShadowVisibilityMask);
}
/** Returns what meta data compression should be used when denoising a signal. */
static ECompressedMetadataLayout GetSignalCompressedMetadata(ESignalProcessing SignalProcessing)
{
if (SignalProcessing == ESignalProcessing::ScreenSpaceDiffuseIndirect)
{
return ECompressedMetadataLayout::DepthAndViewNormal;
}
else if (SignalProcessing == ESignalProcessing::IndirectProbeHierarchy)
{
return ECompressedMetadataLayout::FedDepthAndShadingModelID;
}
return ECompressedMetadataLayout::Disabled;
}
/** Returns the number of signal that might be batched at the same time. */
static int32 SignalMaxBatchSize(ESignalProcessing SignalProcessing)
{
if (SignalProcessing == ESignalProcessing::ShadowVisibilityMask
)
{
return IScreenSpaceDenoiser::kMaxBatchSize;
}
else if (
SignalProcessing == ESignalProcessing::Reflections ||
SignalProcessing == ESignalProcessing::PolychromaticPenumbraHarmonic ||
SignalProcessing == ESignalProcessing::AmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseSphericalHarmonic ||
SignalProcessing == ESignalProcessing::ScreenSpaceDiffuseIndirect ||
SignalProcessing == ESignalProcessing::IndirectProbeHierarchy)
{
return 1;
}
check(0);
return 1;
}
/** Returns whether a signal have a code path for 1 sample per pixel. */
static bool SignalSupport1SPP(ESignalProcessing SignalProcessing)
{
return (
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion);
}
/** Returns whether a signal can denoise multi sample per pixel. */
static bool SignalSupportMultiSPP(ESignalProcessing SignalProcessing)
{
return (
SignalProcessing == ESignalProcessing::ShadowVisibilityMask ||
SignalProcessing == ESignalProcessing::PolychromaticPenumbraHarmonic ||
SignalProcessing == ESignalProcessing::Reflections ||
SignalProcessing == ESignalProcessing::AmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseSphericalHarmonic ||
SignalProcessing == ESignalProcessing::ScreenSpaceDiffuseIndirect ||
SignalProcessing == ESignalProcessing::IndirectProbeHierarchy);
}
// ---------------------------------------------------- Shaders
// Permutation dimension for the type of signal being denoised.
class FSignalProcessingDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_SIGNAL_PROCESSING", ESignalProcessing);
// Permutation dimension for the number of signal being denoised at the same time.
class FSignalBatchSizeDim : SHADER_PERMUTATION_RANGE_INT("DIM_SIGNAL_BATCH_SIZE", 1, IScreenSpaceDenoiser::kMaxBatchSize);
// Permutation dimension for denoising multiple sample at same time.
class FMultiSPPDim : SHADER_PERMUTATION_BOOL("DIM_MULTI_SPP");
const TCHAR* const kInjestResourceNames[] = {
// ShadowVisibilityMask
TEXT("Shadow.Denoiser.Injest0"),
TEXT("Shadow.Denoiser.Injest1"),
nullptr,
nullptr,
// PolychromaticPenumbraHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// Reflections
nullptr,
nullptr,
nullptr,
nullptr,
// AmbientOcclusion
nullptr,
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// DiffuseSphericalHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// ScreenSpaceDiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// IndirectProbeHierarchy
nullptr,
nullptr,
nullptr,
nullptr,
};
const TCHAR* const kReduceResourceNames[] = {
// ShadowVisibilityMask
nullptr,
nullptr,
nullptr,
nullptr,
// PolychromaticPenumbraHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// Reflections
nullptr,
nullptr,
nullptr,
nullptr,
// AmbientOcclusion
nullptr,
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// DiffuseSphericalHarmonic
TEXT("DiffuseHarmonicReduce0"),
TEXT("DiffuseHarmonicReduce1"),
TEXT("DiffuseHarmonicReduce2"),
TEXT("DiffuseHarmonicReduce3"),
// ScreenSpaceDiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// IndirectProbeHierarchy
nullptr,
nullptr,
nullptr,
nullptr,
};
const TCHAR* const kReconstructionResourceNames[] = {
// ShadowVisibilityMask
TEXT("Shadow.Denoiser.Reconstruction0"),
TEXT("Shadow.Denoiser.Reconstruction1"),
TEXT("Shadow.Denoiser.Reconstruction2"),
TEXT("Shadow.Denoiser.Reconstruction3"),
// PolychromaticPenumbraHarmonic
TEXT("PolychromaticPenumbraHarmonicReconstruction0"),
TEXT("PolychromaticPenumbraHarmonicReconstruction1"),
TEXT("PolychromaticPenumbraHarmonicReconstruction2"),
TEXT("PolychromaticPenumbraHarmonicReconstruction3"),
// Reflections
TEXT("Reflections.Denoiser.Reconstruction0"),
TEXT("Reflections.Denoiser.Reconstruction1"),
nullptr,
nullptr,
// AmbientOcclusion
TEXT("AO.Denoiser.Reconstruction0"),
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
TEXT("DiffuseIndirectReconstruction0"),
TEXT("DiffuseIndirectReconstruction1"),
nullptr,
nullptr,
// DiffuseSphericalHarmonic
TEXT("DiffuseHarmonicReconstruction0"),
TEXT("DiffuseHarmonicReconstruction1"),
TEXT("DiffuseHarmonicReconstruction2"),
TEXT("DiffuseHarmonicReconstruction3"),
// ScreenSpaceDiffuseIndirect
TEXT("SSGI.Denoiser.Reconstruction0"),
TEXT("SSGI.Denoiser.Reconstruction1"),
nullptr,
nullptr,
// IndirectProbeHierarchy
nullptr,
nullptr,
nullptr,
nullptr,
};
const TCHAR* const kPreConvolutionResourceNames[] = {
// ShadowVisibilityMask
TEXT("Shadow.Denoiser.PreConvolution0"),
TEXT("Shadow.Denoiser.PreConvolution1"),
TEXT("Shadow.Denoiser.PreConvolution2"),
TEXT("Shadow.Denoiser.PreConvolution3"),
// PolychromaticPenumbraHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// Reflections
TEXT("Reflections.Denoiser.PreConvolution0"),
TEXT("Reflections.Denoiser.PreConvolution1"),
nullptr,
nullptr,
// AmbientOcclusion
TEXT("AO.Denoiser.PreConvolution0"),
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
TEXT("DiffuseIndirectPreConvolution0"),
TEXT("DiffuseIndirectPreConvolution1"),
nullptr,
nullptr,
// DiffuseSphericalHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// ScreenSpaceDiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// IndirectProbeHierarchy
nullptr,
nullptr,
nullptr,
nullptr,
};
const TCHAR* const kRejectionPreConvolutionResourceNames[] = {
// ShadowVisibilityMask
TEXT("Shadow.Denoiser.RejectionPreConvolution0"),
TEXT("Shadow.Denoiser.RejectionPreConvolution1"),
TEXT("Shadow.Denoiser.RejectionPreConvolution2"),
TEXT("Shadow.Denoiser.RejectionPreConvolution3"),
// PolychromaticPenumbraHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// Reflections
TEXT("Reflections.Denoiser.RejectionPreConvolution0"),
TEXT("Reflections.Denoiser.RejectionPreConvolution1"),
TEXT("Reflections.Denoiser.RejectionPreConvolution2"),
nullptr,
// AmbientOcclusion
TEXT("AO.Denoiser.RejectionPreConvolution0"),
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// DiffuseSphericalHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// ScreenSpaceDiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// IndirectProbeHierarchy
nullptr,
nullptr,
nullptr,
nullptr,
};
const TCHAR* const kTemporalAccumulationResourceNames[] = {
// ShadowVisibilityMask
TEXT("Shadow.Denoiser.TemporalAccumulation0"),
TEXT("Shadow.Denoiser.TemporalAccumulation1"),
TEXT("Shadow.Denoiser.TemporalAccumulation2"),
TEXT("Shadow.Denoiser.TemporalAccumulation3"),
// PolychromaticPenumbraHarmonic
TEXT("PolychromaticPenumbraHistory0"),
TEXT("PolychromaticPenumbraHistory1"),
nullptr,
nullptr,
// Reflections
TEXT("Reflections.Denoiser.TemporalAccumulation0"),
TEXT("Reflections.Denoiser.TemporalAccumulation1"),
nullptr,
nullptr,
// AmbientOcclusion
TEXT("AO.Denoiser.TemporalAccumulation0"),
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
TEXT("DiffuseIndirect.Denoiser.TemporalAccumulation0"),
TEXT("DiffuseIndirect.Denoiser.TemporalAccumulation1"),
nullptr,
nullptr,
// DiffuseSphericalHarmonic
TEXT("DiffuseHarmonicTemporalAccumulation0"),
TEXT("DiffuseHarmonicTemporalAccumulation1"),
TEXT("DiffuseHarmonicTemporalAccumulation2"),
TEXT("DiffuseHarmonicTemporalAccumulation3"),
// ScreenSpaceDiffuseIndirect
TEXT("SSGI.Denoiser.TemporalAccumulation0"),
TEXT("SSGI.Denoiser.TemporalAccumulation1"),
nullptr,
nullptr,
// IndirectProbeHierarchy
TEXT("ProbeHierarchy.TemporalAccumulation0"),
TEXT("ProbeHierarchy.TemporalAccumulation1"),
TEXT("ProbeHierarchy.TemporalAccumulation2"),
nullptr,
};
const TCHAR* const kHistoryConvolutionResourceNames[] = {
// ShadowVisibilityMask
TEXT("Shadow.Denoiser.HistoryConvolution0"),
TEXT("Shadow.Denoiser.HistoryConvolution1"),
TEXT("Shadow.Denoiser.HistoryConvolution2"),
TEXT("Shadow.Denoiser.HistoryConvolution3"),
// PolychromaticPenumbraHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// Reflections
TEXT("Reflections.Denoiser.HistoryConvolution0"),
TEXT("Reflections.Denoiser.HistoryConvolution1"),
nullptr,
nullptr,
// AmbientOcclusion
TEXT("AO.Denoiser.HistoryConvolution0"),
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
TEXT("DiffuseIndirect.Denoiser.HistoryConvolution0"),
TEXT("DiffuseIndirect.Denoiser.HistoryConvolution1"),
nullptr,
nullptr,
// DiffuseSphericalHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// ScreenSpaceDiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// IndirectProbeHierarchy
nullptr,
nullptr,
nullptr,
nullptr,
};
const TCHAR* const kDenoiserOutputResourceNames[] = {
// ShadowVisibilityMask
TEXT("Shadow.Denoiser.DenoiserOutput0"),
TEXT("Shadow.Denoiser.DenoiserOutput1"),
TEXT("Shadow.Denoiser.DenoiserOutput2"),
TEXT("Shadow.Denoiser.DenoiserOutput3"),
// PolychromaticPenumbraHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// Reflections
nullptr,
nullptr,
nullptr,
nullptr,
// AmbientOcclusion
nullptr,
nullptr,
nullptr,
nullptr,
// DiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// DiffuseSphericalHarmonic
nullptr,
nullptr,
nullptr,
nullptr,
// ScreenSpaceDiffuseIndirect
nullptr,
nullptr,
nullptr,
nullptr,
// IndirectProbeHierarchy
nullptr,
nullptr,
nullptr,
nullptr,
};
static_assert(UE_ARRAY_COUNT(kReconstructionResourceNames) == int32(ESignalProcessing::MAX) * kMaxBufferProcessingCount, "You forgot me!");
static_assert(UE_ARRAY_COUNT(kRejectionPreConvolutionResourceNames) == int32(ESignalProcessing::MAX) * kMaxBufferProcessingCount, "You forgot me!");
static_assert(UE_ARRAY_COUNT(kTemporalAccumulationResourceNames) == int32(ESignalProcessing::MAX) * kMaxBufferProcessingCount, "You forgot me!");
static_assert(UE_ARRAY_COUNT(kHistoryConvolutionResourceNames) == int32(ESignalProcessing::MAX) * kMaxBufferProcessingCount, "You forgot me!");
static_assert(UE_ARRAY_COUNT(kDenoiserOutputResourceNames) == int32(ESignalProcessing::MAX) * kMaxBufferProcessingCount, "You forgot me!");
/** Returns whether should compile pipeline for a given shader platform.*/
static bool ShouldCompileSignalPipeline(ESignalProcessing SignalProcessing, EShaderPlatform Platform)
{
// Explicitly disable SSD shaders on mobile as they will fail to compile at this moment.
if (IsMobilePlatform(Platform))
{
return false;
}
if (SignalProcessing == ESignalProcessing::ScreenSpaceDiffuseIndirect)
{
return FDataDrivenShaderPlatformInfo::GetCompileSignalProcessingPipeline(Platform) || FDataDrivenShaderPlatformInfo::GetSupportsSSDIndirect(Platform);
}
else if (SignalProcessing == ESignalProcessing::Reflections)
{
return RHISupportsRayTracingShaders(Platform);
}
else if (
SignalProcessing == ESignalProcessing::ShadowVisibilityMask ||
SignalProcessing == ESignalProcessing::AmbientOcclusion ||
SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion)
{
// Only for ray tracing denoising.
return RHISupportsRayTracing(Platform);
}
else if (SignalProcessing == ESignalProcessing::PolychromaticPenumbraHarmonic)
{
return false;
}
else if (
SignalProcessing == ESignalProcessing::DiffuseSphericalHarmonic ||
SignalProcessing == ESignalProcessing::IndirectProbeHierarchy)
{
return DoesPlatformSupportLumenGI(Platform);
}
check(0);
return false;
}
/** Shader parameter structure used for all shaders. */
BEGIN_SHADER_PARAMETER_STRUCT(FSSDCommonParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(Denoiser::FCommonShaderParameters, PublicCommonParameters)
SHADER_PARAMETER(FIntPoint, ViewportMin)
SHADER_PARAMETER(FIntPoint, ViewportMax)
SHADER_PARAMETER(FVector4f, ThreadIdToBufferUV)
SHADER_PARAMETER(FVector2f, BufferUVToOutputPixelPosition)
SHADER_PARAMETER(FMatrix44f, ScreenToView)
SHADER_PARAMETER(FVector2f, BufferUVBilinearCorrection)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSubstrateGlobalUniformParameters, Substrate)
SHADER_PARAMETER_RDG_TEXTURE_ARRAY(Texture2D<uint>, CompressedMetadata, [kCompressedMetadataTextures])
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, EyeAdaptationBuffer)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint>, TileClassificationTexture)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER(uint32, FrameIndex)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FSSDSignalSRVs, )
SHADER_PARAMETER_RDG_TEXTURE_SRV_ARRAY(Texture2D, Textures, [kMaxBufferProcessingCount])
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FSSDSignalUAVs, )
SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(RWTexture2D, UAVs, [kMaxBufferProcessingCount])
END_SHADER_PARAMETER_STRUCT()
/** Shader parameter structure to have all information to spatial filtering. */
BEGIN_SHADER_PARAMETER_STRUCT(FSSDConvolutionMetaData, )
SHADER_PARAMETER_ARRAY(FVector4f, LightPositionAndRadius, [IScreenSpaceDenoiser::kMaxBatchSize])
SHADER_PARAMETER_ARRAY(FVector4f, LightDirectionAndLength, [IScreenSpaceDenoiser::kMaxBatchSize])
SHADER_PARAMETER_SCALAR_ARRAY(float, HitDistanceToWorldBluringRadius, [IScreenSpaceDenoiser::kMaxBatchSize])
SHADER_PARAMETER_SCALAR_ARRAY(uint32, LightType, [IScreenSpaceDenoiser::kMaxBatchSize])
END_SHADER_PARAMETER_STRUCT()
FSSDSignalTextures CreateMultiplexedTextures(
FRDGBuilder& GraphBuilder,
int32 TextureCount,
const TStaticArray<FRDGTextureDesc, kMaxBufferProcessingCount>& DescArray,
const TCHAR* const* TextureNames,
const FIntRect& Viewport)
{
check(TextureCount <= kMaxBufferProcessingCount);
FSSDSignalTextures SignalTextures;
for (int32 i = 0; i < TextureCount; i++)
{
const TCHAR* TextureName = TextureNames[i];
SignalTextures.Textures[i] = GraphBuilder.CreateTexture(DescArray[i], TextureName);
SignalTextures.Textures[i]->EncloseVisualizeExtent(Viewport.Max);
}
return SignalTextures;
}
FSSDSignalSRVs CreateMultiplexedUintSRVs(FRDGBuilder& GraphBuilder, const FSSDSignalTextures& SignalTextures)
{
FSSDSignalSRVs SRVs;
for (int32 i = 0; i < kMaxBufferProcessingCount; i++)
{
if (SignalTextures.Textures[i])
{
EPixelFormat Format = SignalTextures.Textures[i]->Desc.Format;
int32 Bytes = GPixelFormats[Format].BlockBytes;
EPixelFormat UIntFormat = PF_Unknown;
if (Bytes == 1)
UIntFormat = PF_R8_UINT;
else if (Bytes == 2)
UIntFormat = PF_R16_UINT;
else if (Bytes == 4)
UIntFormat = PF_R32_UINT;
else if (Bytes == 8)
UIntFormat = PF_R32G32_UINT;
else if (Bytes == 16)
UIntFormat = PF_R32G32B32A32_UINT;
else
{
check(0);
}
SRVs.Textures[i] = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(SignalTextures.Textures[i], UIntFormat));
}
}
return SRVs;
}
FSSDSignalUAVs CreateMultiplexedUAVs(FRDGBuilder& GraphBuilder, const FSSDSignalTextures& SignalTextures, int32 MipLevel = 0)
{
FSSDSignalUAVs UAVs;
for (int32 i = 0; i < kMaxBufferProcessingCount; i++)
{
if (SignalTextures.Textures[i])
UAVs.UAVs[i] = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SignalTextures.Textures[i], MipLevel));
}
return UAVs;
}
class FSSDCompressMetadataCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSSDCompressMetadataCS);
SHADER_USE_PARAMETER_STRUCT(FSSDCompressMetadataCS, FGlobalShader);
class FMetadataLayoutDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_METADATA_LAYOUT", ECompressedMetadataLayout);
using FPermutationDomain = TShaderPermutationDomain<FMetadataLayoutDim>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FMetadataLayoutDim>() == ECompressedMetadataLayout::Disabled)
{
return false;
}
// Precomputed by denoiser caller.
if (PermutationVector.Get<FMetadataLayoutDim>() == ECompressedMetadataLayout::FedDepthAndShadingModelID)
{
return false;
}
return ShouldCompileSignalPipeline(ESignalProcessing::ScreenSpaceDiffuseIndirect, Parameters.Platform);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDCommonParameters, CommonParameters)
SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(RWTexture2D<uint>, CompressedMetadataOutput, [kCompressedMetadataTextures])
END_SHADER_PARAMETER_STRUCT()
};
class FSSDInjestCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSSDInjestCS);
SHADER_USE_PARAMETER_STRUCT(FSSDInjestCS, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<FSignalProcessingDim, FSignalBatchSizeDim, FMultiSPPDim>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
ESignalProcessing SignalProcessing = PermutationVector.Get<FSignalProcessingDim>();
// Only compile this shader for signal processing that uses it.
if (!SignalUsesInjestion(SignalProcessing))
{
return false;
}
// Not all signal processing allow to batch multiple signals at the same time.
if (PermutationVector.Get<FSignalBatchSizeDim>() > SignalMaxBatchSize(SignalProcessing))
{
return false;
}
// Only compiler multi SPP permutation for signal that supports it.
if (PermutationVector.Get<FMultiSPPDim>() && !SignalSupportMultiSPP(SignalProcessing))
{
return false;
}
// Compile out the shader if this permutation gets remapped.
if (RemapPermutationVector(PermutationVector) != PermutationVector)
{
return false;
}
return ShouldCompileSignalPipeline(SignalProcessing, Parameters.Platform);
}
static FPermutationDomain RemapPermutationVector(FPermutationDomain PermutationVector)
{
ESignalProcessing SignalProcessing = PermutationVector.Get<FSignalProcessingDim>();
// force use the multi sample per pixel code path.
if (!SignalSupport1SPP(SignalProcessing))
{
PermutationVector.Set<FMultiSPPDim>(true);
}
return PermutationVector;
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDCommonParameters, CommonParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDConvolutionMetaData, ConvolutionMetaData)
SHADER_PARAMETER_STRUCT(FSSDSignalTextures, SignalInput)
SHADER_PARAMETER_STRUCT(FSSDSignalUAVs, SignalOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, DebugOutput)
END_SHADER_PARAMETER_STRUCT()
};
class FSSDSpatialAccumulationCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSSDSpatialAccumulationCS);
SHADER_USE_PARAMETER_STRUCT(FSSDSpatialAccumulationCS, FGlobalShader);
static const uint32 kGroupSize = 8;
enum class EStage
{
// Spatial kernel used to process raw input for the temporal accumulation.
ReConstruction,
// Spatial kernel to pre filter.
PreConvolution,
// Spatial kernel used to pre convolve history rejection.
RejectionPreConvolution,
// Spatial kernel used to post filter the temporal accumulation.
PostFiltering,
// Final spatial kernel, that may output specific buffer encoding to integrate with the rest of the renderer
FinalOutput,
MAX
};
class FStageDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_STAGE", EStage);
class FUpscaleDim : SHADER_PERMUTATION_BOOL("DIM_UPSCALE");
using FPermutationDomain = TShaderPermutationDomain<FSignalProcessingDim, FStageDim, FUpscaleDim, FSignalBatchSizeDim, FMultiSPPDim>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
ESignalProcessing SignalProcessing = PermutationVector.Get<FSignalProcessingDim>();
// Only constant pixel density pass layout uses this shader.
if (!UsesConstantPixelDensityPassLayout(PermutationVector.Get<FSignalProcessingDim>()))
{
return false;
}
// Not all signal processing allow to batch multiple signals at the same time.
if (PermutationVector.Get<FSignalBatchSizeDim>() > SignalMaxBatchSize(SignalProcessing))
{
return false;
}
// Only reconstruction have upscale capability for now.
if (PermutationVector.Get<FUpscaleDim>() &&
PermutationVector.Get<FStageDim>() != EStage::ReConstruction)
{
return false;
}
// Only upscale is only for signal that needs it.
if (PermutationVector.Get<FUpscaleDim>() &&
!SignalSupportsUpscaling(SignalProcessing))
{
return false;
}
// Only compile pre convolution for signal that uses it.
if (!SignalUsesPreConvolution(SignalProcessing) &&
PermutationVector.Get<FStageDim>() == EStage::PreConvolution)
{
return false;
}
// Only compile rejection pre convolution for signal that uses it.
if (!SignalUsesRejectionPreConvolution(SignalProcessing) &&
PermutationVector.Get<FStageDim>() == EStage::RejectionPreConvolution)
{
return false;
}
// Only compile post convolution for signal that uses it.
if (!SignalUsesPostConvolution(SignalProcessing) &&
PermutationVector.Get<FStageDim>() == EStage::PostFiltering)
{
return false;
}
// Only compile final convolution for signal that uses it.
if (!SignalUsesFinalConvolution(SignalProcessing) &&
PermutationVector.Get<FStageDim>() == EStage::FinalOutput)
{
return false;
}
// Only compile multi SPP permutation for signal that supports it.
if (PermutationVector.Get<FStageDim>() == EStage::ReConstruction &&
PermutationVector.Get<FMultiSPPDim>() && !SignalSupportMultiSPP(SignalProcessing))
{
return false;
}
// Compile out the shader if this permutation gets remapped.
if (RemapPermutationVector(PermutationVector) != PermutationVector)
{
return false;
}
return ShouldCompileSignalPipeline(SignalProcessing, Parameters.Platform);
}
static FPermutationDomain RemapPermutationVector(FPermutationDomain PermutationVector)
{
ESignalProcessing SignalProcessing = PermutationVector.Get<FSignalProcessingDim>();
if (PermutationVector.Get<FStageDim>() == EStage::ReConstruction)
{
// force use the multi sample per pixel code path.
if (!SignalSupport1SPP(SignalProcessing))
{
PermutationVector.Set<FMultiSPPDim>(true);
}
}
else
{
PermutationVector.Set<FMultiSPPDim>(true);
}
return PermutationVector;
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
// Dead code stripping required or else we have an unrolled loop with a non-compile time specified iteration count.
OutEnvironment.CompilerFlags.Add(CFLAG_ForceOptimization);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_ARRAY(FVector4f, InputBufferUVMinMax, [IScreenSpaceDenoiser::kMaxBatchSize])
SHADER_PARAMETER(uint32, MaxSampleCount)
SHADER_PARAMETER(uint32, PreviousCumulativeMaxSampleCount)
SHADER_PARAMETER(int32, UpscaleFactor)
SHADER_PARAMETER(float, KernelSpreadFactor)
SHADER_PARAMETER(float, HarmonicPeriode)
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDCommonParameters, CommonParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDConvolutionMetaData, ConvolutionMetaData)
SHADER_PARAMETER_STRUCT(FSSDSignalTextures, SignalInput)
SHADER_PARAMETER_STRUCT(FSSDSignalSRVs, SignalInputUint)
SHADER_PARAMETER_STRUCT(FSSDSignalUAVs, SignalOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, DebugOutput) // TODO(Denoiser): remove
END_SHADER_PARAMETER_STRUCT()
};
class FSSDTemporalAccumulationCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSSDTemporalAccumulationCS);
SHADER_USE_PARAMETER_STRUCT(FSSDTemporalAccumulationCS, FGlobalShader);
using FPermutationDomain = TShaderPermutationDomain<FSignalProcessingDim, FSignalBatchSizeDim>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
ESignalProcessing SignalProcessing = PermutationVector.Get<FSignalProcessingDim>();
// Only constant pixel density pass layout uses this shader.
if (!UsesConstantPixelDensityPassLayout(SignalProcessing))
{
return false;
}
// Not all signal processing allow to batch multiple signals at the same time.
if (PermutationVector.Get<FSignalBatchSizeDim>() > SignalMaxBatchSize(SignalProcessing))
{
return false;
}
return ShouldCompileSignalPipeline(SignalProcessing, Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
// Dead code stripping required or else we have an unrolled loop with a non-compile time specified iteration count.
OutEnvironment.CompilerFlags.Add(CFLAG_ForceOptimization);
}
static EShaderCompileJobPriority GetOverrideJobPriority()
{
// FSSDTemporalAccumulationCS takes up to 23s on average
return EShaderCompileJobPriority::ExtraHigh;
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_SCALAR_ARRAY(int32, bCameraCut, [IScreenSpaceDenoiser::kMaxBatchSize])
SHADER_PARAMETER(float, HistoryPreExposureCorrection)
SHADER_PARAMETER(FVector4f, ScreenPosToHistoryBufferUV)
SHADER_PARAMETER(FVector4f, HistoryBufferSizeAndInvSize)
SHADER_PARAMETER(FVector4f, HistoryBufferUVMinMax)
SHADER_PARAMETER_ARRAY(FVector4f, HistoryBufferScissorUVMinMax, [IScreenSpaceDenoiser::kMaxBatchSize])
SHADER_PARAMETER(FVector4f, PrevSceneBufferUVToScreenPosition)
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDCommonParameters, CommonParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDConvolutionMetaData, ConvolutionMetaData)
SHADER_PARAMETER_STRUCT(FSSDSignalTextures, SignalInput)
SHADER_PARAMETER_STRUCT(FSSDSignalTextures, HistoryRejectionSignal)
SHADER_PARAMETER_STRUCT(FSSDSignalUAVs, SignalHistoryOutput)
SHADER_PARAMETER_STRUCT(FSSDSignalTextures, PrevHistory)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, PrevDepthBuffer)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, PrevGBufferA)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, PrevGBufferB)
SHADER_PARAMETER_RDG_TEXTURE_ARRAY(Texture2D<uint>, PrevCompressedMetadata, [kCompressedMetadataTextures])
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, DebugOutput) // TODO(Denoiser): remove
END_SHADER_PARAMETER_STRUCT()
};
class FSSDComposeHarmonicsCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FSSDComposeHarmonicsCS);
SHADER_USE_PARAMETER_STRUCT(FSSDComposeHarmonicsCS, FGlobalShader);
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileSignalPipeline(ESignalProcessing::PolychromaticPenumbraHarmonic, Parameters.Platform);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_ARRAY(FSSDSignalTextures, SignalHarmonics, [IScreenSpaceDenoiser::kMultiPolychromaticPenumbraHarmonics])
SHADER_PARAMETER_STRUCT(FSSDSignalTextures, SignalIntegrand)
SHADER_PARAMETER_STRUCT_INCLUDE(FSSDCommonParameters, CommonParameters)
SHADER_PARAMETER_STRUCT(FSSDSignalUAVs, SignalOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, DebugOutput)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FSSDCompressMetadataCS, "/Engine/Private/ScreenSpaceDenoise/SSDCompressMetadata.usf", "MainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FSSDInjestCS, "/Engine/Private/ScreenSpaceDenoise/SSDInjest.usf", "MainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FSSDSpatialAccumulationCS, "/Engine/Private/ScreenSpaceDenoise/SSDSpatialAccumulation.usf", "MainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FSSDTemporalAccumulationCS, "/Engine/Private/ScreenSpaceDenoise/SSDTemporalAccumulation.usf", "MainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FSSDComposeHarmonicsCS, "/Engine/Private/ScreenSpaceDenoise/SSDComposeHarmonics.usf", "MainCS", SF_Compute);
} // namespace
/** PrevViewInfo and PrevFrameViewInfo pooled render targets to use for temporal storage of scene textures. */
struct FViewInfoPooledRenderTargets
{
TRefCountPtr<IPooledRenderTarget> PrevDepthBuffer;
TRefCountPtr<IPooledRenderTarget> PrevGBufferA;
TRefCountPtr<IPooledRenderTarget> PrevGBufferB;
TRefCountPtr<IPooledRenderTarget> PrevCompressedDepthViewNormal;
TRefCountPtr<IPooledRenderTarget>* NextDepthBuffer;
TRefCountPtr<IPooledRenderTarget>* NextGBufferA;
TRefCountPtr<IPooledRenderTarget>* NextGBufferB;
TRefCountPtr<IPooledRenderTarget>* NextCompressedDepthViewNormal;
};
void SetupSceneViewInfoPooledRenderTargets(
const FViewInfo& View,
FViewInfoPooledRenderTargets* OutViewInfoPooledRenderTargets)
{
auto&& PrevViewInfo = View.PrevViewInfo;
auto&& PrevFrameViewInfo = View.ViewState->PrevFrameViewInfo;
OutViewInfoPooledRenderTargets->PrevDepthBuffer = PrevViewInfo.DepthBuffer;
OutViewInfoPooledRenderTargets->PrevGBufferA = PrevViewInfo.GBufferA;
OutViewInfoPooledRenderTargets->PrevGBufferB = PrevViewInfo.GBufferB;
OutViewInfoPooledRenderTargets->PrevCompressedDepthViewNormal = PrevViewInfo.CompressedDepthViewNormal;
OutViewInfoPooledRenderTargets->NextDepthBuffer = &PrevFrameViewInfo.DepthBuffer;
OutViewInfoPooledRenderTargets->NextGBufferA = &PrevFrameViewInfo.GBufferA;
OutViewInfoPooledRenderTargets->NextGBufferB = &PrevFrameViewInfo.GBufferB;
OutViewInfoPooledRenderTargets->NextCompressedDepthViewNormal = &PrevFrameViewInfo.CompressedDepthViewNormal;
}
void Denoiser::SetupCommonShaderParameters(
const FViewInfo& View,
const FSceneTextureParameters& SceneTextures,
const FIntRect DenoiserFullResViewport,
float DenoisingResolutionFraction,
Denoiser::FCommonShaderParameters* OutPublicCommonParameters)
{
check(OutPublicCommonParameters);
FIntPoint FullResBufferExtent = SceneTextures.SceneDepthTexture->Desc.Extent;
FIntPoint DenoiserBufferExtent = FullResBufferExtent;
FIntRect DenoiserViewport = DenoiserFullResViewport;
if (DenoisingResolutionFraction == 0.5f)
{
DenoiserBufferExtent /= 2;
DenoiserViewport = FIntRect::DivideAndRoundUp(DenoiserViewport, 2);
}
OutPublicCommonParameters->DenoiserBufferSizeAndInvSize = FVector4f(
float(DenoiserBufferExtent.X),
float(DenoiserBufferExtent.Y),
1.0f / float(DenoiserBufferExtent.X),
1.0f / float(DenoiserBufferExtent.Y));
OutPublicCommonParameters->SceneBufferUVToScreenPosition.X = float(FullResBufferExtent.X) / float(View.ViewRect.Width()) * 2.0f;
OutPublicCommonParameters->SceneBufferUVToScreenPosition.Y = -float(FullResBufferExtent.Y) / float(View.ViewRect.Height()) * 2.0f;
OutPublicCommonParameters->SceneBufferUVToScreenPosition.Z = -float(View.ViewRect.Min.X) / float(View.ViewRect.Width()) * 2.0f - 1.0f;
OutPublicCommonParameters->SceneBufferUVToScreenPosition.W = float(View.ViewRect.Min.Y) / float(View.ViewRect.Height()) * 2.0f + 1.0f;
OutPublicCommonParameters->DenoiserBufferBilinearUVMinMax = FVector4f(
float(DenoiserViewport.Min.X + 0.5f) / float(DenoiserBufferExtent.X),
float(DenoiserViewport.Min.Y + 0.5f) / float(DenoiserBufferExtent.Y),
float(DenoiserViewport.Max.X - 0.5f) / float(DenoiserBufferExtent.X),
float(DenoiserViewport.Max.Y - 0.5f) / float(DenoiserBufferExtent.Y));
}
/** View specific denoise settings */
struct FSSDConstantPixelDensityViewSettings
{
// Inputs
FIntRect FullResViewport;
TStaticArray<FIntRect, IScreenSpaceDenoiser::kMaxBatchSize> SignalScissor;
// Generated in DenoiseSignalAtConstantPixelDensity
FIntRect Viewport;
};
/** Generic settings to denoise signal at constant pixel density across the viewport. */
struct FSSDConstantPixelDensitySettings
{
ESignalProcessing SignalProcessing;
int32 SignalBatchSize = 1;
float HarmonicPeriode = 1.0f;
int32 MaxInputSPP = 1;
float InputResolutionFraction = 1.0f;
float DenoisingResolutionFraction = 1.0f;
bool bEnableReconstruction = true;
int32 ReconstructionSamples = 1;
int32 PreConvolutionCount = 0;
float KernelSpreadFactor = 8;
bool bUseTemporalAccumulation = false;
int32 HistoryConvolutionSampleCount = 1;
float HistoryConvolutionKernelSpreadFactor = 1.0f;
TStaticArray<const FLightSceneInfo*, IScreenSpaceDenoiser::kMaxBatchSize> LightSceneInfo;
FRDGTextureRef CompressedDepthTexture = nullptr;
FRDGTextureRef CompressedShadingModelTexture = nullptr;
TArrayView<FSSDConstantPixelDensityViewSettings> ViewSettingsArray;
};
/** Denoises a signal at constant pixel density across the viewport. */
static void DenoiseSignalAtConstantPixelDensity(
FRDGBuilder& GraphBuilder,
TConstArrayView<FViewInfo> Views,
const FSceneTextureParameters& SceneTextures,
const FViewInfoPooledRenderTargets& ViewInfoPooledRenderTargets,
const FSSDSignalTextures& InputSignal,
FSSDConstantPixelDensitySettings Settings,
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevFilteringHistory,
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewFilteringHistory,
FSSDSignalTextures* OutputSignal)
{
check(UsesConstantPixelDensityPassLayout(Settings.SignalProcessing));
check(Views.Num() == Settings.ViewSettingsArray.Num());
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
// Make sure the viewport of the denoiser is within the viewport of the view.
FIntRect Union = View.ViewRect;
Union.Union(ViewSettings.FullResViewport);
check(Union == View.ViewRect);
// Initialize Viewport (defaults to full res)
ViewSettings.Viewport = ViewSettings.FullResViewport;
}
ensure(Settings.InputResolutionFraction == 1.0f || Settings.InputResolutionFraction == 0.5f || Settings.InputResolutionFraction == 0.25f);
auto GetResourceNames = [&](const TCHAR* const ResourceNames[])
{
return ResourceNames + (int32(Settings.SignalProcessing) * kMaxBufferProcessingCount);
};
const bool bUseMultiInputSPPShaderPath = Settings.MaxInputSPP > 1;
FIntPoint FullResBufferExtent = SceneTextures.SceneDepthTexture->Desc.Extent;
FIntPoint BufferExtent = FullResBufferExtent;
if (Settings.DenoisingResolutionFraction == 0.5f)
{
BufferExtent /= 2;
for (FSSDConstantPixelDensityViewSettings& ViewSettings : Settings.ViewSettingsArray)
{
ViewSettings.Viewport = FIntRect::DivideAndRoundUp(ViewSettings.Viewport, 2);
}
}
// Get combined viewport
FIntRect AllViewport = Settings.ViewSettingsArray[0].Viewport;
for (int32 ViewIndex = 1; ViewIndex < Views.Num(); ViewIndex++)
{
AllViewport.Union(Settings.ViewSettingsArray[ViewIndex].Viewport);
}
// Number of signal to batch.
int32 MaxSignalBatchSize = SignalMaxBatchSize(Settings.SignalProcessing);
check(Settings.SignalBatchSize >= 1 && Settings.SignalBatchSize <= MaxSignalBatchSize);
// Number of texture per batched signal.
int32 InjestTextureCount = 0;
int32 ReconstructionTextureCount = 0;
int32 HistoryTextureCountPerSignal = 0;
// Descriptor to allocate internal denoising buffer.
bool bHasReconstructionLayoutDifferentFromHistory = false;
TStaticArray<FRDGTextureDesc, kMaxBufferProcessingCount> InjestDescs;
TStaticArray<FRDGTextureDesc, kMaxBufferProcessingCount> ReconstructionDescs;
TStaticArray<FRDGTextureDesc, kMaxBufferProcessingCount> HistoryDescs;
FRDGTextureDesc DebugDesc;
{
// Manually format texel in the shader to reduce VGPR pressure with overlapped texture fetched.
const bool bManualTexelFormatting = true;
static const EPixelFormat PixelFormatPerChannel[] = {
PF_Unknown,
PF_R16F,
PF_G16R16F,
PF_FloatRGBA, // there is no 16bits float RGB
PF_FloatRGBA,
};
FRDGTextureDesc RefDesc = FRDGTextureDesc::Create2D(
BufferExtent,
PF_Unknown,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
DebugDesc = RefDesc;
DebugDesc.Format = PF_FloatRGBA;
for (int32 i = 0; i < kMaxBufferProcessingCount; i++)
{
InjestDescs[i] = RefDesc;
ReconstructionDescs[i] = RefDesc;
HistoryDescs[i] = RefDesc;
}
if (Settings.SignalProcessing == ESignalProcessing::ShadowVisibilityMask)
{
check(Settings.SignalBatchSize >= 1 && Settings.SignalBatchSize <= IScreenSpaceDenoiser::kMaxBatchSize);
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
InjestDescs[BatchedSignalId / 2].Format = (BatchedSignalId % 2) ? PF_R32G32_UINT : PF_R32_UINT;
InjestTextureCount = BatchedSignalId / 2 + 1;
ReconstructionDescs[BatchedSignalId].Format = PF_FloatRGBA;
HistoryDescs[BatchedSignalId].Format = PF_FloatRGBA;
}
HistoryTextureCountPerSignal = 1;
ReconstructionTextureCount = Settings.SignalBatchSize;
bHasReconstructionLayoutDifferentFromHistory = false;
}
else if (Settings.SignalProcessing == ESignalProcessing::PolychromaticPenumbraHarmonic)
{
ReconstructionTextureCount = 4;
ReconstructionDescs[0].Format = PF_FloatRGBA;
ReconstructionDescs[1].Format = PF_FloatRGBA;
ReconstructionDescs[2].Format = PF_FloatRGBA;
ReconstructionDescs[3].Format = PF_FloatRGBA;
HistoryTextureCountPerSignal = 2;
HistoryDescs[0].Format = PF_FloatRGBA;
HistoryDescs[1].Format = PF_FloatRGBA;
}
else if (Settings.SignalProcessing == ESignalProcessing::Reflections)
{
ReconstructionDescs[0].Format = HistoryDescs[0].Format = PF_FloatRGBA;
ReconstructionDescs[1].Format = HistoryDescs[1].Format = PF_G16R16F;
ReconstructionTextureCount = HistoryTextureCountPerSignal = 2;
bHasReconstructionLayoutDifferentFromHistory = false;
}
else if (Settings.SignalProcessing == ESignalProcessing::AmbientOcclusion)
{
ReconstructionDescs[0].Format = HistoryDescs[0].Format = PF_FloatRGBA;
ReconstructionTextureCount = HistoryTextureCountPerSignal = 1;
bHasReconstructionLayoutDifferentFromHistory = false;
}
else if (Settings.SignalProcessing == ESignalProcessing::DiffuseAndAmbientOcclusion)
{
ReconstructionDescs[0].Format = PF_FloatRGBA;
ReconstructionDescs[1].Format = PF_R16F;
ReconstructionTextureCount = 2;
HistoryDescs[0].Format = PF_FloatRGBA;
HistoryDescs[1].Format = PF_R16F; //PF_FloatRGB;
HistoryTextureCountPerSignal = 2;
bHasReconstructionLayoutDifferentFromHistory = false;
}
else if (Settings.SignalProcessing == ESignalProcessing::DiffuseSphericalHarmonic)
{
ReconstructionDescs[0].Format = PF_FloatRGBA;
ReconstructionDescs[1].Format = PF_FloatRGBA;
ReconstructionTextureCount = 2;
HistoryDescs = ReconstructionDescs;
HistoryTextureCountPerSignal = 2;
bHasReconstructionLayoutDifferentFromHistory = false;
}
else if (Settings.SignalProcessing == ESignalProcessing::ScreenSpaceDiffuseIndirect)
{
ReconstructionDescs[0].Format = PF_FloatR11G11B10;
HistoryDescs[0].Format = PF_FloatR11G11B10;
ReconstructionDescs[1].Format = PF_R8G8;
ReconstructionTextureCount = 2;
HistoryDescs[1].Format = PF_R8G8;
HistoryTextureCountPerSignal = 2;
bHasReconstructionLayoutDifferentFromHistory = false;
}
else if (Settings.SignalProcessing == ESignalProcessing::IndirectProbeHierarchy)
{
ReconstructionDescs[0].Format = PF_FloatR11G11B10;
ReconstructionDescs[1].Format = PF_FloatR11G11B10;
ReconstructionDescs[2].Format = PF_R8;
ReconstructionTextureCount = 3;
HistoryDescs[0].Format = PF_FloatR11G11B10;
HistoryDescs[1].Format = PF_FloatR11G11B10;
HistoryDescs[2].Format = PF_R8;
HistoryTextureCountPerSignal = 3;
bHasReconstructionLayoutDifferentFromHistory = true;
}
else
{
check(0);
}
check(HistoryTextureCountPerSignal > 0);
check(ReconstructionTextureCount > 0);
}
// Create a UAV use to output debugging information from the shader.
auto CreateDebugUAV = [&](const TCHAR* DebugTextureName)
{
return GraphBuilder.CreateUAV(GraphBuilder.CreateTexture(DebugDesc, DebugTextureName));
};
int32 HistoryTextureCount = HistoryTextureCountPerSignal * Settings.SignalBatchSize;
check(HistoryTextureCount <= kMaxBufferProcessingCount);
ECompressedMetadataLayout CompressedMetadataLayout = GetSignalCompressedMetadata(Settings.SignalProcessing);
// Setup common shader parameters.
TArray<FSSDCommonParameters, TInlineAllocator<6>> CommonParametersByView;
TArray<FSSDConvolutionMetaData, TInlineAllocator<6>> ConvolutionMetaDataByView;
CommonParametersByView.SetNum(Views.Num());
ConvolutionMetaDataByView.SetNum(Views.Num());
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
Denoiser::SetupCommonShaderParameters(
View, SceneTextures,
ViewSettings.FullResViewport,
Settings.DenoisingResolutionFraction,
/* out */ &CommonParameters.PublicCommonParameters);
CommonParameters.ViewportMin = ViewSettings.Viewport.Min;
CommonParameters.ViewportMax = ViewSettings.Viewport.Max;
CommonParameters.SceneTextures = SceneTextures;
CommonParameters.Substrate = Substrate::BindSubstrateGlobalUniformParameters(View);
CommonParameters.ViewUniformBuffer = View.ViewUniformBuffer;
CommonParameters.EyeAdaptationBuffer = GraphBuilder.CreateSRV(GetEyeAdaptationBuffer(GraphBuilder, View));
// Remove dependency of the velocity buffer on camera cut, given it's going to be ignored by the shaders.
if (View.bCameraCut || !CommonParameters.SceneTextures.GBufferVelocityTexture)
{
CommonParameters.SceneTextures.GBufferVelocityTexture = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
}
float PixelPositionToFullResPixel = 1.0f / Settings.DenoisingResolutionFraction;
FVector2D FullResPixelOffset = FVector2D(0.5f, 0.5f); // TODO(Denoiser).
CommonParameters.ThreadIdToBufferUV.X = PixelPositionToFullResPixel / float(FullResBufferExtent.X);
CommonParameters.ThreadIdToBufferUV.Y = PixelPositionToFullResPixel / float(FullResBufferExtent.Y);
CommonParameters.ThreadIdToBufferUV.Z = (ViewSettings.Viewport.Min.X * PixelPositionToFullResPixel + FullResPixelOffset.X) / float(FullResBufferExtent.X);
CommonParameters.ThreadIdToBufferUV.W = (ViewSettings.Viewport.Min.Y * PixelPositionToFullResPixel + FullResPixelOffset.Y) / float(FullResBufferExtent.Y);
CommonParameters.BufferUVToOutputPixelPosition.X = BufferExtent.X;
CommonParameters.BufferUVToOutputPixelPosition.Y = BufferExtent.Y;
CommonParameters.ScreenToView = FMatrix44f(FMatrix( // LWC_TODO: Precision loss
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, View.ProjectionMatrixUnadjustedForRHI.M[2][2], 1),
FPlane(0, 0, View.ProjectionMatrixUnadjustedForRHI.M[3][2], 0))
* View.ViewMatrices.GetInvProjectionMatrix());
CommonParameters.BufferUVBilinearCorrection.X = (0.5f * PixelPositionToFullResPixel - FullResPixelOffset.X) / float(FullResBufferExtent.X);
CommonParameters.BufferUVBilinearCorrection.Y = (0.5f * PixelPositionToFullResPixel - FullResPixelOffset.Y) / float(FullResBufferExtent.Y);
CommonParameters.FrameIndex = View.ViewState ? View.ViewState->FrameIndex : 0;
// Setup all the metadata to do spatial convolution.
if (Settings.SignalProcessing == ESignalProcessing::ShadowVisibilityMask)
{
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
FLightSceneProxy* LightSceneProxy = Settings.LightSceneInfo[BatchedSignalId]->Proxy;
FLightRenderParameters Parameters;
LightSceneProxy->GetLightShaderParameters(Parameters);
const FVector3f TranslatedWorldPosition = FVector3f(View.ViewMatrices.GetPreViewTranslation() + Parameters.WorldPosition);
ConvolutionMetaData.LightPositionAndRadius[BatchedSignalId] = FVector4f(
TranslatedWorldPosition, Parameters.SourceRadius);
ConvolutionMetaData.LightDirectionAndLength[BatchedSignalId] = FVector4f(
Parameters.Direction, Parameters.SourceLength);
GET_SCALAR_ARRAY_ELEMENT(ConvolutionMetaData.HitDistanceToWorldBluringRadius, BatchedSignalId) =
FMath::Tan(0.5 * FMath::DegreesToRadians(LightSceneProxy->GetLightSourceAngle()) * LightSceneProxy->GetShadowSourceAngleFactor());
GET_SCALAR_ARRAY_ELEMENT(ConvolutionMetaData.LightType, BatchedSignalId) = LightSceneProxy->GetLightType();
}
}
// Compress the meta data for lower memory bandwidth, half res for coherent memory access, and lower VGPR footprint.
if (CompressedMetadataLayout == ECompressedMetadataLayout::FedDepthAndShadingModelID)
{
check(Settings.CompressedDepthTexture);
check(Settings.CompressedShadingModelTexture);
CommonParameters.CompressedMetadata[0] = Settings.CompressedDepthTexture;
CommonParameters.CompressedMetadata[1] = Settings.CompressedShadingModelTexture;
}
else if (CompressedMetadataLayout != ECompressedMetadataLayout::Disabled)
{
if (CompressedMetadataLayout == ECompressedMetadataLayout::DepthAndNormal ||
CompressedMetadataLayout == ECompressedMetadataLayout::DepthAndViewNormal)
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
BufferExtent,
PF_R32_UINT,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV);
CommonParameters.CompressedMetadata[0] = GraphBuilder.CreateTexture(Desc, TEXT("DenoiserMetadata0"));
CommonParameters.CompressedMetadata[1] = nullptr;
CommonParameters.CompressedMetadata[0]->EncloseVisualizeExtent(ViewSettings.Viewport.Max);
}
else
{
check(0);
}
FSSDCompressMetadataCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSSDCompressMetadataCS::FMetadataLayoutDim>(CompressedMetadataLayout);
FSSDCompressMetadataCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDCompressMetadataCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
for (int32 i = 0; i < kCompressedMetadataTextures; i++)
PassParameters->CompressedMetadataOutput[i] = CommonParameters.CompressedMetadata[i] ? GraphBuilder.CreateUAV(CommonParameters.CompressedMetadata[i]) : nullptr;
TShaderMapRef<FSSDCompressMetadataCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD CompressMetadata %dx%d", ViewSettings.Viewport.Width(), ViewSettings.Viewport.Height()),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FComputeShaderUtils::kGolden2DGroupSize));
}
}
FSSDSignalTextures SignalHistory = InputSignal;
// Injestion pass to precompute some values for the reconstruction pass.
if (SignalUsesInjestion(Settings.SignalProcessing))
{
FSSDSignalTextures NewSignalOutput = CreateMultiplexedTextures(
GraphBuilder,
InjestTextureCount, InjestDescs,
GetResourceNames(kInjestResourceNames),
AllViewport);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
FSSDInjestCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDInjestCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionMetaData = ConvolutionMetaData;
PassParameters->SignalInput = SignalHistory;
PassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, NewSignalOutput);
PassParameters->DebugOutput = CreateDebugUAV(TEXT("DebugDenoiserInjest"));
FSSDInjestCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSignalProcessingDim>(Settings.SignalProcessing);
PermutationVector.Set<FSignalBatchSizeDim>(Settings.SignalBatchSize);
PermutationVector.Set<FMultiSPPDim>(bUseMultiInputSPPShaderPath);
PermutationVector = FSSDInjestCS::RemapPermutationVector(PermutationVector);
TShaderMapRef<FSSDInjestCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD Injest(MultiSPP=%i)",
int32(PermutationVector.Get<FMultiSPPDim>())),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FComputeShaderUtils::kGolden2DGroupSize));
}
SignalHistory = NewSignalOutput;
}
// Spatial reconstruction with ratio estimator to be more precise in the history rejection.
if (Settings.bEnableReconstruction)
{
FSSDSignalTextures NewSignalOutput = CreateMultiplexedTextures(
GraphBuilder,
ReconstructionTextureCount, ReconstructionDescs,
GetResourceNames(kReconstructionResourceNames),
AllViewport);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
FSSDSpatialAccumulationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDSpatialAccumulationCS::FParameters>();
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
FIntRect SignalScissor = ViewSettings.SignalScissor[BatchedSignalId];
PassParameters->InputBufferUVMinMax[BatchedSignalId] = FVector4f(
float(SignalScissor.Min.X + 0.5f) / float(BufferExtent.X),
float(SignalScissor.Min.Y + 0.5f) / float(BufferExtent.Y),
float(SignalScissor.Max.X - 0.5f) / float(BufferExtent.X),
float(SignalScissor.Max.Y - 0.5f) / float(BufferExtent.Y));
}
PassParameters->MaxSampleCount = Settings.ReconstructionSamples;
PassParameters->PreviousCumulativeMaxSampleCount = 1;
PassParameters->UpscaleFactor = int32(Settings.DenoisingResolutionFraction / Settings.InputResolutionFraction);
PassParameters->HarmonicPeriode = Settings.HarmonicPeriode;
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionMetaData = ConvolutionMetaData;
PassParameters->SignalInput = SignalHistory;
//PassParameters->SignalInputUint = CreateMultiplexedUintSRVs(GraphBuilder, SignalHistory);
PassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, NewSignalOutput);
PassParameters->DebugOutput = CreateDebugUAV(TEXT("DebugDenoiserReconstruction"));
FSSDSpatialAccumulationCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSignalProcessingDim>(Settings.SignalProcessing);
PermutationVector.Set<FSignalBatchSizeDim>(Settings.SignalBatchSize);
PermutationVector.Set<FSSDSpatialAccumulationCS::FStageDim>(FSSDSpatialAccumulationCS::EStage::ReConstruction);
PermutationVector.Set<FSSDSpatialAccumulationCS::FUpscaleDim>(PassParameters->UpscaleFactor != 1);
PermutationVector.Set<FMultiSPPDim>(bUseMultiInputSPPShaderPath);
PermutationVector = FSSDSpatialAccumulationCS::RemapPermutationVector(PermutationVector);
TShaderMapRef<FSSDSpatialAccumulationCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD Reconstruction(MaxSamples=%i Scissor=%ix%i%s%s)",
PassParameters->MaxSampleCount,
ViewSettings.Viewport.Width(), ViewSettings.Viewport.Height(),
PermutationVector.Get<FSSDSpatialAccumulationCS::FUpscaleDim>() ? TEXT(" Upscale") : TEXT(""),
PermutationVector.Get<FMultiSPPDim>() ? TEXT("") : TEXT(" 1SPP")),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FSSDSpatialAccumulationCS::kGroupSize));
}
SignalHistory = NewSignalOutput;
}
// Spatial pre convolutions
for (int32 PreConvolutionId = 0; PreConvolutionId < Settings.PreConvolutionCount; PreConvolutionId++)
{
check(SignalUsesPreConvolution(Settings.SignalProcessing));
FSSDSignalTextures NewSignalOutput = CreateMultiplexedTextures(
GraphBuilder,
ReconstructionTextureCount, ReconstructionDescs,
GetResourceNames(kPreConvolutionResourceNames),
AllViewport);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
FSSDSpatialAccumulationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDSpatialAccumulationCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionMetaData = ConvolutionMetaData;
PassParameters->MaxSampleCount = Settings.ReconstructionSamples;
PassParameters->PreviousCumulativeMaxSampleCount = FMath::Pow(static_cast<float>(PassParameters->MaxSampleCount), 1 + PreConvolutionId);
PassParameters->KernelSpreadFactor = Settings.KernelSpreadFactor * (1 << PreConvolutionId);
PassParameters->SignalInput = SignalHistory;
PassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, NewSignalOutput);
PassParameters->DebugOutput = CreateDebugUAV(TEXT("DebugDenoiserPreConvolution"));
FSSDSpatialAccumulationCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSignalProcessingDim>(Settings.SignalProcessing);
PermutationVector.Set<FSignalBatchSizeDim>(Settings.SignalBatchSize);
PermutationVector.Set<FSSDSpatialAccumulationCS::FStageDim>(FSSDSpatialAccumulationCS::EStage::PreConvolution);
PermutationVector.Set<FMultiSPPDim>(true);
TShaderMapRef<FSSDSpatialAccumulationCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME(
"SSD PreConvolution(MaxSamples=%d Spread=%f)",
PassParameters->MaxSampleCount,
PassParameters->KernelSpreadFactor),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FSSDSpatialAccumulationCS::kGroupSize));
}
SignalHistory = NewSignalOutput;
}
bool bExtractSceneDepth = false;
bool bExtractSceneGBufferA = false;
bool bExtractSceneGBufferB = false;
TStaticArray<bool, kCompressedMetadataTextures> bExtractCompressedMetadata;
for (int32 i = 0; i < kCompressedMetadataTextures; i++)
bExtractCompressedMetadata[i] = false;
// Temporal pass.
//
// Note: always done even if there is no ViewState, because it is already not an idea case for the denoiser quality, therefore not really
// care about the performance, and the reconstruction may have a different layout than temporal accumulation output.
if (bHasReconstructionLayoutDifferentFromHistory || Settings.bUseTemporalAccumulation)
{
FSSDSignalTextures RejectionPreConvolutionSignal;
// Temporal rejection might make use of a separable preconvolution.
if (SignalUsesRejectionPreConvolution(Settings.SignalProcessing))
{
{
int32 RejectionTextureCount = 1;
TStaticArray<FRDGTextureDesc,kMaxBufferProcessingCount> RejectionSignalProcessingDescs;
for (int32 i = 0; i < kMaxBufferProcessingCount; i++)
{
RejectionSignalProcessingDescs[i] = HistoryDescs[i];
}
if (Settings.SignalProcessing == ESignalProcessing::ShadowVisibilityMask)
{
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
RejectionSignalProcessingDescs[BatchedSignalId].Format = PF_FloatRGBA;
}
RejectionTextureCount = Settings.SignalBatchSize;
}
else if (Settings.SignalProcessing == ESignalProcessing::AmbientOcclusion)
{
RejectionSignalProcessingDescs[0].Format = PF_FloatRGBA;
}
else
{
check(0);
}
RejectionPreConvolutionSignal = CreateMultiplexedTextures(
GraphBuilder,
RejectionTextureCount, RejectionSignalProcessingDescs,
GetResourceNames(kRejectionPreConvolutionResourceNames),
AllViewport);
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
FSSDSpatialAccumulationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDSpatialAccumulationCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionMetaData = ConvolutionMetaData;
PassParameters->SignalInput = SignalHistory;
PassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, RejectionPreConvolutionSignal);
FSSDSpatialAccumulationCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSignalProcessingDim>(Settings.SignalProcessing);
PermutationVector.Set<FSignalBatchSizeDim>(Settings.SignalBatchSize);
PermutationVector.Set<FSSDSpatialAccumulationCS::FStageDim>(FSSDSpatialAccumulationCS::EStage::RejectionPreConvolution);
PermutationVector.Set<FMultiSPPDim>(true);
PassParameters->DebugOutput = CreateDebugUAV(TEXT("DebugDenoiserRejectionPreConvolution"));
TShaderMapRef<FSSDSpatialAccumulationCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD RejectionPreConvolution(MaxSamples=5)"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FSSDSpatialAccumulationCS::kGroupSize));
}
} // if (SignalUsesRejectionPreConvolution(Settings.SignalProcessing))
FSSDSignalTextures SignalOutput = CreateMultiplexedTextures(
GraphBuilder,
HistoryTextureCount, HistoryDescs,
GetResourceNames(kTemporalAccumulationResourceNames),
AllViewport);
int32 LastViewIndex = Views.Num() - 1;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
FSSDTemporalAccumulationCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSignalProcessingDim>(Settings.SignalProcessing);
PermutationVector.Set<FSignalBatchSizeDim>(Settings.SignalBatchSize);
TShaderMapRef<FSSDTemporalAccumulationCS> ComputeShader(View.ShaderMap, PermutationVector);
FSSDTemporalAccumulationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDTemporalAccumulationCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionMetaData = ConvolutionMetaData;
PassParameters->HistoryPreExposureCorrection = View.PreExposure / View.PrevViewInfo.SceneColorPreExposure;
PassParameters->SignalInput = SignalHistory;
PassParameters->HistoryRejectionSignal = RejectionPreConvolutionSignal;
PassParameters->SignalHistoryOutput = CreateMultiplexedUAVs(GraphBuilder, SignalOutput);
// Setup common previous frame data.
PassParameters->PrevDepthBuffer = RegisterExternalTextureWithFallback(GraphBuilder, ViewInfoPooledRenderTargets.PrevDepthBuffer, GSystemTextures.BlackDummy);
PassParameters->PrevGBufferA = RegisterExternalTextureWithFallback(GraphBuilder, ViewInfoPooledRenderTargets.PrevGBufferA, GSystemTextures.BlackDummy);
PassParameters->PrevGBufferB = RegisterExternalTextureWithFallback(GraphBuilder, ViewInfoPooledRenderTargets.PrevGBufferB, GSystemTextures.BlackDummy);
// The first view state's temporal render targets are used for all views in split screen
bool bGlobalCameraCut = !Views[0].PrevViewInfo.DepthBuffer.IsValid();
if (CompressedMetadataLayout == ECompressedMetadataLayout::DepthAndViewNormal)
{
PassParameters->PrevCompressedMetadata[0] = ViewInfoPooledRenderTargets.PrevCompressedDepthViewNormal
? GraphBuilder.RegisterExternalTexture(ViewInfoPooledRenderTargets.PrevCompressedDepthViewNormal)
: GSystemTextures.GetZeroUIntDummy(GraphBuilder);
bGlobalCameraCut = !Views[0].PrevViewInfo.CompressedDepthViewNormal.IsValid();
}
else if (CompressedMetadataLayout == ECompressedMetadataLayout::FedDepthAndShadingModelID)
{
PassParameters->PrevCompressedMetadata[0] = RegisterExternalTextureWithFallback(
GraphBuilder, Views[0].PrevViewInfo.CompressedOpaqueDepth, GSystemTextures.BlackDummy);
PassParameters->PrevCompressedMetadata[1] = Views[0].PrevViewInfo.CompressedOpaqueShadingModel
? GraphBuilder.RegisterExternalTexture(Views[0].PrevViewInfo.CompressedOpaqueShadingModel)
: GSystemTextures.GetZeroUIntDummy(GraphBuilder);
bGlobalCameraCut = !Views[0].PrevViewInfo.CompressedOpaqueDepth.IsValid() || !Views[0].PrevViewInfo.CompressedOpaqueShadingModel.IsValid();
}
// If split screen count changed, treat it as a global camera cut
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
if (PrevFilteringHistory[BatchedSignalId] && PrevFilteringHistory[BatchedSignalId]->Scissors.Num() != Views.Num())
{
bGlobalCameraCut = true;
}
}
FIntPoint PrevFrameBufferExtent;
if (bGlobalCameraCut)
{
PassParameters->ScreenPosToHistoryBufferUV = FVector4f(1.0f, 1.0f, 1.0f, 1.0f);
PassParameters->HistoryBufferUVMinMax = FVector4f(0.0f, 0.0f, 0.0f, 0.0f);
PassParameters->HistoryBufferSizeAndInvSize = FVector4f(1.0f, 1.0f, 1.0f, 1.0f);
PrevFrameBufferExtent = FIntPoint(1, 1);
}
else
{
FIntPoint ViewportOffset = View.PrevViewInfo.ViewRect.Min;
FIntPoint ViewportExtent = View.PrevViewInfo.ViewRect.Size();
if (PassParameters->PrevCompressedMetadata[0])
{
PrevFrameBufferExtent = PassParameters->PrevCompressedMetadata[0]->Desc.Extent;
}
else
{
PrevFrameBufferExtent = PassParameters->PrevDepthBuffer->Desc.Extent;
}
float InvBufferSizeX = 1.f / float(PrevFrameBufferExtent.X);
float InvBufferSizeY = 1.f / float(PrevFrameBufferExtent.Y);
PassParameters->ScreenPosToHistoryBufferUV = FVector4f(
ViewportExtent.X * 0.5f * InvBufferSizeX,
-ViewportExtent.Y * 0.5f * InvBufferSizeY,
(ViewportExtent.X * 0.5f + ViewportOffset.X) * InvBufferSizeX,
(ViewportExtent.Y * 0.5f + ViewportOffset.Y) * InvBufferSizeY);
PassParameters->HistoryBufferUVMinMax = FVector4f(
(ViewportOffset.X + 0.5f) * InvBufferSizeX,
(ViewportOffset.Y + 0.5f) * InvBufferSizeY,
(ViewportOffset.X + ViewportExtent.X - 0.5f) * InvBufferSizeX,
(ViewportOffset.Y + ViewportExtent.Y - 0.5f) * InvBufferSizeY);
PassParameters->HistoryBufferSizeAndInvSize = FVector4f(PrevFrameBufferExtent.X, PrevFrameBufferExtent.Y, InvBufferSizeX, InvBufferSizeY);
PassParameters->PrevSceneBufferUVToScreenPosition.X = float(PrevFrameBufferExtent.X) / float(ViewportExtent.X) * 2.0f;
PassParameters->PrevSceneBufferUVToScreenPosition.Y = -float(PrevFrameBufferExtent.Y) / float(ViewportExtent.Y) * 2.0f;
PassParameters->PrevSceneBufferUVToScreenPosition.Z = -float(ViewportOffset.X) / float(ViewportExtent.X) * 2.0f - 1.0f;
PassParameters->PrevSceneBufferUVToScreenPosition.W = float(ViewportOffset.Y) / float(ViewportExtent.Y) * 2.0f + 1.0f;
}
if (bGlobalCameraCut)
{
PassParameters->ScreenPosToHistoryBufferUV = FVector4f(1.0f, 1.0f, 1.0f, 1.0f);
PassParameters->HistoryBufferUVMinMax = FVector4f(0.0f, 0.0f, 0.0f, 0.0f);
PassParameters->HistoryBufferSizeAndInvSize = FVector4f(1.0f, 1.0f, 1.0f, 1.0f);
PrevFrameBufferExtent = FIntPoint(1, 1);
}
else
{
FIntPoint ViewportOffset = View.PrevViewInfo.ViewRect.Min;
FIntPoint ViewportExtent = View.PrevViewInfo.ViewRect.Size();
if (PassParameters->PrevCompressedMetadata[0])
{
PrevFrameBufferExtent = PassParameters->PrevCompressedMetadata[0]->Desc.Extent;
}
else
{
PrevFrameBufferExtent = PassParameters->PrevDepthBuffer->Desc.Extent;
}
float InvBufferSizeX = 1.f / float(PrevFrameBufferExtent.X);
float InvBufferSizeY = 1.f / float(PrevFrameBufferExtent.Y);
PassParameters->ScreenPosToHistoryBufferUV = FVector4f(
ViewportExtent.X * 0.5f * InvBufferSizeX,
-ViewportExtent.Y * 0.5f * InvBufferSizeY,
(ViewportExtent.X * 0.5f + ViewportOffset.X) * InvBufferSizeX,
(ViewportExtent.Y * 0.5f + ViewportOffset.Y) * InvBufferSizeY);
PassParameters->HistoryBufferUVMinMax = FVector4f(
(ViewportOffset.X + 0.5f) * InvBufferSizeX,
(ViewportOffset.Y + 0.5f) * InvBufferSizeY,
(ViewportOffset.X + ViewportExtent.X - 0.5f) * InvBufferSizeX,
(ViewportOffset.Y + ViewportExtent.Y - 0.5f) * InvBufferSizeY);
PassParameters->HistoryBufferSizeAndInvSize = FVector4f(PrevFrameBufferExtent.X, PrevFrameBufferExtent.Y, InvBufferSizeX, InvBufferSizeY);
PassParameters->PrevSceneBufferUVToScreenPosition.X = float(PrevFrameBufferExtent.X) / float(ViewportExtent.X) * 2.0f;
PassParameters->PrevSceneBufferUVToScreenPosition.Y = -float(PrevFrameBufferExtent.Y) / float(ViewportExtent.Y) * 2.0f;
PassParameters->PrevSceneBufferUVToScreenPosition.Z = -float(ViewportOffset.X) / float(ViewportExtent.X) * 2.0f - 1.0f;
PassParameters->PrevSceneBufferUVToScreenPosition.W = float(ViewportOffset.Y) / float(ViewportExtent.Y) * 2.0f + 1.0f;
}
FScreenSpaceDenoiserHistory DummyPrevFrameHistory;
DummyPrevFrameHistory.Scissors.SetNumZeroed(Views.Num());
// Setup signals' previous frame historu buffers.
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
FScreenSpaceDenoiserHistory* PrevFrameHistory = PrevFilteringHistory[BatchedSignalId] ? PrevFilteringHistory[BatchedSignalId] : &DummyPrevFrameHistory;
// Use the dummy zeroed out scissors if the number of split screen views changed (this will reject the history samples)
const auto& ScissorsToUse = PrevFrameHistory->Scissors.Num() == Views.Num() ? PrevFrameHistory->Scissors : DummyPrevFrameHistory.Scissors;
GET_SCALAR_ARRAY_ELEMENT(PassParameters->bCameraCut, BatchedSignalId) = !PrevFrameHistory->IsValid();
if (!(View.ViewState && Settings.bUseTemporalAccumulation) || bGlobalCameraCut)
{
GET_SCALAR_ARRAY_ELEMENT(PassParameters->bCameraCut, BatchedSignalId) = true;
}
for (int32 BufferId = 0; BufferId < HistoryTextureCountPerSignal; BufferId++)
{
int32 HistoryBufferId = BatchedSignalId * HistoryTextureCountPerSignal + BufferId;
PassParameters->PrevHistory.Textures[HistoryBufferId] = RegisterExternalTextureWithFallback(
GraphBuilder, PrevFrameHistory->RT[BufferId], GSystemTextures.BlackDummy);
}
PassParameters->HistoryBufferScissorUVMinMax[BatchedSignalId] = FVector4f(
float(ScissorsToUse[ViewIndex].Min.X + 0.5f) / float(PrevFrameBufferExtent.X),
float(ScissorsToUse[ViewIndex].Min.Y + 0.5f) / float(PrevFrameBufferExtent.Y),
float(ScissorsToUse[ViewIndex].Max.X - 0.5f) / float(PrevFrameBufferExtent.X),
float(ScissorsToUse[ViewIndex].Max.Y - 0.5f) / float(PrevFrameBufferExtent.Y));
// Releases the reference on previous frame so the history's render target can be reused ASAP.
// Need to do this for the last view when running split screen, as all views use the same render targets.
if (ViewIndex == LastViewIndex)
{
PrevFrameHistory->SafeRelease();
}
} // for (uint32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
PassParameters->DebugOutput = CreateDebugUAV(TEXT("DebugDenoiserTemporalAccumulation"));
// Manually cleans the unused resource, to find out what the shader is actually going to need for next frame.
{
ClearUnusedGraphResources(ComputeShader, PassParameters);
bExtractSceneDepth = PassParameters->PrevDepthBuffer != nullptr;
bExtractSceneGBufferA = PassParameters->PrevGBufferA != nullptr;
bExtractSceneGBufferB = PassParameters->PrevGBufferB != nullptr;
for (int32 i = 0; i < kCompressedMetadataTextures; i++)
bExtractCompressedMetadata[i] = PassParameters->PrevCompressedMetadata[i] != nullptr;
}
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD TemporalAccumulation%s",
(!Settings.bUseTemporalAccumulation || bGlobalCameraCut) ? TEXT("(Disabled)") : TEXT("")),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FComputeShaderUtils::kGolden2DGroupSize));
}
SignalHistory = SignalOutput;
} // if (View.ViewState && Settings.bUseTemporalAccumulation)
// Spatial filter, to converge history faster.
int32 MaxPostFilterSampleCount = FMath::Clamp(Settings.HistoryConvolutionSampleCount, 1, kStackowiakMaxSampleCountPerSet);
if (MaxPostFilterSampleCount > 1)
{
FSSDSignalTextures SignalOutput = CreateMultiplexedTextures(
GraphBuilder,
HistoryTextureCount, HistoryDescs,
GetResourceNames(kHistoryConvolutionResourceNames),
AllViewport);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
FSSDSpatialAccumulationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDSpatialAccumulationCS::FParameters>();
PassParameters->MaxSampleCount = FMath::Clamp(MaxPostFilterSampleCount, 1, kStackowiakMaxSampleCountPerSet);
PassParameters->KernelSpreadFactor = Settings.HistoryConvolutionKernelSpreadFactor;
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionMetaData = ConvolutionMetaData;
PassParameters->SignalInput = SignalHistory;
PassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, SignalOutput);
FSSDSpatialAccumulationCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSignalProcessingDim>(Settings.SignalProcessing);
PermutationVector.Set<FSignalBatchSizeDim>(Settings.SignalBatchSize);
PermutationVector.Set<FSSDSpatialAccumulationCS::FStageDim>(FSSDSpatialAccumulationCS::EStage::PostFiltering);
PermutationVector.Set<FMultiSPPDim>(true);
PassParameters->DebugOutput = CreateDebugUAV(TEXT("DebugDenoiserPostfilter"));
TShaderMapRef<FSSDSpatialAccumulationCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD HistoryConvolution(MaxSamples=%i)", MaxPostFilterSampleCount),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FSSDSpatialAccumulationCS::kGroupSize));
}
SignalHistory = SignalOutput;
} // if (MaxPostFilterSampleCount > 1)
if (!Views[0].bStatePrevViewInfoIsReadOnly && Settings.bUseTemporalAccumulation)
{
check(Views[0].ViewState);
// Keep depth buffer and GBuffer around for next frame if the temporal accumulation needs it.
// We only need to keep track of render target history for the first view with multi-view split screen.
{
// Might requires the depth.
if (bExtractSceneDepth)
{
GraphBuilder.QueueTextureExtraction(SceneTextures.SceneDepthTexture, ViewInfoPooledRenderTargets.NextDepthBuffer);
}
// Might requires the world normal that are in GBuffer A.
if (bExtractSceneGBufferA)
{
GraphBuilder.QueueTextureExtraction(SceneTextures.GBufferATexture, ViewInfoPooledRenderTargets.NextGBufferA);
}
// Might need the roughness that is in GBuffer B.
if (bExtractSceneGBufferB)
{
GraphBuilder.QueueTextureExtraction(SceneTextures.GBufferBTexture, ViewInfoPooledRenderTargets.NextGBufferB);
}
// Extract the compressed scene texture to make te history re-projection faster.
for (int32 i = 0; i < kCompressedMetadataTextures; i++)
{
TRefCountPtr<IPooledRenderTarget>* Dest = nullptr;
if (CompressedMetadataLayout == ECompressedMetadataLayout::DepthAndViewNormal)
{
if (i == 0)
{
Dest = ViewInfoPooledRenderTargets.NextCompressedDepthViewNormal;
}
}
else if (CompressedMetadataLayout == ECompressedMetadataLayout::FedDepthAndShadingModelID)
{
if (i == 0)
{
Dest = &Views[0].ViewState->PrevFrameViewInfo.CompressedOpaqueDepth;
}
else // if (i == 1)
{
Dest = &Views[0].ViewState->PrevFrameViewInfo.CompressedOpaqueShadingModel;
}
}
check((CommonParametersByView[0].CompressedMetadata[i] != nullptr) == (Dest != nullptr));
if (Dest)
{
check(CommonParametersByView[0].CompressedMetadata[i]);
GraphBuilder.QueueTextureExtraction(CommonParametersByView[0].CompressedMetadata[i], Dest);
}
}
}
// Saves signal histories.
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
FScreenSpaceDenoiserHistory* NewHistory = NewFilteringHistory[BatchedSignalId];
check(NewHistory);
for (int32 BufferId = 0; BufferId < HistoryTextureCountPerSignal; BufferId++)
{
int32 HistoryBufferId = BatchedSignalId * HistoryTextureCountPerSignal + BufferId;
GraphBuilder.QueueTextureExtraction(SignalHistory.Textures[HistoryBufferId], &NewHistory->RT[BufferId]);
}
NewHistory->Scissors.SetNumUninitialized(Views.Num());
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
NewHistory->Scissors[ViewIndex] = ViewSettings.FullResViewport;
}
} // for (uint32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
}
// Final convolution / output to correct
if (SignalUsesFinalConvolution(Settings.SignalProcessing))
{
TStaticArray<FRDGTextureDesc, kMaxBufferProcessingCount> OutputDescs;
for (int32 i = 0; i < kMaxBufferProcessingCount; i++)
{
OutputDescs[i] = HistoryDescs[i];
}
if (Settings.SignalProcessing == ESignalProcessing::ShadowVisibilityMask)
{
for (int32 BatchedSignalId = 0; BatchedSignalId < Settings.SignalBatchSize; BatchedSignalId++)
{
OutputDescs[BatchedSignalId].Format = PF_FloatRGBA;
}
}
else
{
check(0);
}
*OutputSignal = CreateMultiplexedTextures(
GraphBuilder,
Settings.SignalBatchSize, OutputDescs,
GetResourceNames(kDenoiserOutputResourceNames),
AllViewport);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSSDCommonParameters& CommonParameters = CommonParametersByView[ViewIndex];
FSSDConvolutionMetaData& ConvolutionMetaData = ConvolutionMetaDataByView[ViewIndex];
FSSDConstantPixelDensityViewSettings& ViewSettings = Settings.ViewSettingsArray[ViewIndex];
FSSDSpatialAccumulationCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSSDSpatialAccumulationCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionMetaData = ConvolutionMetaData;
PassParameters->SignalInput = SignalHistory;
PassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, *OutputSignal);
FSSDSpatialAccumulationCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FSignalProcessingDim>(Settings.SignalProcessing);
PermutationVector.Set<FSignalBatchSizeDim>(Settings.SignalBatchSize);
PermutationVector.Set<FSSDSpatialAccumulationCS::FStageDim>(FSSDSpatialAccumulationCS::EStage::FinalOutput);
PermutationVector.Set<FMultiSPPDim>(true);
TShaderMapRef<FSSDSpatialAccumulationCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD SpatialAccumulation(Final)"),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(ViewSettings.Viewport.Size(), FSSDSpatialAccumulationCS::kGroupSize));
}
}
else
{
*OutputSignal = SignalHistory;
}
} // DenoiseSignalAtConstantPixelDensity()
// static
IScreenSpaceDenoiser::FHarmonicTextures IScreenSpaceDenoiser::CreateHarmonicTextures(FRDGBuilder& GraphBuilder, FIntPoint Extent, const TCHAR* DebugName)
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
Extent,
PF_FloatRGBA,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
FHarmonicTextures HarmonicTextures;
for (int32 HarmonicBorderId = 0; HarmonicBorderId < kHarmonicBordersCount; HarmonicBorderId++)
{
HarmonicTextures.Harmonics[HarmonicBorderId] = GraphBuilder.CreateTexture(Desc, DebugName);
}
return HarmonicTextures;
}
// static
IScreenSpaceDenoiser::FHarmonicUAVs IScreenSpaceDenoiser::CreateUAVs(FRDGBuilder& GraphBuilder, const FHarmonicTextures& Textures)
{
FHarmonicUAVs UAVs;
for (int32 HarmonicBorderId = 0; HarmonicBorderId < kHarmonicBordersCount; HarmonicBorderId++)
{
UAVs.Harmonics[HarmonicBorderId] = GraphBuilder.CreateUAV(Textures.Harmonics[HarmonicBorderId]);
}
return UAVs;
}
// static
IScreenSpaceDenoiser::FDiffuseIndirectHarmonicUAVs IScreenSpaceDenoiser::CreateUAVs(FRDGBuilder& GraphBuilder, const FDiffuseIndirectHarmonic& Textures)
{
FDiffuseIndirectHarmonicUAVs UAVs;
for (int32 HarmonicBorderId = 0; HarmonicBorderId < kSphericalHarmonicTextureCount; HarmonicBorderId++)
{
UAVs.SphericalHarmonic[HarmonicBorderId] = GraphBuilder.CreateUAV(Textures.SphericalHarmonic[HarmonicBorderId]);
}
return UAVs;
}
/** The implementation of the default denoiser of the renderer. */
class FDefaultScreenSpaceDenoiser : public IScreenSpaceDenoiser
{
public:
const TCHAR* GetDebugName() const override
{
return TEXT("ScreenSpaceDenoiser");
}
virtual EShadowRequirements GetShadowRequirements(
const FViewInfo& View,
const FLightSceneInfo& LightSceneInfo,
const FShadowRayTracingConfig& RayTracingConfig) const override
{
check(SignalSupportMultiSPP(ESignalProcessing::ShadowVisibilityMask));
return IScreenSpaceDenoiser::EShadowRequirements::PenumbraAndClosestOccluder;
}
virtual void DenoiseShadowVisibilityMasks(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const TStaticArray<FShadowVisibilityParameters, IScreenSpaceDenoiser::kMaxBatchSize>& InputParameters,
const int32 InputParameterCount,
TStaticArray<FShadowVisibilityOutputs, IScreenSpaceDenoiser::kMaxBatchSize>& Outputs) const
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, ShadowsDenoiser, "DenoiseShadowVisibilityMasks");
RDG_GPU_STAT_SCOPE(GraphBuilder, ShadowsDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
Settings.SignalProcessing = ESignalProcessing::ShadowVisibilityMask;
Settings.InputResolutionFraction = 1.0f;
Settings.ReconstructionSamples = FMath::Clamp(CVarShadowReconstructionSampleCount.GetValueOnRenderThread(), 1, kStackowiakMaxSampleCountPerSet);
Settings.PreConvolutionCount = CVarShadowPreConvolutionCount.GetValueOnRenderThread();
Settings.bUseTemporalAccumulation = CVarShadowTemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.HistoryConvolutionSampleCount = CVarShadowHistoryConvolutionSampleCount.GetValueOnRenderThread();
Settings.SignalBatchSize = InputParameterCount;
for (int32 BatchedSignalId = 0; BatchedSignalId < InputParameterCount; BatchedSignalId++)
{
Settings.MaxInputSPP = FMath::Max(Settings.MaxInputSPP, InputParameters[BatchedSignalId].RayTracingConfig.RayCountPerPixel);
}
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories = {};
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories = {};
for (int32 BatchedSignalId = 0; BatchedSignalId < InputParameterCount; BatchedSignalId++)
{
const FShadowVisibilityParameters& Parameters = InputParameters[BatchedSignalId];
const FLightSceneProxy* Proxy = Parameters.LightSceneInfo->Proxy;
// Scissor the denoiser.
{
FIntRect LightScissorRect;
if (Proxy->GetScissorRect(/* out */ LightScissorRect, View, View.ViewRect))
{
}
else
{
LightScissorRect = View.ViewRect;
}
if (BatchedSignalId == 0)
{
ViewSettings.FullResViewport = LightScissorRect;
}
else
{
ViewSettings.FullResViewport.Union(LightScissorRect);
}
ViewSettings.SignalScissor[BatchedSignalId] = LightScissorRect;
}
ensure(IsSupportedLightType(ELightComponentType(Proxy->GetLightType())));
Settings.LightSceneInfo[BatchedSignalId] = Parameters.LightSceneInfo;
// Get the packed penumbra and hit distance in Penumbra texture.
InputSignal.Textures[BatchedSignalId] = Parameters.InputTextures.Mask;
const ULightComponent* LightComponent = Settings.LightSceneInfo[BatchedSignalId]->Proxy->GetLightComponent();
TSharedPtr<FScreenSpaceDenoiserHistory>* PrevHistoryEntry = PreviousViewInfos->ShadowHistories.Find(LightComponent);
PrevHistories[BatchedSignalId] = PrevHistoryEntry ? PrevHistoryEntry->Get() : nullptr;
NewHistories[BatchedSignalId] = nullptr;
if (!View.bStatePrevViewInfoIsReadOnly)
{
check(View.ViewState);
TSharedPtr<FScreenSpaceDenoiserHistory>* NewHistoryEntry = View.ViewState->PrevFrameViewInfo.ShadowHistories.Find(LightComponent);
if (NewHistoryEntry == nullptr)
{
FScreenSpaceDenoiserHistory* NewHistory = new FScreenSpaceDenoiserHistory;
View.ViewState->PrevFrameViewInfo.ShadowHistories.Emplace(LightComponent, NewHistory);
NewHistories[BatchedSignalId] = NewHistory;
}
else
{
NewHistories[BatchedSignalId] = NewHistoryEntry->Get();
}
}
}
// Force viewport to be a multiple of 2, to avoid over frame interference between TAA jitter of the frame, and Stackowiack's SampleTrackId.
{
ViewSettings.FullResViewport.Min.X &= ~1;
ViewSettings.FullResViewport.Min.Y &= ~1;
}
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
for (int32 BatchedSignalId = 0; BatchedSignalId < InputParameterCount; BatchedSignalId++)
{
Outputs[BatchedSignalId].Mask = SignalOutput.Textures[BatchedSignalId];
}
}
FPolychromaticPenumbraOutputs DenoisePolychromaticPenumbraHarmonics(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FPolychromaticPenumbraHarmonics& Inputs) const override
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, ShadowsDenoiser, "DenoisePolychromaticPenumbraHarmonics");
RDG_GPU_STAT_SCOPE(GraphBuilder, ShadowsDenoiser);
FRDGTextureRef BlackDummy = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
FRDGTextureRef WhiteDummy = GraphBuilder.RegisterExternalTexture(GSystemTextures.WhiteDummy);
FSSDComposeHarmonicsCS::FParameters* ComposePassParameters = GraphBuilder.AllocParameters<FSSDComposeHarmonicsCS::FParameters>();
// Harmonic 0 doesn't need any reconstruction given it's the highest frequency details.
{
const int32 HarmonicId = 0;
ComposePassParameters->SignalHarmonics[HarmonicId].Textures[0] = Inputs.Diffuse.Harmonics[0];
ComposePassParameters->SignalHarmonics[HarmonicId].Textures[1] = Inputs.Diffuse.Harmonics[1];
ComposePassParameters->SignalHarmonics[HarmonicId].Textures[2] = Inputs.Specular.Harmonics[0];
ComposePassParameters->SignalHarmonics[HarmonicId].Textures[3] = Inputs.Specular.Harmonics[1];
}
// Reconstruct each harmonic independently
for (int32 HarmonicId = 1; HarmonicId < IScreenSpaceDenoiser::kMultiPolychromaticPenumbraHarmonics; HarmonicId++)
{
int32 Periode = 1 << HarmonicId;
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::PolychromaticPenumbraHarmonic;
Settings.HarmonicPeriode = Periode;
Settings.ReconstructionSamples = Periode * Periode; // TODO(Denoiser): should use preconvolution instead for harmonic 3
Settings.bUseTemporalAccumulation = false;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = nullptr;
NewHistories[0] = nullptr;
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = Inputs.Diffuse.Harmonics[HarmonicId + 0];
InputSignal.Textures[1] = Inputs.Diffuse.Harmonics[HarmonicId + 1];
InputSignal.Textures[2] = Inputs.Specular.Harmonics[HarmonicId + 0];
InputSignal.Textures[3] = Inputs.Specular.Harmonics[HarmonicId + 1];
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories, NewHistories,
/* out */ &SignalOutput);
ComposePassParameters->SignalHarmonics[HarmonicId] = SignalOutput;
}
// Denoise the entire integrand signal.
// TODO(Denoiser): this assume all the lights are going into lowest frequency harmonic.
if (1)
{
const int32 HarmonicId = IScreenSpaceDenoiser::kMultiPolychromaticPenumbraHarmonics - 1;
int32 Periode = 1 << HarmonicId;
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::PolychromaticPenumbraHarmonic;
Settings.HarmonicPeriode = Periode;
Settings.ReconstructionSamples = Periode * Periode; // TODO(Denoiser): should use preconvolution instead for harmonic 3
Settings.bUseTemporalAccumulation = false;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = nullptr;
NewHistories[0] = nullptr;
// TODO(Denoiser): pipeline permutation to be faster.
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = Inputs.Diffuse.Harmonics[0];
InputSignal.Textures[1] = BlackDummy;
InputSignal.Textures[2] = Inputs.Specular.Harmonics[0];
InputSignal.Textures[3] = BlackDummy;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories, NewHistories,
/* out */ &ComposePassParameters->SignalIntegrand);
}
else
{
ComposePassParameters->SignalIntegrand.Textures[0] = WhiteDummy;
ComposePassParameters->SignalIntegrand.Textures[1] = BlackDummy;
ComposePassParameters->SignalIntegrand.Textures[2] = WhiteDummy;
ComposePassParameters->SignalIntegrand.Textures[3] = BlackDummy;
}
// Merges the different harmonics.
FSSDSignalTextures ComposedHarmonics;
{
FIntPoint BufferExtent = SceneTextures.SceneDepthTexture->Desc.Extent;
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
BufferExtent,
PF_FloatRGBA,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_UAV);
ComposedHarmonics.Textures[0] = GraphBuilder.CreateTexture(Desc, TEXT("PolychromaticPenumbraComposition0"));
ComposedHarmonics.Textures[1] = GraphBuilder.CreateTexture(Desc, TEXT("PolychromaticPenumbraComposition1"));
ComposedHarmonics.Textures[0]->EncloseVisualizeExtent(View.ViewRect.Max);
ComposedHarmonics.Textures[1]->EncloseVisualizeExtent(View.ViewRect.Max);
}
ComposePassParameters->CommonParameters.ViewUniformBuffer = View.ViewUniformBuffer;
ComposePassParameters->CommonParameters.SceneTextures = SceneTextures;
ComposePassParameters->CommonParameters.ViewportMin = View.ViewRect.Min;
ComposePassParameters->CommonParameters.ViewportMax = View.ViewRect.Max;
ComposePassParameters->CommonParameters.PublicCommonParameters.DenoiserBufferBilinearUVMinMax = FVector4f(
float(View.ViewRect.Min.X + 0.5f) / float(BufferExtent.X),
float(View.ViewRect.Min.Y + 0.5f) / float(BufferExtent.Y),
float(View.ViewRect.Max.X - 0.5f) / float(BufferExtent.X),
float(View.ViewRect.Max.Y - 0.5f) / float(BufferExtent.Y));
ComposePassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, ComposedHarmonics);
{
FRDGTextureDesc DebugDesc = FRDGTextureDesc::Create2D(
SceneTextures.SceneDepthTexture->Desc.Extent,
PF_FloatRGBA,
FClearValueBinding::Black,
TexCreate_ShaderResource | TexCreate_UAV);
FRDGTextureRef DebugTexture = GraphBuilder.CreateTexture(DebugDesc, TEXT("DebugHarmonicComposition"));
ComposePassParameters->DebugOutput = GraphBuilder.CreateUAV(DebugTexture);
}
TShaderMapRef<FSSDComposeHarmonicsCS> ComputeShader(View.ShaderMap);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("SSD ComposeHarmonics"),
ComputeShader, ComposePassParameters,
FComputeShaderUtils::GetGroupCount(View.ViewRect.Size(), FSSDSpatialAccumulationCS::kGroupSize));
}
FPolychromaticPenumbraOutputs Outputs;
{
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::PolychromaticPenumbraHarmonic;
Settings.bEnableReconstruction = false;
Settings.bUseTemporalAccumulation = CVarShadowTemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->PolychromaticPenumbraHarmonicsHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.PolychromaticPenumbraHarmonicsHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
ComposedHarmonics, Settings,
PrevHistories, NewHistories,
/* out */ &SignalOutput);
Outputs.Diffuse = SignalOutput.Textures[0];
Outputs.Specular = SignalOutput.Textures[1];
}
return Outputs;
}
FReflectionsOutputs DenoiseReflections(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FReflectionsInputs& ReflectionInputs,
const FReflectionsRayTracingConfig RayTracingConfig) const override
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, ReflectionsDenoiser, "DenoiseReflections");
RDG_GPU_STAT_SCOPE(GraphBuilder, ReflectionsDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = ReflectionInputs.Color;
InputSignal.Textures[1] = ReflectionInputs.RayHitDistance;
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::Reflections;
Settings.InputResolutionFraction = RayTracingConfig.ResolutionFraction;
Settings.ReconstructionSamples = CVarReflectionReconstructionSampleCount.GetValueOnRenderThread();
Settings.PreConvolutionCount = CVarReflectionPreConvolutionCount.GetValueOnRenderThread();
Settings.bUseTemporalAccumulation = CVarReflectionTemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.MaxInputSPP = RayTracingConfig.RayCountPerPixel;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->ReflectionsHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.ReflectionsHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
FReflectionsOutputs ReflectionsOutput;
ReflectionsOutput.Color = SignalOutput.Textures[0];
return ReflectionsOutput;
}
FReflectionsOutputs DenoiseWaterReflections(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FReflectionsInputs& ReflectionInputs,
const FReflectionsRayTracingConfig RayTracingConfig) const override
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, ReflectionsDenoiser, "DenoiseWaterReflections");
RDG_GPU_STAT_SCOPE(GraphBuilder, ReflectionsDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = ReflectionInputs.Color;
InputSignal.Textures[1] = ReflectionInputs.RayHitDistance;
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::Reflections; // TODO: water reflection to denoise only water pixels
Settings.InputResolutionFraction = RayTracingConfig.ResolutionFraction;
Settings.ReconstructionSamples = CVarReflectionReconstructionSampleCount.GetValueOnRenderThread();
Settings.PreConvolutionCount = CVarReflectionPreConvolutionCount.GetValueOnRenderThread();
Settings.bUseTemporalAccumulation = CVarReflectionTemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.MaxInputSPP = RayTracingConfig.RayCountPerPixel;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->WaterReflectionsHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.WaterReflectionsHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures,
ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
FReflectionsOutputs ReflectionsOutput;
ReflectionsOutput.Color = SignalOutput.Textures[0];
return ReflectionsOutput;
}
FAmbientOcclusionOutputs DenoiseAmbientOcclusion(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FAmbientOcclusionInputs& ReflectionInputs,
const FAmbientOcclusionRayTracingConfig RayTracingConfig) const override
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, AmbientOcclusionDenoiser, "DenoiseAmbientOcclusion");
RDG_GPU_STAT_SCOPE(GraphBuilder, AmbientOcclusionDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = ReflectionInputs.Mask;
InputSignal.Textures[1] = ReflectionInputs.RayHitDistance;
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::AmbientOcclusion;
Settings.InputResolutionFraction = RayTracingConfig.ResolutionFraction;
Settings.ReconstructionSamples = FMath::Clamp(CVarAOReconstructionSampleCount.GetValueOnRenderThread(), 1, kStackowiakMaxSampleCountPerSet);
Settings.PreConvolutionCount = CVarAOPreConvolutionCount.GetValueOnRenderThread();
Settings.KernelSpreadFactor = CVarAOKernelSpreadFactor.GetValueOnRenderThread();
Settings.bUseTemporalAccumulation = CVarAOTemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.HistoryConvolutionSampleCount = CVarAOHistoryConvolutionSampleCount.GetValueOnRenderThread();
Settings.HistoryConvolutionKernelSpreadFactor = CVarAOHistoryConvolutionKernelSpreadFactor.GetValueOnRenderThread();
Settings.MaxInputSPP = RayTracingConfig.RayCountPerPixel;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->AmbientOcclusionHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.AmbientOcclusionHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
FAmbientOcclusionOutputs AmbientOcclusionOutput;
AmbientOcclusionOutput.AmbientOcclusionMask = SignalOutput.Textures[0];
return AmbientOcclusionOutput;
}
FSSDSignalTextures DenoiseDiffuseIndirect(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FDiffuseIndirectInputs& Inputs,
const FAmbientOcclusionRayTracingConfig Config) const override
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, DiffuseIndirectDenoiser, "DenoiseDiffuseIndirect");
RDG_GPU_STAT_SCOPE(GraphBuilder, DiffuseIndirectDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = Inputs.Color;
InputSignal.Textures[1] = Inputs.RayHitDistance;
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::DiffuseAndAmbientOcclusion;
Settings.InputResolutionFraction = Config.ResolutionFraction;
Settings.ReconstructionSamples = FMath::Clamp(CVarGIReconstructionSampleCount.GetValueOnRenderThread(), 1, kStackowiakMaxSampleCountPerSet);
Settings.PreConvolutionCount = CVarGIPreConvolutionCount.GetValueOnRenderThread();
Settings.bUseTemporalAccumulation = CVarGITemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.HistoryConvolutionSampleCount = CVarGIHistoryConvolutionSampleCount.GetValueOnRenderThread();
Settings.HistoryConvolutionKernelSpreadFactor = CVarGIHistoryConvolutionKernelSpreadFactor.GetValueOnRenderThread();
Settings.MaxInputSPP = Config.RayCountPerPixel;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->DiffuseIndirectHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.DiffuseIndirectHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
return SignalOutput;
}
FDiffuseIndirectOutputs DenoiseSkyLight(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FDiffuseIndirectInputs& Inputs,
const FAmbientOcclusionRayTracingConfig Config) const override
{
return IScreenSpaceDenoiser::DenoiseSkyLight(
GraphBuilder,
TConstArrayView<FViewInfo>(&View, 1),
PreviousViewInfos,
SceneTextures,
Inputs,
Config);
}
FSSDSignalTextures DenoiseDiffuseIndirectHarmonic(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FDiffuseIndirectHarmonic& Inputs,
const HybridIndirectLighting::FCommonParameters& CommonDiffuseParameters) const override
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, DiffuseIndirectDenoiser, "DenoiseDiffuseIndirectHarmonic");
RDG_GPU_STAT_SCOPE(GraphBuilder, DiffuseIndirectDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
for (int32 i = 0; i < IScreenSpaceDenoiser::kSphericalHarmonicTextureCount; i++)
InputSignal.Textures[i] = Inputs.SphericalHarmonic[i];
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::DiffuseSphericalHarmonic;
Settings.InputResolutionFraction = 1.0f / float(CommonDiffuseParameters.DownscaleFactor);
Settings.ReconstructionSamples = CVarGIReconstructionSampleCount.GetValueOnRenderThread();
Settings.bUseTemporalAccumulation = CVarGITemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.MaxInputSPP = CommonDiffuseParameters.RayCountPerPixel;
Settings.DenoisingResolutionFraction = 1.0f / float(CommonDiffuseParameters.DownscaleFactor);
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->DiffuseIndirectHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.DiffuseIndirectHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
return SignalOutput;
}
bool SupportsScreenSpaceDiffuseIndirectDenoiser(EShaderPlatform Platform) const override
{
return ShouldCompileSignalPipeline(ESignalProcessing::ScreenSpaceDiffuseIndirect, Platform);
}
FSSDSignalTextures DenoiseScreenSpaceDiffuseIndirect(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FDiffuseIndirectInputs& Inputs,
const FAmbientOcclusionRayTracingConfig Config) const override
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, DiffuseIndirectDenoiser, "DenoiseScreenSpaceDiffuseIndirect");
RDG_GPU_STAT_SCOPE(GraphBuilder, DiffuseIndirectDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = Inputs.Color;
InputSignal.Textures[1] = Inputs.AmbientOcclusionMask;
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::ScreenSpaceDiffuseIndirect;
Settings.InputResolutionFraction = Config.ResolutionFraction;
Settings.DenoisingResolutionFraction = Config.ResolutionFraction;
Settings.ReconstructionSamples = 8;
Settings.bUseTemporalAccumulation = CVarGITemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.MaxInputSPP = Config.RayCountPerPixel;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->DiffuseIndirectHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.DiffuseIndirectHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
return SignalOutput;
}
}; // class FDefaultScreenSpaceDenoiser
// static
FSSDSignalTextures IScreenSpaceDenoiser::DenoiseIndirectProbeHierarchy(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FSSDSignalTextures& InputSignal,
FRDGTextureRef CompressedDepthTexture,
FRDGTextureRef CompressedShadingModelTexture)
{
FSSDConstantPixelDensitySettings Settings;
FSSDConstantPixelDensityViewSettings ViewSettings;
ViewSettings.FullResViewport = View.ViewRect;
Settings.SignalProcessing = ESignalProcessing::IndirectProbeHierarchy;
Settings.bEnableReconstruction = false;
Settings.bUseTemporalAccumulation = CVarGITemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.MaxInputSPP = 8;
Settings.CompressedDepthTexture = CompressedDepthTexture;
Settings.CompressedShadingModelTexture = CompressedShadingModelTexture;
Settings.ViewSettingsArray = TArrayView<FSSDConstantPixelDensityViewSettings>(&ViewSettings, 1);
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->DiffuseIndirectHistory;
NewHistories[0] = View.ViewState ? &View.ViewState->PrevFrameViewInfo.DiffuseIndirectHistory : nullptr;
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(View, &ViewInfoPooledRenderTargets);
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, TConstArrayView<FViewInfo>(&View, 1), SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
return SignalOutput;
}
// static
const IScreenSpaceDenoiser* IScreenSpaceDenoiser::GetDefaultDenoiser()
{
static IScreenSpaceDenoiser* GDefaultDenoiser = new FDefaultScreenSpaceDenoiser;
return GDefaultDenoiser;
}
int GetReflectionsDenoiserMode()
{
return CVarUseReflectionDenoiser.GetValueOnRenderThread();
}
// static
IScreenSpaceDenoiser::EMode IScreenSpaceDenoiser::GetDenoiserMode(const TAutoConsoleVariable<int32>& CVar)
{
int32 CVarSettings = CVar.GetValueOnRenderThread();
if (CVarSettings == 0)
{
return EMode::Disabled;
}
else if (CVarSettings == 1 || GScreenSpaceDenoiser == GetDefaultDenoiser())
{
return EMode::DefaultDenoiser;
}
return EMode::ThirdPartyDenoiser;
}
IScreenSpaceDenoiser::FDiffuseIndirectOutputs IScreenSpaceDenoiser::DenoiseSkyLight(
FRDGBuilder& GraphBuilder,
TConstArrayView<FViewInfo> Views,
FPreviousViewInfo* PreviousViewInfos,
const FSceneTextureParameters& SceneTextures,
const FDiffuseIndirectInputs& Inputs,
const FAmbientOcclusionRayTracingConfig Config)
{
RDG_EVENT_SCOPE_STAT(GraphBuilder, DiffuseIndirectDenoiser, "DenoiseSkyLight");
RDG_GPU_STAT_SCOPE(GraphBuilder, DiffuseIndirectDenoiser);
FViewInfoPooledRenderTargets ViewInfoPooledRenderTargets;
SetupSceneViewInfoPooledRenderTargets(Views[0], &ViewInfoPooledRenderTargets);
FSSDSignalTextures InputSignal;
InputSignal.Textures[0] = Inputs.Color;
InputSignal.Textures[1] = Inputs.RayHitDistance;
FSSDConstantPixelDensitySettings Settings;
Settings.SignalProcessing = ESignalProcessing::DiffuseAndAmbientOcclusion;
Settings.InputResolutionFraction = Config.ResolutionFraction;
Settings.ReconstructionSamples = FMath::Clamp(CVarGIReconstructionSampleCount.GetValueOnRenderThread(), 1, kStackowiakMaxSampleCountPerSet);
Settings.PreConvolutionCount = CVarGIPreConvolutionCount.GetValueOnRenderThread();
Settings.bUseTemporalAccumulation = CVarGITemporalAccumulation.GetValueOnRenderThread() != 0;
Settings.HistoryConvolutionSampleCount = CVarGIHistoryConvolutionSampleCount.GetValueOnRenderThread();
Settings.HistoryConvolutionKernelSpreadFactor = CVarGIHistoryConvolutionKernelSpreadFactor.GetValueOnRenderThread();
Settings.MaxInputSPP = Config.RayCountPerPixel;
TArray<FSSDConstantPixelDensityViewSettings, TInlineAllocator<6>> ViewSettingsArray;
ViewSettingsArray.SetNum(Views.Num());
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
ViewSettingsArray[ViewIndex].FullResViewport = Views[ViewIndex].ViewRect;
}
Settings.ViewSettingsArray = ViewSettingsArray;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> PrevHistories;
TStaticArray<FScreenSpaceDenoiserHistory*, IScreenSpaceDenoiser::kMaxBatchSize> NewHistories;
PrevHistories[0] = &PreviousViewInfos->SkyLightHistory;
NewHistories[0] = Views[0].ViewState ? &Views[0].ViewState->PrevFrameViewInfo.SkyLightHistory : nullptr;
FSSDSignalTextures SignalOutput;
DenoiseSignalAtConstantPixelDensity(
GraphBuilder, Views, SceneTextures, ViewInfoPooledRenderTargets,
InputSignal, Settings,
PrevHistories,
NewHistories,
&SignalOutput);
FDiffuseIndirectOutputs GlobalIlluminationOutputs;
GlobalIlluminationOutputs.Color = SignalOutput.Textures[0];
return GlobalIlluminationOutputs;
}