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

3083 lines
126 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
DiaphragmDOFPasses.cpp: Implementations of all diaphragm DOF's passes.
=============================================================================*/
#include "PostProcess/DiaphragmDOF.h"
#include "StaticBoundShaderState.h"
#include "SceneUtils.h"
#include "PostProcess/SceneRenderTargets.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/PostProcessBokehDOF.h"
#include "SceneRenderTargetParameters.h"
#include "PostProcess/PostProcessing.h"
#include "DeferredShadingRenderer.h"
#include "PipelineStateCache.h"
#include "ScenePrivate.h"
#include "ClearQuad.h"
#include "SpriteIndexBuffer.h"
#include "PostProcess/TemporalAA.h"
#include "SceneTextureParameters.h"
#include "TranslucentRendering.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "ScreenPass.h"
// ---------------------------------------------------- Cvars
namespace
{
DECLARE_GPU_STAT(DepthOfField);
TAutoConsoleVariable<int32> CVarDOFGatherResDivisor(
TEXT("r.DOF.Gather.ResolutionDivisor"), 2,
TEXT("Selects the resolution divisor of the gather pass.\n")
TEXT(" 1: Do gathering pass at full resolution;\n")
TEXT(" 2: Do gathering pass at half resolution (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarAccumulatorQuality(
TEXT("r.DOF.Gather.AccumulatorQuality"),
1,
TEXT("Controles the quality of the gathering accumulator.\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarGatherDistinctionLimit(
TEXT("r.DOF.Gather.DistinctionLimit"),
0.0,
TEXT("Bokeh that are not scattered but exceed this distinction limit are discarded instead of gathered.\n")
TEXT("Distinction is calculcated based on perceived brightness and radius.\n")
TEXT("This is used to reduce noise in gathered bokeh, without drawing additional scattered bokeh.\n")
TEXT("This can cause bokeh to become darker than they should be, which is a compromise for noise reduction.\n")
TEXT("See also: r.DOF.Scatter.NeighborCompareMaxColor \n")
TEXT("Set to 0 to disable.\n")
TEXT("(default: 0)"),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarEnableGatherBokehSettings(
TEXT("r.DOF.Gather.EnableBokehSettings"),
1,
TEXT("Whether to applies bokeh settings on foreground and background gathering.\n")
TEXT(" 0: Disable;\n 1: Enable (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarPostFilteringMethod(
TEXT("r.DOF.Gather.PostfilterMethod"),
1,
TEXT("Method to use to post filter a gather pass.\n")
TEXT(" 0: None;\n")
TEXT(" 1: Per RGB channel median 3x3 (default);\n")
TEXT(" 2: Per RGB channel max 3x3."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarRingCount(
TEXT("r.DOF.Gather.RingCount"),
5,
TEXT("Number of rings for gathering kernels [[3; 5]]. Default to 5.\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarHybridScatterForegroundMode(
TEXT("r.DOF.Scatter.ForegroundCompositing"),
1,
TEXT("Compositing mode of the foreground hybrid scattering.\n")
TEXT(" 0: Disabled;\n")
TEXT(" 1: Additive (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarHybridScatterBackgroundMode(
TEXT("r.DOF.Scatter.BackgroundCompositing"),
2,
TEXT("Compositing mode of the background hybrid scattering.\n")
TEXT(" 0: Disabled;\n")
TEXT(" 1: Additive;\n")
TEXT(" 2: Gather occlusion (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarEnableScatterBokehSettings(
TEXT("r.DOF.Scatter.EnableBokehSettings"),
1,
TEXT("Whether to enable bokeh settings on scattering.\n")
TEXT(" 0: Disable;\n 1: Enable (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarScatterMinCocRadius(
TEXT("r.DOF.Scatter.MinCocRadius"),
3.0f,
TEXT("Minimal Coc radius required to be scattered (default = 3)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarScatterMaxSpriteRatio(
TEXT("r.DOF.Scatter.MaxSpriteRatio"),
0.1f,
TEXT("Maximum ratio of scattered pixel quad as sprite, usefull to control DOF's scattering upperbound. ")
TEXT(" 1 will allow to scatter 100% pixel quads, whereas 0.2 will only allow 20% (default = 0.1)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarEnableRecombineBokehSettings(
TEXT("r.DOF.Recombine.EnableBokehSettings"),
1,
TEXT("Whether to applies bokeh settings on slight out of focus done in recombine pass.\n")
TEXT(" 0: Disable;\n 1: Enable (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarRecombineQuality(
TEXT("r.DOF.Recombine.Quality"),
2,
TEXT("Configures the quality of the recombine pass.\n")
TEXT(" 0: No slight out of focus;\n")
TEXT(" 1: Slight out of focus 24spp;\n")
TEXT(" 2: Slight out of focus 32spp (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarMinimalFullresBlurRadius(
TEXT("r.DOF.Recombine.MinFullresBlurRadius"),
0.1f,
TEXT("Minimal blurring radius used in full resolution pixel width to actually do ")
TEXT("DOF when slight out of focus is enabled (default = 0.1)."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarDOFTemporalAAQuality(
TEXT("r.DOF.TemporalAAQuality"),
1,
TEXT("Quality of temporal AA pass done in DOF.\n")
TEXT(" 0: Faster but lower quality;")
TEXT(" 1: Higher quality pass (default)."),
ECVF_Scalability | ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarScatterNeighborCompareMaxColor(
TEXT("r.DOF.Scatter.NeighborCompareMaxColor"),
10,
TEXT("Threshold used when deciding if a pixel should get scattered if it has a neighbor that is already getting scattered. \n")
TEXT("The linear color of the pixel and its neighbors are compared. If the difference is lower than this threshold, it is not scattered. \n")
TEXT("Too low, and you may not scatter enough, causing quality issues. Too high you may scatter unnecessarily too much in highlights, causing overdraw. \n")
TEXT("See also: r.DOF.Gather.DistinctionLimit \n")
TEXT(" (Default: 10)."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarCoCBilateralFilterStrength(
TEXT("r.DOF.TAA.CoCBilateralFilterStrength"),
0.0,
TEXT("If 1, a bilateral filter based on the circle-of-confusion is used to reject history in the pre-DoF TAA pass.\n")
TEXT("If 0, this is disabled.\n")
TEXT("When enabled, you may get stronger noise on edges of objects that separate foreground and background.\n")
TEXT("When disabled, you may get stronger ghosting along those same edges.\n")
TEXT(" (Default: 0)."),
ECVF_RenderThreadSafe);
} // namespace
// ---------------------------------------------------- COMMON
bool DiaphragmDOF::IsSupported(const FStaticShaderPlatform ShaderPlatform)
{
// Only compile diaphragm DOF on platform it has been tested to ensure this is not blocking anyone else.
return FDataDrivenShaderPlatformInfo::GetSupportsDiaphragmDOF(ShaderPlatform);
}
namespace
{
/** Defines which layer to process. */
enum class EDiaphragmDOFLayerProcessing
{
// Foreground layer only.
ForegroundOnly,
// Foreground hole filling.
ForegroundHoleFilling,
// Background layer only.
BackgroundOnly,
// Both foreground and background layers.
ForegroundAndBackground,
// Slight out of focus layer.
SlightOutOfFocus,
MAX
};
/** Defines which layer to process. */
enum class EDiaphragmDOFPostfilterMethod
{
// Disable post filtering.
None,
// Per RGB channel median on 3x3 neighborhood.
RGBMedian3x3,
// Per RGB channel max on 3x3 neighborhood.
RGBMax3x3,
MAX
};
/** Modes to simulate a bokeh. */
enum class EDiaphragmDOFBokehSimulation
{
// No bokeh simulation.
Disabled,
// Symmetric bokeh (even number of blade).
SimmetricBokeh,
// Generic bokeh.
GenericBokeh,
MAX,
};
/** Dilate mode of the pass. */
enum class EDiaphragmDOFDilateCocMode
{
// One single dilate pass.
StandAlone,
// Dilate min foreground and max background coc radius.
MinForegroundAndMaxBackground,
// Dilate everything else from dilated min foreground and max background coc radius.
MinimalAbsoluteRadiuses,
MAX
};
/** Quality configurations for gathering passes. */
enum class EDiaphragmDOFGatherQuality
{
// Lower but faster accumulator.
LowQualityAccumulator,
// High quality accumulator.
HighQuality,
// High quality accumulator with hybrid scatter occlusion buffer output.
// TODO: distinct shader permutation dimension for hybrid scatter occlusion?
HighQualityWithHybridScatterOcclusion,
// High quality accumulator, with layered full disks and hybrid scatter occlusion.
Cinematic,
MAX,
};
/** Quality configurations for scattering passes. */
enum class EDiaphragmDOFScatterQuality
{
Medium,
High,
MAX,
};
/** Format of the LUT to generate. */
enum class EDiaphragmDOFBokehLUTFormat
{
// Look up table that stores a factor to transform a CocRadius to a BokehEdge distance.
// Used for scattering and low res focus gathering.
CocRadiusToBokehEdgeFactor,
// Look up table that stores Coc distance to compare against neighbor's CocRadius.
// Used exclusively for full res gathering in recombine pass.
FullResOffsetToCocDistance,
// Look up table to stores the gathering sample pos within the kernel.
// Used for low res back and foreground gathering.
GatherSamplePos,
MAX,
};
const int32 kDefaultGroupSize = 8;
/** Number of half res pixel are covered by a CocTile */
const int32 kCocTileSize = kDefaultGroupSize;
/** Resolution divisor of the Coc tiles. */
const int32 kMaxCocDilateSampleRadiusCount = 3;
/** Resolution divisor of the Coc tiles. */
const int32 kMaxMipLevelCount = 4;
/** Minimum number of ring. */
const int32 kMinGatheringRingCount = 3;
const int32 kMaxGatheringRingCount = 5;
/** Maximum number of ring for slight out of focus pass. Same as USH's MAX_RECOMBINE_ABS_COC_RADIUS. */
const int32 kMaxSlightOutOfFocusCocRadius = 3;
/** Maximum quality level of the recombine pass. */
const int32 kMaxRecombineQuality = 2;
/** Absolute minim coc radius required for a bokeh to be scattered */
const float kMinScatteringCocRadius = 3.0f;
FIntPoint CocTileGridSize(FIntPoint FullResSize)
{
uint32 TilesX = FMath::DivideAndRoundUp(FullResSize.X, kCocTileSize);
uint32 TilesY = FMath::DivideAndRoundUp(FullResSize.Y, kCocTileSize);
return FIntPoint(TilesX, TilesY);
}
/** Returns the lower res's viewport from a given view size. */
FIntRect GetLowerResViewport(const FIntRect& ViewRect, int32 ResDivisor)
{
check(ResDivisor >= 1);
check(FMath::IsPowerOfTwo(ResDivisor));
FIntRect DestViewport;
// All diaphragm DOF's lower res viewports are top left cornered to only do a min(SampleUV, MaxUV) when doing convolution.
DestViewport.Min = FIntPoint::ZeroValue;
DestViewport.Max.X = FMath::DivideAndRoundUp(ViewRect.Width(), ResDivisor);
DestViewport.Max.Y = FMath::DivideAndRoundUp(ViewRect.Height(), ResDivisor);
return DestViewport;
}
EDiaphragmDOFPostfilterMethod GetPostfilteringMethod()
{
int32 i = CVarPostFilteringMethod.GetValueOnRenderThread();
if (i >= 0 && i < int32(EDiaphragmDOFPostfilterMethod::MAX))
{
return EDiaphragmDOFPostfilterMethod(i);
}
return EDiaphragmDOFPostfilterMethod::None;
}
enum class EHybridScatterMode
{
Disabled,
Additive,
Occlusion,
};
const TCHAR* GetEventName(EDiaphragmDOFLayerProcessing e)
{
static const TCHAR* const kArray[] = {
TEXT("FgdOnly"),
TEXT("FgdFill"),
TEXT("BgdOnly"),
TEXT("Fgd&Bgd"),
TEXT("FocusOnly"),
};
int32 i = int32(e);
check(i < UE_ARRAY_COUNT(kArray));
return kArray[i];
}
const TCHAR* GetEventName(EDiaphragmDOFPostfilterMethod e)
{
static const TCHAR* const kArray[] = {
TEXT("Median3x3"),
TEXT("Max3x3"),
};
int32 i = int32(e) - 1;
check(i < UE_ARRAY_COUNT(kArray));
return kArray[i];
}
const TCHAR* GetEventName(EDiaphragmDOFBokehSimulation e)
{
static const TCHAR* const kArray[] = {
TEXT("None"),
TEXT("Symmetric"),
TEXT("Generic"),
};
int32 i = int32(e);
check(i < UE_ARRAY_COUNT(kArray));
return kArray[i];
}
const TCHAR* GetEventName(EDiaphragmDOFBokehLUTFormat e)
{
static const TCHAR* const kArray[] = {
TEXT("Scatter"),
TEXT("Recombine"),
TEXT("Gather"),
};
int32 i = int32(e);
check(i < UE_ARRAY_COUNT(kArray));
return kArray[i];
}
const TCHAR* GetEventName(EDiaphragmDOFGatherQuality e)
{
static const TCHAR* const kArray[] = {
TEXT("LowQ"),
TEXT("HighQ"),
TEXT("ScatterOcclusion"),
TEXT("Cinematic"),
};
int32 i = int32(e);
check(i < UE_ARRAY_COUNT(kArray));
return kArray[i];
}
const TCHAR* GetEventName(EDiaphragmDOFDilateCocMode e)
{
static const TCHAR* const kArray[] = {
TEXT("StandAlone"),
TEXT("MinMax"),
TEXT("MinAbs"),
};
int32 i = int32(e);
check(i < UE_ARRAY_COUNT(kArray));
return kArray[i];
}
// Returns X and Y for F(M) = saturate(M * X + Y) so that F(LowM) = 0 and F(HighM) = 1
FVector2f GenerateSaturatedAffineTransformation(float LowM, float HighM)
{
float X = 1.0f / (HighM - LowM);
return FVector2f(X, -X * LowM);
}
// Affine transformtations that always return 0 or 1.
const FVector2f kContantlyPassingAffineTransformation(0, 1);
const FVector2f kContantlyBlockingAffineTransformation(0, 0);
/** Base shader class for diaphragm DOF. */
class FDiaphragmDOFShader : public FGlobalShader
{
public:
class FAlphaChannelDim : SHADER_PERMUTATION_BOOL("DIM_ALPHA_CHANNEL");
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DiaphragmDOF::IsSupported(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
bool bSM5OrPreview = Parameters.Platform == SP_PCD3D_SM5 || FDataDrivenShaderPlatformInfo::GetIsPreviewPlatform(Parameters.Platform);
if (FDataDrivenShaderPlatformInfo::GetSupportsRealTypes(Parameters.Platform) == ERHIFeatureSupport::RuntimeGuaranteed)
{
OutEnvironment.CompilerFlags.Add(CFLAG_AllowRealTypes);
}
if (!bSM5OrPreview && FDataDrivenShaderPlatformInfo::GetSupportsWaveOperations(Parameters.Platform) != ERHIFeatureSupport::Unsupported)
{
OutEnvironment.SetDefine(TEXT("WAVE_OPS"), 1);
}
OutEnvironment.SetDefine(TEXT("COC_TILE_SIZE"), kCocTileSize);
// If on console, don't compile any dynamic CoC offset to avoid performance differences
const bool bIsConsole = FDataDrivenShaderPlatformInfo::GetIsConsole(Parameters.Platform);
OutEnvironment.SetDefine(TEXT("WITH_DYNAMIC_COC_OFFSET"), bIsConsole ? 0 : 1);
OutEnvironment.SetDefine(TEXT("MAX_MATTE_BOX_FLAGS"), (int)DiaphragmDOF::FPhysicalCocModel::MaxMatteBoxFlags);
}
FDiaphragmDOFShader() {}
FDiaphragmDOFShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{ }
};
} // namespace
// ---------------------------------------------------- Global resource
namespace
{
class FDOFGlobalResource : public FRenderResource
{
public:
// Index buffer to have 4 vertex shader invocation per scatter group that is the most efficient in therms of vertex processing
// using if RHI does not support rect list topology.
FSpriteIndexBuffer<16> ScatterIndexBuffer;
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
{
if (!GRHISupportsRectTopology)
{
ScatterIndexBuffer.InitRHI(RHICmdList);
}
}
virtual void ReleaseRHI() override
{
if (!GRHISupportsRectTopology)
{
ScatterIndexBuffer.ReleaseRHI();
}
}
};
TGlobalResource<FDOFGlobalResource> GDOFGlobalResource;
}
// ---------------------------------------------------- Shader permutation dimensions
namespace
{
class FDDOFDilateRadiusDim : SHADER_PERMUTATION_RANGE_INT("DIM_DILATE_RADIUS", 1, 3);
class FDDOFDilateModeDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_DILATE_MODE", EDiaphragmDOFDilateCocMode);
class FDDOFLayerProcessingDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_LAYER_PROCESSING", EDiaphragmDOFLayerProcessing);
class FDDOFGatherRingCountDim : SHADER_PERMUTATION_RANGE_INT("DIM_GATHER_RING_COUNT", kMinGatheringRingCount, kMaxGatheringRingCount - kMinGatheringRingCount + 1);
class FDDOFGatherQualityDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_GATHER_QUALITY", EDiaphragmDOFGatherQuality);
class FDDOFPostfilterMethodDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_POSTFILTER_METHOD", EDiaphragmDOFPostfilterMethod);
class FDDOFRGBColorBufferDim : SHADER_PERMUTATION_BOOL("DIM_RGB_COLOR_BUFFER");
class FDDOFBokehSimulationDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_BOKEH_SIMULATION", EDiaphragmDOFBokehSimulation);
class FDDOFScatterOcclusionDim : SHADER_PERMUTATION_BOOL("DIM_SCATTER_OCCLUSION");
} // namespace
// ---------------------------------------------------- Shared shader parameters
namespace
{
struct FDOFGatherInputDescs
{
FRDGTextureDesc SceneColor;
FRDGTextureDesc SeparateCoc;
};
BEGIN_SHADER_PARAMETER_STRUCT(FDOFGatherInputTextures, )
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColor)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SeparateCoc)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FDOFGatherInputSRVs, )
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SceneColor)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SeparateCoc)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FDOFGatherInputUAVs, )
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, SceneColor)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, SeparateCoc)
END_SHADER_PARAMETER_STRUCT()
FDOFGatherInputTextures CreateTextures(FRDGBuilder& GraphBuilder, const FDOFGatherInputDescs& Descs, const TCHAR* DebugName)
{
FDOFGatherInputTextures Textures;
Textures.SceneColor = GraphBuilder.CreateTexture(Descs.SceneColor, DebugName);
if (Descs.SeparateCoc.Format != PF_Unknown)
{
Textures.SeparateCoc = GraphBuilder.CreateTexture(Descs.SeparateCoc, DebugName);
}
return Textures;
}
FDOFGatherInputSRVs CreateSRVs(FRDGBuilder& GraphBuilder, const FDOFGatherInputTextures& Textures, uint8 MipLevel = 0)
{
FDOFGatherInputSRVs SRVs;
SRVs.SceneColor = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(Textures.SceneColor, MipLevel));
if (Textures.SeparateCoc)
{
SRVs.SeparateCoc = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(Textures.SeparateCoc, MipLevel));
}
return SRVs;
}
FDOFGatherInputUAVs CreateUAVs(FRDGBuilder& GraphBuilder, const FDOFGatherInputTextures& Textures, uint8 MipLevel = 0)
{
FDOFGatherInputUAVs UAVs;
UAVs.SceneColor = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(Textures.SceneColor, MipLevel));
if (Textures.SeparateCoc)
{
UAVs.SeparateCoc = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(Textures.SeparateCoc, MipLevel));
}
return UAVs;
}
struct FDOFConvolutionDescs
{
FRDGTextureDesc SceneColor;
FRDGTextureDesc SeparateAlpha;
};
BEGIN_SHADER_PARAMETER_STRUCT(FDOFConvolutionTextures, )
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColor)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SeparateAlpha)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FDOFConvolutionUAVs, )
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, SceneColor)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, SeparateAlpha)
END_SHADER_PARAMETER_STRUCT()
FDOFConvolutionTextures CreateTextures(FRDGBuilder& GraphBuilder, const FDOFConvolutionDescs& Descs, const TCHAR* DebugName)
{
FDOFConvolutionTextures Textures;
Textures.SceneColor = GraphBuilder.CreateTexture(Descs.SceneColor, DebugName);
if (Descs.SeparateAlpha.Format != PF_Unknown)
{
Textures.SeparateAlpha = GraphBuilder.CreateTexture(Descs.SeparateAlpha, DebugName);
}
return Textures;
}
FDOFConvolutionUAVs CreateUAVs(FRDGBuilder& GraphBuilder, const FDOFConvolutionTextures& Textures)
{
FDOFConvolutionUAVs UAVs;
UAVs.SceneColor = GraphBuilder.CreateUAV(Textures.SceneColor);
if (Textures.SeparateAlpha)
{
UAVs.SeparateAlpha = GraphBuilder.CreateUAV(Textures.SeparateAlpha);
}
return UAVs;
}
struct FDOFTileClassificationDescs
{
FRDGTextureDesc Foreground;
FRDGTextureDesc Background;
};
BEGIN_SHADER_PARAMETER_STRUCT(FDOFTileClassificationTextures, )
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, Foreground)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, Background)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FDOFTileClassificationUAVs, )
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, Foreground)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, Background)
END_SHADER_PARAMETER_STRUCT()
FDOFTileClassificationTextures CreateTextures(FRDGBuilder& GraphBuilder, const FDOFTileClassificationDescs& Descs, const TCHAR* DebugNames[2])
{
FDOFTileClassificationTextures Textures;
Textures.Foreground = GraphBuilder.CreateTexture(Descs.Foreground, DebugNames[0]);
Textures.Background = GraphBuilder.CreateTexture(Descs.Background, DebugNames[1]);
return Textures;
}
FDOFTileClassificationUAVs CreateUAVs(FRDGBuilder& GraphBuilder, const FDOFTileClassificationTextures& Textures)
{
FDOFTileClassificationUAVs UAVs;
UAVs.Foreground = GraphBuilder.CreateUAV(Textures.Foreground);
UAVs.Background = GraphBuilder.CreateUAV(Textures.Background);
return UAVs;
}
BEGIN_SHADER_PARAMETER_STRUCT(FDOFCommonShaderParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
END_SHADER_PARAMETER_STRUCT()
BEGIN_SHADER_PARAMETER_STRUCT(FDOFTileDecisionParameters, )
SHADER_PARAMETER(float, MinGatherRadius)
SHADER_PARAMETER(float, SlightOutOfFocusRadiusBoundary)
END_SHADER_PARAMETER_STRUCT()
} // namespace
namespace DiaphragmDOF
{
void SetCocModelParameters(
FRDGBuilder& GraphBuilder,
FDOFCocModelShaderParameters* OutParameters,
const FPhysicalCocModel& CocModel,
float CocRadiusBasis)
{
OutParameters->CocInfinityRadius = CocRadiusBasis * CocModel.InfinityBackgroundCocRadius;
OutParameters->CocInFocusRadius = CocRadiusBasis * CocModel.InFocusRadius;
OutParameters->bCocEnableDynamicRadiusOffset = CocModel.bEnableDynamicOffset;
OutParameters->CocMinRadius = CocRadiusBasis * CocModel.MinForegroundCocRadius;
OutParameters->CocMaxRadius = CocRadiusBasis * CocModel.MaxBackgroundCocRadius;
OutParameters->CocSqueeze = CocModel.Squeeze;
OutParameters->CocInvSqueeze = 1.0f / CocModel.Squeeze;
OutParameters->DepthBlurRadius = CocRadiusBasis * CocModel.MaxDepthBlurRadius;
OutParameters->DepthBlurExponent = CocModel.DepthBlurExponent;
if (CocModel.DynamicRadiusOffsetLUT)
{
OutParameters->DynamicRadiusOffsetLUT = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(CocModel.DynamicRadiusOffsetLUT, TEXT("DynamicRadiusOffsetLUT")));
}
else
{
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Get(GraphBuilder);
OutParameters->DynamicRadiusOffsetLUT = SystemTextures.Black;
}
OutParameters->DynamicRadiusOffsetLUTSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Wrap>::GetRHI();
}
}
// ---------------------------------------------------- Shaders
namespace
{
/** Returns whether hybrid scattering is supported. */
static FORCEINLINE bool SupportsHybridScatter(EShaderPlatform ShaderPlatform)
{
return !!FDataDrivenShaderPlatformInfo::GetSupportsDOFHybridScattering(ShaderPlatform);
}
/** Returns the number maximum number of ring available. */
static FORCEINLINE int32 MaxGatheringRingCount(EShaderPlatform ShaderPlatform)
{
if (IsPCPlatform(ShaderPlatform))
{
return kMaxGatheringRingCount;
}
return 4;
}
/** Returns whether the shader for bokeh simulation are compiled. */
static FORCEINLINE bool SupportsBokehSimmulation(EShaderPlatform ShaderPlatform)
{
// Shaders of gathering pass are big, so only compile them on desktop.
return IsPCPlatform(ShaderPlatform);
}
/** Returns whether separate coc buffer is supported. */
static FORCEINLINE bool SupportsRGBColorBuffer(EShaderPlatform ShaderPlatform)
{
// There is no point when alpha channel is supported because needs 4 channel anyway for fast gathering tiles.
if (IsPostProcessingWithAlphaChannelSupported())
{
return false;
}
// There is high number of UAV to write in reduce pass.
return FDataDrivenShaderPlatformInfo::GetSupportsRGBColorBuffer(ShaderPlatform);
}
class FDiaphragmDOFSetupCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFSetupCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFSetupCS, FDiaphragmDOFShader);
class FOutputResDivisor : SHADER_PERMUTATION_INT("DIM_OUTPUT_RES_DIVISOR", 3);
using FPermutationDomain = TShaderPermutationDomain<FDiaphragmDOFShader::FAlphaChannelDim, FOutputResDivisor>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(DiaphragmDOF::FDOFCocModelShaderParameters, CocModel)
SHADER_PARAMETER(FVector2f, CocRadiusBasis) // TODO: decompose
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthTexture)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, Output0)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, Output1)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, Output2)
END_SHADER_PARAMETER_STRUCT()
};
class FDiaphragmDOFCocFlattenCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFCocFlattenCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFCocFlattenCS, FDiaphragmDOFShader);
class FDoCocGather4 : SHADER_PERMUTATION_BOOL("DIM_DO_COC_GATHER4");
using FPermutationDomain = TShaderPermutationDomain<FDiaphragmDOFShader::FAlphaChannelDim, FDoCocGather4>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntRect, ViewportRect)
SHADER_PARAMETER(FVector2f, ThreadIdToBufferUV)
SHADER_PARAMETER(FVector2f, MaxBufferUV)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER_STRUCT(FDOFGatherInputTextures, GatherInput)
SHADER_PARAMETER_STRUCT(FDOFTileClassificationUAVs, TileOutput)
END_SHADER_PARAMETER_STRUCT()
static_assert(ISceneViewFamilyScreenPercentage::kMinTAAUpsampleResolutionFraction == 0.5f,
"Gather4 shader permutation assumes with min TAAU screen percentage = 50%.");
static_assert(ISceneViewFamilyScreenPercentage::kMaxTAAUpsampleResolutionFraction == 2.0f,
"Gather4 shader permutation assumes with max TAAU screen percentage = 200%.");
};
class FDiaphragmDOFCocDilateCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFCocDilateCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFCocDilateCS, FDiaphragmDOFShader);
using FPermutationDomain = TShaderPermutationDomain<FDDOFDilateRadiusDim, FDDOFDilateModeDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntRect, ViewportRect)
SHADER_PARAMETER(int32, SampleOffsetMultipler)
SHADER_PARAMETER(float, fSampleOffsetMultipler)
SHADER_PARAMETER(float, CocRadiusToBucketDistanceUpperBound)
SHADER_PARAMETER(float, BucketDistanceToCocRadius)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER_STRUCT(FDOFTileClassificationTextures, TileInput)
SHADER_PARAMETER_STRUCT(FDOFTileClassificationTextures, DilatedTileMinMax)
SHADER_PARAMETER_STRUCT(FDOFTileClassificationUAVs, TileOutput)
END_SHADER_PARAMETER_STRUCT()
};
class FDiaphragmDOFDownsampleCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFDownsampleCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFDownsampleCS, FDiaphragmDOFShader);
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
if (!SupportsHybridScatter(Parameters.Platform))
{
return false;
}
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
using FPermutationDomain = TShaderPermutationDomain<FDiaphragmDOFShader::FAlphaChannelDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntRect, ViewportRect)
SHADER_PARAMETER(FVector2f, MaxBufferUV)
SHADER_PARAMETER(float, OutputCocRadiusMultiplier)
SHADER_PARAMETER(FVector4f, GatherInputSize)
SHADER_PARAMETER_STRUCT(FDOFGatherInputTextures, GatherInput)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER_STRUCT(FDOFGatherInputUAVs, OutDownsampledGatherInput)
END_SHADER_PARAMETER_STRUCT()
};
class FDiaphragmDOFReduceCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFReduceCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFReduceCS, FDiaphragmDOFShader);
class FReduceMipCount : SHADER_PERMUTATION_RANGE_INT("DIM_REDUCE_MIP_COUNT", 2, 3);
class FHybridScatterForeground : SHADER_PERMUTATION_BOOL("DIM_HYBRID_SCATTER_FGD");
class FHybridScatterBackground : SHADER_PERMUTATION_BOOL("DIM_HYBRID_SCATTER_BGD");
class FDisableOutputMip0 : SHADER_PERMUTATION_BOOL("DIM_DISABLE_OUTPUT_MIP0");
using FPermutationDomain = TShaderPermutationDomain<
FDiaphragmDOFShader::FAlphaChannelDim,
FReduceMipCount,
FHybridScatterForeground,
FHybridScatterBackground,
FDDOFRGBColorBufferDim,
FDisableOutputMip0>;
/** Returns the number of mip level the reduce pass is able to output. */
static int32 GetMaxReductionMipLevelCount(bool bSupportAlphaChannel)
{
// Can only have 8 UAVs, but need to output 3x UAV for hybird scatter + 2 UAV per mips for RGBA + SeparateCoc.
return bSupportAlphaChannel ? 2 : kMaxMipLevelCount;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
const int32 MaxMipLevelCount = GetMaxReductionMipLevelCount(PermutationVector.Get<FDiaphragmDOFShader::FAlphaChannelDim>());
if (PermutationVector.Get<FDisableOutputMip0>())
{
// Hybrid scatter is done only on first reduction, that needs to output mip0
if (PermutationVector.Get<FHybridScatterForeground>() || PermutationVector.Get<FHybridScatterBackground>())
{
return false;
}
// Do not output mip level that are more than supported.
if (PermutationVector.Get<FReduceMipCount>() > (MaxMipLevelCount + 1))
{
return false;
}
}
else
{
// Do not output mip level that are more than supported.
if (PermutationVector.Get<FReduceMipCount>() > MaxMipLevelCount)
{
return false;
}
}
// Do not compile storing Coc independently of RGB if not supported.
if (PermutationVector.Get<FDDOFRGBColorBufferDim>() && !SupportsRGBColorBuffer(Parameters.Platform))
{
return false;
}
if (!SupportsHybridScatter(Parameters.Platform))
{
if (PermutationVector.Get<FHybridScatterForeground>() || PermutationVector.Get<FHybridScatterBackground>())
{
return false;
}
}
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntRect, ViewportRect)
SHADER_PARAMETER(FVector2f, MaxInputBufferUV)
SHADER_PARAMETER(FVector4f, ScatteringViewportSize)
SHADER_PARAMETER(float, ScatteringScaling)
SHADER_PARAMETER(int32, MaxScatteringGroupCount)
SHADER_PARAMETER(float, PreProcessingToProcessingCocRadiusFactor)
SHADER_PARAMETER(float, BokehGatherDistinctionLimit)
SHADER_PARAMETER(float, MinScatteringCocRadius)
SHADER_PARAMETER(float, NeighborCompareMaxColor)
SHADER_PARAMETER_STRUCT_INCLUDE(DiaphragmDOF::FDOFCocModelShaderParameters, CocModel)
SHADER_PARAMETER(FVector2f, SensorSize)
SHADER_PARAMETER(float, Aperture)
SHADER_PARAMETER(float, LensImageDistance)
SHADER_PARAMETER(float, BarrelRadius)
SHADER_PARAMETER(float, BarrelLength)
SHADER_PARAMETER_ARRAY(FUintVector4, MatteBoxPlanes, [DiaphragmDOF::FPhysicalCocModel::MaxMatteBoxFlags])
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, EyeAdaptationBuffer)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER(FVector4f, GatherInputSize)
SHADER_PARAMETER_STRUCT(FDOFGatherInputSRVs, GatherInput)
SHADER_PARAMETER(FVector4f, QuarterResGatherInputSize)
SHADER_PARAMETER_STRUCT(FDOFGatherInputTextures, QuarterResGatherInput)
SHADER_PARAMETER_STRUCT_ARRAY(FDOFGatherInputUAVs, OutputMips, [kMaxMipLevelCount])
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, OutScatterDrawIndirectParameters)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<float4>, OutForegroundScatterDrawList)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<float4>, OutBackgroundScatterDrawList)
END_SHADER_PARAMETER_STRUCT()
};
class FDiaphragmDOFScatterGroupPackCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFScatterGroupPackCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFScatterGroupPackCS, FDiaphragmDOFShader);
using FPermutationDomain = TShaderPermutationDomain<
FDiaphragmDOFReduceCS::FHybridScatterForeground,
FDiaphragmDOFReduceCS::FHybridScatterBackground>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
if (!SupportsHybridScatter(Parameters.Platform))
{
return false;
}
FPermutationDomain PermutationVector(Parameters.PermutationId);
// This shader is used there is at least foreground and/or background to scatter.
if (!PermutationVector.Get<FDiaphragmDOFReduceCS::FHybridScatterForeground>() &&
!PermutationVector.Get<FDiaphragmDOFReduceCS::FHybridScatterBackground>())
{
return false;
}
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(int32, MaxScatteringGroupCount)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, OutScatterDrawIndirectParameters)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<float4>, OutForegroundScatterDrawList)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<float4>, OutBackgroundScatterDrawList)
END_SHADER_PARAMETER_STRUCT()
};
class FDiaphragmDOFBuildBokehLUTCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFBuildBokehLUTCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFBuildBokehLUTCS, FDiaphragmDOFShader);
class FBokehSimulationDim : SHADER_PERMUTATION_BOOL("DIM_ROUND_BLADES");
class FLUTFormatDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_LUT_FORMAT", EDiaphragmDOFBokehLUTFormat);
using FPermutationDomain = TShaderPermutationDomain<FBokehSimulationDim, FLUTFormatDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(uint32, BladeCount)
SHADER_PARAMETER(float, DiaphragmRotation)
SHADER_PARAMETER(float, CocRadiusToCircumscribedRadius)
SHADER_PARAMETER(float, CocRadiusToIncircleRadius)
SHADER_PARAMETER(float, DiaphragmBladeRadius)
SHADER_PARAMETER(float, DiaphragmBladeCenterOffset)
SHADER_PARAMETER(float, CocSqueeze)
SHADER_PARAMETER(float, CocInvSqueeze)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, BokehLUTOutput)
END_SHADER_PARAMETER_STRUCT()
};
class FDiaphragmDOFGatherCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFGatherCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFGatherCS, FDiaphragmDOFShader);
using FPermutationDomain = TShaderPermutationDomain<
FDiaphragmDOFShader::FAlphaChannelDim,
FDDOFLayerProcessingDim,
FDDOFGatherRingCountDim,
FDDOFBokehSimulationDim,
FDDOFGatherQualityDim,
FDDOFRGBColorBufferDim>;
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
// There is a lot of permutation, so no longer compile permutation.
if (1)
{
// Alway simulate bokeh generically.
if (PermutationVector.Get<FDDOFBokehSimulationDim>() == EDiaphragmDOFBokehSimulation::SimmetricBokeh)
{
PermutationVector.Set<FDDOFBokehSimulationDim>(EDiaphragmDOFBokehSimulation::GenericBokeh);
}
}
return PermutationVector;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
// Do not compile this permutation if we know this is going to be remapped.
if (RemapPermutation(PermutationVector) != PermutationVector)
{
return false;
}
// Some platforms might be to slow for even considering large number of gathering samples.
if (PermutationVector.Get<FDDOFGatherRingCountDim>() > MaxGatheringRingCount(Parameters.Platform))
{
return false;
}
// Do not compile storing Coc independently of RGB.
if (PermutationVector.Get<FDDOFRGBColorBufferDim>() && !SupportsRGBColorBuffer(Parameters.Platform))
{
return false;
}
// No point compiling gather pass with hybrid scatter occlusion if the shader platform doesn't support if.
if (!SupportsHybridScatter(Parameters.Platform) &&
PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::HighQualityWithHybridScatterOcclusion) return false;
// Do not compile bokeh simulation shaders on platform that couldn't handle them anyway.
if (!SupportsBokehSimmulation(Parameters.Platform) &&
PermutationVector.Get<FDDOFBokehSimulationDim>() != EDiaphragmDOFBokehSimulation::Disabled) return false;
if (PermutationVector.Get<FDDOFLayerProcessingDim>() == EDiaphragmDOFLayerProcessing::ForegroundOnly)
{
// Foreground does not support CocVariance output yet.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::HighQualityWithHybridScatterOcclusion) return false;
// Storing Coc independently of RGB is only supported for low gathering quality.
if (PermutationVector.Get<FDDOFRGBColorBufferDim>() &&
PermutationVector.Get<FDDOFGatherQualityDim>() != EDiaphragmDOFGatherQuality::LowQualityAccumulator)
return false;
}
else if (PermutationVector.Get<FDDOFLayerProcessingDim>() == EDiaphragmDOFLayerProcessing::ForegroundHoleFilling)
{
// Foreground hole filling does not need to output CocVariance, since this is the job of foreground pass.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::HighQualityWithHybridScatterOcclusion) return false;
// Foreground hole filling doesn't have lower quality accumulator.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::LowQualityAccumulator) return false;
// Foreground hole filling doesn't need cinematic quality.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::Cinematic) return false;
// No bokeh simulation on hole filling, always use euclidian closest distance to compute opacity alpha channel.
if (PermutationVector.Get<FDDOFBokehSimulationDim>() != EDiaphragmDOFBokehSimulation::Disabled) return false;
// Storing Coc independently of RGB is only supported for RecombineQuality == 0.
if (PermutationVector.Get<FDDOFRGBColorBufferDim>())
return false;
}
else if (PermutationVector.Get<FDDOFLayerProcessingDim>() == EDiaphragmDOFLayerProcessing::SlightOutOfFocus)
{
// Slight out of focus don't need to output CocVariance since there is no hybrid scattering.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::HighQualityWithHybridScatterOcclusion) return false;
// Slight out of focus filling can't have lower quality accumulator since it needs to brute force the focus areas.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::LowQualityAccumulator) return false;
// Slight out of focus doesn't have cinematic quality, yet.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::Cinematic) return false;
// Number of rings is dynamic in the shader.
if (PermutationVector.Get<FDDOFGatherRingCountDim>() != kMinGatheringRingCount) return false;
// Storing Coc independently of RGB is only supported for RecombineQuality == 0.
if (PermutationVector.Get<FDDOFRGBColorBufferDim>()) return false;
}
else if (PermutationVector.Get<FDDOFLayerProcessingDim>() == EDiaphragmDOFLayerProcessing::BackgroundOnly)
{
// There is no performance point doing high quality gathering without scattering occlusion.
if (PermutationVector.Get<FDDOFGatherQualityDim>() == EDiaphragmDOFGatherQuality::HighQuality) return false;
// Storing Coc independently of RGB is only supported for low gathering quality.
if (PermutationVector.Get<FDDOFRGBColorBufferDim>() &&
PermutationVector.Get<FDDOFGatherQualityDim>() != EDiaphragmDOFGatherQuality::LowQualityAccumulator)
return false;
}
else if (PermutationVector.Get<FDDOFLayerProcessingDim>() == EDiaphragmDOFLayerProcessing::ForegroundAndBackground)
{
// Gathering foreground and backrgound at same time is not supported yet.
return false;
}
else
{
check(0);
}
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FDiaphragmDOFShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
// This shader takes a very long time to compile with FXC, so we pre-compile it with DXC first and then forward the optimized HLSL to FXC.
OutEnvironment.CompilerFlags.Add(CFLAG_PrecompileWithDXC);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FVector4f, ViewportSize)
SHADER_PARAMETER(FIntRect, ViewportRect)
SHADER_PARAMETER(FVector2f, TemporalJitterPixels)
SHADER_PARAMETER(FVector2f, DispatchThreadIdToInputBufferUV)
SHADER_PARAMETER(FVector2f, ConsiderCocRadiusAffineTransformation0)
SHADER_PARAMETER(FVector2f, ConsiderCocRadiusAffineTransformation1)
SHADER_PARAMETER(FVector2f, ConsiderAbsCocRadiusAffineTransformation)
SHADER_PARAMETER(FVector2f, InputBufferUVToOutputPixel)
SHADER_PARAMETER(float, MipBias)
SHADER_PARAMETER(float, MaxRecombineAbsCocRadius)
SHADER_PARAMETER(float, CocSqueeze)
SHADER_PARAMETER(float, CocInvSqueeze)
SHADER_PARAMETER(float, Petzval)
SHADER_PARAMETER(float, PetzvalFalloffPower)
SHADER_PARAMETER(FVector2f, PetzvalExclusionBoxExtents)
SHADER_PARAMETER(float, PetzvalExclusionBoxRadius)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFTileDecisionParameters, TileDecisionParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER(FVector4f, GatherInputSize)
SHADER_PARAMETER(FVector2f, GatherInputViewportSize)
SHADER_PARAMETER_STRUCT(FDOFGatherInputTextures, GatherInput)
SHADER_PARAMETER_STRUCT(FDOFTileClassificationTextures, TileClassification)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, BokehLUT)
SHADER_PARAMETER_STRUCT(FDOFConvolutionUAVs, ConvolutionOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, ScatterOcclusionOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, DebugOutput)
END_SHADER_PARAMETER_STRUCT()
}; // class FDiaphragmDOFGatherCS
class FDiaphragmDOFPostfilterCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFPostfilterCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFPostfilterCS, FDiaphragmDOFShader);
class FTileOptimization : SHADER_PERMUTATION_BOOL("DIM_TILE_PERMUTATION");
using FPermutationDomain = TShaderPermutationDomain<
FDiaphragmDOFShader::FAlphaChannelDim,
FDDOFLayerProcessingDim,
FDDOFPostfilterMethodDim,
FTileOptimization>;
static FPermutationDomain RemapPermutationVector(FPermutationDomain PermutationVector)
{
// Tile permutation optimisation is only for Max3x3 post filtering.
if (PermutationVector.Get<FDDOFPostfilterMethodDim>() != EDiaphragmDOFPostfilterMethod::RGBMax3x3)
PermutationVector.Set<FTileOptimization>(false);
return PermutationVector;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (RemapPermutationVector(PermutationVector) != PermutationVector) return false;
if (PermutationVector.Get<FDDOFPostfilterMethodDim>() == EDiaphragmDOFPostfilterMethod::None) return false;
if (PermutationVector.Get<FDDOFLayerProcessingDim>() != EDiaphragmDOFLayerProcessing::ForegroundOnly &&
PermutationVector.Get<FDDOFLayerProcessingDim>() != EDiaphragmDOFLayerProcessing::BackgroundOnly) return false;
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntRect, ViewportRect)
SHADER_PARAMETER(FVector2f, MaxInputBufferUV)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFTileDecisionParameters, TileDecisionParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER(FVector4f, ConvolutionInputSize)
SHADER_PARAMETER_STRUCT(FDOFConvolutionTextures, ConvolutionInput)
SHADER_PARAMETER_STRUCT(FDOFTileClassificationTextures, TileClassification)
SHADER_PARAMETER_STRUCT(FDOFConvolutionUAVs, ConvolutionOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, DebugOutput)
END_SHADER_PARAMETER_STRUCT()
}; // class FDiaphragmDOFPostfilterCS
BEGIN_SHADER_PARAMETER_STRUCT(FDOFHybridScatterParameters, )
SHADER_PARAMETER(FVector4f, ViewportSize)
SHADER_PARAMETER(float, CocRadiusToCircumscribedRadius)
SHADER_PARAMETER(float, ScatteringScaling)
SHADER_PARAMETER(float, Petzval)
SHADER_PARAMETER(float, PetzvalFalloffPower)
SHADER_PARAMETER(FVector2f, PetzvalExclusionBoxExtents)
SHADER_PARAMETER(float, PetzvalExclusionBoxRadius)
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(DiaphragmDOF::FDOFCocModelShaderParameters, CocModel)
SHADER_PARAMETER(FVector4f, ScatterOcclusionSize)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ScatterOcclusion)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, BokehLUT)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, ScatterDrawList)
RDG_BUFFER_ACCESS(IndirectDrawParameter, ERHIAccess::IndirectArgs)
SHADER_PARAMETER(FVector2f, SensorSize)
SHADER_PARAMETER(float, Aperture)
SHADER_PARAMETER(float, LensImageDistance)
SHADER_PARAMETER(float, BarrelRadius)
SHADER_PARAMETER(float, BarrelLength)
SHADER_PARAMETER_ARRAY(FUintVector4, MatteBoxPlanes, [DiaphragmDOF::FPhysicalCocModel::MaxMatteBoxFlags])
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
class FDDOFScatterQualityDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_SCATTER_QUALITY", EDiaphragmDOFScatterQuality);
class FDiaphragmDOFHybridScatterVS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFHybridScatterVS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFHybridScatterVS, FDiaphragmDOFShader);
using FPermutationDomain = TShaderPermutationDomain<FDDOFScatterQualityDim>;
using FParameters = FDOFHybridScatterParameters;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
if (!SupportsHybridScatter(Parameters.Platform))
{
return false;
}
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FDiaphragmDOFShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SCATTER_QUALITY_MEDIUM"), (int)EDiaphragmDOFScatterQuality::Medium);
OutEnvironment.SetDefine(TEXT("SCATTER_QUALITY_HIGH"), (int)EDiaphragmDOFScatterQuality::High);
}
};
class FDiaphragmDOFHybridScatterPS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFHybridScatterPS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFHybridScatterPS, FDiaphragmDOFShader);
class FBokehSimulationDim : SHADER_PERMUTATION_BOOL("DIM_BOKEH_SIMULATION");
using FPermutationDomain = TShaderPermutationDomain<FDDOFLayerProcessingDim, FBokehSimulationDim, FDDOFScatterOcclusionDim, FDDOFScatterQualityDim>;
using FParameters = FDOFHybridScatterParameters;
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
// Pixel shader are exactly the same between foreground and background when there is no bokeh LUT.
if (PermutationVector.Get<FDDOFLayerProcessingDim>() == EDiaphragmDOFLayerProcessing::BackgroundOnly &&
!PermutationVector.Get<FBokehSimulationDim>())
{
PermutationVector.Set<FDDOFLayerProcessingDim>(EDiaphragmDOFLayerProcessing::ForegroundOnly);
}
return PermutationVector;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
if (!SupportsHybridScatter(Parameters.Platform))
{
return false;
}
FPermutationDomain PermutationVector(Parameters.PermutationId);
// Do not compile this permutation if it gets remapped at runtime.
if (RemapPermutation(PermutationVector) != PermutationVector)
{
return false;
}
if (PermutationVector.Get<FDDOFLayerProcessingDim>() != EDiaphragmDOFLayerProcessing::ForegroundOnly &&
PermutationVector.Get<FDDOFLayerProcessingDim>() != EDiaphragmDOFLayerProcessing::BackgroundOnly) return false;
if (PermutationVector.Get<FDDOFScatterQualityDim>() != EDiaphragmDOFScatterQuality::Medium &&
PermutationVector.Get<FDDOFScatterQualityDim>() != EDiaphragmDOFScatterQuality::High) return false;
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FDiaphragmDOFShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SCATTER_QUALITY_MEDIUM"), (int)EDiaphragmDOFScatterQuality::Medium);
OutEnvironment.SetDefine(TEXT("SCATTER_QUALITY_HIGH"), (int)EDiaphragmDOFScatterQuality::High);
}
};
class FDiaphragmDOFRecombineCS : public FDiaphragmDOFShader
{
DECLARE_GLOBAL_SHADER(FDiaphragmDOFRecombineCS);
SHADER_USE_PARAMETER_STRUCT(FDiaphragmDOFRecombineCS, FDiaphragmDOFShader);
class FQualityDim : SHADER_PERMUTATION_INT("DIM_QUALITY", 3);
using FPermutationDomain = TShaderPermutationDomain<
FDiaphragmDOFShader::FAlphaChannelDim,
FDDOFLayerProcessingDim,
FDDOFBokehSimulationDim,
FQualityDim>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FDDOFLayerProcessingDim>() != EDiaphragmDOFLayerProcessing::ForegroundOnly &&
PermutationVector.Get<FDDOFLayerProcessingDim>() != EDiaphragmDOFLayerProcessing::BackgroundOnly &&
PermutationVector.Get<FDDOFLayerProcessingDim>() != EDiaphragmDOFLayerProcessing::ForegroundAndBackground) return false;
// Do not compile bokeh simulation shaders on platform that couldn't handle them anyway.
if (!SupportsBokehSimmulation(Parameters.Platform) &&
PermutationVector.Get<FDDOFBokehSimulationDim>() != EDiaphragmDOFBokehSimulation::Disabled) return false;
return FDiaphragmDOFShader::ShouldCompilePermutation(Parameters);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FDOFCommonShaderParameters, CommonParameters)
SHADER_PARAMETER_STRUCT_INCLUDE(DiaphragmDOF::FDOFCocModelShaderParameters, CocModel)
SHADER_PARAMETER(FIntRect, ViewportRect)
SHADER_PARAMETER(FVector4f, ViewportSize)
SHADER_PARAMETER(FScreenTransform, DispatchThreadIdToDOFBufferUV)
SHADER_PARAMETER(FVector2f, DOFBufferUVMax)
SHADER_PARAMETER(FVector4f, SeparateTranslucencyBilinearUVMinMax)
SHADER_PARAMETER(int32, SeparateTranslucencyUpscaling)
SHADER_PARAMETER(float, EncodedCocRadiusToRecombineCocRadius)
SHADER_PARAMETER(float, MaxRecombineAbsCocRadius)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, BokehLUT)
// Full res textures.
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorInput)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneSeparateCoc)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneSeparateTranslucency)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneSeparateTranslucencyModulateColor)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, LowResDepthTexture)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, FullResDepthTexture)
// Half res convolution textures.
SHADER_PARAMETER(FVector4f, ConvolutionInputSize)
SHADER_PARAMETER_STRUCT(FDOFConvolutionTextures, ForegroundConvolution)
SHADER_PARAMETER_STRUCT(FDOFConvolutionTextures, ForegroundHoleFillingConvolution)
SHADER_PARAMETER_STRUCT(FDOFConvolutionTextures, SlightOutOfFocusConvolution)
SHADER_PARAMETER_STRUCT(FDOFConvolutionTextures, BackgroundConvolution)
SHADER_PARAMETER(FVector2f, SeparateTranslucencyTextureLowResExtentInverse)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, SceneColorOutput)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, DebugOutput)
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFSetupCS, "/Engine/Private/DiaphragmDOF/DOFSetup.usf", "SetupCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFCocFlattenCS, "/Engine/Private/DiaphragmDOF/DOFCocTileFlatten.usf", "CocFlattenMainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFCocDilateCS, "/Engine/Private/DiaphragmDOF/DOFCocTileDilate.usf", "CocDilateMainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFDownsampleCS, "/Engine/Private/DiaphragmDOF/DOFDownsample.usf", "DownsampleCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFReduceCS, "/Engine/Private/DiaphragmDOF/DOFReduce.usf", "ReduceCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFScatterGroupPackCS, "/Engine/Private/DiaphragmDOF/DOFHybridScatterCompilation.usf", "ScatterGroupPackMainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFBuildBokehLUTCS, "/Engine/Private/DiaphragmDOF/DOFBokehLUT.usf", "BuildBokehLUTMainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFGatherCS, "/Engine/Private/DiaphragmDOF/DOFGatherPass.usf", "GatherMainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFPostfilterCS, "/Engine/Private/DiaphragmDOF/DOFPostfiltering.usf", "PostfilterMainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFHybridScatterVS, "/Engine/Private/DiaphragmDOF/DOFHybridScatterVertexShader.usf", "ScatterMainVS", SF_Vertex);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFHybridScatterPS, "/Engine/Private/DiaphragmDOF/DOFHybridScatterPixelShader.usf", "ScatterMainPS", SF_Pixel);
IMPLEMENT_GLOBAL_SHADER(FDiaphragmDOFRecombineCS, "/Engine/Private/DiaphragmDOF/DOFRecombine.usf", "RecombineMainCS", SF_Compute);
} // namespace
namespace DiaphragmDOF
{
class FDOFViewState
{
public:
TStaticArray<TRefCountPtr<IPooledRenderTarget>, (int)EDiaphragmDOFBokehLUTFormat::MAX> BokehLUTs;
FBokehModel BokehModel;
FDOFViewState()
: BokehLUTs()
, BokehModel()
{
}
};
}
bool DiaphragmDOF::IsEnabled(const FViewInfo& View)
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DepthOfFieldQuality"));
check(CVar);
const bool bDepthOfFieldRequestedByCVar = CVar->GetValueOnAnyThread() > 0;
return
DiaphragmDOF::IsSupported(View.GetShaderPlatform()) &&
View.Family->EngineShowFlags.DepthOfField &&
bDepthOfFieldRequestedByCVar &&
!(View.Family->EngineShowFlags.PathTracing && PathTracing::UsesReferenceDOF(View)) &&
(View.FinalPostProcessSettings.DepthOfFieldFocalDistance > 0.f || View.FinalPostProcessSettings.DepthOfFieldDepthBlurRadius > 0.f);
}
bool DiaphragmDOF::AddPasses(
FRDGBuilder& GraphBuilder,
const FSceneTextureParameters& SceneTextures,
const FViewInfo& View,
FRDGTextureRef InputSceneColor,
const FTranslucencyPassResources& TranslucencyPassResources,
FRDGTextureRef& OutputColor)
{
if (View.Family->EngineShowFlags.VisualizeDOF)
{
// no need for this pass
OutputColor = InputSceneColor;
return false;
}
// Format of the scene color.
EPixelFormat SceneColorFormat = InputSceneColor->Desc.Format;
// Whether should process alpha channel of the scene or not.
const bool bProcessSceneAlpha = IsPostProcessingWithAlphaChannelSupported();
const EShaderPlatform ShaderPlatform = View.GetShaderPlatform();
// Number of sampling ring in the gathering kernel.
const int32 HalfResRingCount = FMath::Clamp(CVarRingCount.GetValueOnRenderThread(), kMinGatheringRingCount, MaxGatheringRingCount(ShaderPlatform));
// Post filtering method to do.
const EDiaphragmDOFPostfilterMethod PostfilterMethod = GetPostfilteringMethod();
// The mode for hybrid scattering.
const EHybridScatterMode FgdHybridScatteringMode = EHybridScatterMode(CVarHybridScatterForegroundMode.GetValueOnRenderThread());
const EHybridScatterMode BgdHybridScatteringMode = EHybridScatterMode(CVarHybridScatterBackgroundMode.GetValueOnRenderThread());
const float MinScatteringCocRadius = FMath::Max(CVarScatterMinCocRadius.GetValueOnRenderThread(), kMinScatteringCocRadius);
// Whether the platform support gather bokeh simmulation.
const bool bSupportGatheringBokehSimulation = SupportsBokehSimmulation(ShaderPlatform);
// Whether should use shade permutation that does lower quality accumulation.
// TODO: this is becoming a mess.
const bool bUseLowAccumulatorQuality = CVarAccumulatorQuality.GetValueOnRenderThread() == 0;
const bool bUseCinematicAccumulatorQuality = CVarAccumulatorQuality.GetValueOnRenderThread() == 2;
// Setting for scattering budget upper bound.
const float MaxScatteringRatio = FMath::Clamp(CVarScatterMaxSpriteRatio.GetValueOnRenderThread(), 0.0f, 1.0f);
// Slight out of focus is not supporting with DOF's TAA upsampling, because of the brute force
// kernel used in GatherCS for slight out of focus stability buffer.
const bool bSupportsSlightOutOfFocus = true; // View.PrimaryScreenPercentageMethod != EPrimaryScreenPercentageMethod::TemporalUpscale;
// Quality setting for the recombine pass.
const int32 RecombineQuality = bSupportsSlightOutOfFocus ? FMath::Clamp(CVarRecombineQuality.GetValueOnRenderThread(), 0, kMaxRecombineQuality) : 0;
// Resolution divisor.
const int32 PrefilteringResolutionDivisor = CVarDOFGatherResDivisor.GetValueOnRenderThread() >= 2 ? 2 : 1;
// Minimal absolute Coc radius to spawn a gather pass. Blurring radius under this are considered not great looking.
// This is assuming the pass is opacity blending with a ramp from 1 to 2. This can not be exposed as a cvar,
// because the slight out focus's lower res pass uses for full res convolution stability depends on this.
const float kMinimalAbsGatherPassCocRadius = 1;
// Minimal CocRadius to wire lower res gathering passes.
const float BackgroundCocRadiusMaximumForUniquePass = HalfResRingCount * 4.0; // TODO: polish that.
// Whether the recombine pass does slight out of focus convolution.
const bool bRecombineDoesSlightOutOfFocus = RecombineQuality > 0;
// Whether the recombine pass wants separate input buffer for foreground hole filling.
const bool bRecombineDoesSeparateForegroundHoleFilling = RecombineQuality > 0;
// Compute the required blurring radius to actually perform depth of field, that depends on whether
// doing slight out of focus convolution.
const float MinRequiredBlurringRadius = bRecombineDoesSlightOutOfFocus
? (CVarMinimalFullresBlurRadius.GetValueOnRenderThread() * 0.5f)
: kMinimalAbsGatherPassCocRadius;
// Whether to use R11G11B10 + separate C0C buffer.
const bool bRGBBufferSeparateCocBuffer = (
SceneColorFormat == PF_FloatR11G11B10 &&
// Can't use FloatR11G11B10 if also need to support alpha channel.
!bProcessSceneAlpha &&
// This is just to get the number of shader permutation down.
RecombineQuality == 0 &&
bUseLowAccumulatorQuality &&
SupportsRGBColorBuffer(ShaderPlatform));
// Derives everything needed from the view.
FSceneViewState* const ViewState = View.ViewState;
FPhysicalCocModel CocModel;
CocModel.Compile(View);
FBokehModel BokehModel;
BokehModel.Compile(View, CocModel);
FDOFViewState* DOFViewState = nullptr;
TStaticArray<bool, (int)EDiaphragmDOFBokehLUTFormat::MAX> HasCachedBokehLUT {};
if (View.ViewState)
{
if (!View.ViewState->DepthOfFieldState.IsValid())
{
View.ViewState->DepthOfFieldState = MakePimpl<FDOFViewState>();
}
DOFViewState = View.ViewState->DepthOfFieldState.Get();
if (DOFViewState->BokehModel != BokehModel)
{
for (int Index = 0; Index < (int)EDiaphragmDOFBokehLUTFormat::MAX; ++Index)
{
DOFViewState->BokehLUTs[Index] = nullptr;
}
DOFViewState->BokehModel = BokehModel;
}
for (int Index = 0; Index < (int)EDiaphragmDOFBokehLUTFormat::MAX; ++Index)
{
HasCachedBokehLUT[Index] = DOFViewState->BokehLUTs[Index] != nullptr;
}
}
// Prepare preprocessing TAA pass.
FTAAPassParameters TAAParameters(View);
{
TAAParameters.Pass = ETAAPassConfig::DiaphragmDOF;
// When using dynamic resolution, the blur introduce by TAA's history resolution changes is quite noticeable on DOF.
// Therefore we switch for a temporal upsampling technic to maintain the same history resolution.
if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale)
{
TAAParameters.Pass = ETAAPassConfig::DiaphragmDOFUpsampling;
}
TAAParameters.SetupViewRect(View, PrefilteringResolutionDivisor);
TAAParameters.Quality = CVarDOFTemporalAAQuality.GetValueOnRenderThread() == 0 ? ETAAQuality::Medium : ETAAQuality::High;
}
// Size of the view in GatherColorSetup.
FIntPoint FullResViewSize = View.ViewRect.Size();
FIntPoint PreprocessViewSize = FIntPoint::DivideAndRoundUp(FullResViewSize, PrefilteringResolutionDivisor);
FIntPoint GatheringViewSize = PreprocessViewSize;
if (IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && ViewState)
{
PreprocessViewSize = FIntPoint::DivideAndRoundUp(TAAParameters.OutputViewRect.Size(), PrefilteringResolutionDivisor);
}
// Basis of the coc encoded in all buffer
const float EncodedCocRadiusBasis = PreprocessViewSize.X;
const float GatheringCocRadiusBasis = float(GatheringViewSize.X);
const float MaxBackgroundCocRadius = CocModel.ComputeViewMaxBackgroundCocRadius(GatheringViewSize.X);
const float MinForegroundCocRadius = CocModel.ComputeViewMinForegroundCocRadius(GatheringViewSize.X);
const float AbsMaxForegroundCocRadius = FMath::Abs(MinForegroundCocRadius);
const float MaxBluringRadius = FMath::Max(MaxBackgroundCocRadius, AbsMaxForegroundCocRadius);
// Whether should hybrid scatter for foreground and background.
bool bForegroundHybridScattering = FgdHybridScatteringMode != EHybridScatterMode::Disabled && AbsMaxForegroundCocRadius > MinScatteringCocRadius && MaxScatteringRatio > 0.0f;
bool bBackgroundHybridScattering = BgdHybridScatteringMode != EHybridScatterMode::Disabled && MaxBackgroundCocRadius > MinScatteringCocRadius && MaxScatteringRatio > 0.0f;
if (!SupportsHybridScatter(ShaderPlatform))
{
bForegroundHybridScattering = false;
bBackgroundHybridScattering = false;
}
// Compute the reference buffer size for PrefilteringResolutionDivisor.
FIntPoint RefBufferSize = FIntPoint::DivideAndRoundUp(InputSceneColor->Desc.Extent, PrefilteringResolutionDivisor);
EDiaphragmDOFBokehSimulation BokehSimulation = EDiaphragmDOFBokehSimulation::Disabled;
if (BokehModel.BokehShape != EBokehShape::Circle)
{
BokehSimulation = (BokehModel.DiaphragmBladeCount % 2)
? EDiaphragmDOFBokehSimulation::GenericBokeh
: EDiaphragmDOFBokehSimulation::SimmetricBokeh;
}
// If the max blurring radius is too small, do not wire any passes.
if (MaxBluringRadius < MinRequiredBlurringRadius)
{
OutputColor = InputSceneColor;
return false;
}
RDG_EVENT_SCOPE_STAT(GraphBuilder, DepthOfField, "DOF(Alpha=%s)", bProcessSceneAlpha ? TEXT("Yes") : TEXT("No"));
RDG_GPU_STAT_SCOPE(GraphBuilder, DepthOfField);
bool bGatherBackground = MaxBackgroundCocRadius > kMinimalAbsGatherPassCocRadius;
bool bGatherForeground = AbsMaxForegroundCocRadius > kMinimalAbsGatherPassCocRadius;
const bool bEnableGatherBokehSettings = (
bSupportGatheringBokehSimulation &&
CVarEnableGatherBokehSettings.GetValueOnRenderThread() == 1);
const bool bEnableScatterBokehSettings = CVarEnableScatterBokehSettings.GetValueOnRenderThread() == 1;
const bool bEnableSlightOutOfFocusBokeh = bSupportGatheringBokehSimulation && bRecombineDoesSlightOutOfFocus && CVarEnableRecombineBokehSettings.GetValueOnRenderThread();
bool bEnableVignette = true;
// Setup all the descriptors.
FRDGTextureDesc FullResDesc;
{
FullResDesc = InputSceneColor->Desc;
// Reset so that the number of samples of descriptor becomes 1, which is totally legal still with MSAA because
// the scene color will already be resolved to ShaderResource texture that is always 1. This is to work around
// hack that MSAA will have targetable texture with MSAA != shader resource, and still have descriptor indicating
// the number of samples of the targetable resource.
FullResDesc.Reset();
FullResDesc.Format = PF_FloatRGBA;
FullResDesc.Flags |= TexCreate_UAV;
FullResDesc.Flags &= ~(TexCreate_RenderTargetable | TexCreate_FastVRAM);
}
FDOFGatherInputDescs FullResGatherInputDescs;
{
FullResGatherInputDescs.SceneColor = FullResDesc;
FullResGatherInputDescs.SceneColor.Format = PF_FloatRGBA;
FullResGatherInputDescs.SeparateCoc = FullResDesc;
FullResGatherInputDescs.SeparateCoc.Format = PF_R16F;
}
FDOFGatherInputDescs HalfResGatherInputDescs;
{
HalfResGatherInputDescs.SceneColor = FullResDesc;
HalfResGatherInputDescs.SceneColor.Extent /= PrefilteringResolutionDivisor;
HalfResGatherInputDescs.SceneColor.Format = PF_FloatRGBA;
HalfResGatherInputDescs.SceneColor.Flags |= GFastVRamConfig.DOFSetup;
HalfResGatherInputDescs.SeparateCoc = FullResDesc;
HalfResGatherInputDescs.SeparateCoc.Extent /= PrefilteringResolutionDivisor;
HalfResGatherInputDescs.SeparateCoc.Format = PF_R16F;
HalfResGatherInputDescs.SeparateCoc.Flags |= GFastVRamConfig.DOFSetup;
}
// Setup the shader parameter used in all shaders.
FDOFCommonShaderParameters CommonParameters;
CommonParameters.ViewUniformBuffer = View.ViewUniformBuffer;
FDOFGatherInputTextures FullResGatherInputTextures;
FDOFGatherInputTextures HalfResGatherInputTextures;
// Setup at lower resolution from full resolution scene color and scene depth.
{
FullResGatherInputTextures = CreateTextures(GraphBuilder, FullResGatherInputDescs, TEXT("DOF.FullResSetup"));
HalfResGatherInputTextures = CreateTextures(GraphBuilder, HalfResGatherInputDescs, TEXT("DOF.HalfResSetup"));
bool bOutputFullResolution = (bRecombineDoesSlightOutOfFocus && !bProcessSceneAlpha) || PrefilteringResolutionDivisor == 1;
bool bOutputHalfResolution = PrefilteringResolutionDivisor == 2;
FDiaphragmDOFSetupCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
FIntPoint PassViewSize = FullResViewSize;
if (bOutputFullResolution && bOutputHalfResolution)
{
PermutationVector.Set<FDiaphragmDOFSetupCS::FOutputResDivisor>(0);
}
else if (bOutputFullResolution)
{
PermutationVector.Set<FDiaphragmDOFSetupCS::FOutputResDivisor>(1);
}
else if (bOutputHalfResolution)
{
PermutationVector.Set<FDiaphragmDOFSetupCS::FOutputResDivisor>(2);
PassViewSize = GatheringViewSize;
}
else
{
check(0);
}
FDiaphragmDOFSetupCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFSetupCS::FParameters>();
{
PassParameters->CommonParameters = CommonParameters;
SetCocModelParameters(GraphBuilder, &PassParameters->CocModel, CocModel, EncodedCocRadiusBasis);
PassParameters->SceneColorTexture = InputSceneColor;
PassParameters->SceneDepthTexture = SceneTextures.SceneDepthTexture;
if (!bOutputFullResolution)
{
FullResGatherInputTextures.SceneColor = InputSceneColor;
}
else if (bProcessSceneAlpha)
{
// No point passing through the full res scene color, the shader just output SeparateCoc.
PassParameters->Output0 = CreateUAVs(GraphBuilder, FullResGatherInputTextures).SeparateCoc;
FullResGatherInputTextures.SceneColor = InputSceneColor;
}
else
{
PassParameters->Output0 = CreateUAVs(GraphBuilder, FullResGatherInputTextures).SceneColor;
}
if (bOutputHalfResolution)
{
PassParameters->Output1 = CreateUAVs(GraphBuilder, HalfResGatherInputTextures).SceneColor;
PassParameters->Output2 = CreateUAVs(GraphBuilder, HalfResGatherInputTextures).SeparateCoc;
}
}
FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(PassViewSize, kDefaultGroupSize);
TShaderMapRef<FDiaphragmDOFSetupCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF Setup(%s CoC=[%d;%d]) %dx%d",
!bOutputFullResolution ? TEXT("HalfRes") : (!bOutputHalfResolution ? TEXT("FullRes") : TEXT("Full&HalfRes")),
FMath::FloorToInt(CocModel.ComputeViewMinForegroundCocRadius(PassViewSize.X)),
FMath::CeilToInt(CocModel.ComputeViewMaxBackgroundCocRadius(PassViewSize.X)),
PassViewSize.X, PassViewSize.Y),
ComputeShader,
PassParameters,
GroupCount);
if (!bOutputFullResolution || bProcessSceneAlpha)
{
FullResGatherInputTextures.SceneColor = InputSceneColor;
}
if (!bOutputHalfResolution)
{
check(PrefilteringResolutionDivisor == 1);
HalfResGatherInputTextures = FullResGatherInputTextures;
}
}
// TAA the setup for the convolution to be temporally stable.
if (IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && ViewState)
{
TAAParameters.SceneDepthTexture = SceneTextures.SceneDepthTexture;
TAAParameters.SceneVelocityTexture = SceneTextures.GBufferVelocityTexture;
TAAParameters.SceneColorInput = HalfResGatherInputTextures.SceneColor;
TAAParameters.SceneMetadataInput = HalfResGatherInputTextures.SeparateCoc;
TAAParameters.CoCBilateralFilterStrength = FMath::Clamp(CVarCoCBilateralFilterStrength.GetValueOnRenderThread(), 0.0f, 1.0f);
FTAAOutputs TAAOutputs = AddTemporalAAPass(
GraphBuilder,
View,
TAAParameters,
View.PrevViewInfo.DOFSetupHistory,
&ViewState->PrevFrameViewInfo.DOFSetupHistory);
HalfResGatherInputTextures.SceneColor = TAAOutputs.SceneColor;
HalfResGatherInputTextures.SeparateCoc = TAAOutputs.SceneMetadata;
HalfResGatherInputDescs.SceneColor = TAAOutputs.SceneColor->Desc;
if (TAAOutputs.SceneMetadata)
{
HalfResGatherInputDescs.SeparateCoc = TAAOutputs.SceneMetadata->Desc;
}
}
// Tile classify work than needs to be done.
FDOFTileClassificationTextures TileClassificationTextures;
{
// Setup the descriptors for tile classification.
FDOFTileClassificationDescs TileClassificationDescs;
{
FIntPoint MaxTileCount = CocTileGridSize(HalfResGatherInputTextures.SceneColor->Desc.Extent);
TileClassificationDescs.Foreground = FRDGTextureDesc::Create2D(
MaxTileCount, PF_G16R16F, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV);
TileClassificationDescs.Background = FRDGTextureDesc::Create2D(
MaxTileCount, PF_FloatRGBA, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV);
}
// Adds a coc flatten pass.
FDOFTileClassificationTextures FlattenedTileClassificationTextures;
{
FIntPoint SrcSize = HalfResGatherInputTextures.SceneColor->Desc.Extent;
static const TCHAR* OutputDebugNames[2] = {
TEXT("DOF.FlattenFgdCoc"),
TEXT("DOF.FlattenBgdCoc"),
};
FlattenedTileClassificationTextures = CreateTextures(GraphBuilder, TileClassificationDescs, OutputDebugNames);
FDiaphragmDOFCocFlattenCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFCocFlattenCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ViewportRect = FIntRect(0, 0, GatheringViewSize.X, GatheringViewSize.Y);
PassParameters->ThreadIdToBufferUV = FVector2f(
PreprocessViewSize.X / float(GatheringViewSize.X * SrcSize.X),
PreprocessViewSize.Y / float(GatheringViewSize.Y * SrcSize.Y));
PassParameters->MaxBufferUV = FVector2f(
(PreprocessViewSize.X - 1.0f) / float(SrcSize.X),
(PreprocessViewSize.Y - 1.0f) / float(SrcSize.Y));
PassParameters->GatherInput = HalfResGatherInputTextures;
PassParameters->TileOutput = CreateUAVs(GraphBuilder, FlattenedTileClassificationTextures);
FDiaphragmDOFCocFlattenCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
PermutationVector.Set<FDiaphragmDOFCocFlattenCS::FDoCocGather4>(PreprocessViewSize != GatheringViewSize);
TShaderMapRef<FDiaphragmDOFCocFlattenCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF FlattenCoc(Gather4=%s) %dx%d",
PermutationVector.Get<FDiaphragmDOFCocFlattenCS::FDoCocGather4>() ? TEXT("Yes") : TEXT("No"),
GatheringViewSize.X, GatheringViewSize.Y),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(GatheringViewSize, kDefaultGroupSize));
}
// Error introduced by the random offset of the gathering kernel's center.
const float BluringRadiusErrorMultiplier = 1.0f + 1.0f / (HalfResRingCount + 0.5f);
// Number of group to dispatch for dilate passes.
const FIntPoint DilatePassViewSize = FIntPoint::DivideAndRoundUp(GatheringViewSize, kCocTileSize);
const FIntVector DilateGroupCount = FComputeShaderUtils::GetGroupCount(DilatePassViewSize, kDefaultGroupSize);
// Add one coc dilate pass.
auto AddCocDilatePass = [&](
EDiaphragmDOFDilateCocMode Mode,
const FDOFTileClassificationTextures& TileInput,
const FDOFTileClassificationTextures& DilatedTileMinMax,
int32 SampleRadiusCount, int32 SampleOffsetMultipler)
{
FDOFTileClassificationDescs OutputDescs = TileClassificationDescs;
const TCHAR* OutputDebugNames[2] = {
TEXT("DOF.DilateFgdCoc"),
TEXT("DOF.DilateBgdCoc"),
};
if (Mode == EDiaphragmDOFDilateCocMode::MinForegroundAndMaxBackground)
{
OutputDebugNames[0] = TEXT("DOF.DilateMinFgdCoc");
OutputDebugNames[1] = TEXT("DOF.DilateMaxBgdCoc");
OutputDescs.Foreground.Format = PF_R16F;
OutputDescs.Background.Format = PF_R16F;
}
FDOFTileClassificationTextures TileClassificationOutputTextures = CreateTextures(GraphBuilder, OutputDescs, OutputDebugNames);
FDiaphragmDOFCocDilateCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFCocDilateCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ViewportRect = FIntRect(0, 0, DilatePassViewSize.X, DilatePassViewSize.Y);
PassParameters->SampleOffsetMultipler = SampleOffsetMultipler;
PassParameters->fSampleOffsetMultipler = SampleOffsetMultipler;
PassParameters->CocRadiusToBucketDistanceUpperBound = (GatheringCocRadiusBasis / EncodedCocRadiusBasis) * BluringRadiusErrorMultiplier;
PassParameters->BucketDistanceToCocRadius = 1.0f / PassParameters->CocRadiusToBucketDistanceUpperBound;
PassParameters->TileInput = TileInput;
PassParameters->DilatedTileMinMax = DilatedTileMinMax;
PassParameters->TileOutput = CreateUAVs(GraphBuilder, TileClassificationOutputTextures);
FDiaphragmDOFCocDilateCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDDOFDilateRadiusDim>(SampleRadiusCount);
PermutationVector.Set<FDDOFDilateModeDim>(Mode);
// TODO: permutation to do foreground and background separately, to have higher occupancy?
TShaderMapRef<FDiaphragmDOFCocDilateCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF DilateCoc(1/16 %s radius=%d step=%d) %dx%d",
GetEventName(Mode), SampleRadiusCount, SampleOffsetMultipler,
DilatePassViewSize.X, DilatePassViewSize.Y),
ComputeShader,
PassParameters,
DilateGroupCount);
return TileClassificationOutputTextures;
};
// Parameters for the dilate Coc passes.
int32 DilateCount = 1;
int32 SampleRadiusCount[3];
int32 SampleDistanceMultiplier[3];
{
const int32 MaxSampleRadiusCount = kMaxCocDilateSampleRadiusCount;
// Compute the maximum tile dilation.
int32 MaximumTileDilation = FMath::CeilToInt(
(MaxBluringRadius * BluringRadiusErrorMultiplier) / kCocTileSize);
// There is always at least one dilate pass so that even small Coc radius conservatively dilate on next neighboor.
int32 CurrentConvolutionRadius = FMath::Min(
MaximumTileDilation, MaxSampleRadiusCount);
SampleDistanceMultiplier[0] = 1;
SampleRadiusCount[0] = CurrentConvolutionRadius;
// If the theoric radius is too big, setup more dilate passes.
for (int32 i = 1; i < UE_ARRAY_COUNT(SampleDistanceMultiplier); i++)
{
if (MaximumTileDilation <= CurrentConvolutionRadius)
{
break;
}
// Highest upper bound possible for SampleDistanceMultiplier to not step over any tile.
int32 HighestPossibleMultiplierUpperBound = CurrentConvolutionRadius + 1;
// Find out how many step we need to do on dilate radius.
SampleRadiusCount[i] = FMath::Min(
MaximumTileDilation / HighestPossibleMultiplierUpperBound,
MaxSampleRadiusCount);
// Find out ideal multiplier to not dilate an area too large.
// TODO: Could add control over the radius of the last.
int32 IdealMultiplier = FMath::DivideAndRoundUp(MaximumTileDilation - CurrentConvolutionRadius, SampleRadiusCount[1]); // TODO: why 1?
SampleDistanceMultiplier[i] = FMath::Min(IdealMultiplier, HighestPossibleMultiplierUpperBound);
CurrentConvolutionRadius += SampleRadiusCount[i] * SampleDistanceMultiplier[i];
DilateCount++;
}
}
if (DilateCount > 1)
{
// TODO.
FDOFTileClassificationTextures MinMaxTexture = FlattenedTileClassificationTextures;
// Dilate min foreground and max background coc radii first.
for (int32 i = 0; i < DilateCount; i++)
{
MinMaxTexture = AddCocDilatePass(
EDiaphragmDOFDilateCocMode::MinForegroundAndMaxBackground,
MinMaxTexture, FDOFTileClassificationTextures(),
SampleRadiusCount[i], SampleDistanceMultiplier[i]);
}
TileClassificationTextures = FlattenedTileClassificationTextures;
// Dilates everything else.
for (int32 i = 0; i < DilateCount; i++)
{
TileClassificationTextures = AddCocDilatePass(
EDiaphragmDOFDilateCocMode::MinimalAbsoluteRadiuses,
TileClassificationTextures, MinMaxTexture,
SampleRadiusCount[i], SampleDistanceMultiplier[i]);
}
}
else
{
TileClassificationTextures = AddCocDilatePass(
EDiaphragmDOFDilateCocMode::StandAlone,
FlattenedTileClassificationTextures, FDOFTileClassificationTextures(),
SampleRadiusCount[0], SampleDistanceMultiplier[0]);
}
}
bool bMatteBoxActive = false;
TStaticArray<FUintVector4, FPhysicalCocModel::MaxMatteBoxFlags, SHADER_PARAMETER_ARRAY_ELEMENT_ALIGNMENT> MatteBoxPlanes;
for (int Index = 0; Index < FPhysicalCocModel::MaxMatteBoxFlags; ++Index)
{
float PlanePitch = (CocModel.MatteBoxFlags[Index].Pitch + 90.0f) * PI / 180.0;
float PlaneRoll = CocModel.MatteBoxFlags[Index].Roll * PI / 180.0;
float PlaneLength = CocModel.MatteBoxFlags[Index].Length;
float SinPlanePitch;
float CosPlanePitch;
float SinPlaneRoll;
float CosPlaneRoll;
float RcpSinPlanePitch;
FMath::SinCos(&SinPlanePitch, &CosPlanePitch, PlanePitch);
FMath::SinCos(&SinPlaneRoll, &CosPlaneRoll, PlaneRoll);
RcpSinPlanePitch = 1.0f / SinPlanePitch;
uint32 Packed0 = FVector2DHalf(SinPlanePitch, CosPlanePitch).AsUInt32();
uint32 Packed1 = FVector2DHalf(SinPlaneRoll, CosPlaneRoll).AsUInt32();
uint32 Packed2 = *reinterpret_cast<uint32*>(&RcpSinPlanePitch);
uint32 Packed3 = *reinterpret_cast<uint32*>(&PlaneLength);
MatteBoxPlanes[Index] = FUintVector4(Packed0, Packed1, Packed2, Packed3);
bMatteBoxActive = bMatteBoxActive || PlaneLength > 0;
}
bool bVignetteActive = CocModel.BarrelLength > 0 || bMatteBoxActive;
// Add the reduce pass
FDOFGatherInputTextures ReducedGatherInputTextures;
FRDGBufferRef DrawIndirectParametersBuffer = nullptr;
FRDGBufferRef ForegroundScatterDrawListBuffer = nullptr;
FRDGBufferRef BackgroundScatterDrawListBuffer = nullptr;
{
FIntPoint SrcSize = HalfResGatherInputDescs.SceneColor.Extent;
// Compute the number of mip level required by the gathering pass.
const int32 MipLevelCount = FMath::Max(FMath::CeilToInt(FMath::Log2(MaxBluringRadius * 0.5 / HalfResRingCount)) + (bUseLowAccumulatorQuality ? 1 : 0), 2);
// Maximum number of mip level that can be done.
const int32 MaxReductionMipLevelCount = FDiaphragmDOFReduceCS::GetMaxReductionMipLevelCount(IsPostProcessingWithAlphaChannelSupported());
// Maximum number of scattering group per draw instance.
// TODO: depends.
const uint32 kMaxScatteringGroupPerInstance = 21;
// Maximum number of scattering group allowed per frame.
const uint32 MaxScatteringGroupCount = FMath::Max(MaxScatteringRatio * 0.25f * SrcSize.X * SrcSize.Y - kMaxScatteringGroupPerInstance, float(kMaxScatteringGroupPerInstance));
// Allocate the reduced gather input textures.
{
FDOFGatherInputDescs ReducedGatherInputDescs = HalfResGatherInputDescs;
ReducedGatherInputDescs.SceneColor.NumMips = MipLevelCount;
ReducedGatherInputDescs.SceneColor.Flags = (ReducedGatherInputDescs.SceneColor.Flags & ~(TexCreate_FastVRAM)) | (ETextureCreateFlags)GFastVRamConfig.DOFReduce;
// Make sure the mip 0 is a multiple of 2^NumMips so there is no per mip level UV conversion to do in the gathering shader.
// Also make sure it is a multiple of group size because reduce shader unconditionally output Mip0.
int32 Multiple = FMath::Max(1 << (MipLevelCount - 1), kDefaultGroupSize);
ReducedGatherInputDescs.SceneColor.Extent.X = Multiple * FMath::DivideAndRoundUp(ReducedGatherInputDescs.SceneColor.Extent.X, Multiple);
ReducedGatherInputDescs.SceneColor.Extent.Y = Multiple * FMath::DivideAndRoundUp(ReducedGatherInputDescs.SceneColor.Extent.Y, Multiple);
ReducedGatherInputDescs.SeparateCoc = ReducedGatherInputDescs.SceneColor;
ReducedGatherInputDescs.SeparateCoc.Format = HalfResGatherInputDescs.SeparateCoc.Format;
if (bRGBBufferSeparateCocBuffer)
{
ReducedGatherInputDescs.SceneColor.Format = PF_FloatR11G11B10;
ReducedGatherInputDescs.SeparateCoc.Format = PF_R16F;
}
ReducedGatherInputTextures = CreateTextures(GraphBuilder, ReducedGatherInputDescs, TEXT("DOF.Reduce"));
}
// Downsample the gather color setup to have faster neighborhood comparisons.
FDOFGatherInputTextures QuarterResGatherInputTextures;
if (bForegroundHybridScattering || bBackgroundHybridScattering)
{
// Allocate quarter res textures.
{
FDOFGatherInputDescs QuarterResGatherInputDescs = HalfResGatherInputDescs;
QuarterResGatherInputDescs.SceneColor.Extent /= 2;
QuarterResGatherInputDescs.SeparateCoc.Extent /= 2;
// Lower the bit depth to speed up texture fetches in reduce pass, that is ok since this is used only for comparison purposes.
if (bRGBBufferSeparateCocBuffer && !bProcessSceneAlpha)
QuarterResGatherInputDescs.SceneColor.Format = PF_FloatR11G11B10;
QuarterResGatherInputTextures = CreateTextures(GraphBuilder, QuarterResGatherInputDescs, TEXT("DOF.Downsample"));
}
FIntPoint PassViewSize = FIntPoint::DivideAndRoundUp(PreprocessViewSize, 2);
FDiaphragmDOFDownsampleCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
FDiaphragmDOFDownsampleCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFDownsampleCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
PassParameters->ViewportRect = FIntRect(0, 0, PassViewSize.X, PassViewSize.Y);
PassParameters->MaxBufferUV = FVector2f(
(PreprocessViewSize.X - 0.5f) / float(SrcSize.X),
(PreprocessViewSize.Y - 0.5f) / float(SrcSize.Y));
PassParameters->OutputCocRadiusMultiplier = GatheringCocRadiusBasis / EncodedCocRadiusBasis;
PassParameters->GatherInputSize = FVector4f(SrcSize.X, SrcSize.Y, 1.0f / SrcSize.X, 1.0f / SrcSize.Y);
PassParameters->GatherInput = HalfResGatherInputTextures;
PassParameters->OutDownsampledGatherInput = CreateUAVs(GraphBuilder, QuarterResGatherInputTextures);
TShaderMapRef<FDiaphragmDOFDownsampleCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF Downsample %dx%d", PassViewSize.X, PassViewSize.Y),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(PassViewSize, kDefaultGroupSize));
}
// Create and clears buffers for indirect scatter.
if (bForegroundHybridScattering || bBackgroundHybridScattering)
{
DrawIndirectParametersBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc<FRHIDrawIndexedIndirectParameters>(2), TEXT("DOFIndirectDrawParameters"));
const uint32 PixelCountPerScatterGroup = 2 * 2;
const uint32 VignetteDataPerPixel = 3;
const uint32 ScatterDrawListDataPerPixel = 1 + VignetteDataPerPixel;
const uint32 ScatterDrawListGroupStride = 1 + (PixelCountPerScatterGroup * ScatterDrawListDataPerPixel);
FRDGBufferDesc DrawListDescs = FRDGBufferDesc::CreateStructuredDesc(sizeof(float) * 4, ScatterDrawListGroupStride * MaxScatteringGroupCount);
if (bForegroundHybridScattering)
ForegroundScatterDrawListBuffer = GraphBuilder.CreateBuffer(DrawListDescs, TEXT("DOF.ForegroundDrawList"));
if (bBackgroundHybridScattering)
BackgroundScatterDrawListBuffer = GraphBuilder.CreateBuffer(DrawListDescs, TEXT("DOF.BackgroundDrawList"));
}
// Number of mip level that has been reduced.
int32 ReducedMipLevelCount;
// Adds the first reduce pass.
{
int32 ProcessingMipLevelCount = FMath::Min(MipLevelCount, MaxReductionMipLevelCount);
FIntPoint PassViewSize = PreprocessViewSize;
FDiaphragmDOFReduceCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
PermutationVector.Set<FDiaphragmDOFReduceCS::FReduceMipCount>(ProcessingMipLevelCount);
PermutationVector.Set<FDiaphragmDOFReduceCS::FHybridScatterForeground>(bForegroundHybridScattering);
PermutationVector.Set<FDiaphragmDOFReduceCS::FHybridScatterBackground>(bBackgroundHybridScattering);
PermutationVector.Set<FDDOFRGBColorBufferDim>(bRGBBufferSeparateCocBuffer);
FDiaphragmDOFReduceCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFReduceCS::FParameters>();
PassParameters->ViewportRect = FIntRect(0, 0, PassViewSize.X, PassViewSize.Y);
PassParameters->MaxInputBufferUV = FVector2f(
(PreprocessViewSize.X - 0.5f) / SrcSize.X,
(PreprocessViewSize.Y - 0.5f) / SrcSize.Y);
PassParameters->ScatteringViewportSize = FVector4f(GatheringViewSize.X, GatheringViewSize.Y, 1.0f / GatheringViewSize.X, 1.0f / GatheringViewSize.Y);
PassParameters->ScatteringScaling = float(GatheringViewSize.X) / float(PreprocessViewSize.X);
PassParameters->MaxScatteringGroupCount = MaxScatteringGroupCount;
PassParameters->PreProcessingToProcessingCocRadiusFactor = GatheringCocRadiusBasis / EncodedCocRadiusBasis;
PassParameters->BokehGatherDistinctionLimit = CVarGatherDistinctionLimit.GetValueOnRenderThread();
PassParameters->MinScatteringCocRadius = MinScatteringCocRadius;
PassParameters->NeighborCompareMaxColor = CVarScatterNeighborCompareMaxColor.GetValueOnRenderThread();
SetCocModelParameters(GraphBuilder, &PassParameters->CocModel, CocModel, float(GatheringViewSize.X));
PassParameters->LensImageDistance = 1.0f / (1.0f/CocModel.VerticalFocalLength - 1.0f/CocModel.FocusDistance);
PassParameters->SensorSize = FVector2f(CocModel.SensorWidth, CocModel.SensorHeight);
PassParameters->Aperture = CocModel.GetLensRadius().Y * 2.0f;
PassParameters->BarrelRadius = CocModel.BarrelRadius;
PassParameters->BarrelLength = bVignetteActive ? CocModel.BarrelLength : -1.0f;
PassParameters->MatteBoxPlanes = MatteBoxPlanes;
PassParameters->EyeAdaptationBuffer = GraphBuilder.CreateSRV(GetEyeAdaptationBuffer(GraphBuilder, View));
PassParameters->CommonParameters = CommonParameters;
PassParameters->GatherInputSize = FVector4f(SrcSize.X, SrcSize.Y, 1.0f / SrcSize.X, 1.0f / SrcSize.Y);
PassParameters->GatherInput = CreateSRVs(GraphBuilder, HalfResGatherInputTextures);
PassParameters->QuarterResGatherInputSize = FVector4f(SrcSize.X / 2, SrcSize.Y / 2, 2.0f / SrcSize.X, 2.0f / SrcSize.Y);
PassParameters->QuarterResGatherInput = QuarterResGatherInputTextures;
for (int32 MipLevel = 0; MipLevel < ProcessingMipLevelCount; MipLevel++)
{
PassParameters->OutputMips[MipLevel] = CreateUAVs(GraphBuilder, ReducedGatherInputTextures, MipLevel);
}
if (bForegroundHybridScattering || bBackgroundHybridScattering)
{
PassParameters->OutScatterDrawIndirectParameters = GraphBuilder.CreateUAV(DrawIndirectParametersBuffer);
if (ForegroundScatterDrawListBuffer)
PassParameters->OutForegroundScatterDrawList = GraphBuilder.CreateUAV(ForegroundScatterDrawListBuffer);
if (BackgroundScatterDrawListBuffer)
PassParameters->OutBackgroundScatterDrawList = GraphBuilder.CreateUAV(BackgroundScatterDrawListBuffer);
AddClearUAVPass(GraphBuilder, PassParameters->OutScatterDrawIndirectParameters, 0);
}
TShaderMapRef<FDiaphragmDOFReduceCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF Reduce(Mips=[0;%d] FgdScatter=%s BgdScatter=%s%s) %dx%d",
ProcessingMipLevelCount - 1,
bForegroundHybridScattering ? TEXT("Yes") : TEXT("No"),
bBackgroundHybridScattering ? TEXT("Yes") : TEXT("No"),
bRGBBufferSeparateCocBuffer ? TEXT(" R11G11B10") : TEXT(""),
PassViewSize.X, PassViewSize.Y),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(PassViewSize, kDefaultGroupSize));
ReducedMipLevelCount = ProcessingMipLevelCount;
}
// Complete the reduction.
while (ReducedMipLevelCount < MipLevelCount)
{
int32 ProcessingMipLevelCount = FMath::Min(MipLevelCount - ReducedMipLevelCount, IsPostProcessingWithAlphaChannelSupported() ? 2 : (kMaxMipLevelCount - 1));
int32 InputMipLevel = ReducedMipLevelCount - 1;
FIntPoint GatherInputExtent = ReducedGatherInputTextures.SceneColor->Desc.Extent;
FIntPoint PassViewSize = FIntPoint::DivideAndRoundUp(PreprocessViewSize, 1 << InputMipLevel);
FDiaphragmDOFReduceCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
PermutationVector.Set<FDiaphragmDOFReduceCS::FReduceMipCount>(ProcessingMipLevelCount + 1);
PermutationVector.Set<FDDOFRGBColorBufferDim>(bRGBBufferSeparateCocBuffer);
PermutationVector.Set<FDiaphragmDOFReduceCS::FDisableOutputMip0>(true);
FDiaphragmDOFReduceCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFReduceCS::FParameters>();
PassParameters->ViewportRect = FIntRect(0, 0, PassViewSize.X, PassViewSize.Y);
PassParameters->MaxInputBufferUV = FVector2f(
(PreprocessViewSize.X - 0.5f) / GatherInputExtent.X,
(PreprocessViewSize.Y - 0.5f) / GatherInputExtent.Y);
PassParameters->EyeAdaptationBuffer = GraphBuilder.CreateSRV(GetEyeAdaptationBuffer(GraphBuilder, View));
PassParameters->CommonParameters = CommonParameters;
float InputMipLevelPow2 = 1 << InputMipLevel;
PassParameters->GatherInputSize = FVector4f(GatherInputExtent.X / InputMipLevelPow2, GatherInputExtent.Y / InputMipLevelPow2, InputMipLevelPow2 / GatherInputExtent.X, InputMipLevelPow2 / GatherInputExtent.Y);
PassParameters->GatherInput = CreateSRVs(GraphBuilder, ReducedGatherInputTextures, InputMipLevel);
for (int32 MipLevel = 0; MipLevel < ProcessingMipLevelCount; MipLevel++)
{
PassParameters->OutputMips[1 + MipLevel] = CreateUAVs(GraphBuilder, ReducedGatherInputTextures, ReducedMipLevelCount + MipLevel);
}
TShaderMapRef<FDiaphragmDOFReduceCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF Reduce(Mips=[%d;%d]%s) %dx%d",
ReducedMipLevelCount,
ReducedMipLevelCount + ProcessingMipLevelCount - 1,
bRGBBufferSeparateCocBuffer ? TEXT(" R11G11B10") : TEXT(""),
PassViewSize.X, PassViewSize.Y),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(PassViewSize, kDefaultGroupSize));
ReducedMipLevelCount += ProcessingMipLevelCount;
}
// Pack multiple scattering group on same primitive instance to increase wave occupancy in the scattering vertex shader.
if (bForegroundHybridScattering || bBackgroundHybridScattering)
{
// TODO: could avoid multiple shader permutation by doing multiple passes with a no barrier UAV that isn't implemented yet.
FDiaphragmDOFScatterGroupPackCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFScatterGroupPackCS::FParameters>();
PassParameters->MaxScatteringGroupCount = MaxScatteringGroupCount;
PassParameters->OutScatterDrawIndirectParameters = GraphBuilder.CreateUAV(DrawIndirectParametersBuffer);
if (ForegroundScatterDrawListBuffer)
PassParameters->OutForegroundScatterDrawList = GraphBuilder.CreateUAV(ForegroundScatterDrawListBuffer);
if (BackgroundScatterDrawListBuffer)
PassParameters->OutBackgroundScatterDrawList = GraphBuilder.CreateUAV(BackgroundScatterDrawListBuffer);
FDiaphragmDOFScatterGroupPackCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFReduceCS::FHybridScatterForeground>(bForegroundHybridScattering);
PermutationVector.Set<FDiaphragmDOFReduceCS::FHybridScatterBackground>(bBackgroundHybridScattering);
TShaderMapRef<FDiaphragmDOFScatterGroupPackCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF ScatterGroupPack"),
ComputeShader,
PassParameters,
FIntVector(2, 1, 1));
}
}
// Add a pass to build a bokeh LUTs.
auto AddBuildBokehLUTPass = [&](EDiaphragmDOFBokehLUTFormat LUTFormat)
{
if (BokehModel.BokehShape == EBokehShape::Circle)
{
return FRDGTextureRef();
}
static const TCHAR* const DebugNames[] = {
TEXT("DOF.ScatterBokehLUT"),
TEXT("DOF.RecombineBokehLUT"),
TEXT("DOF.GatherBokehLUT"),
};
FRDGTextureDesc BokehLUTDesc = FRDGTextureDesc::Create2D(
FIntPoint(32, 32),
LUTFormat == EDiaphragmDOFBokehLUTFormat::GatherSamplePos ? PF_G16R16F : PF_R16F,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
// Retrieve cached BokehLUT from DOFViewState, if any
if (HasCachedBokehLUT[int32(LUTFormat)])
{
return GraphBuilder.RegisterExternalTexture(DOFViewState->BokehLUTs[int32(LUTFormat)], DebugNames[int32(LUTFormat)]);
}
FRDGTextureRef BokehLUT = GraphBuilder.CreateTexture(BokehLUTDesc, DebugNames[int32(LUTFormat)]);
FDiaphragmDOFBuildBokehLUTCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFBuildBokehLUTCS::FBokehSimulationDim>(BokehModel.BokehShape == DiaphragmDOF::EBokehShape::RoundedBlades);
PermutationVector.Set<FDiaphragmDOFBuildBokehLUTCS::FLUTFormatDim>(LUTFormat);
FDiaphragmDOFBuildBokehLUTCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFBuildBokehLUTCS::FParameters>();
PassParameters->BladeCount = BokehModel.DiaphragmBladeCount;
PassParameters->DiaphragmRotation = BokehModel.DiaphragmRotation;
PassParameters->CocRadiusToCircumscribedRadius = BokehModel.CocRadiusToCircumscribedRadius;
PassParameters->CocRadiusToIncircleRadius = BokehModel.CocRadiusToIncircleRadius;
PassParameters->DiaphragmBladeRadius = BokehModel.RoundedBlades.DiaphragmBladeRadius;
PassParameters->DiaphragmBladeCenterOffset = BokehModel.RoundedBlades.DiaphragmBladeCenterOffset;
PassParameters->CocSqueeze = BokehModel.Squeeze;
PassParameters->CocInvSqueeze = 1.0f / BokehModel.Squeeze;
PassParameters->BokehLUTOutput = GraphBuilder.CreateUAV(BokehLUT);
TShaderMapRef<FDiaphragmDOFBuildBokehLUTCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF BuildBokehLUT(Blades=%d Shape=%s, LUT=%s) %dx%d",
BokehModel.DiaphragmBladeCount,
BokehModel.BokehShape == DiaphragmDOF::EBokehShape::RoundedBlades ? TEXT("Rounded") : TEXT("Straight"),
GetEventName(LUTFormat),
BokehLUTDesc.Extent.X, BokehLUTDesc.Extent.Y),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(BokehLUTDesc.Extent, kDefaultGroupSize));
if (DOFViewState)
{
GraphBuilder.QueueTextureExtraction(BokehLUT, &DOFViewState->BokehLUTs[int32(LUTFormat)]);
}
return BokehLUT;
};
// Add all passes for convolutions.
FDOFConvolutionTextures ForegroundConvolutionTextures;
FDOFConvolutionTextures ForegroundHoleFillingConvolutionTextures;
FDOFConvolutionTextures BackgroundConvolutionTextures;
FDOFConvolutionTextures SlightOutOfFocusConvolutionTextures;
{
// High level configuration of a convolution
struct FConvolutionSettings
{
/** Which layer to gather. */
EDiaphragmDOFLayerProcessing LayerProcessing;
/** Configuration of the pass. */
EDiaphragmDOFGatherQuality QualityConfig;
/** Postfilter method to apply on this gather pass. */
EDiaphragmDOFPostfilterMethod PostfilterMethod;
/** Bokeh simulation to do. */
EDiaphragmDOFBokehSimulation BokehSimulation;
/** Number of rings. */
int32 RingCount;
/** Whether there is a scattering pass. */
bool bHasScatterPass = false;
FConvolutionSettings()
: LayerProcessing(EDiaphragmDOFLayerProcessing::ForegroundAndBackground)
, QualityConfig(EDiaphragmDOFGatherQuality::HighQuality)
, PostfilterMethod(EDiaphragmDOFPostfilterMethod::None)
, BokehSimulation(EDiaphragmDOFBokehSimulation::Disabled)
{ }
};
// Add a gather pass
auto AddGatherPass = [&](
const FConvolutionSettings& ConvolutionSettings,
FRDGTextureRef BokehLUT,
FDOFConvolutionTextures* ConvolutionOutputTextures,
FRDGTextureRef* ScatterOcclusionTexture)
{
bool bUsesScatterOcclusionTexture =
ConvolutionSettings.QualityConfig == EDiaphragmDOFGatherQuality::HighQualityWithHybridScatterOcclusion ||
ConvolutionSettings.QualityConfig == EDiaphragmDOFGatherQuality::Cinematic;
// Allocate output textures
{
FRDGTextureDesc Desc = ReducedGatherInputTextures.SceneColor->Desc;
Desc.Extent = RefBufferSize;
Desc.Format = PF_FloatRGBA;
Desc.Flags |= TexCreate_UAV;
Desc.NumMips = 1;
{
const TCHAR* DebugName = nullptr;
if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::ForegroundOnly)
DebugName = TEXT("DOF.GatherForeground");
else if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::ForegroundHoleFilling)
DebugName = TEXT("DOF.GatherForegroundFill");
else if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::BackgroundOnly)
DebugName = TEXT("DOF.GatherBackground");
else if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::SlightOutOfFocus)
DebugName = TEXT("DOF.GatherFocus");
else
check(0);
FRDGTextureDesc SceneColorDesc = Desc;
// Scattering pass will be drawing directly into the color of the gathered texture, so need to be render targetable.
if (ConvolutionSettings.bHasScatterPass && ConvolutionSettings.PostfilterMethod == EDiaphragmDOFPostfilterMethod::None)
{
SceneColorDesc.Flags |= TexCreate_RenderTargetable;
}
ConvolutionOutputTextures->SceneColor = GraphBuilder.CreateTexture(SceneColorDesc, DebugName);
if (bProcessSceneAlpha)
{
Desc.Format = PF_R16F;
ConvolutionOutputTextures->SeparateAlpha = GraphBuilder.CreateTexture(Desc, DebugName);
}
}
if (bUsesScatterOcclusionTexture)
{
Desc.Format = PF_G16R16F;
const TCHAR* DebugName = nullptr;
if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::BackgroundOnly)
DebugName = TEXT("DOF.ScatterOcclusionBackground");
else
check(0);
*ScatterOcclusionTexture = GraphBuilder.CreateTexture(Desc, DebugName);
}
}
FIntPoint ReduceOutputRectMip0(
kDefaultGroupSize * FMath::DivideAndRoundUp(PreprocessViewSize.X, kDefaultGroupSize),
kDefaultGroupSize * FMath::DivideAndRoundUp(PreprocessViewSize.Y, kDefaultGroupSize));
FIntPoint SrcSize = ReducedGatherInputTextures.SceneColor->Desc.Extent;
FDiaphragmDOFGatherCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
PermutationVector.Set<FDDOFLayerProcessingDim>(ConvolutionSettings.LayerProcessing);
PermutationVector.Set<FDDOFGatherRingCountDim>(ConvolutionSettings.RingCount);
PermutationVector.Set<FDDOFGatherQualityDim>(ConvolutionSettings.QualityConfig);
PermutationVector.Set<FDDOFBokehSimulationDim>(ConvolutionSettings.BokehSimulation);
PermutationVector.Set<FDDOFRGBColorBufferDim>(bRGBBufferSeparateCocBuffer);
PermutationVector = FDiaphragmDOFGatherCS::RemapPermutation(PermutationVector);
// Affine transformtation to control whether a CocRadius is considered or not.
FVector2f ConsiderCocRadiusAffineTransformation0 = kContantlyPassingAffineTransformation;
FVector2f ConsiderCocRadiusAffineTransformation1 = kContantlyPassingAffineTransformation;
FVector2f ConsiderAbsCocRadiusAffineTransformation = kContantlyPassingAffineTransformation;
{
// Coc radius considered.
const float RecombineCocRadiusBorder = kMaxSlightOutOfFocusCocRadius - 1.0f;
if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::ForegroundOnly)
{
ConsiderCocRadiusAffineTransformation0 = GenerateSaturatedAffineTransformation(
-(RecombineCocRadiusBorder - 1.0f), -RecombineCocRadiusBorder);
ConsiderAbsCocRadiusAffineTransformation = GenerateSaturatedAffineTransformation(
RecombineCocRadiusBorder - 1.0f, RecombineCocRadiusBorder);
}
else if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::ForegroundHoleFilling)
{
ConsiderCocRadiusAffineTransformation0 = GenerateSaturatedAffineTransformation(
RecombineCocRadiusBorder, RecombineCocRadiusBorder + 1.0f);
}
else if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::BackgroundOnly)
{
ConsiderCocRadiusAffineTransformation0 = GenerateSaturatedAffineTransformation(
RecombineCocRadiusBorder - 1.0f, RecombineCocRadiusBorder);
ConsiderAbsCocRadiusAffineTransformation = GenerateSaturatedAffineTransformation(
RecombineCocRadiusBorder - 1.0f, RecombineCocRadiusBorder);
}
else if (ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::SlightOutOfFocus)
{
ConsiderAbsCocRadiusAffineTransformation = GenerateSaturatedAffineTransformation(
RecombineCocRadiusBorder + 1.0f, RecombineCocRadiusBorder);
}
else
{
checkf(0, TEXT("What layer processing is that?"));
}
}
FDiaphragmDOFGatherCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFGatherCS::FParameters>();
PassParameters->ViewportSize = FVector4f(GatheringViewSize.X, GatheringViewSize.Y, 1.0f / GatheringViewSize.X, 1.0f / GatheringViewSize.Y);
PassParameters->ViewportRect = FIntRect(0, 0, GatheringViewSize.X, GatheringViewSize.Y);
PassParameters->TemporalJitterPixels = FVector2f(View.TemporalJitterPixels); // LWC_TODO: Precision loss
PassParameters->DispatchThreadIdToInputBufferUV = FVector2f(
float(PreprocessViewSize.X) / float(GatheringViewSize.X * SrcSize.X),
float(PreprocessViewSize.Y) / float(GatheringViewSize.Y * SrcSize.Y));;
PassParameters->ConsiderCocRadiusAffineTransformation0 = ConsiderCocRadiusAffineTransformation0;
PassParameters->ConsiderCocRadiusAffineTransformation1 = ConsiderCocRadiusAffineTransformation1;
PassParameters->ConsiderAbsCocRadiusAffineTransformation = ConsiderAbsCocRadiusAffineTransformation;
PassParameters->InputBufferUVToOutputPixel = FVector2f(
float(SrcSize.X * GatheringViewSize.X) / float(PreprocessViewSize.X),
float(SrcSize.Y * GatheringViewSize.Y) / float(PreprocessViewSize.Y));
PassParameters->MipBias = FMath::Log2(float(PreprocessViewSize.X) / float(GatheringViewSize.X));
PassParameters->MaxRecombineAbsCocRadius = float(kMaxSlightOutOfFocusCocRadius);
PassParameters->CocSqueeze = CocModel.Squeeze;
PassParameters->CocInvSqueeze = 1.0f / CocModel.Squeeze;
PassParameters->Petzval = CocModel.Petzval;
PassParameters->PetzvalFalloffPower = CocModel.PetzvalFalloffPower;
PassParameters->PetzvalExclusionBoxExtents = CocModel.PetzvalExclusionBoxExtents;
PassParameters->PetzvalExclusionBoxRadius = CocModel.PetzvalExclusionBoxRadius;
PassParameters->TileDecisionParameters.MinGatherRadius = float(kMaxSlightOutOfFocusCocRadius) - 1;
PassParameters->TileDecisionParameters.SlightOutOfFocusRadiusBoundary = float(kMaxSlightOutOfFocusCocRadius);
PassParameters->CommonParameters = CommonParameters;
PassParameters->GatherInputSize = FVector4f(SrcSize.X, SrcSize.Y, 1.0f / SrcSize.X, 1.0f / SrcSize.Y);
PassParameters->GatherInputViewportSize = FVector2f(PreprocessViewSize.X, PreprocessViewSize.Y);
PassParameters->GatherInput = ReducedGatherInputTextures;
PassParameters->TileClassification = TileClassificationTextures;
PassParameters->BokehLUT = BokehLUT;
PassParameters->ConvolutionOutput = CreateUAVs(GraphBuilder, *ConvolutionOutputTextures);
if (bUsesScatterOcclusionTexture)
{
PassParameters->ScatterOcclusionOutput = GraphBuilder.CreateUAV(*ScatterOcclusionTexture);
}
{
FRDGTextureDesc DebugDesc = FRDGTextureDesc::Create2D(
RefBufferSize,
PF_FloatRGBA,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
PassParameters->DebugOutput = GraphBuilder.CreateUAV(GraphBuilder.CreateTexture(DebugDesc, TEXT("Debug.DOF.Gather")));
}
TShaderMapRef<FDiaphragmDOFGatherCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF Gather(%s %s Bokeh=%s Rings=%d%s) %dx%d",
GetEventName(ConvolutionSettings.LayerProcessing),
GetEventName(ConvolutionSettings.QualityConfig),
GetEventName(ConvolutionSettings.BokehSimulation),
int32(PermutationVector.Get<FDDOFGatherRingCountDim>()),
PermutationVector.Get<FDDOFRGBColorBufferDim>() ? TEXT(" R11G11B10") : TEXT(""),
GatheringViewSize.X, GatheringViewSize.Y),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(GatheringViewSize, kDefaultGroupSize));
}; // AddGatherPass()
auto AddPostFilterPass = [&](
const FConvolutionSettings& ConvolutionSettings,
FDOFConvolutionTextures* ConvolutionTextures)
{
if (ConvolutionSettings.PostfilterMethod == EDiaphragmDOFPostfilterMethod::None)
{
return;
}
FDOFConvolutionTextures NewConvolutionTextures;
{
FRDGTextureDesc Desc = ConvolutionTextures->SceneColor->Desc;
// Scattering pass will be drawing directly into the post filtered color texture, so need to be render targetable.
if (ConvolutionSettings.bHasScatterPass)
{
Desc.Flags |= TexCreate_RenderTargetable;
}
NewConvolutionTextures.SceneColor = GraphBuilder.CreateTexture(Desc, ConvolutionTextures->SceneColor->Name);
}
if (ConvolutionTextures->SeparateAlpha)
{
NewConvolutionTextures.SeparateAlpha = GraphBuilder.CreateTexture(ConvolutionTextures->SeparateAlpha->Desc, ConvolutionTextures->SeparateAlpha->Name);
}
FDiaphragmDOFPostfilterCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
PermutationVector.Set<FDDOFLayerProcessingDim>(ConvolutionSettings.LayerProcessing);
PermutationVector.Set<FDDOFPostfilterMethodDim>(ConvolutionSettings.PostfilterMethod);
PermutationVector.Set<FDiaphragmDOFPostfilterCS::FTileOptimization>(true); // TODO
PermutationVector = FDiaphragmDOFPostfilterCS::RemapPermutationVector(PermutationVector);
float MaxRecombineAbsCocRadius = float(kMaxSlightOutOfFocusCocRadius);
FDiaphragmDOFPostfilterCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFPostfilterCS::FParameters>();
PassParameters->ViewportRect = FIntRect(0, 0, GatheringViewSize.X, GatheringViewSize.Y);
PassParameters->MaxInputBufferUV = FVector2f(
(GatheringViewSize.X - 0.5f) / float(RefBufferSize.X),
(GatheringViewSize.Y - 0.5f) / float(RefBufferSize.Y));
PassParameters->TileDecisionParameters.MinGatherRadius = MaxRecombineAbsCocRadius - 1;
PassParameters->CommonParameters = CommonParameters;
PassParameters->ConvolutionInputSize = FVector4f(RefBufferSize.X, RefBufferSize.Y, 1.0f / RefBufferSize.X, 1.0f / RefBufferSize.Y);
PassParameters->ConvolutionInput = *ConvolutionTextures;
PassParameters->TileClassification = TileClassificationTextures;
PassParameters->ConvolutionOutput = CreateUAVs(GraphBuilder, NewConvolutionTextures);
{
FRDGTextureDesc DebugDesc = FRDGTextureDesc::Create2D(
RefBufferSize,
PF_FloatRGBA,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
PassParameters->DebugOutput = GraphBuilder.CreateUAV(GraphBuilder.CreateTexture(DebugDesc, TEXT("Debug.DOF.PostFilter")));
}
TShaderMapRef<FDiaphragmDOFPostfilterCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF Postfilter(%s %s%s) %dx%d",
GetEventName(ConvolutionSettings.LayerProcessing), GetEventName(ConvolutionSettings.PostfilterMethod),
PermutationVector.Get<FDiaphragmDOFPostfilterCS::FTileOptimization>() ? TEXT(" TileOptimisation") : TEXT(""),
GatheringViewSize.X, GatheringViewSize.Y),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(GatheringViewSize, kDefaultGroupSize));
*ConvolutionTextures = NewConvolutionTextures;
}; // AddPostFilterPass()
FRDGTextureRef GatheringBokehLUT = nullptr;
if (bEnableGatherBokehSettings)
GatheringBokehLUT = AddBuildBokehLUTPass(EDiaphragmDOFBokehLUTFormat::GatherSamplePos);
FRDGTextureRef ScatteringBokehLUT = nullptr;
if (bEnableScatterBokehSettings || bEnableSlightOutOfFocusBokeh)
ScatteringBokehLUT = AddBuildBokehLUTPass(EDiaphragmDOFBokehLUTFormat::CocRadiusToBokehEdgeFactor);
auto AddHybridScatterPass = [&](
const FConvolutionSettings& ConvolutionSettings,
FDOFConvolutionTextures* ConvolutionTextures,
FRDGTextureRef ScatterOcclusionTexture,
FRDGBufferRef ScatterDrawList)
{
bool bIsForeground = ConvolutionSettings.LayerProcessing == EDiaphragmDOFLayerProcessing::ForegroundOnly;
int32 DrawIndirectParametersOffset = bIsForeground ? 0 : 1;
EDiaphragmDOFScatterQuality Quality {};
if (ConvolutionSettings.QualityConfig >= EDiaphragmDOFGatherQuality::HighQuality)
{
Quality = EDiaphragmDOFScatterQuality::High;
}
else
{
Quality = EDiaphragmDOFScatterQuality::Medium;
}
FDiaphragmDOFHybridScatterVS::FPermutationDomain VSPermutationVector;
VSPermutationVector.Set<FDDOFScatterQualityDim>(Quality);
FDiaphragmDOFHybridScatterPS::FPermutationDomain PSPermutationVector;
PSPermutationVector.Set<FDDOFLayerProcessingDim>(ConvolutionSettings.LayerProcessing);
PSPermutationVector.Set<FDiaphragmDOFHybridScatterPS::FBokehSimulationDim>(ScatteringBokehLUT ? true : false);
PSPermutationVector.Set<FDDOFScatterOcclusionDim>(ScatterOcclusionTexture ? true : false);
PSPermutationVector.Set<FDDOFScatterQualityDim>(Quality);
PSPermutationVector = FDiaphragmDOFHybridScatterPS::RemapPermutation(PSPermutationVector);
TShaderMapRef<FDiaphragmDOFHybridScatterVS> VertexShader(View.ShaderMap, VSPermutationVector);
TShaderMapRef<FDiaphragmDOFHybridScatterPS> PixelShader(View.ShaderMap, PSPermutationVector);
FDOFHybridScatterParameters* PassParameters = GraphBuilder.AllocParameters<FDOFHybridScatterParameters>();
PassParameters->ViewportSize = FVector4f(GatheringViewSize.X, GatheringViewSize.Y, 1.0f / GatheringViewSize.X, 1.0f / GatheringViewSize.Y);
PassParameters->CocRadiusToCircumscribedRadius = BokehModel.CocRadiusToCircumscribedRadius;
PassParameters->ScatteringScaling = float(GatheringViewSize.X) / float(PreprocessViewSize.X);
PassParameters->Petzval = CocModel.Petzval;
PassParameters->PetzvalFalloffPower = CocModel.PetzvalFalloffPower;
PassParameters->PetzvalExclusionBoxExtents = CocModel.PetzvalExclusionBoxExtents;
PassParameters->PetzvalExclusionBoxRadius = CocModel.PetzvalExclusionBoxRadius;
PassParameters->CommonParameters = CommonParameters;
SetCocModelParameters(GraphBuilder, &PassParameters->CocModel, CocModel, float(GatheringViewSize.X));
PassParameters->BokehLUT = ScatteringBokehLUT;
PassParameters->ScatterOcclusionSize = FVector4f(RefBufferSize.X, RefBufferSize.Y, 1.0f / RefBufferSize.X, 1.0f / RefBufferSize.Y);
PassParameters->ScatterOcclusion = ScatterOcclusionTexture;
PassParameters->IndirectDrawParameter = DrawIndirectParametersBuffer;
PassParameters->ScatterDrawList = GraphBuilder.CreateSRV(ScatterDrawList);
PassParameters->RenderTargets[0] = FRenderTargetBinding(
ConvolutionTextures->SceneColor, ERenderTargetLoadAction::ELoad);
PassParameters->LensImageDistance = 1.0f / (1.0f/CocModel.VerticalFocalLength - 1.0f/CocModel.FocusDistance);
PassParameters->SensorSize = FVector2f(CocModel.SensorWidth, CocModel.SensorHeight);
PassParameters->Aperture = CocModel.GetLensRadius().Y * 2.0f;
PassParameters->BarrelRadius = CocModel.BarrelRadius;
PassParameters->BarrelLength = bVignetteActive ? CocModel.BarrelLength : -1.0f;
PassParameters->MatteBoxPlanes = MatteBoxPlanes;
ClearUnusedGraphResources(VertexShader, PixelShader, PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("DOF IndirectScatter(%s Bokeh=%s Occlusion=%s) %dx%d",
GetEventName(bIsForeground ? EDiaphragmDOFLayerProcessing::ForegroundOnly : EDiaphragmDOFLayerProcessing::BackgroundOnly),
PSPermutationVector.Get<FDiaphragmDOFHybridScatterPS::FBokehSimulationDim>() ? TEXT("Generic") : TEXT("None"),
PSPermutationVector.Get<FDDOFScatterOcclusionDim>() ? TEXT("Yes") : TEXT("No"),
GatheringViewSize.X, GatheringViewSize.Y),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, VertexShader, PixelShader, GatheringViewSize, DrawIndirectParametersOffset](FRDGAsyncTask, FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(0, 0, 0.0f, GatheringViewSize.X, GatheringViewSize.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.PrimitiveType = GRHISupportsRectTopology ? PT_RectList : PT_TriangleList;
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GEmptyVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), *PassParameters);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
RHICmdList.SetStreamSource(0, NULL, 0);
// Marks the indirect draw parameter as used by the pass, given it's not used directly by any of the shaders.
PassParameters->IndirectDrawParameter->MarkResourceAsUsed();
if (GRHISupportsRectTopology)
{
RHICmdList.DrawPrimitiveIndirect(
PassParameters->IndirectDrawParameter->GetIndirectRHICallBuffer(),
sizeof(FRHIDrawIndirectParameters) * DrawIndirectParametersOffset);
}
else
{
RHICmdList.DrawIndexedPrimitiveIndirect(
GDOFGlobalResource.ScatterIndexBuffer.IndexBufferRHI,
PassParameters->IndirectDrawParameter->GetIndirectRHICallBuffer(),
sizeof(FRHIDrawIndexedIndirectParameters) * DrawIndirectParametersOffset);
}
});
}; // AddHybridScatterPass()
struct FDOFPassDescriptor
{
FConvolutionSettings ConvolutionSettings;
FRDGTextureRef BokehLUT;
FDOFConvolutionTextures* ConvolutionTextures;
FRDGBufferRef ScatterDrawList;
FRDGTextureRef ScatterOcclusionTexture = nullptr;
uint32 bDoGather : 1;
uint32 bDoPostFilter : 1;
uint32 bDoScatter : 1;
};
TArray<FDOFPassDescriptor, TInlineAllocator<4>> Passes;
// Wire foreground gathering passes.
if (bGatherForeground)
{
FConvolutionSettings ConvolutionSettings;
ConvolutionSettings.LayerProcessing = EDiaphragmDOFLayerProcessing::ForegroundOnly;
ConvolutionSettings.PostfilterMethod = PostfilterMethod;
ConvolutionSettings.RingCount = HalfResRingCount;
ConvolutionSettings.bHasScatterPass = bForegroundHybridScattering;
if (bEnableGatherBokehSettings)
ConvolutionSettings.BokehSimulation = BokehSimulation;
if (bUseLowAccumulatorQuality)
{
ConvolutionSettings.QualityConfig = EDiaphragmDOFGatherQuality::LowQualityAccumulator;
}
Passes.Emplace(FDOFPassDescriptor {
.ConvolutionSettings = ConvolutionSettings,
.BokehLUT = GatheringBokehLUT,
.ConvolutionTextures = &ForegroundConvolutionTextures,
.ScatterDrawList = ForegroundScatterDrawListBuffer,
.bDoGather = true,
.bDoPostFilter = true,
.bDoScatter = bForegroundHybridScattering
});
}
// Wire hole filling gathering passes.
if (bRecombineDoesSeparateForegroundHoleFilling)
{
FConvolutionSettings ConvolutionSettings;
ConvolutionSettings.LayerProcessing = EDiaphragmDOFLayerProcessing::ForegroundHoleFilling;
ConvolutionSettings.PostfilterMethod = PostfilterMethod;
ConvolutionSettings.RingCount = HalfResRingCount;
Passes.Emplace(FDOFPassDescriptor {
.ConvolutionSettings = ConvolutionSettings,
.BokehLUT = nullptr,
.ConvolutionTextures = &ForegroundHoleFillingConvolutionTextures,
.bDoGather = true,
.bDoPostFilter = false,
.bDoScatter = false
});
}
// Gather slight out of focus.
if (bRecombineDoesSlightOutOfFocus)
{
FConvolutionSettings ConvolutionSettings;
ConvolutionSettings.LayerProcessing = EDiaphragmDOFLayerProcessing::SlightOutOfFocus;
if (bEnableSlightOutOfFocusBokeh)
ConvolutionSettings.BokehSimulation = BokehSimulation;
// Number of rings is dynamic in shader.
ConvolutionSettings.RingCount = kMinGatheringRingCount;
Passes.Emplace(FDOFPassDescriptor {
.ConvolutionSettings = ConvolutionSettings,
.BokehLUT = bEnableSlightOutOfFocusBokeh ? ScatteringBokehLUT : nullptr,
.ConvolutionTextures = &SlightOutOfFocusConvolutionTextures,
.bDoGather = true,
.bDoPostFilter = false,
.bDoScatter = false
});
}
// Wire background gathering passes.
if (bGatherBackground)
{
FConvolutionSettings ConvolutionSettings;
ConvolutionSettings.LayerProcessing = EDiaphragmDOFLayerProcessing::BackgroundOnly;
ConvolutionSettings.PostfilterMethod = PostfilterMethod;
ConvolutionSettings.RingCount = HalfResRingCount;
ConvolutionSettings.bHasScatterPass = bBackgroundHybridScattering;
if (bEnableGatherBokehSettings)
ConvolutionSettings.BokehSimulation = BokehSimulation;
ConvolutionSettings.QualityConfig = EDiaphragmDOFGatherQuality::LowQualityAccumulator;
if (bBackgroundHybridScattering && BgdHybridScatteringMode == EHybridScatterMode::Occlusion)
{
if (bUseCinematicAccumulatorQuality)
{
ConvolutionSettings.QualityConfig = EDiaphragmDOFGatherQuality::Cinematic;
}
else
{
ConvolutionSettings.QualityConfig = EDiaphragmDOFGatherQuality::HighQualityWithHybridScatterOcclusion;
}
}
Passes.Emplace(FDOFPassDescriptor {
.ConvolutionSettings = ConvolutionSettings,
.BokehLUT = GatheringBokehLUT,
.ConvolutionTextures = &BackgroundConvolutionTextures,
.ScatterDrawList = BackgroundScatterDrawListBuffer,
.bDoGather = true,
.bDoPostFilter = true,
.bDoScatter = bBackgroundHybridScattering
});
}
const bool bAvoidPSOSwitch = true;
if (bAvoidPSOSwitch)
{
for (FDOFPassDescriptor& Pass : Passes)
{
if (Pass.bDoGather)
AddGatherPass(Pass.ConvolutionSettings, Pass.BokehLUT, Pass.ConvolutionTextures, &Pass.ScatterOcclusionTexture);
}
for (FDOFPassDescriptor& Pass : Passes)
{
if (Pass.bDoPostFilter)
AddPostFilterPass(Pass.ConvolutionSettings, Pass.ConvolutionTextures);
}
for (FDOFPassDescriptor& Pass : Passes)
{
if (Pass.bDoScatter)
AddHybridScatterPass(Pass.ConvolutionSettings, Pass.ConvolutionTextures, Pass.ScatterOcclusionTexture, Pass.ScatterDrawList);
}
}
else
{
for (FDOFPassDescriptor& Pass : Passes)
{
if (Pass.bDoGather)
AddGatherPass(Pass.ConvolutionSettings, Pass.BokehLUT, Pass.ConvolutionTextures, &Pass.ScatterOcclusionTexture);
if (Pass.bDoPostFilter)
AddPostFilterPass(Pass.ConvolutionSettings, Pass.ConvolutionTextures);
if (Pass.bDoScatter)
AddHybridScatterPass(Pass.ConvolutionSettings, Pass.ConvolutionTextures, Pass.ScatterOcclusionTexture, Pass.ScatterDrawList);
}
}
}
// Recombine lower res out of focus with full res scene color.
FRDGTextureRef NewSceneColor;
{
{
FRDGTextureDesc Desc = InputSceneColor->Desc;
Desc.Reset();
Desc.Flags |= TexCreate_UAV;
NewSceneColor = GraphBuilder.CreateTexture(Desc, TEXT("DOF.Recombine"));
}
bool bScaleSeparateTranslucency = false;
FScreenPassTextureViewport SeparateTranslucencyViewport;
if (TranslucencyPassResources.IsValid())
{
SeparateTranslucencyViewport = TranslucencyPassResources.GetTextureViewport();
bScaleSeparateTranslucency = SeparateTranslucencyViewport.Rect.Size() != FullResViewSize;
}
else
{
SeparateTranslucencyViewport = FScreenPassTextureViewport(FIntPoint(0, 0), FIntRect(0, 0, 1, 1));
}
FIntRect PassViewRect = View.ViewRect;
FDiaphragmDOFRecombineCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDiaphragmDOFShader::FAlphaChannelDim>(bProcessSceneAlpha);
if (bGatherForeground && bGatherBackground)
{
PermutationVector.Set<FDDOFLayerProcessingDim>(EDiaphragmDOFLayerProcessing::ForegroundAndBackground);
}
else if (bGatherForeground && !bGatherBackground)
{
PermutationVector.Set<FDDOFLayerProcessingDim>(EDiaphragmDOFLayerProcessing::ForegroundOnly);
}
else if (!bGatherForeground && bGatherBackground)
{
PermutationVector.Set<FDDOFLayerProcessingDim>(EDiaphragmDOFLayerProcessing::BackgroundOnly);
}
else
{
PermutationVector.Set<FDDOFLayerProcessingDim>(EDiaphragmDOFLayerProcessing::BackgroundOnly);
}
if (bEnableSlightOutOfFocusBokeh)
PermutationVector.Set<FDDOFBokehSimulationDim>(BokehSimulation);
PermutationVector.Set<FDiaphragmDOFRecombineCS::FQualityDim>(RecombineQuality);
FRDGTextureRef SeparateTranslucency = TranslucencyPassResources.GetColorForRead(GraphBuilder);
FRDGTextureRef SeparateTranslucencyDepth = TranslucencyPassResources.GetDepthForRead(GraphBuilder);
FRDGTextureRef SeparateTranslucencyModulateColor = TranslucencyPassResources.GetColorModulateForRead(GraphBuilder);
FDiaphragmDOFRecombineCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDiaphragmDOFRecombineCS::FParameters>();
PassParameters->CommonParameters = CommonParameters;
SetCocModelParameters(GraphBuilder, &PassParameters->CocModel, CocModel, /* CocRadiusBasis = */ float(GatheringViewSize.X));
PassParameters->ViewportRect = PassViewRect;
PassParameters->ViewportSize = FVector4f(PassViewRect.Width(), PassViewRect.Height(), 1.0f / PassViewRect.Width(), 1.0f / PassViewRect.Height());
PassParameters->DispatchThreadIdToDOFBufferUV = (
FScreenTransform::DispatchThreadIdToViewportUV(PassViewRect) *
FScreenTransform::ChangeTextureBasisFromTo(
RefBufferSize,
FIntRect(FIntPoint::ZeroValue, GatheringViewSize),
FScreenTransform::ETextureBasis::ViewportUV,
FScreenTransform::ETextureBasis::TextureUV)
- FVector2f(View.TemporalJitterPixels) * FVector2f(1.0f / float(InputSceneColor->Desc.Extent.X), 1.0f / float(InputSceneColor->Desc.Extent.Y)));
PassParameters->DOFBufferUVMax = FVector2f(
(GatheringViewSize.X - 0.5f) / float(RefBufferSize.X),
(GatheringViewSize.Y - 0.5f) / float(RefBufferSize.Y));
PassParameters->EncodedCocRadiusToRecombineCocRadius = float(GatheringViewSize.X) / EncodedCocRadiusBasis;
PassParameters->MaxRecombineAbsCocRadius = float(kMaxSlightOutOfFocusCocRadius) * PassParameters->EncodedCocRadiusToRecombineCocRadius;
PassParameters->SeparateTranslucencyBilinearUVMinMax.X = (SeparateTranslucencyViewport.Rect.Min.X + 0.5f) / float(SeparateTranslucencyViewport.Extent.X);
PassParameters->SeparateTranslucencyBilinearUVMinMax.Y = (SeparateTranslucencyViewport.Rect.Min.Y + 0.5f) / float(SeparateTranslucencyViewport.Extent.Y);
PassParameters->SeparateTranslucencyBilinearUVMinMax.Z = (SeparateTranslucencyViewport.Rect.Max.X - 0.5f) / float(SeparateTranslucencyViewport.Extent.X);
PassParameters->SeparateTranslucencyBilinearUVMinMax.W = (SeparateTranslucencyViewport.Rect.Max.Y - 0.5f) / float(SeparateTranslucencyViewport.Extent.Y);
PassParameters->SeparateTranslucencyUpscaling = bScaleSeparateTranslucency ? 1 : 0;
PassParameters->SceneColorInput = FullResGatherInputTextures.SceneColor;
PassParameters->SceneDepthTexture = SceneTextures.SceneDepthTexture;
PassParameters->SceneSeparateCoc = FullResGatherInputTextures.SeparateCoc; // TODO looks useless.
PassParameters->SceneSeparateTranslucency = SeparateTranslucency;
PassParameters->SceneSeparateTranslucencyModulateColor = SeparateTranslucencyModulateColor;
PassParameters->ConvolutionInputSize = FVector4f(RefBufferSize.X, RefBufferSize.Y, 1.0f / RefBufferSize.X, 1.0f / RefBufferSize.Y);
PassParameters->ForegroundConvolution = ForegroundConvolutionTextures;
PassParameters->ForegroundHoleFillingConvolution = ForegroundHoleFillingConvolutionTextures;
PassParameters->SlightOutOfFocusConvolution = SlightOutOfFocusConvolutionTextures;
PassParameters->BackgroundConvolution = BackgroundConvolutionTextures;
// Separate translucency upsampling
PassParameters->FullResDepthTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMetaData(SceneTextures.SceneDepthTexture, ERDGTextureMetaDataAccess::Depth));
PassParameters->LowResDepthTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMetaData(SeparateTranslucencyDepth, ERDGTextureMetaDataAccess::Depth));
const FIntPoint LowResExtent = SeparateTranslucency->Desc.Extent;
PassParameters->SeparateTranslucencyTextureLowResExtentInverse = FVector2f(1.0f / LowResExtent.X, 1.0f / LowResExtent.Y);
if (!bGatherForeground && !bGatherBackground)
{
check(PassParameters->BackgroundConvolution.SceneColor == nullptr);
check(PassParameters->BackgroundConvolution.SeparateAlpha == nullptr);
PassParameters->BackgroundConvolution.SceneColor = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
PassParameters->BackgroundConvolution.SeparateAlpha = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
}
if (bEnableSlightOutOfFocusBokeh) // && ScatteringBokehLUTOutput.IsValid() && SlightOutOfFocusConvolutionOutput.IsValid())
{
PassParameters->BokehLUT = AddBuildBokehLUTPass(EDiaphragmDOFBokehLUTFormat::FullResOffsetToCocDistance);
}
PassParameters->SceneColorOutput = GraphBuilder.CreateUAV(NewSceneColor);
{
FRDGTextureDesc DebugDesc = FRDGTextureDesc::Create2D(
InputSceneColor->Desc.Extent,
PF_FloatRGBA,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV);
PassParameters->DebugOutput = GraphBuilder.CreateUAV(GraphBuilder.CreateTexture(DebugDesc, TEXT("Debug.DOF.Recombine")));
}
TShaderMapRef<FDiaphragmDOFRecombineCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("DOF Recombine(%s Quality=%d Bokeh=%s%s) %dx%d",
GetEventName(PermutationVector.Get<FDDOFLayerProcessingDim>()),
RecombineQuality,
GetEventName(PermutationVector.Get<FDDOFBokehSimulationDim>()),
bScaleSeparateTranslucency ? TEXT(" RescaleSeparateTranslucency") : TEXT(""),
PassViewRect.Width(), PassViewRect.Height()),
ComputeShader,
PassParameters,
FComputeShaderUtils::GetGroupCount(PassViewRect.Size(), kDefaultGroupSize));
}
OutputColor = NewSceneColor;
return true;
}