Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/GlobalDistanceField.cpp
mickael gilabert d8e082633e Added Distance field shadow / AO support on Switch
Don't compile distance field shader permutations if bUseDistanceFields is unset or false
Added UAV output to pixel shader
Clear Tiny UAV uses command buffer ClearBuffer command instead of allocating a temp buffer and copying it to UAV

anthony.bills
#rnx

#ROBOMERGE-OWNER: ryan.vance
#ROBOMERGE-AUTHOR: mickael.gilabert
#ROBOMERGE-SOURCE: CL 6077502 via CL 6077551 via CL 6080478 via CL 6080627
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 6084211 by mickael gilabert in Dev-VR branch]
2019-04-24 17:06:06 -04:00

1591 lines
72 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
GlobalDistanceField.cpp
=============================================================================*/
#include "GlobalDistanceField.h"
#include "DistanceFieldLightingShared.h"
#include "RendererModule.h"
#include "ClearQuad.h"
DECLARE_GPU_STAT(GlobalDistanceFieldUpdate);
int32 GAOGlobalDistanceField = 1;
FAutoConsoleVariableRef CVarAOGlobalDistanceField(
TEXT("r.AOGlobalDistanceField"),
GAOGlobalDistanceField,
TEXT("Whether to use a global distance field to optimize occlusion cone traces.\n")
TEXT("The global distance field is created by compositing object distance fields into clipmaps as the viewer moves through the level."),
ECVF_RenderThreadSafe
);
int32 GAOUpdateGlobalDistanceField = 1;
FAutoConsoleVariableRef CVarAOUpdateGlobalDistanceField(
TEXT("r.AOUpdateGlobalDistanceField"),
GAOUpdateGlobalDistanceField,
TEXT("Whether to update the global distance field, useful for debugging."),
ECVF_RenderThreadSafe
);
int32 GAOGlobalDistanceFieldCacheMostlyStaticSeparately = 1;
FAutoConsoleVariableRef CVarAOGlobalDistanceFieldCacheMostlyStaticSeparately(
TEXT("r.AOGlobalDistanceFieldCacheMostlyStaticSeparately"),
GAOGlobalDistanceFieldCacheMostlyStaticSeparately,
TEXT("Whether to cache mostly static primitives separately from movable primitives, which reduces global DF update cost when a movable primitive is modified. Adds another 12Mb of volume textures."),
ECVF_RenderThreadSafe
);
int32 GAOGlobalDistanceFieldPartialUpdates = 1;
FAutoConsoleVariableRef CVarAOGlobalDistanceFieldPartialUpdates(
TEXT("r.AOGlobalDistanceFieldPartialUpdates"),
GAOGlobalDistanceFieldPartialUpdates,
TEXT("Whether to allow partial updates of the global distance field. When profiling it's useful to disable this and get the worst case composition time that happens on camera cuts."),
ECVF_RenderThreadSafe
);
int32 GAOGlobalDistanceFieldStaggeredUpdates = 1;
FAutoConsoleVariableRef CVarAOGlobalDistanceFieldStaggeredUpdatess(
TEXT("r.AOGlobalDistanceFieldStaggeredUpdates"),
GAOGlobalDistanceFieldStaggeredUpdates,
TEXT("Whether to allow the larger clipmaps to be updated less frequently."),
ECVF_RenderThreadSafe
);
int32 GAOGlobalDistanceFieldForceFullUpdate = 0;
FAutoConsoleVariableRef CVarAOGlobalDistanceFieldForceFullUpdate(
TEXT("r.AOGlobalDistanceFieldForceFullUpdate"),
GAOGlobalDistanceFieldForceFullUpdate,
TEXT("Whether to force full global distance field update every frame."),
ECVF_RenderThreadSafe
);
int32 GAOLogGlobalDistanceFieldModifiedPrimitives = 0;
FAutoConsoleVariableRef CVarAOLogGlobalDistanceFieldModifiedPrimitives(
TEXT("r.AOGlobalDistanceFieldLogModifiedPrimitives"),
GAOLogGlobalDistanceFieldModifiedPrimitives,
TEXT("Whether to log primitive modifications (add, remove, updatetransform) that caused an update of the global distance field.\n")
TEXT("This can be useful for tracking down why updating the global distance field is always costing a lot, since it should be mostly cached."),
ECVF_RenderThreadSafe
);
float GAOGlobalDFClipmapDistanceExponent = 2;
FAutoConsoleVariableRef CVarAOGlobalDFClipmapDistanceExponent(
TEXT("r.AOGlobalDFClipmapDistanceExponent"),
GAOGlobalDFClipmapDistanceExponent,
TEXT("Exponent used to derive each clipmap's size, together with r.AOInnerGlobalDFClipmapDistance."),
ECVF_RenderThreadSafe
);
int32 GAOGlobalDFResolution = 128;
FAutoConsoleVariableRef CVarAOGlobalDFResolution(
TEXT("r.AOGlobalDFResolution"),
GAOGlobalDFResolution,
TEXT("Resolution of the global distance field. Higher values increase fidelity but also increase memory and composition cost."),
ECVF_RenderThreadSafe
);
float GAOGlobalDFStartDistance = 100;
FAutoConsoleVariableRef CVarAOGlobalDFStartDistance(
TEXT("r.AOGlobalDFStartDistance"),
GAOGlobalDFStartDistance,
TEXT("World space distance along a cone trace to switch to using the global distance field instead of the object distance fields.\n")
TEXT("This has to be large enough to hide the low res nature of the global distance field, but smaller values result in faster cone tracing."),
ECVF_RenderThreadSafe
);
int32 GAOGlobalDistanceFieldRepresentHeightfields = 1;
FAutoConsoleVariableRef CVarAOGlobalDistanceFieldRepresentHeightfields(
TEXT("r.AOGlobalDistanceFieldRepresentHeightfields"),
GAOGlobalDistanceFieldRepresentHeightfields,
TEXT("Whether to put landscape in the global distance field. Changing this won't propagate until the global distance field gets recached (fly away and back)."),
ECVF_RenderThreadSafe
);
void FGlobalDistanceFieldInfo::UpdateParameterData(float MaxOcclusionDistance)
{
if (Clipmaps.Num() > 0)
{
for (int32 ClipmapIndex = 0; ClipmapIndex < GMaxGlobalDistanceFieldClipmaps; ClipmapIndex++)
{
FTextureRHIParamRef TextureValue = ClipmapIndex < Clipmaps.Num()
? Clipmaps[ClipmapIndex].RenderTarget->GetRenderTargetItem().ShaderResourceTexture
: NULL;
ParameterData.Textures[ClipmapIndex] = TextureValue;
if (ClipmapIndex < Clipmaps.Num())
{
const FGlobalDistanceFieldClipmap& Clipmap = Clipmaps[ClipmapIndex];
ParameterData.CenterAndExtent[ClipmapIndex] = FVector4(Clipmap.Bounds.GetCenter(), Clipmap.Bounds.GetExtent().X);
// GlobalUV = (WorldPosition - GlobalVolumeCenterAndExtent[ClipmapIndex].xyz + GlobalVolumeScollOffset[ClipmapIndex].xyz) / (GlobalVolumeCenterAndExtent[ClipmapIndex].w * 2) + .5f;
// WorldToUVMul = 1.0f / (GlobalVolumeCenterAndExtent[ClipmapIndex].w * 2)
// WorldToUVAdd = (GlobalVolumeScollOffset[ClipmapIndex].xyz - GlobalVolumeCenterAndExtent[ClipmapIndex].xyz) / (GlobalVolumeCenterAndExtent[ClipmapIndex].w * 2) + .5f
const FVector WorldToUVAdd = (Clipmap.ScrollOffset - Clipmap.Bounds.GetCenter()) / (Clipmap.Bounds.GetExtent().X * 2) + FVector(.5f);
ParameterData.WorldToUVAddAndMul[ClipmapIndex] = FVector4(WorldToUVAdd, 1.0f / (Clipmap.Bounds.GetExtent().X * 2));
}
else
{
ParameterData.CenterAndExtent[ClipmapIndex] = FVector4(0, 0, 0, 0);
ParameterData.WorldToUVAddAndMul[ClipmapIndex] = FVector4(0, 0, 0, 0);
}
}
ParameterData.GlobalDFResolution = GAOGlobalDFResolution;
extern float GAOConeHalfAngle;
const float GlobalMaxSphereQueryRadius = MaxOcclusionDistance / (1 + FMath::Tan(GAOConeHalfAngle));
ParameterData.MaxDistance = GlobalMaxSphereQueryRadius;
}
else
{
FPlatformMemory::Memzero(&ParameterData, sizeof(ParameterData));
}
bInitialized = true;
}
TGlobalResource<FDistanceFieldObjectBufferResource> GGlobalDistanceFieldCulledObjectBuffers;
uint32 CullObjectsGroupSize = 64;
class FCullObjectsForVolumeCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCullObjectsForVolumeCS,Global)
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Parameters.Platform) && IsUsingDistanceFields(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters,OutEnvironment);
OutEnvironment.SetDefine(TEXT("CULLOBJECTS_THREADGROUP_SIZE"), CullObjectsGroupSize);
}
FCullObjectsForVolumeCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
ObjectBufferParameters.Bind(Initializer.ParameterMap);
CulledObjectParameters.Bind(Initializer.ParameterMap);
AOGlobalMaxSphereQueryRadius.Bind(Initializer.ParameterMap, TEXT("AOGlobalMaxSphereQueryRadius"));
VolumeBounds.Bind(Initializer.ParameterMap, TEXT("VolumeBounds"));
AcceptOftenMovingObjectsOnly.Bind(Initializer.ParameterMap, TEXT("AcceptOftenMovingObjectsOnly"));
}
FCullObjectsForVolumeCS()
{
}
void SetParameters(FRHICommandList& RHICmdList, const FScene* Scene, const FSceneView& View, float MaxOcclusionDistance, const FVector4& VolumeBoundsValue, FGlobalDFCacheType CacheType)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
ObjectBufferParameters.Set(RHICmdList, ShaderRHI, *(Scene->DistanceFieldSceneData.ObjectBuffers), Scene->DistanceFieldSceneData.NumObjectsInBuffer);
FUnorderedAccessViewRHIParamRef OutUAVs[4];
OutUAVs[0] = GGlobalDistanceFieldCulledObjectBuffers.Buffers.ObjectIndirectArguments.UAV;
OutUAVs[1] = GGlobalDistanceFieldCulledObjectBuffers.Buffers.Bounds.UAV;
OutUAVs[2] = GGlobalDistanceFieldCulledObjectBuffers.Buffers.Data.UAV;
OutUAVs[3] = GGlobalDistanceFieldCulledObjectBuffers.Buffers.BoxBounds.UAV;
RHICmdList.TransitionResources(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, ARRAY_COUNT(OutUAVs));
CulledObjectParameters.Set(RHICmdList, ShaderRHI, GGlobalDistanceFieldCulledObjectBuffers.Buffers);
extern float GAOConeHalfAngle;
const float GlobalMaxSphereQueryRadius = MaxOcclusionDistance / (1 + FMath::Tan(GAOConeHalfAngle));
SetShaderValue(RHICmdList, ShaderRHI, AOGlobalMaxSphereQueryRadius, GlobalMaxSphereQueryRadius);
SetShaderValue(RHICmdList, ShaderRHI, VolumeBounds, VolumeBoundsValue);
uint32 AcceptOftenMovingObjectsOnlyValue = 0;
if (!GAOGlobalDistanceFieldCacheMostlyStaticSeparately)
{
AcceptOftenMovingObjectsOnlyValue = 2;
}
else if (CacheType == GDF_Full)
{
// First cache is for mostly static, second contains both, inheriting static objects distance fields with a lookup
// So only composite often moving objects into the full global distance field
AcceptOftenMovingObjectsOnlyValue = 1;
}
SetShaderValue(RHICmdList, ShaderRHI, AcceptOftenMovingObjectsOnly, AcceptOftenMovingObjectsOnlyValue);
}
void UnsetParameters(FRHICommandList& RHICmdList, const FScene* Scene)
{
ObjectBufferParameters.UnsetParameters(RHICmdList, GetComputeShader(), *(Scene->DistanceFieldSceneData.ObjectBuffers));
CulledObjectParameters.UnsetParameters(RHICmdList, GetComputeShader());
TArray<FUnorderedAccessViewRHIParamRef> UAVs;
CulledObjectParameters.GetUAVs(GGlobalDistanceFieldCulledObjectBuffers.Buffers, UAVs);
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, UAVs.GetData(), UAVs.Num());
}
virtual bool Serialize(FArchive& Ar)
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << ObjectBufferParameters;
Ar << CulledObjectParameters;
Ar << AOGlobalMaxSphereQueryRadius;
Ar << VolumeBounds;
Ar << AcceptOftenMovingObjectsOnly;
return bShaderHasOutdatedParameters;
}
private:
FDistanceFieldObjectBufferParameters ObjectBufferParameters;
FDistanceFieldCulledObjectBufferParameters CulledObjectParameters;
FShaderParameter AOGlobalMaxSphereQueryRadius;
FShaderParameter VolumeBounds;
FShaderParameter AcceptOftenMovingObjectsOnly;
};
IMPLEMENT_SHADER_TYPE(,FCullObjectsForVolumeCS,TEXT("/Engine/Private/GlobalDistanceField.usf"),TEXT("CullObjectsForVolumeCS"),SF_Compute);
const int32 GMaxGridCulledObjects = 2048;
class FObjectGridBuffers : public FRenderResource
{
public:
int32 GridDimension;
bool b16BitIndices;
FRWBuffer CullGridObjectNum;
FRWBuffer CullGridObjectArray;
FObjectGridBuffers()
{
GridDimension = 0;
}
virtual void InitDynamicRHI() override
{
if (GridDimension > 0)
{
const uint32 FastVRamFlag = GFastVRamConfig.GlobalDistanceFieldCullGridBuffers | (IsTransientResourceBufferAliasingEnabled() ? BUF_Transient : BUF_None);
const uint32 TileNum = GridDimension * GridDimension * GridDimension;
CullGridObjectNum.Initialize(
sizeof(uint32),
TileNum,
PF_R32_UINT,
BUF_Static | FastVRamFlag,
TEXT("GlobalDistanceField::TileObjectNum"));
CullGridObjectArray.Initialize(
b16BitIndices ? sizeof(uint16) : sizeof(uint32),
TileNum * GMaxGridCulledObjects,
b16BitIndices ? PF_R16_UINT : PF_R32_UINT,
BUF_Static | FastVRamFlag,
TEXT("GlobalDistanceField::TileObjectArray"));
}
}
void AcquireTransientResource()
{
CullGridObjectNum.AcquireTransientResource();
CullGridObjectArray.AcquireTransientResource();
}
void DiscardTransientResource()
{
CullGridObjectNum.DiscardTransientResource();
CullGridObjectArray.DiscardTransientResource();
}
virtual void ReleaseDynamicRHI() override
{
CullGridObjectNum.Release();
CullGridObjectArray.Release();
}
size_t GetSizeBytes() const
{
return CullGridObjectNum.NumBytes + CullGridObjectArray.NumBytes;
}
};
TGlobalResource<FObjectGridBuffers> GObjectGridBuffers;
const int32 GCullGridTileSize = 16;
class FCullObjectsToGridCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCullObjectsToGridCS,Global)
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Parameters.Platform) && IsUsingDistanceFields(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters,OutEnvironment);
OutEnvironment.SetDefine(TEXT("CULL_GRID_TILE_SIZE"), GCullGridTileSize);
OutEnvironment.SetDefine(TEXT("MAX_GRID_CULLED_DF_OBJECTS"), GMaxGridCulledObjects);
}
FCullObjectsToGridCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
CulledObjectBufferParameters.Bind(Initializer.ParameterMap);
GlobalDistanceFieldParameters.Bind(Initializer.ParameterMap);
CullGridObjectNum.Bind(Initializer.ParameterMap, TEXT("CullGridObjectNum"));
CullGridObjectArray.Bind(Initializer.ParameterMap, TEXT("CullGridObjectArray"));
CullGridDimension.Bind(Initializer.ParameterMap, TEXT("CullGridDimension"));
VolumeTexelSize.Bind(Initializer.ParameterMap, TEXT("VolumeTexelSize"));
UpdateRegionVolumeMin.Bind(Initializer.ParameterMap, TEXT("UpdateRegionVolumeMin"));
ClipmapIndex.Bind(Initializer.ParameterMap, TEXT("ClipmapIndex"));
AOGlobalMaxSphereQueryRadius.Bind(Initializer.ParameterMap, TEXT("AOGlobalMaxSphereQueryRadius"));
}
FCullObjectsToGridCS()
{
}
void SetParameters(
FRHICommandList& RHICmdList,
const FScene* Scene,
const FSceneView& View,
float MaxOcclusionDistance,
const FGlobalDistanceFieldInfo& GlobalDistanceFieldInfo,
int32 ClipmapIndexValue,
const FVolumeUpdateRegion& UpdateRegion)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
CulledObjectBufferParameters.Set(RHICmdList, ShaderRHI, GGlobalDistanceFieldCulledObjectBuffers.Buffers);
GlobalDistanceFieldParameters.Set(RHICmdList, ShaderRHI, GlobalDistanceFieldInfo.ParameterData);
RHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, GObjectGridBuffers.CullGridObjectNum.UAV);
RHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, GObjectGridBuffers.CullGridObjectArray.UAV);
CullGridObjectNum.SetBuffer(RHICmdList, ShaderRHI, GObjectGridBuffers.CullGridObjectNum);
CullGridObjectArray.SetBuffer(RHICmdList, ShaderRHI, GObjectGridBuffers.CullGridObjectArray);
const FIntVector GridDimensionValue(
FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.X, GCullGridTileSize),
FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Y, GCullGridTileSize),
FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Z, GCullGridTileSize));
SetShaderValue(RHICmdList, ShaderRHI, CullGridDimension, GridDimensionValue);
SetShaderValue(RHICmdList, ShaderRHI, VolumeTexelSize, FVector(1.0f / GAOGlobalDFResolution));
SetShaderValue(RHICmdList, ShaderRHI, UpdateRegionVolumeMin, UpdateRegion.Bounds.Min);
SetShaderValue(RHICmdList, ShaderRHI, ClipmapIndex, ClipmapIndexValue);
extern float GAOConeHalfAngle;
const float GlobalMaxSphereQueryRadius = MaxOcclusionDistance / (1 + FMath::Tan(GAOConeHalfAngle));
SetShaderValue(RHICmdList, ShaderRHI, AOGlobalMaxSphereQueryRadius, GlobalMaxSphereQueryRadius);
}
void UnsetParameters(FRHICommandList& RHICmdList)
{
CullGridObjectNum.UnsetUAV(RHICmdList, GetComputeShader());
CullGridObjectArray.UnsetUAV(RHICmdList, GetComputeShader());
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, GObjectGridBuffers.CullGridObjectNum.UAV);
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, GObjectGridBuffers.CullGridObjectArray.UAV);
}
virtual bool Serialize(FArchive& Ar)
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << CulledObjectBufferParameters;
Ar << GlobalDistanceFieldParameters;
Ar << CullGridObjectNum;
Ar << CullGridObjectArray;
Ar << CullGridDimension;
Ar << VolumeTexelSize;
Ar << UpdateRegionVolumeMin;
Ar << ClipmapIndex;
Ar << AOGlobalMaxSphereQueryRadius;
return bShaderHasOutdatedParameters;
}
private:
FDistanceFieldCulledObjectBufferParameters CulledObjectBufferParameters;
FGlobalDistanceFieldParameters GlobalDistanceFieldParameters;
FRWShaderParameter CullGridObjectNum;
FRWShaderParameter CullGridObjectArray;
FShaderParameter CullGridDimension;
FShaderParameter VolumeTexelSize;
FShaderParameter UpdateRegionVolumeMin;
FShaderParameter ClipmapIndex;
FShaderParameter AOGlobalMaxSphereQueryRadius;
};
IMPLEMENT_SHADER_TYPE(,FCullObjectsToGridCS,TEXT("/Engine/Private/GlobalDistanceField.usf"),TEXT("CullObjectsToGridCS"),SF_Compute);
enum EFlattenedDimension
{
Flatten_XAxis = 0,
Flatten_YAxis = 1,
Flatten_ZAxis = 2,
Flatten_None
};
static int32 GetCompositeTileSize(int32 Dimension, EFlattenedDimension FlattenedDimension)
{
if (FlattenedDimension == Flatten_None)
{
return 4;
}
return Dimension == (int32)FlattenedDimension ? 1 : 8;
}
template<bool bUseParentDistanceField, EFlattenedDimension FlattenedDimension>
class TCompositeObjectDistanceFieldsCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(TCompositeObjectDistanceFieldsCS,Global)
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Parameters.Platform) && IsUsingDistanceFields(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters,OutEnvironment);
OutEnvironment.SetDefine(TEXT("COMPOSITE_THREADGROUP_SIZEX"), GetCompositeTileSize(0, FlattenedDimension));
OutEnvironment.SetDefine(TEXT("COMPOSITE_THREADGROUP_SIZEY"), GetCompositeTileSize(1, FlattenedDimension));
OutEnvironment.SetDefine(TEXT("COMPOSITE_THREADGROUP_SIZEZ"), GetCompositeTileSize(2, FlattenedDimension));
OutEnvironment.SetDefine(TEXT("CULL_GRID_TILE_SIZE"), GCullGridTileSize);
OutEnvironment.SetDefine(TEXT("MAX_GRID_CULLED_DF_OBJECTS"), GMaxGridCulledObjects);
OutEnvironment.SetDefine(TEXT("USE_PARENT_DISTANCE_FIELD"), bUseParentDistanceField ? 1 : 0);
}
TCompositeObjectDistanceFieldsCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
CulledObjectBufferParameters.Bind(Initializer.ParameterMap);
GlobalDistanceFieldParameters.Bind(Initializer.ParameterMap);
GlobalDistanceFieldTexture.Bind(Initializer.ParameterMap, TEXT("GlobalDistanceFieldTexture"));
ParentGlobalDistanceFieldTexture.Bind(Initializer.ParameterMap, TEXT("ParentGlobalDistanceFieldTexture"));
CullGridObjectNum.Bind(Initializer.ParameterMap, TEXT("CullGridObjectNum"));
CullGridObjectArray.Bind(Initializer.ParameterMap, TEXT("CullGridObjectArray"));
UpdateRegionSize.Bind(Initializer.ParameterMap, TEXT("UpdateRegionSize"));
CullGridDimension.Bind(Initializer.ParameterMap, TEXT("CullGridDimension"));
VolumeTexelSize.Bind(Initializer.ParameterMap, TEXT("VolumeTexelSize"));
UpdateRegionVolumeMin.Bind(Initializer.ParameterMap, TEXT("UpdateRegionVolumeMin"));
ClipmapIndex.Bind(Initializer.ParameterMap, TEXT("ClipmapIndex"));
AOGlobalMaxSphereQueryRadius.Bind(Initializer.ParameterMap, TEXT("AOGlobalMaxSphereQueryRadius"));
}
TCompositeObjectDistanceFieldsCS()
{
}
void SetParameters(
FRHICommandList& RHICmdList,
const FScene* Scene,
const FSceneView& View,
float MaxOcclusionDistance,
const FGlobalDistanceFieldParameterData& ParameterData,
const FGlobalDistanceFieldClipmap& Clipmap,
IPooledRenderTarget* ParentDistanceField,
int32 ClipmapIndexValue,
const FVolumeUpdateRegion& UpdateRegion)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
CulledObjectBufferParameters.Set(RHICmdList, ShaderRHI, GGlobalDistanceFieldCulledObjectBuffers.Buffers);
GlobalDistanceFieldParameters.Set(RHICmdList, ShaderRHI, ParameterData);
const FSceneRenderTargetItem& ClipMapRTI = Clipmap.RenderTarget->GetRenderTargetItem();
RHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, ClipMapRTI.UAV);
GlobalDistanceFieldTexture.SetTexture(RHICmdList, ShaderRHI, ClipMapRTI.ShaderResourceTexture, ClipMapRTI.UAV);
if (bUseParentDistanceField)
{
SetTextureParameter(RHICmdList, ShaderRHI, ParentGlobalDistanceFieldTexture, ParentDistanceField->GetRenderTargetItem().ShaderResourceTexture);
}
else
{
check(!ParentGlobalDistanceFieldTexture.IsBound());
}
SetSRVParameter(RHICmdList, ShaderRHI, CullGridObjectNum, GObjectGridBuffers.CullGridObjectNum.SRV);
SetSRVParameter(RHICmdList, ShaderRHI, CullGridObjectArray, GObjectGridBuffers.CullGridObjectArray.SRV);
const FIntVector GridDimensionValue(
FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.X, GCullGridTileSize),
FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Y, GCullGridTileSize),
FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Z, GCullGridTileSize));
SetShaderValue(RHICmdList, ShaderRHI, CullGridDimension, GridDimensionValue);
SetShaderValue(RHICmdList, ShaderRHI, UpdateRegionSize, UpdateRegion.CellsSize);
SetShaderValue(RHICmdList, ShaderRHI, VolumeTexelSize, FVector(1.0f / GAOGlobalDFResolution));
SetShaderValue(RHICmdList, ShaderRHI, UpdateRegionVolumeMin, UpdateRegion.Bounds.Min);
SetShaderValue(RHICmdList, ShaderRHI, ClipmapIndex, ClipmapIndexValue);
extern float GAOConeHalfAngle;
const float GlobalMaxSphereQueryRadius = MaxOcclusionDistance / (1 + FMath::Tan(GAOConeHalfAngle));
SetShaderValue(RHICmdList, ShaderRHI, AOGlobalMaxSphereQueryRadius, GlobalMaxSphereQueryRadius);
}
void UnsetParameters(FRHICommandList& RHICmdList,const FGlobalDistanceFieldClipmap& Clipmap)
{
GlobalDistanceFieldTexture.UnsetUAV(RHICmdList, GetComputeShader());
const FSceneRenderTargetItem& ClipMapRTI = Clipmap.RenderTarget->GetRenderTargetItem();
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, ClipMapRTI.UAV);
}
virtual bool Serialize(FArchive& Ar)
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << CulledObjectBufferParameters;
Ar << GlobalDistanceFieldParameters;
Ar << GlobalDistanceFieldTexture;
Ar << ParentGlobalDistanceFieldTexture;
Ar << CullGridObjectNum;
Ar << CullGridObjectArray;
Ar << UpdateRegionSize;
Ar << CullGridDimension;
Ar << VolumeTexelSize;
Ar << UpdateRegionVolumeMin;
Ar << ClipmapIndex;
Ar << AOGlobalMaxSphereQueryRadius;
return bShaderHasOutdatedParameters;
}
private:
FDistanceFieldCulledObjectBufferParameters CulledObjectBufferParameters;
FGlobalDistanceFieldParameters GlobalDistanceFieldParameters;
FRWShaderParameter GlobalDistanceFieldTexture;
FShaderResourceParameter ParentGlobalDistanceFieldTexture;
FShaderResourceParameter CullGridObjectNum;
FShaderResourceParameter CullGridObjectArray;
FShaderParameter UpdateRegionSize;
FShaderParameter CullGridDimension;
FShaderParameter VolumeTexelSize;
FShaderParameter UpdateRegionVolumeMin;
FShaderParameter ClipmapIndex;
FShaderParameter AOGlobalMaxSphereQueryRadius;
};
#define IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(bUseParentDistanceField, FlattenedDimension) \
typedef TCompositeObjectDistanceFieldsCS<bUseParentDistanceField, FlattenedDimension> TCompositeObjectDistanceFieldsCS##bUseParentDistanceField##FlattenedDimension; \
IMPLEMENT_SHADER_TYPE(template<>,TCompositeObjectDistanceFieldsCS##bUseParentDistanceField##FlattenedDimension,TEXT("/Engine/Private/GlobalDistanceField.usf"),TEXT("CompositeObjectDistanceFieldsCS"),SF_Compute);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(true, Flatten_None);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(true, Flatten_XAxis);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(true, Flatten_YAxis);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(true, Flatten_ZAxis);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(false, Flatten_None);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(false, Flatten_XAxis);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(false, Flatten_YAxis);
IMPLEMENT_GLOBALDF_COMPOSITE_CS_TYPE(false, Flatten_ZAxis);
const int32 HeightfieldCompositeTileSize = 8;
class FCompositeHeightfieldsIntoGlobalDistanceFieldCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCompositeHeightfieldsIntoGlobalDistanceFieldCS, Global)
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform) && IsUsingDistanceFields(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("COMPOSITE_HEIGHTFIELDS_THREADGROUP_SIZE"), HeightfieldCompositeTileSize);
}
FCompositeHeightfieldsIntoGlobalDistanceFieldCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
GlobalDistanceFieldParameters.Bind(Initializer.ParameterMap);
GlobalDistanceFieldTexture.Bind(Initializer.ParameterMap, TEXT("GlobalDistanceFieldTexture"));
UpdateRegionSize.Bind(Initializer.ParameterMap, TEXT("UpdateRegionSize"));
VolumeTexelSize.Bind(Initializer.ParameterMap, TEXT("VolumeTexelSize"));
UpdateRegionVolumeMin.Bind(Initializer.ParameterMap, TEXT("UpdateRegionVolumeMin"));
UpdateRegionVolumeStep.Bind(Initializer.ParameterMap, TEXT("UpdateRegionVolumeStep"));
ClipmapIndex.Bind(Initializer.ParameterMap, TEXT("ClipmapIndex"));
AOGlobalMaxSphereQueryRadius.Bind(Initializer.ParameterMap, TEXT("AOGlobalMaxSphereQueryRadius"));
HeightfieldDescriptionParameters.Bind(Initializer.ParameterMap);
HeightfieldTextureParameters.Bind(Initializer.ParameterMap);
}
FCompositeHeightfieldsIntoGlobalDistanceFieldCS()
{
}
void SetParameters(
FRHICommandList& RHICmdList,
const FScene* Scene,
const FSceneView& View,
float GlobalMaxSphereQueryRadius,
const FGlobalDistanceFieldInfo& GlobalDistanceFieldInfo,
const FGlobalDistanceFieldClipmap& Clipmap,
int32 ClipmapIndexValue,
const FVolumeUpdateRegion& UpdateRegion,
UTexture2D* HeightfieldTextureValue,
int32 NumHeightfieldsValue)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
GlobalDistanceFieldParameters.Set(RHICmdList, ShaderRHI, GlobalDistanceFieldInfo.ParameterData);
const FSceneRenderTargetItem& ClipMapRTI = Clipmap.RenderTarget->GetRenderTargetItem();
GlobalDistanceFieldTexture.SetTexture(RHICmdList, ShaderRHI, ClipMapRTI.ShaderResourceTexture, ClipMapRTI.UAV);
const float VolumeStep = (2.0f * GlobalDistanceFieldInfo.ParameterData.CenterAndExtent[ClipmapIndexValue].W) / GAOGlobalDFResolution;
SetShaderValue(RHICmdList, ShaderRHI, UpdateRegionSize, UpdateRegion.CellsSize);
SetShaderValue(RHICmdList, ShaderRHI, VolumeTexelSize, FVector(1.0f / GAOGlobalDFResolution));
SetShaderValue(RHICmdList, ShaderRHI, UpdateRegionVolumeMin, UpdateRegion.Bounds.Min + 0.5f * VolumeStep); // World space value for corner texel.
SetShaderValue(RHICmdList, ShaderRHI, UpdateRegionVolumeStep, VolumeStep);
SetShaderValue(RHICmdList, ShaderRHI, ClipmapIndex, ClipmapIndexValue);
SetShaderValue(RHICmdList, ShaderRHI, AOGlobalMaxSphereQueryRadius, GlobalMaxSphereQueryRadius);
HeightfieldDescriptionParameters.Set(RHICmdList, ShaderRHI, GetHeightfieldDescriptionsSRV(), NumHeightfieldsValue);
HeightfieldTextureParameters.Set(RHICmdList, ShaderRHI, HeightfieldTextureValue, NULL);
}
void UnsetParameters(FRHICommandList& RHICmdList, const FGlobalDistanceFieldInfo& GlobalDistanceFieldInfo, const FGlobalDistanceFieldClipmap& Clipmap)
{
GlobalDistanceFieldTexture.UnsetUAV(RHICmdList, GetComputeShader());
const FSceneRenderTargetItem& ClipMapRTI = Clipmap.RenderTarget->GetRenderTargetItem();
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, ClipMapRTI.UAV);
}
virtual bool Serialize(FArchive& Ar)
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << GlobalDistanceFieldParameters;
Ar << GlobalDistanceFieldTexture;
Ar << UpdateRegionSize;
Ar << VolumeTexelSize;
Ar << UpdateRegionVolumeMin;
Ar << UpdateRegionVolumeStep;
Ar << ClipmapIndex;
Ar << AOGlobalMaxSphereQueryRadius;
Ar << HeightfieldDescriptionParameters;
Ar << HeightfieldTextureParameters;
return bShaderHasOutdatedParameters;
}
private:
FGlobalDistanceFieldParameters GlobalDistanceFieldParameters;
FRWShaderParameter GlobalDistanceFieldTexture;
FShaderParameter UpdateRegionSize;
FShaderParameter VolumeTexelSize;
FShaderParameter UpdateRegionVolumeMin;
FShaderParameter UpdateRegionVolumeStep;
FShaderParameter ClipmapIndex;
FShaderParameter AOGlobalMaxSphereQueryRadius;
FHeightfieldDescriptionParameters HeightfieldDescriptionParameters;
FHeightfieldTextureParameters HeightfieldTextureParameters;
};
IMPLEMENT_SHADER_TYPE(, FCompositeHeightfieldsIntoGlobalDistanceFieldCS, TEXT("/Engine/Private/GlobalDistanceField.usf"), TEXT("CompositeHeightfieldsIntoGlobalDistanceFieldCS"), SF_Compute);
extern void UploadHeightfieldDescriptions(const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions, FVector2D InvLightingAtlasSize, float InvDownsampleFactor);
void FHeightfieldLightingViewInfo::CompositeHeightfieldsIntoGlobalDistanceField(
FRHICommandList& RHICmdList,
const FScene* Scene,
const FViewInfo& View,
float GlobalMaxSphereQueryRadius,
const FGlobalDistanceFieldInfo& GlobalDistanceFieldInfo,
FGlobalDistanceFieldClipmap const& Clipmap,
int32 ClipmapIndexValue,
const FVolumeUpdateRegion& UpdateRegion) const
{
const int32 NumPrimitives = Scene->DistanceFieldSceneData.HeightfieldPrimitives.Num();
const FPooledRenderTarget* ClipmapRT = static_cast<const FPooledRenderTarget*>(
Clipmap.RenderTarget.GetReference());
const EPixelFormat ClipmapPixelFormat = ClipmapRT->GetDesc().Format;
if (GAOGlobalDistanceFieldRepresentHeightfields
&& GDynamicRHI->RHIIsTypedUAVLoadSupported(ClipmapPixelFormat)
&& NumPrimitives > 0
&& SupportsDistanceFieldAO(Scene->GetFeatureLevel(), Scene->GetShaderPlatform())
&& !IsMetalPlatform(Scene->GetShaderPlatform()))
{
FHeightfieldDescription UpdateRegionHeightfield;
for (int32 HeightfieldPrimitiveIndex = 0; HeightfieldPrimitiveIndex < NumPrimitives; HeightfieldPrimitiveIndex++)
{
const FPrimitiveSceneInfo* HeightfieldPrimitive = Scene->DistanceFieldSceneData.HeightfieldPrimitives[HeightfieldPrimitiveIndex];
const FBoxSphereBounds& PrimitiveBounds = HeightfieldPrimitive->Proxy->GetBounds();
const float DistanceToPrimitiveSq = (PrimitiveBounds.Origin - View.ViewMatrices.GetViewOrigin()).SizeSquared();
// Expand bounding box by a SDF max influence distance (only in local Z axis, as distance is computed from a top down projected heightmap point).
const FVector QueryInfluenceExpand = HeightfieldPrimitive->Proxy->GetLocalToWorld().GetUnitAxis(EAxis::Z) * FVector(0.0f, 0.0f, GlobalMaxSphereQueryRadius);
const FBox HeightfieldInfluenceBox = PrimitiveBounds.GetBox().ExpandBy(QueryInfluenceExpand, QueryInfluenceExpand);
if (UpdateRegion.Bounds.Intersect(HeightfieldInfluenceBox))
{
UTexture2D* HeightfieldTexture = NULL;
UTexture2D* DiffuseColorTexture = NULL;
FHeightfieldComponentDescription NewComponentDescription(HeightfieldPrimitive->Proxy->GetLocalToWorld());
HeightfieldPrimitive->Proxy->GetHeightfieldRepresentation(HeightfieldTexture, DiffuseColorTexture, NewComponentDescription);
if (HeightfieldTexture && HeightfieldTexture->Resource->TextureRHI)
{
const FIntPoint HeightfieldSize = NewComponentDescription.HeightfieldRect.Size();
if (UpdateRegionHeightfield.Rect.Area() == 0)
{
UpdateRegionHeightfield.Rect = NewComponentDescription.HeightfieldRect;
}
else
{
UpdateRegionHeightfield.Rect.Union(NewComponentDescription.HeightfieldRect);
}
TArray<FHeightfieldComponentDescription>& ComponentDescriptions = UpdateRegionHeightfield.ComponentDescriptions.FindOrAdd(FHeightfieldComponentTextures(HeightfieldTexture, DiffuseColorTexture));
ComponentDescriptions.Add(NewComponentDescription);
}
}
}
if (UpdateRegionHeightfield.ComponentDescriptions.Num() > 0)
{
for (TMap<FHeightfieldComponentTextures, TArray<FHeightfieldComponentDescription>>::TConstIterator It(UpdateRegionHeightfield.ComponentDescriptions); It; ++It)
{
const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions = It.Value();
if (HeightfieldDescriptions.Num() > 0)
{
UploadHeightfieldDescriptions(HeightfieldDescriptions, FVector2D(1, 1), 1.0f / UpdateRegionHeightfield.DownsampleFactor);
UTexture2D* HeightfieldTexture = It.Key().HeightAndNormal;
TShaderMapRef<FCompositeHeightfieldsIntoGlobalDistanceFieldCS> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, GlobalMaxSphereQueryRadius, GlobalDistanceFieldInfo, Clipmap, ClipmapIndexValue, UpdateRegion, HeightfieldTexture, HeightfieldDescriptions.Num());
//@todo - match typical update sizes. Camera movement creates narrow slabs.
const uint32 NumGroupsX = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.X, HeightfieldCompositeTileSize);
const uint32 NumGroupsY = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Y, HeightfieldCompositeTileSize);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, 1);
ComputeShader->UnsetParameters(RHICmdList, GlobalDistanceFieldInfo, Clipmap);
}
}
}
}
}
/** Constructs and adds an update region based on camera movement for the given axis. */
static void AddUpdateRegionForAxis(FIntVector Movement, const FBox& ClipmapBounds, float CellSize, int32 ComponentIndex, TArray<FVolumeUpdateRegion, TInlineAllocator<3> >& UpdateRegions)
{
FVolumeUpdateRegion UpdateRegion;
UpdateRegion.Bounds = ClipmapBounds;
UpdateRegion.CellsSize = FIntVector(GAOGlobalDFResolution);
UpdateRegion.CellsSize[ComponentIndex] = FMath::Min(FMath::Abs(Movement[ComponentIndex]), GAOGlobalDFResolution);
if (Movement[ComponentIndex] > 0)
{
// Positive axis movement, set the min of that axis to contain the newly exposed area
UpdateRegion.Bounds.Min[ComponentIndex] = FMath::Max(ClipmapBounds.Max[ComponentIndex] - Movement[ComponentIndex] * CellSize, ClipmapBounds.Min[ComponentIndex]);
}
else if (Movement[ComponentIndex] < 0)
{
// Negative axis movement, set the max of that axis to contain the newly exposed area
UpdateRegion.Bounds.Max[ComponentIndex] = FMath::Min(ClipmapBounds.Min[ComponentIndex] - Movement[ComponentIndex] * CellSize, ClipmapBounds.Max[ComponentIndex]);
}
if (UpdateRegion.CellsSize[ComponentIndex] > 0)
{
UpdateRegions.Add(UpdateRegion);
}
}
/** Constructs and adds an update region based on the given primitive bounds. */
static void AddUpdateRegionForPrimitive(const FVector4& Bounds, float MaxSphereQueryRadius, const FBox& ClipmapBounds, float CellSize, TArray<FVolumeUpdateRegion, TInlineAllocator<3> >& UpdateRegions)
{
// Object influence bounds
FBox BoundingBox((FVector)Bounds - Bounds.W - MaxSphereQueryRadius, (FVector)Bounds + Bounds.W + MaxSphereQueryRadius);
FVolumeUpdateRegion UpdateRegion;
UpdateRegion.Bounds.Init();
// Snap the min and clamp to clipmap bounds
UpdateRegion.Bounds.Min.X = FMath::Max(CellSize * FMath::FloorToFloat(BoundingBox.Min.X / CellSize), ClipmapBounds.Min.X);
UpdateRegion.Bounds.Min.Y = FMath::Max(CellSize * FMath::FloorToFloat(BoundingBox.Min.Y / CellSize), ClipmapBounds.Min.Y);
UpdateRegion.Bounds.Min.Z = FMath::Max(CellSize * FMath::FloorToFloat(BoundingBox.Min.Z / CellSize), ClipmapBounds.Min.Z);
// Derive the max from the snapped min and size, clamp to clipmap bounds
UpdateRegion.Bounds.Max = UpdateRegion.Bounds.Min + FVector(FMath::CeilToFloat((Bounds.W + MaxSphereQueryRadius) * 2 / CellSize)) * CellSize;
UpdateRegion.Bounds.Max.X = FMath::Min(UpdateRegion.Bounds.Max.X, ClipmapBounds.Max.X);
UpdateRegion.Bounds.Max.Y = FMath::Min(UpdateRegion.Bounds.Max.Y, ClipmapBounds.Max.Y);
UpdateRegion.Bounds.Max.Z = FMath::Min(UpdateRegion.Bounds.Max.Z, ClipmapBounds.Max.Z);
const FVector UpdateRegionSize = UpdateRegion.Bounds.GetSize();
UpdateRegion.CellsSize.X = FMath::TruncToInt(UpdateRegionSize.X / CellSize + .5f);
UpdateRegion.CellsSize.Y = FMath::TruncToInt(UpdateRegionSize.Y / CellSize + .5f);
UpdateRegion.CellsSize.Z = FMath::TruncToInt(UpdateRegionSize.Z / CellSize + .5f);
// Only add update regions with positive area
if (UpdateRegion.CellsSize.X > 0 && UpdateRegion.CellsSize.Y > 0 && UpdateRegion.CellsSize.Z > 0)
{
checkSlow(UpdateRegion.CellsSize.X <= GAOGlobalDFResolution && UpdateRegion.CellsSize.Y <= GAOGlobalDFResolution && UpdateRegion.CellsSize.Z <= GAOGlobalDFResolution);
UpdateRegions.Add(UpdateRegion);
}
}
static void TrimOverlappingAxis(int32 TrimAxis, float CellSize, const FVolumeUpdateRegion& OtherUpdateRegion, FVolumeUpdateRegion& UpdateRegion)
{
int32 OtherAxis0 = (TrimAxis + 1) % 3;
int32 OtherAxis1 = (TrimAxis + 2) % 3;
// Check if the UpdateRegion is entirely contained in 2d
if (UpdateRegion.Bounds.Max[OtherAxis0] <= OtherUpdateRegion.Bounds.Max[OtherAxis0]
&& UpdateRegion.Bounds.Min[OtherAxis0] >= OtherUpdateRegion.Bounds.Min[OtherAxis0]
&& UpdateRegion.Bounds.Max[OtherAxis1] <= OtherUpdateRegion.Bounds.Max[OtherAxis1]
&& UpdateRegion.Bounds.Min[OtherAxis1] >= OtherUpdateRegion.Bounds.Min[OtherAxis1])
{
if (UpdateRegion.Bounds.Min[TrimAxis] >= OtherUpdateRegion.Bounds.Min[TrimAxis] && UpdateRegion.Bounds.Min[TrimAxis] <= OtherUpdateRegion.Bounds.Max[TrimAxis])
{
// Min on this axis is completely contained within the other region, clip it so there's no overlapping update region
UpdateRegion.Bounds.Min[TrimAxis] = OtherUpdateRegion.Bounds.Max[TrimAxis];
}
else
{
// otherwise Max on this axis must be inside the other region, because we know the two volumes intersect
UpdateRegion.Bounds.Max[TrimAxis] = OtherUpdateRegion.Bounds.Min[TrimAxis];
}
UpdateRegion.CellsSize[TrimAxis] = FMath::TruncToInt((FMath::Max(UpdateRegion.Bounds.Max[TrimAxis] - UpdateRegion.Bounds.Min[TrimAxis], 0.0f)) / CellSize + .5f);
}
}
static void AllocateClipmapTexture(FRHICommandListImmediate& RHICmdList, int32 ClipmapIndex, FGlobalDFCacheType CacheType, TRefCountPtr<IPooledRenderTarget>& Texture)
{
const TCHAR* TextureName = CacheType == GDF_MostlyStatic ? TEXT("MostlyStaticGlobalDistanceField0") : TEXT("GlobalDistanceField0");
if (ClipmapIndex == 1)
{
TextureName = CacheType == GDF_MostlyStatic ? TEXT("MostlyStaticGlobalDistanceField1") : TEXT("GlobalDistanceField1");
}
else if (ClipmapIndex == 2)
{
TextureName = CacheType == GDF_MostlyStatic ? TEXT("MostlyStaticGlobalDistanceField2") : TEXT("GlobalDistanceField2");
}
else if (ClipmapIndex == 3)
{
TextureName = CacheType == GDF_MostlyStatic ? TEXT("MostlyStaticGlobalDistanceField3") : TEXT("GlobalDistanceField3");
}
FPooledRenderTargetDesc VolumeDesc = FPooledRenderTargetDesc(FPooledRenderTargetDesc::CreateVolumeDesc(
GAOGlobalDFResolution,
GAOGlobalDFResolution,
GAOGlobalDFResolution,
PF_R16F,
FClearValueBinding::None,
0,
// TexCreate_ReduceMemoryWithTilingMode used because 128^3 texture comes out 4x bigger on PS4 with recommended volume texture tiling modes
TexCreate_ShaderResource | TexCreate_RenderTargetable | TexCreate_UAV | TexCreate_ReduceMemoryWithTilingMode,
false));
VolumeDesc.AutoWritable = false;
GRenderTargetPool.FindFreeElement(
RHICmdList,
VolumeDesc,
Texture,
TextureName,
true,
ERenderTargetTransience::NonTransient
);
}
static void GetUpdateFrequencyForClipmap(int32 ClipmapIndex, int32& OutFrequency, int32& OutPhase)
{
OutFrequency = 1;
OutPhase = 0;
if (ClipmapIndex == 0 || !GAOGlobalDistanceFieldStaggeredUpdates)
{
OutFrequency = 1;
OutPhase = 0;
}
else if (ClipmapIndex == 1)
{
OutFrequency = 2;
OutPhase = 0;
}
else if (ClipmapIndex == 2)
{
OutFrequency = 4;
OutPhase = 1;
}
else
{
check(ClipmapIndex == 3);
OutFrequency = 4;
OutPhase = 3;
}
}
/** Staggers clipmap updates so there are only 2 per frame */
static bool ShouldUpdateClipmapThisFrame(int32 ClipmapIndex, int32 GlobalDistanceFieldUpdateIndex)
{
int32 Frequency;
int32 Phase;
GetUpdateFrequencyForClipmap(ClipmapIndex, Frequency, Phase);
return GlobalDistanceFieldUpdateIndex % Frequency == Phase;
}
static float ComputeClipmapExtent(int32 ClipmapIndex, const FScene* Scene)
{
const float InnerClipmapDistance = Scene->GlobalDistanceFieldViewDistance / FMath::Pow(GAOGlobalDFClipmapDistanceExponent, 3);
return InnerClipmapDistance * FMath::Pow(GAOGlobalDFClipmapDistanceExponent, ClipmapIndex);
}
static void ComputeUpdateRegionsAndUpdateViewState(
FRHICommandListImmediate& RHICmdList,
const FViewInfo& View,
const FScene* Scene,
FGlobalDistanceFieldInfo& GlobalDistanceFieldInfo,
int32 NumClipmaps,
float MaxOcclusionDistance)
{
GlobalDistanceFieldInfo.Clipmaps.AddZeroed(NumClipmaps);
GlobalDistanceFieldInfo.MostlyStaticClipmaps.AddZeroed(NumClipmaps);
// Cache the heightfields update region boxes for fast reuse for each clip region.
TArray<FBox> PendingStreamingHeightfieldBoxes;
for (const FPrimitiveSceneInfo* HeightfieldPrimitive : Scene->DistanceFieldSceneData.HeightfieldPrimitives)
{
if (HeightfieldPrimitive->Proxy->HeightfieldHasPendingStreaming())
{
PendingStreamingHeightfieldBoxes.Add(HeightfieldPrimitive->Proxy->GetBounds().GetBox());
}
}
if (View.ViewState)
{
View.ViewState->GlobalDistanceFieldUpdateIndex++;
if (View.ViewState->GlobalDistanceFieldUpdateIndex > 4)
{
View.ViewState->GlobalDistanceFieldUpdateIndex = 0;
}
for (int32 ClipmapIndex = 0; ClipmapIndex < NumClipmaps; ClipmapIndex++)
{
FGlobalDistanceFieldClipmapState& ClipmapViewState = View.ViewState->GlobalDistanceFieldClipmapState[ClipmapIndex];
const float Extent = ComputeClipmapExtent(ClipmapIndex, Scene);
const float CellSize = (Extent * 2) / GAOGlobalDFResolution;
bool bReallocated = false;
// Accumulate primitive modifications in the viewstate in case we don't update the clipmap this frame
for (uint32 CacheType = 0; CacheType < GDF_Num; CacheType++)
{
const uint32 SourceCacheType = GAOGlobalDistanceFieldCacheMostlyStaticSeparately ? CacheType : GDF_Full;
ClipmapViewState.Cache[CacheType].PrimitiveModifiedBounds.Append(Scene->DistanceFieldSceneData.PrimitiveModifiedBounds[SourceCacheType]);
if (CacheType == GDF_Full || GAOGlobalDistanceFieldCacheMostlyStaticSeparately)
{
TRefCountPtr<IPooledRenderTarget>& RenderTarget = ClipmapViewState.Cache[CacheType].VolumeTexture;
if (!RenderTarget || RenderTarget->GetDesc().Extent.X != GAOGlobalDFResolution)
{
AllocateClipmapTexture(RHICmdList, ClipmapIndex, (FGlobalDFCacheType)CacheType, RenderTarget);
bReallocated = true;
}
}
}
const bool bForceFullUpdate = bReallocated
|| !View.ViewState->bInitializedGlobalDistanceFieldOrigins
// Detect when max occlusion distance has changed
|| ClipmapViewState.CachedMaxOcclusionDistance != MaxOcclusionDistance
|| ClipmapViewState.CachedGlobalDistanceFieldViewDistance != Scene->GlobalDistanceFieldViewDistance
|| ClipmapViewState.CacheMostlyStaticSeparately != GAOGlobalDistanceFieldCacheMostlyStaticSeparately
|| ClipmapViewState.LastUsedSceneDataForFullUpdate != &Scene->DistanceFieldSceneData
|| GAOGlobalDistanceFieldForceFullUpdate;
if (ShouldUpdateClipmapThisFrame(ClipmapIndex, View.ViewState->GlobalDistanceFieldUpdateIndex)
|| bForceFullUpdate)
{
const FVector NewCenter = View.ViewMatrices.GetViewOrigin();
FIntVector GridCenter;
GridCenter.X = FMath::FloorToInt(NewCenter.X / CellSize);
GridCenter.Y = FMath::FloorToInt(NewCenter.Y / CellSize);
GridCenter.Z = FMath::FloorToInt(NewCenter.Z / CellSize);
const FVector SnappedCenter = FVector(GridCenter) * CellSize;
const FBox ClipmapBounds(SnappedCenter - Extent, SnappedCenter + Extent);
const bool bUsePartialUpdates = GAOGlobalDistanceFieldPartialUpdates && !bForceFullUpdate;
if (!bUsePartialUpdates)
{
// Store the location of the full update
ClipmapViewState.FullUpdateOrigin = GridCenter;
View.ViewState->bInitializedGlobalDistanceFieldOrigins = true;
ClipmapViewState.LastUsedSceneDataForFullUpdate = &Scene->DistanceFieldSceneData;
}
const FGlobalDFCacheType StartCacheType = GAOGlobalDistanceFieldCacheMostlyStaticSeparately ? GDF_MostlyStatic : GDF_Full;
for (uint32 CacheType = StartCacheType; CacheType < GDF_Num; CacheType++)
{
FGlobalDistanceFieldClipmap& Clipmap = *(CacheType == GDF_MostlyStatic
? &GlobalDistanceFieldInfo.MostlyStaticClipmaps[ClipmapIndex]
: &GlobalDistanceFieldInfo.Clipmaps[ClipmapIndex]);
bool bLocalUsePartialUpdates = bUsePartialUpdates
// Only use partial updates with small numbers of primitive modifications
&& ClipmapViewState.Cache[CacheType].PrimitiveModifiedBounds.Num() < 100;
if (bLocalUsePartialUpdates)
{
FIntVector Movement = GridCenter - ClipmapViewState.LastPartialUpdateOrigin;
if (CacheType == GDF_MostlyStatic || !GAOGlobalDistanceFieldCacheMostlyStaticSeparately)
{
// Add an update region for each potential axis of camera movement
AddUpdateRegionForAxis(Movement, ClipmapBounds, CellSize, 0, Clipmap.UpdateRegions);
AddUpdateRegionForAxis(Movement, ClipmapBounds, CellSize, 1, Clipmap.UpdateRegions);
AddUpdateRegionForAxis(Movement, ClipmapBounds, CellSize, 2, Clipmap.UpdateRegions);
}
else
{
// Inherit from parent
Clipmap.UpdateRegions.Append(GlobalDistanceFieldInfo.MostlyStaticClipmaps[ClipmapIndex].UpdateRegions);
}
extern float GAOConeHalfAngle;
const float GlobalMaxSphereQueryRadius = MaxOcclusionDistance / (1 + FMath::Tan(GAOConeHalfAngle));
// Add an update region for each primitive that has been modified
for (int32 BoundsIndex = 0; BoundsIndex < ClipmapViewState.Cache[CacheType].PrimitiveModifiedBounds.Num(); BoundsIndex++)
{
AddUpdateRegionForPrimitive(ClipmapViewState.Cache[CacheType].PrimitiveModifiedBounds[BoundsIndex], GlobalMaxSphereQueryRadius, ClipmapBounds, CellSize, Clipmap.UpdateRegions);
}
int32 TotalTexelsBeingUpdated = 0;
// Trim fully contained update regions
for (int32 UpdateRegionIndex = 0; UpdateRegionIndex < Clipmap.UpdateRegions.Num(); UpdateRegionIndex++)
{
const FVolumeUpdateRegion& UpdateRegion = Clipmap.UpdateRegions[UpdateRegionIndex];
bool bCompletelyContained = false;
for (int32 OtherUpdateRegionIndex = 0; OtherUpdateRegionIndex < Clipmap.UpdateRegions.Num(); OtherUpdateRegionIndex++)
{
if (UpdateRegionIndex != OtherUpdateRegionIndex)
{
const FVolumeUpdateRegion& OtherUpdateRegion = Clipmap.UpdateRegions[OtherUpdateRegionIndex];
if (OtherUpdateRegion.Bounds.IsInsideOrOn(UpdateRegion.Bounds.Min)
&& OtherUpdateRegion.Bounds.IsInsideOrOn(UpdateRegion.Bounds.Max))
{
bCompletelyContained = true;
break;
}
}
}
if (bCompletelyContained)
{
Clipmap.UpdateRegions.RemoveAt(UpdateRegionIndex);
UpdateRegionIndex--;
}
}
// Trim overlapping regions
for (int32 UpdateRegionIndex = 0; UpdateRegionIndex < Clipmap.UpdateRegions.Num(); UpdateRegionIndex++)
{
FVolumeUpdateRegion& UpdateRegion = Clipmap.UpdateRegions[UpdateRegionIndex];
bool bEmptyRegion = false;
for (int32 OtherUpdateRegionIndex = 0; OtherUpdateRegionIndex < Clipmap.UpdateRegions.Num(); OtherUpdateRegionIndex++)
{
if (UpdateRegionIndex != OtherUpdateRegionIndex)
{
const FVolumeUpdateRegion& OtherUpdateRegion = Clipmap.UpdateRegions[OtherUpdateRegionIndex];
if (OtherUpdateRegion.Bounds.Intersect(UpdateRegion.Bounds))
{
TrimOverlappingAxis(0, CellSize, OtherUpdateRegion, UpdateRegion);
TrimOverlappingAxis(1, CellSize, OtherUpdateRegion, UpdateRegion);
TrimOverlappingAxis(2, CellSize, OtherUpdateRegion, UpdateRegion);
if (UpdateRegion.CellsSize.X == 0 || UpdateRegion.CellsSize.Y == 0 || UpdateRegion.CellsSize.Z == 0)
{
bEmptyRegion = true;
break;
}
}
}
}
if (bEmptyRegion)
{
Clipmap.UpdateRegions.RemoveAt(UpdateRegionIndex);
UpdateRegionIndex--;
}
}
// Count how many texels are being updated
for (int32 UpdateRegionIndex = 0; UpdateRegionIndex < Clipmap.UpdateRegions.Num(); UpdateRegionIndex++)
{
const FVolumeUpdateRegion& UpdateRegion = Clipmap.UpdateRegions[UpdateRegionIndex];
TotalTexelsBeingUpdated += UpdateRegion.CellsSize.X * UpdateRegion.CellsSize.Y * UpdateRegion.CellsSize.Z;
}
// Fall back to a full update if the partial updates were going to do more work
if (TotalTexelsBeingUpdated >= GAOGlobalDFResolution * GAOGlobalDFResolution * GAOGlobalDFResolution)
{
Clipmap.UpdateRegions.Reset();
bLocalUsePartialUpdates = false;
}
}
if (!bLocalUsePartialUpdates)
{
FVolumeUpdateRegion UpdateRegion;
UpdateRegion.Bounds = ClipmapBounds;
UpdateRegion.CellsSize = FIntVector(GAOGlobalDFResolution);
Clipmap.UpdateRegions.Add(UpdateRegion);
}
// Check if the clipmap intersects with a pending update region
bool bHasPendingStreaming = false;
for (const FBox& HeightfieldBox : PendingStreamingHeightfieldBoxes)
{
if (ClipmapBounds.Intersect(HeightfieldBox))
{
bHasPendingStreaming = true;
break;
}
}
// If some of the height fields has pending streaming regions, postpone a full update.
if (bHasPendingStreaming)
{
// Mark a pending update for this height field. It will get processed when all pending texture streaming affecting it will be completed.
View.ViewState->DeferredGlobalDistanceFieldUpdates[CacheType].AddUnique(ClipmapIndex);
// Remove the height fields from the update.
for (FVolumeUpdateRegion& UpdateRegion : Clipmap.UpdateRegions)
{
UpdateRegion.UpdateType = (EVolumeUpdateType)(UpdateRegion.UpdateType & ~VUT_Heightfields);
}
}
else if (View.ViewState->DeferredGlobalDistanceFieldUpdates[CacheType].Remove(ClipmapIndex) > 0)
{
// Remove the height fields from the current update as we are pushing a new full update.
for (FVolumeUpdateRegion& UpdateRegion : Clipmap.UpdateRegions)
{
UpdateRegion.UpdateType = (EVolumeUpdateType)(UpdateRegion.UpdateType & ~VUT_Heightfields);
}
FVolumeUpdateRegion UpdateRegion;
UpdateRegion.Bounds = ClipmapBounds;
UpdateRegion.CellsSize = FIntVector(GAOGlobalDFResolution);
UpdateRegion.UpdateType = VUT_Heightfields;
Clipmap.UpdateRegions.Add(UpdateRegion);
}
ClipmapViewState.Cache[CacheType].PrimitiveModifiedBounds.Reset();
}
ClipmapViewState.LastPartialUpdateOrigin = GridCenter;
}
const FVector Center = FVector(ClipmapViewState.LastPartialUpdateOrigin) * CellSize;
const FGlobalDFCacheType StartCacheType = GAOGlobalDistanceFieldCacheMostlyStaticSeparately ? GDF_MostlyStatic : GDF_Full;
for (uint32 CacheType = StartCacheType; CacheType < GDF_Num; CacheType++)
{
FGlobalDistanceFieldClipmap& Clipmap = *(CacheType == GDF_MostlyStatic
? &GlobalDistanceFieldInfo.MostlyStaticClipmaps[ClipmapIndex]
: &GlobalDistanceFieldInfo.Clipmaps[ClipmapIndex]);
// Setup clipmap properties from view state exclusively, so we can skip updating on some frames
Clipmap.RenderTarget = ClipmapViewState.Cache[CacheType].VolumeTexture;
Clipmap.Bounds = FBox(Center - Extent, Center + Extent);
// Scroll offset so the contents of the global distance field don't have to be moved as the camera moves around, only updated in slabs
Clipmap.ScrollOffset = FVector(ClipmapViewState.LastPartialUpdateOrigin - ClipmapViewState.FullUpdateOrigin) * CellSize;
}
ClipmapViewState.CachedMaxOcclusionDistance = MaxOcclusionDistance;
ClipmapViewState.CachedGlobalDistanceFieldViewDistance = Scene->GlobalDistanceFieldViewDistance;
ClipmapViewState.CacheMostlyStaticSeparately = GAOGlobalDistanceFieldCacheMostlyStaticSeparately;
}
}
else
{
for (int32 ClipmapIndex = 0; ClipmapIndex < NumClipmaps; ClipmapIndex++)
{
const FGlobalDFCacheType StartCacheType = GAOGlobalDistanceFieldCacheMostlyStaticSeparately ? GDF_MostlyStatic : GDF_Full;
for (uint32 CacheType = StartCacheType; CacheType < GDF_Num; CacheType++)
{
FGlobalDistanceFieldClipmap& Clipmap = *(CacheType == GDF_MostlyStatic
? &GlobalDistanceFieldInfo.MostlyStaticClipmaps[ClipmapIndex]
: &GlobalDistanceFieldInfo.Clipmaps[ClipmapIndex]);
AllocateClipmapTexture(RHICmdList, ClipmapIndex, (FGlobalDFCacheType)CacheType, Clipmap.RenderTarget);
Clipmap.ScrollOffset = FVector::ZeroVector;
const float Extent = ComputeClipmapExtent(ClipmapIndex, Scene);
FVector Center = View.ViewMatrices.GetViewOrigin();
const float CellSize = (Extent * 2) / GAOGlobalDFResolution;
FIntVector GridCenter;
GridCenter.X = FMath::FloorToInt(Center.X / CellSize);
GridCenter.Y = FMath::FloorToInt(Center.Y / CellSize);
GridCenter.Z = FMath::FloorToInt(Center.Z / CellSize);
Center = FVector(GridCenter) * CellSize;
FBox ClipmapBounds(Center - Extent, Center + Extent);
Clipmap.Bounds = ClipmapBounds;
FVolumeUpdateRegion UpdateRegion;
UpdateRegion.Bounds = ClipmapBounds;
UpdateRegion.CellsSize = FIntVector(GAOGlobalDFResolution);
Clipmap.UpdateRegions.Add(UpdateRegion);
}
}
}
GlobalDistanceFieldInfo.UpdateParameterData(MaxOcclusionDistance);
}
void FViewInfo::SetupDefaultGlobalDistanceFieldUniformBufferParameters(FViewUniformShaderParameters& ViewUniformShaderParameters) const
{
// Initialize global distance field members to defaults, because View.GlobalDistanceFieldInfo is not valid yet
for (int32 Index = 0; Index < GMaxGlobalDistanceFieldClipmaps; Index++)
{
ViewUniformShaderParameters.GlobalVolumeCenterAndExtent[Index] = FVector4(0);
ViewUniformShaderParameters.GlobalVolumeWorldToUVAddAndMul[Index] = FVector4(0);
}
ViewUniformShaderParameters.GlobalVolumeDimension = 0.0f;
ViewUniformShaderParameters.GlobalVolumeTexelSize = 0.0f;
ViewUniformShaderParameters.MaxGlobalDistance = 0.0f;
ViewUniformShaderParameters.GlobalDistanceFieldTexture0 = OrBlack3DIfNull(GBlackVolumeTexture->TextureRHI.GetReference());
ViewUniformShaderParameters.GlobalDistanceFieldSampler0 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
ViewUniformShaderParameters.GlobalDistanceFieldTexture1 = OrBlack3DIfNull(GBlackVolumeTexture->TextureRHI.GetReference());
ViewUniformShaderParameters.GlobalDistanceFieldSampler1 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
ViewUniformShaderParameters.GlobalDistanceFieldTexture2 = OrBlack3DIfNull(GBlackVolumeTexture->TextureRHI.GetReference());
ViewUniformShaderParameters.GlobalDistanceFieldSampler2 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
ViewUniformShaderParameters.GlobalDistanceFieldTexture3 = OrBlack3DIfNull(GBlackVolumeTexture->TextureRHI.GetReference());
ViewUniformShaderParameters.GlobalDistanceFieldSampler3 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
}
void FViewInfo::SetupGlobalDistanceFieldUniformBufferParameters(FViewUniformShaderParameters& ViewUniformShaderParameters) const
{
check(GlobalDistanceFieldInfo.bInitialized);
for (int32 Index = 0; Index < GMaxGlobalDistanceFieldClipmaps; Index++)
{
ViewUniformShaderParameters.GlobalVolumeCenterAndExtent[Index] = GlobalDistanceFieldInfo.ParameterData.CenterAndExtent[Index];
ViewUniformShaderParameters.GlobalVolumeWorldToUVAddAndMul[Index] = GlobalDistanceFieldInfo.ParameterData.WorldToUVAddAndMul[Index];
}
ViewUniformShaderParameters.GlobalVolumeDimension = GlobalDistanceFieldInfo.ParameterData.GlobalDFResolution;
ViewUniformShaderParameters.GlobalVolumeTexelSize = 1.0f / GlobalDistanceFieldInfo.ParameterData.GlobalDFResolution;
ViewUniformShaderParameters.MaxGlobalDistance = GlobalDistanceFieldInfo.ParameterData.MaxDistance;
ViewUniformShaderParameters.GlobalDistanceFieldTexture0 = OrBlack3DIfNull(GlobalDistanceFieldInfo.ParameterData.Textures[0]);
ViewUniformShaderParameters.GlobalDistanceFieldSampler0 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
ViewUniformShaderParameters.GlobalDistanceFieldTexture1 = OrBlack3DIfNull(GlobalDistanceFieldInfo.ParameterData.Textures[1]);
ViewUniformShaderParameters.GlobalDistanceFieldSampler1 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
ViewUniformShaderParameters.GlobalDistanceFieldTexture2 = OrBlack3DIfNull(GlobalDistanceFieldInfo.ParameterData.Textures[2]);
ViewUniformShaderParameters.GlobalDistanceFieldSampler2 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
ViewUniformShaderParameters.GlobalDistanceFieldTexture3 = OrBlack3DIfNull(GlobalDistanceFieldInfo.ParameterData.Textures[3]);
ViewUniformShaderParameters.GlobalDistanceFieldSampler3 = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
}
/**
* Updates the global distance field for a view.
* Typically issues updates for just the newly exposed regions of the volume due to camera movement.
* In the worst case of a camera cut or large distance field scene changes, a full update of the global distance field will be done.
**/
void UpdateGlobalDistanceFieldVolume(
FRHICommandListImmediate& RHICmdList,
FViewInfo& View,
const FScene* Scene,
float MaxOcclusionDistance,
FGlobalDistanceFieldInfo& GlobalDistanceFieldInfo)
{
SCOPED_GPU_STAT(RHICmdList, GlobalDistanceFieldUpdate);
extern float GAOConeHalfAngle;
const float GlobalMaxSphereQueryRadius = MaxOcclusionDistance / (1 + FMath::Tan(GAOConeHalfAngle));
if (Scene->DistanceFieldSceneData.NumObjectsInBuffer > 0)
{
ComputeUpdateRegionsAndUpdateViewState(RHICmdList, View, Scene, GlobalDistanceFieldInfo, GMaxGlobalDistanceFieldClipmaps, MaxOcclusionDistance);
// Recreate the view uniform buffer now that we have updated GlobalDistanceFieldInfo
View.SetupGlobalDistanceFieldUniformBufferParameters(*View.CachedViewUniformShaderParameters);
View.ViewUniformBuffer = TUniformBufferRef<FViewUniformShaderParameters>::CreateUniformBufferImmediate(*View.CachedViewUniformShaderParameters, UniformBuffer_SingleFrame);
bool bHasUpdateRegions = false;
for (int32 ClipmapIndex = 0; ClipmapIndex < GlobalDistanceFieldInfo.Clipmaps.Num(); ClipmapIndex++)
{
bHasUpdateRegions = bHasUpdateRegions || GlobalDistanceFieldInfo.Clipmaps[ClipmapIndex].UpdateRegions.Num() > 0;
}
for (int32 ClipmapIndex = 0; ClipmapIndex < GlobalDistanceFieldInfo.MostlyStaticClipmaps.Num(); ClipmapIndex++)
{
bHasUpdateRegions = bHasUpdateRegions || GlobalDistanceFieldInfo.MostlyStaticClipmaps[ClipmapIndex].UpdateRegions.Num() > 0;
}
if (bHasUpdateRegions && GAOUpdateGlobalDistanceField)
{
SCOPED_DRAW_EVENT(RHICmdList, UpdateGlobalDistanceFieldVolume);
if (!GGlobalDistanceFieldCulledObjectBuffers.IsInitialized()
|| GGlobalDistanceFieldCulledObjectBuffers.Buffers.MaxObjects < Scene->DistanceFieldSceneData.NumObjectsInBuffer
|| GGlobalDistanceFieldCulledObjectBuffers.Buffers.MaxObjects > 3 * Scene->DistanceFieldSceneData.NumObjectsInBuffer)
{
GGlobalDistanceFieldCulledObjectBuffers.Buffers.MaxObjects = Scene->DistanceFieldSceneData.NumObjectsInBuffer * 5 / 4;
GGlobalDistanceFieldCulledObjectBuffers.ReleaseResource();
GGlobalDistanceFieldCulledObjectBuffers.InitResource();
}
GGlobalDistanceFieldCulledObjectBuffers.Buffers.AcquireTransientResource();
const uint32 MaxCullGridDimension = GAOGlobalDFResolution / GCullGridTileSize;
const bool b16BitObjectIndices = Scene->DistanceFieldSceneData.CanUse16BitObjectIndices();
if (GObjectGridBuffers.GridDimension != MaxCullGridDimension || GObjectGridBuffers.b16BitIndices != b16BitObjectIndices)
{
GObjectGridBuffers.b16BitIndices = b16BitObjectIndices;
GObjectGridBuffers.GridDimension = MaxCullGridDimension;
GObjectGridBuffers.UpdateRHI();
}
GObjectGridBuffers.AcquireTransientResource();
const FGlobalDFCacheType StartCacheType = GAOGlobalDistanceFieldCacheMostlyStaticSeparately ? GDF_MostlyStatic : GDF_Full;
for (int32 CacheType = StartCacheType; CacheType < GDF_Num; CacheType++)
{
TArray<FGlobalDistanceFieldClipmap>& Clipmaps = CacheType == GDF_MostlyStatic
? GlobalDistanceFieldInfo.MostlyStaticClipmaps
: GlobalDistanceFieldInfo.Clipmaps;
for (int32 ClipmapIndex = 0; ClipmapIndex < Clipmaps.Num(); ClipmapIndex++)
{
SCOPED_DRAW_EVENTF(RHICmdList, Clipmap, TEXT("CacheType %s Clipmap %u"), CacheType == GDF_MostlyStatic ? TEXT("MostlyStatic") : TEXT("Movable"), ClipmapIndex);
FGlobalDistanceFieldClipmap& Clipmap = Clipmaps[ClipmapIndex];
for (int32 UpdateRegionIndex = 0; UpdateRegionIndex < Clipmap.UpdateRegions.Num(); UpdateRegionIndex++)
{
const FVolumeUpdateRegion& UpdateRegion = Clipmap.UpdateRegions[UpdateRegionIndex];
if (UpdateRegion.UpdateType & VUT_MeshDistanceFields)
{
{
SCOPED_DRAW_EVENT(RHICmdList, GridCull);
// Cull the global objects to the volume being updated
{
ClearUAV(RHICmdList, GGlobalDistanceFieldCulledObjectBuffers.Buffers.ObjectIndirectArguments, 0);
TShaderMapRef<FCullObjectsForVolumeCS> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
const FVector4 VolumeBounds(UpdateRegion.Bounds.GetCenter(), UpdateRegion.Bounds.GetExtent().Size());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, VolumeBounds, (FGlobalDFCacheType)CacheType);
DispatchComputeShader(RHICmdList, *ComputeShader, FMath::DivideAndRoundUp<uint32>(Scene->DistanceFieldSceneData.NumObjectsInBuffer, CullObjectsGroupSize), 1, 1);
ComputeShader->UnsetParameters(RHICmdList, Scene);
}
// Further cull the objects into a low resolution grid
{
TShaderMapRef<FCullObjectsToGridCS> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo, ClipmapIndex, UpdateRegion);
const uint32 NumGroupsX = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.X, GCullGridTileSize);
const uint32 NumGroupsY = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Y, GCullGridTileSize);
const uint32 NumGroupsZ = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Z, GCullGridTileSize);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList);
}
}
// Further cull the objects to the dispatch tile and composite the global distance field by computing the min distance from intersecting per-object distance fields
{
SCOPED_DRAW_EVENTF(RHICmdList, TileCullAndComposite, TEXT("TileCullAndComposite %ux%ux%u"), UpdateRegion.CellsSize.X, UpdateRegion.CellsSize.Y, UpdateRegion.CellsSize.Z);
int32 MinDimension = 2;
if (UpdateRegion.CellsSize.X < UpdateRegion.CellsSize.Y && UpdateRegion.CellsSize.X < UpdateRegion.CellsSize.Z)
{
MinDimension = 0;
}
else if (UpdateRegion.CellsSize.Y < UpdateRegion.CellsSize.X && UpdateRegion.CellsSize.Y < UpdateRegion.CellsSize.Z)
{
MinDimension = 1;
}
int32 MinSize = UpdateRegion.CellsSize[MinDimension];
int32 MaxSize = FMath::Max(UpdateRegion.CellsSize.X, FMath::Max(UpdateRegion.CellsSize.Y, UpdateRegion.CellsSize.Z));
const EFlattenedDimension FlattenedDimension = MaxSize >= MinSize * 8 ? (EFlattenedDimension)MinDimension : Flatten_None;
const uint32 NumGroupsX = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.X, GetCompositeTileSize(0, FlattenedDimension));
const uint32 NumGroupsY = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Y, GetCompositeTileSize(1, FlattenedDimension));
const uint32 NumGroupsZ = FMath::DivideAndRoundUp<int32>(UpdateRegion.CellsSize.Z, GetCompositeTileSize(2, FlattenedDimension));
IPooledRenderTarget* ParentDistanceField = GlobalDistanceFieldInfo.MostlyStaticClipmaps[ClipmapIndex].RenderTarget;
if (CacheType == GDF_Full && GAOGlobalDistanceFieldCacheMostlyStaticSeparately && ParentDistanceField)
{
if (FlattenedDimension == Flatten_None)
{
TShaderMapRef<TCompositeObjectDistanceFieldsCS<true, Flatten_None>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, ParentDistanceField, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
else if (FlattenedDimension == Flatten_XAxis)
{
TShaderMapRef<TCompositeObjectDistanceFieldsCS<true, Flatten_XAxis>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, ParentDistanceField, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
else if (FlattenedDimension == Flatten_YAxis)
{
TShaderMapRef<TCompositeObjectDistanceFieldsCS<true, Flatten_YAxis>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, ParentDistanceField, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
else
{
check(FlattenedDimension == Flatten_ZAxis);
TShaderMapRef<TCompositeObjectDistanceFieldsCS<true, Flatten_ZAxis>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, ParentDistanceField, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
}
else
{
if (FlattenedDimension == Flatten_None)
{
TShaderMapRef<TCompositeObjectDistanceFieldsCS<false, Flatten_None>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, NULL, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
else if (FlattenedDimension == Flatten_XAxis)
{
TShaderMapRef<TCompositeObjectDistanceFieldsCS<false, Flatten_XAxis>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, NULL, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
else if (FlattenedDimension == Flatten_YAxis)
{
TShaderMapRef<TCompositeObjectDistanceFieldsCS<false, Flatten_YAxis>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, NULL, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
else
{
check(FlattenedDimension == Flatten_ZAxis);
TShaderMapRef<TCompositeObjectDistanceFieldsCS<false, Flatten_ZAxis>> ComputeShader(View.ShaderMap);
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, View, MaxOcclusionDistance, GlobalDistanceFieldInfo.ParameterData, Clipmap, NULL, ClipmapIndex, UpdateRegion);
DispatchComputeShader(RHICmdList, *ComputeShader, NumGroupsX, NumGroupsY, NumGroupsZ);
ComputeShader->UnsetParameters(RHICmdList, Clipmap);
}
}
}
}
}
}
// Make sure we finish all writing into clipmaps and they are ready to be read.
for (int32 ClipmapIndex = 0; ClipmapIndex < Clipmaps.Num(); ClipmapIndex++)
{
const FGlobalDistanceFieldClipmap& Clipmap = Clipmaps[ClipmapIndex];
const FSceneRenderTargetItem& ClipMapRTI = Clipmap.RenderTarget->GetRenderTargetItem();
RHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, ClipMapRTI.UAV);
}
// Composite heighfields.
if (CacheType == GDF_MostlyStatic || !GAOGlobalDistanceFieldCacheMostlyStaticSeparately)
{
SCOPED_DRAW_EVENT(RHICmdList, CompositeHeightfields);
for (int32 ClipmapIndex = 0; ClipmapIndex < Clipmaps.Num(); ClipmapIndex++)
{
SCOPED_DRAW_EVENTF(RHICmdList, Clipmap, TEXT("CacheType %s Clipmap %u"), CacheType == GDF_MostlyStatic ? TEXT("MostlyStatic") : TEXT("Movable"), ClipmapIndex);
FGlobalDistanceFieldClipmap& Clipmap = Clipmaps[ClipmapIndex];
for (int32 UpdateRegionIndex = 0; UpdateRegionIndex < Clipmap.UpdateRegions.Num(); UpdateRegionIndex++)
{
const FVolumeUpdateRegion& UpdateRegion = Clipmap.UpdateRegions[UpdateRegionIndex];
if (UpdateRegion.UpdateType & VUT_Heightfields)
{
View.HeightfieldLightingViewInfo.CompositeHeightfieldsIntoGlobalDistanceField(RHICmdList, Scene, View, GlobalMaxSphereQueryRadius, GlobalDistanceFieldInfo, Clipmap, ClipmapIndex, UpdateRegion);
}
}
}
}
// Transition clipmaps from compute to gfx.
for (int32 ClipmapIndex = 0; ClipmapIndex < Clipmaps.Num(); ClipmapIndex++)
{
FGlobalDistanceFieldClipmap& Clipmap = Clipmaps[ClipmapIndex];
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToGfx, Clipmap.RenderTarget->GetRenderTargetItem().UAV);
}
}
if (IsTransientResourceBufferAliasingEnabled())
{
GGlobalDistanceFieldCulledObjectBuffers.Buffers.DiscardTransientResource();
GObjectGridBuffers.DiscardTransientResource();
}
}
}
}
void ListGlobalDistanceFieldMemory()
{
UE_LOG(LogRenderer, Log, TEXT(" Global DF culled objects %.3fMb"), (GGlobalDistanceFieldCulledObjectBuffers.Buffers.GetSizeBytes() + GObjectGridBuffers.GetSizeBytes()) / 1024.0f / 1024.0f);
}