You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
11f5b21210
#rnx [CL 13753156 by Marc Audy in ue5-main branch]
1445 lines
61 KiB
C++
1445 lines
61 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
DistanceFieldObjectManagement.cpp
|
|
=============================================================================*/
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Stats/Stats.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "RHI.h"
|
|
#include "RenderResource.h"
|
|
#include "ShaderParameters.h"
|
|
#include "RendererInterface.h"
|
|
#include "Shader.h"
|
|
#include "SceneUtils.h"
|
|
#include "GlobalShader.h"
|
|
#include "DeferredShadingRenderer.h"
|
|
#include "ScenePrivate.h"
|
|
#include "DistanceFieldLightingShared.h"
|
|
#include "DistanceFieldAmbientOcclusion.h"
|
|
|
|
float GAOMaxObjectBoundingRadius = 50000;
|
|
FAutoConsoleVariableRef CVarAOMaxObjectBoundingRadius(
|
|
TEXT("r.AOMaxObjectBoundingRadius"),
|
|
GAOMaxObjectBoundingRadius,
|
|
TEXT("Objects larger than this will not contribute to AO calculations, to improve performance."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
int32 GAOLogObjectBufferReallocation = 0;
|
|
FAutoConsoleVariableRef CVarAOLogObjectBufferReallocation(
|
|
TEXT("r.AOLogObjectBufferReallocation"),
|
|
GAOLogObjectBufferReallocation,
|
|
TEXT(""),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
// Must match equivalent shader defines
|
|
template<> int32 TDistanceFieldObjectBuffers<DFPT_SignedDistanceField>::ObjectDataStride = 17;
|
|
template<> int32 TDistanceFieldObjectBuffers<DFPT_HeightField>::ObjectDataStride = 6;
|
|
template<> int32 TDistanceFieldCulledObjectBuffers<DFPT_SignedDistanceField>::ObjectDataStride = 17;
|
|
template<> int32 TDistanceFieldCulledObjectBuffers<DFPT_SignedDistanceField>::ObjectBoxBoundsStride = 5;
|
|
template<> int32 TDistanceFieldCulledObjectBuffers<DFPT_HeightField>::ObjectDataStride = 6;
|
|
template<> int32 TDistanceFieldCulledObjectBuffers<DFPT_HeightField>::ObjectBoxBoundsStride = 5;
|
|
|
|
// In float4's. Must match corresponding usf definition
|
|
int32 UploadObjectDataStride = 1 + FDistanceFieldObjectBuffers::ObjectDataStride;
|
|
int32 UploadHeightFieldObjectDataStride = 2 + FHeightFieldObjectBuffers::ObjectDataStride;
|
|
|
|
template <EDistanceFieldPrimitiveType PrimitiveType>
|
|
void TDistanceFieldObjectBuffers<PrimitiveType>::Initialize()
|
|
{
|
|
if (MaxObjects > 0)
|
|
{
|
|
const uint32 BufferFlags = BUF_ShaderResource;
|
|
|
|
uint32 NumComponents = 4;
|
|
EPixelFormat BufferFormat = PF_R32_FLOAT;
|
|
|
|
if (RHISupports4ComponentUAVReadWrite(GMaxRHIShaderPlatform))
|
|
{
|
|
NumComponents = 1;
|
|
BufferFormat = PF_A32B32G32R32F;
|
|
}
|
|
|
|
uint32 BoundsNumElements;
|
|
const TCHAR* BoundsDebugName;
|
|
const TCHAR* DataDebugName;
|
|
|
|
if (PrimitiveType == DFPT_HeightField)
|
|
{
|
|
BoundsNumElements = NumComponents * 2 * MaxObjects;
|
|
BoundsDebugName = TEXT("FHeightFieldObjectBuffers_Bounds");
|
|
DataDebugName = TEXT("FHeightFieldObjectBuffers_Data");
|
|
}
|
|
else
|
|
{
|
|
check(PrimitiveType == DFPT_SignedDistanceField);
|
|
BoundsNumElements = NumComponents * MaxObjects;
|
|
BoundsDebugName = TEXT("FDistanceFieldObjectBuffers_Bounds");
|
|
DataDebugName = TEXT("FDistanceFieldObjectBuffers_Data");
|
|
}
|
|
|
|
Bounds.Initialize(GPixelFormats[BufferFormat].BlockBytes, BoundsNumElements, BufferFormat, 0, BoundsDebugName);
|
|
Data.Initialize(GPixelFormats[BufferFormat].BlockBytes, NumComponents * MaxObjects * ObjectDataStride, BufferFormat, 0, DataDebugName);
|
|
}
|
|
}
|
|
|
|
template <EDistanceFieldPrimitiveType PrimitiveType>
|
|
class TDistanceFieldUploadDataResource : public FRenderResource
|
|
{
|
|
public:
|
|
|
|
FCPUUpdatedBuffer UploadData;
|
|
|
|
TDistanceFieldUploadDataResource()
|
|
{
|
|
// PS4 volatile only supports 8Mb, switch to dynamic once that is fixed.
|
|
// PS4 volatile only supports 8Mb, switch to volatile once that is fixed.
|
|
UploadData.bVolatile = false;
|
|
|
|
UploadData.Format = PF_A32B32G32R32F;
|
|
UploadData.Stride = PrimitiveType == DFPT_HeightField ? UploadHeightFieldObjectDataStride : UploadObjectDataStride;
|
|
}
|
|
|
|
virtual void InitDynamicRHI() override
|
|
{
|
|
UploadData.Initialize();
|
|
}
|
|
|
|
virtual void ReleaseDynamicRHI() override
|
|
{
|
|
UploadData.Release();
|
|
}
|
|
};
|
|
|
|
TGlobalResource<TDistanceFieldUploadDataResource<DFPT_SignedDistanceField>> GDistanceFieldUploadData;
|
|
TGlobalResource<TDistanceFieldUploadDataResource<DFPT_HeightField>> GHeightFieldUploadData;
|
|
|
|
class FDistanceFieldUploadIndicesResource : public FRenderResource
|
|
{
|
|
public:
|
|
|
|
FCPUUpdatedBuffer UploadIndices;
|
|
|
|
FDistanceFieldUploadIndicesResource()
|
|
{
|
|
// PS4 volatile only supports 8Mb, switch to volatile once that is fixed.
|
|
UploadIndices.bVolatile = false;
|
|
|
|
UploadIndices.Format = PF_R32_UINT;
|
|
UploadIndices.Stride = 1;
|
|
}
|
|
|
|
virtual void InitDynamicRHI() override
|
|
{
|
|
UploadIndices.Initialize();
|
|
}
|
|
|
|
virtual void ReleaseDynamicRHI() override
|
|
{
|
|
UploadIndices.Release();
|
|
}
|
|
};
|
|
|
|
TGlobalResource<FDistanceFieldUploadIndicesResource> GDistanceFieldUploadIndices;
|
|
TGlobalResource<FDistanceFieldUploadIndicesResource> GHeightFieldUploadIndices;
|
|
|
|
class FDistanceFieldRemoveIndicesResource : public FRenderResource
|
|
{
|
|
public:
|
|
|
|
FCPUUpdatedBuffer RemoveIndices;
|
|
|
|
FDistanceFieldRemoveIndicesResource()
|
|
{
|
|
RemoveIndices.Format = PF_R32G32B32A32_UINT;
|
|
RemoveIndices.Stride = 1;
|
|
}
|
|
|
|
virtual void InitDynamicRHI() override
|
|
{
|
|
RemoveIndices.Initialize();
|
|
}
|
|
|
|
virtual void ReleaseDynamicRHI() override
|
|
{
|
|
RemoveIndices.Release();
|
|
}
|
|
};
|
|
|
|
TGlobalResource<FDistanceFieldRemoveIndicesResource> GDistanceFieldRemoveIndices;
|
|
TGlobalResource<FDistanceFieldRemoveIndicesResource> GHeightFieldRemoveIndices;
|
|
|
|
const uint32 UpdateObjectsGroupSize = 64;
|
|
|
|
template <EDistanceFieldPrimitiveType PrimitiveType>
|
|
class TUploadObjectsToBufferCS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(TUploadObjectsToBufferCS,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("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
|
|
OutEnvironment.SetDefine(TEXT("DISTANCEFIELD_PRIMITIVE_TYPE"), (int32)PrimitiveType);
|
|
}
|
|
|
|
TUploadObjectsToBufferCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{
|
|
NumUploadOperations.Bind(Initializer.ParameterMap, TEXT("NumUploadOperations"));
|
|
UploadOperationIndices.Bind(Initializer.ParameterMap, TEXT("UploadOperationIndices"));
|
|
UploadOperationData.Bind(Initializer.ParameterMap, TEXT("UploadOperationData"));
|
|
ObjectBufferParameters.Bind(Initializer.ParameterMap);
|
|
}
|
|
|
|
TUploadObjectsToBufferCS()
|
|
{
|
|
}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, const FScene* Scene, uint32 NumUploadOperationsValue, FRHIShaderResourceView* InUploadOperationIndices, FRHIShaderResourceView* InUploadOperationData)
|
|
{
|
|
FRHIComputeShader* ShaderRHI = RHICmdList.GetBoundComputeShader();
|
|
|
|
SetShaderValue(RHICmdList, ShaderRHI, NumUploadOperations, NumUploadOperationsValue);
|
|
|
|
SetSRVParameter(RHICmdList, ShaderRHI, UploadOperationIndices, InUploadOperationIndices);
|
|
SetSRVParameter(RHICmdList, ShaderRHI, UploadOperationData, InUploadOperationData);
|
|
|
|
constexpr bool bIsHeightField = PrimitiveType == DFPT_HeightField;
|
|
const FDistanceFieldSceneData& SceneData = Scene->DistanceFieldSceneData;
|
|
const auto& ObjectBuffers = TSelector<bIsHeightField>()(*SceneData.GetHeightFieldObjectBuffers(), *SceneData.GetCurrentObjectBuffers());
|
|
const uint32 NumObjectsInBuffer = bIsHeightField ? SceneData.NumHeightFieldObjectsInBuffer : SceneData.NumObjectsInBuffer;
|
|
|
|
FRHITexture* TextureAtlas;
|
|
int32 AtlasSizeX;
|
|
int32 AtlasSizeY;
|
|
int32 AtlasSizeZ;
|
|
|
|
if (bIsHeightField)
|
|
{
|
|
TextureAtlas = GHeightFieldTextureAtlas.GetAtlasTexture();
|
|
AtlasSizeX = GHeightFieldTextureAtlas.GetSizeX();
|
|
AtlasSizeY = GHeightFieldTextureAtlas.GetSizeY();
|
|
AtlasSizeZ = 1;
|
|
}
|
|
else
|
|
{
|
|
TextureAtlas = GDistanceFieldVolumeTextureAtlas.VolumeTextureRHI;
|
|
AtlasSizeX = GDistanceFieldVolumeTextureAtlas.GetSizeX();
|
|
AtlasSizeY = GDistanceFieldVolumeTextureAtlas.GetSizeY();
|
|
AtlasSizeZ = GDistanceFieldVolumeTextureAtlas.GetSizeZ();
|
|
}
|
|
|
|
ObjectBufferParameters.Set(RHICmdList, ShaderRHI, ObjectBuffers, NumObjectsInBuffer, TextureAtlas, AtlasSizeX, AtlasSizeY, AtlasSizeZ, true);
|
|
}
|
|
|
|
void UnsetParameters(FRHICommandList& RHICmdList, const FScene* Scene)
|
|
{
|
|
constexpr bool bIsHeightField = PrimitiveType == DFPT_HeightField;
|
|
const FDistanceFieldSceneData& SceneData = Scene->DistanceFieldSceneData;
|
|
const auto& ObjectBuffers = TSelector<bIsHeightField>()(*SceneData.GetHeightFieldObjectBuffers(), *SceneData.GetCurrentObjectBuffers());
|
|
|
|
ObjectBufferParameters.UnsetParameters(RHICmdList, RHICmdList.GetBoundComputeShader(), ObjectBuffers, true);
|
|
}
|
|
|
|
private:
|
|
|
|
LAYOUT_FIELD(FShaderParameter, NumUploadOperations)
|
|
LAYOUT_FIELD(FShaderResourceParameter, UploadOperationIndices)
|
|
LAYOUT_FIELD(FShaderResourceParameter, UploadOperationData)
|
|
|
|
// clang is segfaulting with the templated IMPLEMENT_SHADER_TYPE below with this templated type directly in the LAYOUT_FIELD, so the uses of using like this in this file works around it
|
|
// (and also in DistanceFieldShadowing.cpp)
|
|
// a bugreport has been sent to Apple
|
|
using FDistanceFieldObjectBufferParametersType = TDistanceFieldObjectBufferParameters<PrimitiveType>;
|
|
LAYOUT_FIELD(FDistanceFieldObjectBufferParametersType, ObjectBufferParameters)
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(template<>, TUploadObjectsToBufferCS<DFPT_SignedDistanceField>, TEXT("/Engine/Private/DistanceFieldObjectCulling.usf"), TEXT("UploadObjectsToBufferCS"), SF_Compute);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TUploadObjectsToBufferCS<DFPT_HeightField>, TEXT("/Engine/Private/DistanceFieldObjectCulling.usf"), TEXT("UploadObjectsToBufferCS"), SF_Compute);
|
|
|
|
template <EDistanceFieldPrimitiveType PrimitiveType>
|
|
class TCopyObjectBufferCS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(TCopyObjectBufferCS, Global);
|
|
|
|
public:
|
|
typedef typename TChooseClass<PrimitiveType == DFPT_HeightField, FHeightFieldObjectBuffers, FDistanceFieldObjectBuffers>::Result ObjectBuffersType;
|
|
|
|
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("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
|
|
OutEnvironment.SetDefine(TEXT("DISTANCEFIELD_PRIMITIVE_TYPE"), (int32)PrimitiveType);
|
|
}
|
|
|
|
TCopyObjectBufferCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{
|
|
CopyObjectBounds.Bind(Initializer.ParameterMap, TEXT("CopyObjectBounds"));
|
|
CopyObjectData.Bind(Initializer.ParameterMap, TEXT("CopyObjectData"));
|
|
ObjectBufferParameters.Bind(Initializer.ParameterMap);
|
|
}
|
|
|
|
TCopyObjectBufferCS()
|
|
{
|
|
}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, ObjectBuffersType& ObjectBuffersSource, ObjectBuffersType& ObjectBuffersDest, int32 NumObjectsValue)
|
|
{
|
|
FRHIComputeShader* ShaderRHI = RHICmdList.GetBoundComputeShader();
|
|
|
|
FRHIUnorderedAccessView* OutUAVs[2];
|
|
OutUAVs[0] = ObjectBuffersDest.Bounds.UAV;
|
|
OutUAVs[1] = ObjectBuffersDest.Data.UAV;
|
|
RHICmdList.TransitionResources(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, UE_ARRAY_COUNT(OutUAVs));
|
|
|
|
CopyObjectBounds.SetBuffer(RHICmdList, ShaderRHI, ObjectBuffersDest.Bounds);
|
|
CopyObjectData.SetBuffer(RHICmdList, ShaderRHI, ObjectBuffersDest.Data);
|
|
|
|
FRHITexture* TextureAtlas;
|
|
int32 AtlasSizeX;
|
|
int32 AtlasSizeY;
|
|
int32 AtlasSizeZ;
|
|
|
|
if (PrimitiveType == DFPT_HeightField)
|
|
{
|
|
TextureAtlas = GHeightFieldTextureAtlas.GetAtlasTexture();
|
|
AtlasSizeX = GHeightFieldTextureAtlas.GetSizeX();
|
|
AtlasSizeY = GHeightFieldTextureAtlas.GetSizeY();
|
|
AtlasSizeZ = 1;
|
|
}
|
|
else
|
|
{
|
|
TextureAtlas = GDistanceFieldVolumeTextureAtlas.VolumeTextureRHI;
|
|
AtlasSizeX = GDistanceFieldVolumeTextureAtlas.GetSizeX();
|
|
AtlasSizeY = GDistanceFieldVolumeTextureAtlas.GetSizeY();
|
|
AtlasSizeZ = GDistanceFieldVolumeTextureAtlas.GetSizeZ();
|
|
}
|
|
|
|
ObjectBufferParameters.Set(RHICmdList, ShaderRHI, ObjectBuffersSource, NumObjectsValue, TextureAtlas, AtlasSizeX, AtlasSizeY, AtlasSizeZ);
|
|
}
|
|
|
|
void UnsetParameters(FRHICommandList& RHICmdList, ObjectBuffersType& ObjectBuffersDest)
|
|
{
|
|
ObjectBufferParameters.UnsetParameters(RHICmdList, RHICmdList.GetBoundComputeShader(), ObjectBuffersDest);
|
|
CopyObjectBounds.UnsetUAV(RHICmdList, RHICmdList.GetBoundComputeShader());
|
|
CopyObjectData.UnsetUAV(RHICmdList, RHICmdList.GetBoundComputeShader());
|
|
|
|
FRHIUnorderedAccessView* OutUAVs[2];
|
|
OutUAVs[0] = ObjectBuffersDest.Bounds.UAV;
|
|
OutUAVs[1] = ObjectBuffersDest.Data.UAV;
|
|
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, UE_ARRAY_COUNT(OutUAVs));
|
|
}
|
|
|
|
private:
|
|
|
|
LAYOUT_FIELD(FRWShaderParameter, CopyObjectBounds)
|
|
LAYOUT_FIELD(FRWShaderParameter, CopyObjectData)
|
|
using FDistanceFieldObjectBufferParametersType = TDistanceFieldObjectBufferParameters<PrimitiveType>;
|
|
LAYOUT_FIELD(FDistanceFieldObjectBufferParametersType, ObjectBufferParameters)
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(template<>, TCopyObjectBufferCS<DFPT_SignedDistanceField>, TEXT("/Engine/Private/DistanceFieldObjectCulling.usf"), TEXT("CopyObjectBufferCS"), SF_Compute);
|
|
IMPLEMENT_SHADER_TYPE(template<>, TCopyObjectBufferCS<DFPT_HeightField>, TEXT("/Engine/Private/DistanceFieldObjectCulling.usf"), TEXT("CopyObjectBufferCS"), SF_Compute);
|
|
|
|
|
|
template<bool bRemoveFromSameBuffer, EDistanceFieldPrimitiveType PrimitiveType>
|
|
class TRemoveObjectsFromBufferCS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(TRemoveObjectsFromBufferCS,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("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
|
|
OutEnvironment.SetDefine(TEXT("REMOVE_FROM_SAME_BUFFER"), bRemoveFromSameBuffer);
|
|
OutEnvironment.SetDefine(TEXT("DISTANCEFIELD_PRIMITIVE_TYPE"), (int32)PrimitiveType);
|
|
}
|
|
|
|
TRemoveObjectsFromBufferCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{
|
|
NumRemoveOperations.Bind(Initializer.ParameterMap, TEXT("NumRemoveOperations"));
|
|
RemoveOperationIndices.Bind(Initializer.ParameterMap, TEXT("RemoveOperationIndices"));
|
|
ObjectBufferParameters.Bind(Initializer.ParameterMap);
|
|
ObjectBounds2.Bind(Initializer.ParameterMap, TEXT("ObjectBounds2"));
|
|
ObjectData2.Bind(Initializer.ParameterMap, TEXT("ObjectData2"));
|
|
}
|
|
|
|
TRemoveObjectsFromBufferCS()
|
|
{
|
|
}
|
|
|
|
void SetParameters(
|
|
FRHICommandList& RHICmdList,
|
|
const FScene* Scene,
|
|
uint32 NumRemoveOperationsValue,
|
|
FRHIShaderResourceView* InRemoveOperationIndices,
|
|
FRHIShaderResourceView* InObjectBounds2,
|
|
FRHIShaderResourceView* InObjectData2)
|
|
{
|
|
FRHIComputeShader* ShaderRHI = RHICmdList.GetBoundComputeShader();
|
|
|
|
SetShaderValue(RHICmdList, ShaderRHI, NumRemoveOperations, NumRemoveOperationsValue);
|
|
SetSRVParameter(RHICmdList, ShaderRHI, RemoveOperationIndices, InRemoveOperationIndices);
|
|
|
|
constexpr bool bIsHeightField = PrimitiveType == DFPT_HeightField;
|
|
const FDistanceFieldSceneData& SceneData = Scene->DistanceFieldSceneData;
|
|
const auto& ObjectBuffers = TSelector<bIsHeightField>()(*SceneData.GetHeightFieldObjectBuffers(), *SceneData.GetCurrentObjectBuffers());
|
|
const uint32 NumObjectsInBuffer = bIsHeightField ? SceneData.NumHeightFieldObjectsInBuffer : SceneData.NumObjectsInBuffer;
|
|
|
|
FRHITexture* TextureAtlas;
|
|
int32 AtlasSizeX;
|
|
int32 AtlasSizeY;
|
|
int32 AtlasSizeZ;
|
|
|
|
if (PrimitiveType == DFPT_HeightField)
|
|
{
|
|
TextureAtlas = GHeightFieldTextureAtlas.GetAtlasTexture();
|
|
AtlasSizeX = GHeightFieldTextureAtlas.GetSizeX();
|
|
AtlasSizeY = GHeightFieldTextureAtlas.GetSizeY();
|
|
AtlasSizeZ = 1;
|
|
}
|
|
else
|
|
{
|
|
check(PrimitiveType == DFPT_SignedDistanceField);
|
|
TextureAtlas = GDistanceFieldVolumeTextureAtlas.VolumeTextureRHI;
|
|
AtlasSizeX = GDistanceFieldVolumeTextureAtlas.GetSizeX();
|
|
AtlasSizeY = GDistanceFieldVolumeTextureAtlas.GetSizeY();
|
|
AtlasSizeZ = GDistanceFieldVolumeTextureAtlas.GetSizeZ();
|
|
}
|
|
|
|
ObjectBufferParameters.Set(RHICmdList, ShaderRHI, ObjectBuffers, NumObjectsInBuffer, TextureAtlas, AtlasSizeX, AtlasSizeY, AtlasSizeZ, true);
|
|
|
|
SetSRVParameter(RHICmdList, ShaderRHI, ObjectBounds2, InObjectBounds2);
|
|
SetSRVParameter(RHICmdList, ShaderRHI, ObjectData2, InObjectData2);
|
|
}
|
|
|
|
void UnsetParameters(FRHICommandList& RHICmdList, const FScene* Scene)
|
|
{
|
|
constexpr bool bIsHeightField = PrimitiveType == DFPT_HeightField;
|
|
const FDistanceFieldSceneData& SceneData = Scene->DistanceFieldSceneData;
|
|
const auto& ObjectBuffers = TSelector<bIsHeightField>()(*SceneData.GetHeightFieldObjectBuffers(), *SceneData.GetCurrentObjectBuffers());
|
|
|
|
ObjectBufferParameters.UnsetParameters(RHICmdList, RHICmdList.GetBoundComputeShader(), ObjectBuffers, true);
|
|
}
|
|
|
|
private:
|
|
|
|
LAYOUT_FIELD(FShaderParameter, NumRemoveOperations)
|
|
LAYOUT_FIELD(FShaderResourceParameter, RemoveOperationIndices)
|
|
using FDistanceFieldObjectBufferParametersType = TDistanceFieldObjectBufferParameters<PrimitiveType>;
|
|
LAYOUT_FIELD(FDistanceFieldObjectBufferParametersType, ObjectBufferParameters)
|
|
LAYOUT_FIELD(FShaderResourceParameter, ObjectBounds2)
|
|
LAYOUT_FIELD(FShaderResourceParameter, ObjectData2)
|
|
};
|
|
|
|
#define IMPLEMENT_REMOVE_OBJECTS_FROM_BUFFER_SHADER_TYPE(bRemoveFromSameBuffer, PrimitiveType) \
|
|
typedef TRemoveObjectsFromBufferCS<bRemoveFromSameBuffer, PrimitiveType> FRemoveObjectsFromBufferCS##bRemoveFromSameBuffer##PrimitiveType; \
|
|
IMPLEMENT_SHADER_TYPE(template<>, FRemoveObjectsFromBufferCS##bRemoveFromSameBuffer##PrimitiveType, TEXT("/Engine/Private/DistanceFieldObjectCulling.usf"), TEXT("RemoveObjectsFromBufferCS"), SF_Compute);
|
|
|
|
IMPLEMENT_REMOVE_OBJECTS_FROM_BUFFER_SHADER_TYPE(true, DFPT_SignedDistanceField);
|
|
IMPLEMENT_REMOVE_OBJECTS_FROM_BUFFER_SHADER_TYPE(false, DFPT_SignedDistanceField);
|
|
IMPLEMENT_REMOVE_OBJECTS_FROM_BUFFER_SHADER_TYPE(true, DFPT_HeightField);
|
|
|
|
void UpdateGlobalDistanceFieldObjectRemoves(FRHICommandListImmediate& RHICmdList, FScene* Scene)
|
|
{
|
|
FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData;
|
|
|
|
TArray<FIntRect> RemoveObjectIndices;
|
|
|
|
if (DistanceFieldSceneData.PendingRemoveOperations.Num() > 0)
|
|
{
|
|
TArray<int32, SceneRenderingAllocator> PendingRemoveOperations;
|
|
|
|
for (int32 RemoveIndex = 0; RemoveIndex < DistanceFieldSceneData.PendingRemoveOperations.Num(); RemoveIndex++)
|
|
{
|
|
// Can't dereference the primitive here, it has already been deleted
|
|
const FPrimitiveSceneInfo* Primitive = DistanceFieldSceneData.PendingRemoveOperations[RemoveIndex].Primitive;
|
|
const TArray<int32, TInlineAllocator<1>>& DistanceFieldInstanceIndices = DistanceFieldSceneData.PendingRemoveOperations[RemoveIndex].DistanceFieldInstanceIndices;
|
|
|
|
for (int32 RemoveInstanceIndex = 0; RemoveInstanceIndex < DistanceFieldInstanceIndices.Num(); RemoveInstanceIndex++)
|
|
{
|
|
const int32 InstanceIndex = DistanceFieldInstanceIndices[RemoveInstanceIndex];
|
|
|
|
// InstanceIndex will be -1 with zero scale meshes
|
|
if (InstanceIndex >= 0)
|
|
{
|
|
FGlobalDFCacheType CacheType = DistanceFieldSceneData.PendingRemoveOperations[RemoveIndex].bOftenMoving ? GDF_Full : GDF_MostlyStatic;
|
|
DistanceFieldSceneData.PrimitiveModifiedBounds[CacheType].Add(DistanceFieldSceneData.PrimitiveInstanceMapping[InstanceIndex].BoundingSphere);
|
|
PendingRemoveOperations.Add(InstanceIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
DistanceFieldSceneData.PendingRemoveOperations.Reset();
|
|
|
|
if (PendingRemoveOperations.Num() > 0)
|
|
{
|
|
check(DistanceFieldSceneData.NumObjectsInBuffer >= PendingRemoveOperations.Num());
|
|
|
|
// Sort from smallest to largest
|
|
PendingRemoveOperations.Sort();
|
|
|
|
// We have multiple remove requests enqueued in PendingRemoveOperations, can only use the RemoveAtSwap version when there won't be collisions
|
|
const bool bUseRemoveAtSwap = PendingRemoveOperations.Last() < DistanceFieldSceneData.NumObjectsInBuffer - PendingRemoveOperations.Num();
|
|
|
|
FDistanceFieldObjectBuffers*& CurrentObjectBuffers = DistanceFieldSceneData.ObjectBuffers[DistanceFieldSceneData.ObjectBufferIndex];
|
|
|
|
if (bUseRemoveAtSwap)
|
|
{
|
|
// Remove everything in parallel in the same buffer with a RemoveAtSwap algorithm
|
|
for (int32 RemovePrimitiveIndex = 0; RemovePrimitiveIndex < PendingRemoveOperations.Num(); RemovePrimitiveIndex++)
|
|
{
|
|
DistanceFieldSceneData.NumObjectsInBuffer--;
|
|
const int32 RemoveIndex = PendingRemoveOperations[RemovePrimitiveIndex];
|
|
const int32 MoveFromIndex = DistanceFieldSceneData.NumObjectsInBuffer;
|
|
|
|
check(RemoveIndex != MoveFromIndex);
|
|
// Queue a compute shader move
|
|
RemoveObjectIndices.Add(FIntRect(RemoveIndex, MoveFromIndex, 0, 0));
|
|
|
|
// Fixup indices of the primitive that is being moved
|
|
FPrimitiveAndInstance& PrimitiveAndInstanceBeingMoved = DistanceFieldSceneData.PrimitiveInstanceMapping[MoveFromIndex];
|
|
check(PrimitiveAndInstanceBeingMoved.Primitive && PrimitiveAndInstanceBeingMoved.Primitive->DistanceFieldInstanceIndices.Num() > 0);
|
|
PrimitiveAndInstanceBeingMoved.Primitive->DistanceFieldInstanceIndices[PrimitiveAndInstanceBeingMoved.InstanceIndex] = RemoveIndex;
|
|
|
|
DistanceFieldSceneData.PrimitiveInstanceMapping.RemoveAtSwap(RemoveIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const double StartTime = FPlatformTime::Seconds();
|
|
|
|
TArray<FPrimitiveAndInstance> OriginalPrimitiveInstanceMapping;
|
|
|
|
// Have to copy the object data to allow parallel removing
|
|
int32 NextObjectBufferIndex = (DistanceFieldSceneData.ObjectBufferIndex + 1) & 1;
|
|
FDistanceFieldObjectBuffers*& NextObjectBuffers = DistanceFieldSceneData.ObjectBuffers[NextObjectBufferIndex];
|
|
|
|
check(CurrentObjectBuffers != nullptr);
|
|
|
|
DistanceFieldSceneData.ObjectBufferIndex = NextObjectBufferIndex;
|
|
|
|
if (NextObjectBuffers != nullptr && NextObjectBuffers->MaxObjects < CurrentObjectBuffers->MaxObjects)
|
|
{
|
|
NextObjectBuffers->Release();
|
|
delete NextObjectBuffers;
|
|
NextObjectBuffers = nullptr;
|
|
}
|
|
|
|
if (NextObjectBuffers == nullptr)
|
|
{
|
|
NextObjectBuffers = new FDistanceFieldObjectBuffers();
|
|
NextObjectBuffers->MaxObjects = CurrentObjectBuffers->MaxObjects;
|
|
NextObjectBuffers->Initialize();
|
|
}
|
|
|
|
OriginalPrimitiveInstanceMapping = DistanceFieldSceneData.PrimitiveInstanceMapping;
|
|
DistanceFieldSceneData.PrimitiveInstanceMapping.Reset();
|
|
|
|
const int32 NumDestObjects = DistanceFieldSceneData.NumObjectsInBuffer - PendingRemoveOperations.Num();
|
|
int32 SourceIndex = 0;
|
|
int32 NextPendingRemoveIndex = 0;
|
|
|
|
for (int32 DestinationIndex = 0; DestinationIndex < NumDestObjects; DestinationIndex++)
|
|
{
|
|
while (NextPendingRemoveIndex < PendingRemoveOperations.Num()
|
|
&& PendingRemoveOperations[NextPendingRemoveIndex] == SourceIndex)
|
|
{
|
|
NextPendingRemoveIndex++;
|
|
SourceIndex++;
|
|
}
|
|
|
|
// Queue a compute shader move
|
|
RemoveObjectIndices.Add(FIntRect(DestinationIndex, SourceIndex, 0, 0));
|
|
|
|
// Fixup indices of the primitive that is being moved
|
|
FPrimitiveAndInstance& PrimitiveAndInstanceBeingMoved = OriginalPrimitiveInstanceMapping[SourceIndex];
|
|
check(PrimitiveAndInstanceBeingMoved.Primitive && PrimitiveAndInstanceBeingMoved.Primitive->DistanceFieldInstanceIndices.Num() > 0);
|
|
PrimitiveAndInstanceBeingMoved.Primitive->DistanceFieldInstanceIndices[PrimitiveAndInstanceBeingMoved.InstanceIndex] = DestinationIndex;
|
|
|
|
check(DistanceFieldSceneData.PrimitiveInstanceMapping.Num() == DestinationIndex);
|
|
DistanceFieldSceneData.PrimitiveInstanceMapping.Add(PrimitiveAndInstanceBeingMoved);
|
|
|
|
SourceIndex++;
|
|
}
|
|
|
|
DistanceFieldSceneData.NumObjectsInBuffer = NumDestObjects;
|
|
|
|
if (GAOLogObjectBufferReallocation)
|
|
{
|
|
const float ElapsedTime = (float)(FPlatformTime::Seconds() - StartTime);
|
|
UE_LOG(LogDistanceField,Warning,TEXT("Global object buffer realloc %.3fs"), ElapsedTime);
|
|
}
|
|
|
|
/*
|
|
// Have to remove one at a time while any entries to remove are at the end of the buffer
|
|
DistanceFieldSceneData.NumObjectsInBuffer--;
|
|
const int32 RemoveIndex = DistanceFieldSceneData.PendingRemoveOperations[ParallelConflictIndex];
|
|
const int32 MoveFromIndex = DistanceFieldSceneData.NumObjectsInBuffer;
|
|
|
|
if (RemoveIndex != MoveFromIndex)
|
|
{
|
|
// Queue a compute shader move
|
|
RemoveObjectIndices.Add(FIntRect(RemoveIndex, MoveFromIndex, 0, 0));
|
|
|
|
// Fixup indices of the primitive that is being moved
|
|
FPrimitiveAndInstance& PrimitiveAndInstanceBeingMoved = DistanceFieldSceneData.PrimitiveInstanceMapping[MoveFromIndex];
|
|
check(PrimitiveAndInstanceBeingMoved.Primitive && PrimitiveAndInstanceBeingMoved.Primitive->DistanceFieldInstanceIndices.Num() > 0);
|
|
PrimitiveAndInstanceBeingMoved.Primitive->DistanceFieldInstanceIndices[PrimitiveAndInstanceBeingMoved.InstanceIndex] = RemoveIndex;
|
|
}
|
|
|
|
DistanceFieldSceneData.PrimitiveInstanceMapping.RemoveAtSwap(RemoveIndex);
|
|
DistanceFieldSceneData.PendingRemoveOperations.RemoveAtSwap(ParallelConflictIndex);
|
|
*/
|
|
}
|
|
|
|
PendingRemoveOperations.Reset();
|
|
|
|
if (RemoveObjectIndices.Num() > 0)
|
|
{
|
|
if (RemoveObjectIndices.Num() > GDistanceFieldRemoveIndices.RemoveIndices.MaxElements)
|
|
{
|
|
GDistanceFieldRemoveIndices.RemoveIndices.MaxElements = RemoveObjectIndices.Num() * 5 / 4;
|
|
GDistanceFieldRemoveIndices.RemoveIndices.Release();
|
|
GDistanceFieldRemoveIndices.RemoveIndices.Initialize();
|
|
}
|
|
|
|
void* LockedBuffer = RHILockVertexBuffer(GDistanceFieldRemoveIndices.RemoveIndices.Buffer, 0, GDistanceFieldRemoveIndices.RemoveIndices.Buffer->GetSize(), RLM_WriteOnly);
|
|
const uint32 MemcpySize = RemoveObjectIndices.GetTypeSize() * RemoveObjectIndices.Num();
|
|
check(GDistanceFieldRemoveIndices.RemoveIndices.Buffer->GetSize() >= MemcpySize);
|
|
FPlatformMemory::Memcpy(LockedBuffer, RemoveObjectIndices.GetData(), MemcpySize);
|
|
RHIUnlockVertexBuffer(GDistanceFieldRemoveIndices.RemoveIndices.Buffer);
|
|
|
|
if (bUseRemoveAtSwap)
|
|
{
|
|
TShaderMapRef<TRemoveObjectsFromBufferCS<true, DFPT_SignedDistanceField>> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
|
|
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
|
|
ComputeShader->SetParameters(RHICmdList, Scene, RemoveObjectIndices.Num(), GDistanceFieldRemoveIndices.RemoveIndices.BufferSRV, NULL, NULL);
|
|
|
|
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), FMath::DivideAndRoundUp<uint32>(RemoveObjectIndices.Num(), UpdateObjectsGroupSize), 1, 1);
|
|
ComputeShader->UnsetParameters(RHICmdList, Scene);
|
|
}
|
|
else
|
|
{
|
|
TShaderMapRef<TRemoveObjectsFromBufferCS<false, DFPT_SignedDistanceField>> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
|
|
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
|
|
ComputeShader->SetParameters(RHICmdList, Scene, RemoveObjectIndices.Num(), GDistanceFieldRemoveIndices.RemoveIndices.BufferSRV, CurrentObjectBuffers->Bounds.SRV, CurrentObjectBuffers->Data.SRV);
|
|
|
|
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), FMath::DivideAndRoundUp<uint32>(RemoveObjectIndices.Num(), UpdateObjectsGroupSize), 1, 1);
|
|
ComputeShader->UnsetParameters(RHICmdList, Scene);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateGlobalHeightFieldObjectRemoves(FRHICommandListImmediate& RHICmdList, FScene* Scene)
|
|
{
|
|
FDistanceFieldSceneData& SceneData = Scene->DistanceFieldSceneData;
|
|
TArray<FIntRect> DstSrcIndices;
|
|
|
|
if (SceneData.PendingHeightFieldRemoveOps.Num())
|
|
{
|
|
TArray<int32, SceneRenderingAllocator> PendingRemoveObjectIndices;
|
|
|
|
for (int32 Idx = 0; Idx < SceneData.PendingHeightFieldRemoveOps.Num(); ++Idx)
|
|
{
|
|
const FHeightFieldPrimitiveRemoveInfo& RemoveInfo = SceneData.PendingHeightFieldRemoveOps[Idx];
|
|
check(RemoveInfo.DistanceFieldInstanceIndices.Num() == 1);
|
|
const int32 ObjectIdx = RemoveInfo.DistanceFieldInstanceIndices[0];
|
|
|
|
if (ObjectIdx >= 0)
|
|
{
|
|
const FGlobalDFCacheType CacheType = RemoveInfo.bOftenMoving ? GDF_Full : GDF_MostlyStatic;
|
|
SceneData.PrimitiveModifiedBounds[CacheType].Add(RemoveInfo.SphereBound);
|
|
PendingRemoveObjectIndices.Add(ObjectIdx);
|
|
}
|
|
}
|
|
|
|
SceneData.PendingHeightFieldRemoveOps.Reset();
|
|
|
|
if (PendingRemoveObjectIndices.Num())
|
|
{
|
|
check(SceneData.NumHeightFieldObjectsInBuffer >= PendingRemoveObjectIndices.Num());
|
|
check(SceneData.NumHeightFieldObjectsInBuffer == SceneData.HeightfieldPrimitives.Num());
|
|
|
|
Algo::Sort(PendingRemoveObjectIndices);
|
|
|
|
for (int32 Idx = 0; Idx < PendingRemoveObjectIndices.Num(); ++Idx)
|
|
{
|
|
const int32 LastIdx = PendingRemoveObjectIndices.Num() - 1;
|
|
const int32 RemoveIdx = PendingRemoveObjectIndices[Idx];
|
|
const int32 LastRemoveIdx = PendingRemoveObjectIndices[LastIdx];
|
|
const int32 LastObjectIdx = --SceneData.NumHeightFieldObjectsInBuffer;
|
|
|
|
if (LastRemoveIdx < LastObjectIdx)
|
|
{
|
|
DstSrcIndices.Add(FIntRect(RemoveIdx, LastObjectIdx, 0, 0));
|
|
|
|
FPrimitiveSceneInfo* Primitive = SceneData.HeightfieldPrimitives[LastObjectIdx];
|
|
check(Primitive && Primitive->DistanceFieldInstanceIndices.Num() == 1);
|
|
Primitive->DistanceFieldInstanceIndices[0] = RemoveIdx;
|
|
SceneData.HeightfieldPrimitives.RemoveAtSwap(RemoveIdx);
|
|
}
|
|
else
|
|
{
|
|
check(LastRemoveIdx == LastObjectIdx);
|
|
SceneData.HeightfieldPrimitives.RemoveAt(LastObjectIdx);
|
|
PendingRemoveObjectIndices.RemoveAt(LastIdx, 1, false);
|
|
--Idx;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DstSrcIndices.Num())
|
|
{
|
|
FCPUUpdatedBuffer& RemoveIndices = GHeightFieldRemoveIndices.RemoveIndices;
|
|
if (DstSrcIndices.Num() > RemoveIndices.MaxElements)
|
|
{
|
|
RemoveIndices.MaxElements = DstSrcIndices.Num() * 5 / 4;
|
|
RemoveIndices.Release();
|
|
RemoveIndices.Initialize();
|
|
}
|
|
|
|
void* LockedBuffer = RHILockVertexBuffer(RemoveIndices.Buffer, 0, RemoveIndices.Buffer->GetSize(), RLM_WriteOnly);
|
|
const uint32 MemcpySize = DstSrcIndices.GetTypeSize() * DstSrcIndices.Num();
|
|
check(RemoveIndices.Buffer->GetSize() >= MemcpySize);
|
|
FMemory::Memcpy(LockedBuffer, DstSrcIndices.GetData(), MemcpySize);
|
|
RHIUnlockVertexBuffer(RemoveIndices.Buffer);
|
|
|
|
TShaderMapRef<TRemoveObjectsFromBufferCS<true, DFPT_HeightField>> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
|
|
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
|
|
ComputeShader->SetParameters(RHICmdList, Scene, DstSrcIndices.Num(), RemoveIndices.BufferSRV, nullptr, nullptr);
|
|
DispatchComputeShader(RHICmdList, ComputeShader, FMath::DivideAndRoundUp<uint32>(DstSrcIndices.Num(), UpdateObjectsGroupSize), 1, 1);
|
|
ComputeShader->UnsetParameters(RHICmdList, Scene);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Gathers the information needed to represent a single object's distance field and appends it to the upload buffers. */
|
|
bool ProcessPrimitiveUpdate(
|
|
bool bIsAddOperation,
|
|
FRHICommandListImmediate& RHICmdList,
|
|
FSceneRenderer& SceneRenderer,
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo,
|
|
int32 OriginalNumObjects,
|
|
FVector InvTextureDim,
|
|
bool bAnyViewEnabledDistanceCulling,
|
|
TArray<FMatrix>& ObjectLocalToWorldTransforms,
|
|
TArray<uint32>& UploadObjectIndices,
|
|
TArray<FVector4>& UploadObjectData)
|
|
{
|
|
FScene* Scene = SceneRenderer.Scene;
|
|
FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData;
|
|
|
|
ObjectLocalToWorldTransforms.Reset();
|
|
|
|
FBox LocalVolumeBounds;
|
|
FVector2D DistanceMinMax;
|
|
FIntVector BlockMin;
|
|
FIntVector BlockSize;
|
|
bool bBuiltAsIfTwoSided;
|
|
bool bMeshWasPlane;
|
|
float SelfShadowBias;
|
|
bool bThrottled;
|
|
PrimitiveSceneInfo->Proxy->GetDistancefieldAtlasData(LocalVolumeBounds, DistanceMinMax, BlockMin, BlockSize, bBuiltAsIfTwoSided, bMeshWasPlane, SelfShadowBias, ObjectLocalToWorldTransforms, bThrottled);
|
|
|
|
if (bThrottled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (BlockMin.X >= 0
|
|
&& BlockMin.Y >= 0
|
|
&& BlockMin.Z >= 0
|
|
&& ObjectLocalToWorldTransforms.Num() > 0)
|
|
{
|
|
const float BoundingRadius = PrimitiveSceneInfo->Proxy->GetBounds().SphereRadius;
|
|
const FGlobalDFCacheType CacheType = PrimitiveSceneInfo->Proxy->IsOftenMoving() ? GDF_Full : GDF_MostlyStatic;
|
|
|
|
// Proxy bounds are only useful if single instance
|
|
if (ObjectLocalToWorldTransforms.Num() > 1 || BoundingRadius < GAOMaxObjectBoundingRadius)
|
|
{
|
|
if (bIsAddOperation)
|
|
{
|
|
PrimitiveSceneInfo->DistanceFieldInstanceIndices.Empty(ObjectLocalToWorldTransforms.Num());
|
|
PrimitiveSceneInfo->DistanceFieldInstanceIndices.AddZeroed(ObjectLocalToWorldTransforms.Num());
|
|
}
|
|
|
|
for (int32 TransformIndex = 0; TransformIndex < ObjectLocalToWorldTransforms.Num(); TransformIndex++)
|
|
{
|
|
FMatrix LocalToWorld = ObjectLocalToWorldTransforms[TransformIndex];
|
|
const float MaxScale = LocalToWorld.GetMaximumAxisScale();
|
|
|
|
// Skip degenerate primitives
|
|
if (MaxScale > 0)
|
|
{
|
|
uint32 UploadIndex;
|
|
|
|
if (bIsAddOperation)
|
|
{
|
|
UploadIndex = OriginalNumObjects + UploadObjectIndices.Num();
|
|
DistanceFieldSceneData.NumObjectsInBuffer++;
|
|
}
|
|
else
|
|
{
|
|
UploadIndex = PrimitiveSceneInfo->DistanceFieldInstanceIndices[TransformIndex];
|
|
}
|
|
|
|
UploadObjectIndices.Add(UploadIndex);
|
|
|
|
if (bMeshWasPlane)
|
|
{
|
|
FVector LocalScales = LocalToWorld.GetScaleVector();
|
|
FVector AbsLocalScales(FMath::Abs(LocalScales.X), FMath::Abs(LocalScales.Y), FMath::Abs(LocalScales.Z));
|
|
float MidScale = FMath::Min(AbsLocalScales.X, AbsLocalScales.Y);
|
|
float ScaleAdjust = FMath::Sign(LocalScales.Z) * MidScale / AbsLocalScales.Z;
|
|
// The mesh was determined to be a plane flat in Z during the build process, so we can change the Z scale
|
|
// Helps in cases with modular ground pieces with scales of (10, 10, 1) and some triangles just above Z=0
|
|
LocalToWorld.SetAxis(2, LocalToWorld.GetScaledAxis(EAxis::Z) * ScaleAdjust);
|
|
}
|
|
|
|
const FMatrix VolumeToWorld = FScaleMatrix(LocalVolumeBounds.GetExtent())
|
|
* FTranslationMatrix(LocalVolumeBounds.GetCenter())
|
|
* LocalToWorld;
|
|
|
|
const FVector4 ObjectBoundingSphere(VolumeToWorld.GetOrigin(), VolumeToWorld.GetScaleVector().Size());
|
|
|
|
UploadObjectData.Add(ObjectBoundingSphere);
|
|
|
|
const float MaxExtent = LocalVolumeBounds.GetExtent().GetMax();
|
|
|
|
const FMatrix UniformScaleVolumeToWorld = FScaleMatrix(MaxExtent)
|
|
* FTranslationMatrix(LocalVolumeBounds.GetCenter())
|
|
* LocalToWorld;
|
|
|
|
const FVector InvBlockSize(1.0f / BlockSize.X, 1.0f / BlockSize.Y, 1.0f / BlockSize.Z);
|
|
|
|
//float3 VolumeUV = (VolumePosition / LocalPositionExtent * .5f * UVScale + .5f * UVScale + UVAdd;
|
|
const FVector LocalPositionExtent = LocalVolumeBounds.GetExtent() / FVector(MaxExtent);
|
|
const FVector UVScale = FVector(BlockSize) * InvTextureDim;
|
|
const float VolumeScale = UniformScaleVolumeToWorld.GetMaximumAxisScale();
|
|
|
|
const FMatrix WorldToVolumeT = UniformScaleVolumeToWorld.Inverse().GetTransposed();
|
|
// WorldToVolumeT
|
|
UploadObjectData.Add(*(FVector4*)&WorldToVolumeT.M[0]);
|
|
UploadObjectData.Add(*(FVector4*)&WorldToVolumeT.M[1]);
|
|
UploadObjectData.Add(*(FVector4*)&WorldToVolumeT.M[2]);
|
|
|
|
const float OftenMovingValue = CacheType == GDF_Full ? 1.0f : 0.0f;
|
|
|
|
// Clamp to texel center by subtracting a half texel in the [-1,1] position space
|
|
// LocalPositionExtent
|
|
UploadObjectData.Add(FVector4(LocalPositionExtent - InvBlockSize, OftenMovingValue));
|
|
|
|
// UVScale, VolumeScale and sign gives bGeneratedAsTwoSided
|
|
const float WSign = bBuiltAsIfTwoSided ? -1 : 1;
|
|
UploadObjectData.Add(FVector4(FVector(BlockSize) * InvTextureDim * .5f / LocalPositionExtent, WSign * VolumeScale));
|
|
|
|
// UVAdd
|
|
UploadObjectData.Add(FVector4(FVector(BlockMin) * InvTextureDim + .5f * UVScale, SelfShadowBias));
|
|
|
|
// xy - DistanceFieldMAD
|
|
// zw - MinDrawDistance^2, MaxDrawDistance^2
|
|
// [0, 1] -> [MinVolumeDistance, MaxVolumeDistance]
|
|
const int32 PrimIdx = PrimitiveSceneInfo->GetIndex();
|
|
const FPrimitiveBounds& PrimBounds = Scene->PrimitiveBounds[PrimIdx];
|
|
float MinDrawDist2 = PrimBounds.MinDrawDistanceSq;
|
|
// For IEEE compatible machines, float operations goes to inf if overflow
|
|
// In this case, it will effectively disable max draw distance culling
|
|
float MaxDrawDist = FMath::Max(PrimBounds.MaxCullDistance, 0.f) * GetCachedScalabilityCVars().ViewDistanceScale;
|
|
#if WITH_EDITOR
|
|
if (!bAnyViewEnabledDistanceCulling)
|
|
{
|
|
MinDrawDist2 = 0.f;
|
|
MaxDrawDist = 0.f;
|
|
}
|
|
#endif
|
|
// This is needed to bypass the check Nan/Inf behavior of FVector4.
|
|
// If the check is turned on, FVector4 constructor automatically converts
|
|
// the FVector4 being constructed to (0, 0, 0, 1) when any of inputs
|
|
// to the constructor contains Nan/Inf
|
|
UploadObjectData.AddUninitialized();
|
|
FVector4& TmpVec4 = UploadObjectData.Last();
|
|
TmpVec4.X = DistanceMinMax.Y - DistanceMinMax.X;
|
|
TmpVec4.Y = DistanceMinMax.X;
|
|
TmpVec4.Z = MinDrawDist2;
|
|
TmpVec4.W = MaxDrawDist * MaxDrawDist;
|
|
|
|
UploadObjectData.Add(*(FVector4*)&UniformScaleVolumeToWorld.M[0]);
|
|
UploadObjectData.Add(*(FVector4*)&UniformScaleVolumeToWorld.M[1]);
|
|
UploadObjectData.Add(*(FVector4*)&UniformScaleVolumeToWorld.M[2]);
|
|
|
|
FMatrix LocalToWorldT = LocalToWorld.GetTransposed();
|
|
UploadObjectData.Add(*(FVector4*)&LocalToWorldT.M[0]);
|
|
UploadObjectData.Add(*(FVector4*)&LocalToWorldT.M[1]);
|
|
UploadObjectData.Add(*(FVector4*)&LocalToWorldT.M[2]);
|
|
|
|
//Note: removed with Distance Field GI
|
|
UploadObjectData.Add(FVector4(0, 0, 0, 0));
|
|
|
|
FMatrix VolumeToWorldT = VolumeToWorld.GetTransposed();
|
|
UploadObjectData.Add(*(FVector4*)&VolumeToWorldT.M[0]);
|
|
UploadObjectData.Add(*(FVector4*)&VolumeToWorldT.M[1]);
|
|
UploadObjectData.Add(*(FVector4*)&VolumeToWorldT.M[2]);
|
|
|
|
checkSlow(UploadObjectData.Num() % UploadObjectDataStride == 0);
|
|
|
|
if (bIsAddOperation)
|
|
{
|
|
const int32 AddIndex = UploadIndex;
|
|
DistanceFieldSceneData.PrimitiveInstanceMapping.Add(FPrimitiveAndInstance(ObjectBoundingSphere, PrimitiveSceneInfo, TransformIndex));
|
|
PrimitiveSceneInfo->DistanceFieldInstanceIndices[TransformIndex] = AddIndex;
|
|
}
|
|
else
|
|
{
|
|
// InstanceIndex will be -1 with zero scale meshes
|
|
const int32 InstanceIndex = PrimitiveSceneInfo->DistanceFieldInstanceIndices[TransformIndex];
|
|
if (InstanceIndex >= 0)
|
|
{
|
|
// For an update transform we have to dirty the previous bounds and the new bounds, in case of large movement (teleport)
|
|
DistanceFieldSceneData.PrimitiveModifiedBounds[CacheType].Add(DistanceFieldSceneData.PrimitiveInstanceMapping[InstanceIndex].BoundingSphere);
|
|
DistanceFieldSceneData.PrimitiveInstanceMapping[InstanceIndex].BoundingSphere = ObjectBoundingSphere;
|
|
}
|
|
}
|
|
|
|
DistanceFieldSceneData.PrimitiveModifiedBounds[CacheType].Add(ObjectBoundingSphere);
|
|
|
|
extern int32 GAOLogGlobalDistanceFieldModifiedPrimitives;
|
|
|
|
if (GAOLogGlobalDistanceFieldModifiedPrimitives)
|
|
{
|
|
UE_LOG(LogDistanceField,Log,TEXT("Global Distance Field %s primitive %s %s %s bounding radius %.1f"), PrimitiveSceneInfo->Proxy->IsOftenMoving() ? TEXT("CACHED") : TEXT("Movable"), (bIsAddOperation ? TEXT("add") : TEXT("update")), *PrimitiveSceneInfo->Proxy->GetOwnerName().ToString(), *PrimitiveSceneInfo->Proxy->GetResourceName().ToString(), BoundingRadius);
|
|
}
|
|
}
|
|
else if (bIsAddOperation)
|
|
{
|
|
// Set to -1 for zero scale meshes
|
|
PrimitiveSceneInfo->DistanceFieldInstanceIndices[TransformIndex] = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogDistanceField,Log,TEXT("Primitive %s %s excluded due to bounding radius %f"), *PrimitiveSceneInfo->Proxy->GetOwnerName().ToString(), *PrimitiveSceneInfo->Proxy->GetResourceName().ToString(), BoundingRadius);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ProcessHeightFieldPrimitiveUpdate(
|
|
bool bIsAddOperation,
|
|
FRHICommandListImmediate& RHICmdList,
|
|
FScene* Scene,
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo,
|
|
int32 OriginalNumObjects,
|
|
TArray<uint32>& UploadObjectIndices,
|
|
TArray<FVector4>& UploadObjectData)
|
|
{
|
|
FDistanceFieldSceneData& SceneData = Scene->DistanceFieldSceneData;
|
|
|
|
UTexture2D* HeightNormalTexture;
|
|
UTexture2D* DiffuseColorTexture;
|
|
UTexture2D* VisibilityTexture;
|
|
FHeightfieldComponentDescription HeightFieldCompDesc(PrimitiveSceneInfo->Proxy->GetLocalToWorld());
|
|
PrimitiveSceneInfo->Proxy->GetHeightfieldRepresentation(HeightNormalTexture, DiffuseColorTexture, VisibilityTexture, HeightFieldCompDesc);
|
|
|
|
const uint32 Handle = GHeightFieldTextureAtlas.GetAllocationHandle(HeightNormalTexture);
|
|
if (Handle == INDEX_NONE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32 UploadIdx;
|
|
if (bIsAddOperation)
|
|
{
|
|
++SceneData.NumHeightFieldObjectsInBuffer;
|
|
SceneData.HeightfieldPrimitives.Add(PrimitiveSceneInfo);
|
|
|
|
const FGlobalDFCacheType CacheType = PrimitiveSceneInfo->Proxy->IsOftenMoving() ? GDF_Full : GDF_MostlyStatic;
|
|
const FBoxSphereBounds Bounds = PrimitiveSceneInfo->Proxy->GetBounds();
|
|
SceneData.PrimitiveModifiedBounds[CacheType].Add(FVector4(Bounds.Origin, Bounds.SphereRadius));
|
|
|
|
UploadIdx = OriginalNumObjects + UploadObjectIndices.Num();
|
|
PrimitiveSceneInfo->DistanceFieldInstanceIndices.Empty(1);
|
|
PrimitiveSceneInfo->DistanceFieldInstanceIndices.Add(UploadIdx);
|
|
}
|
|
else
|
|
{
|
|
UploadIdx = PrimitiveSceneInfo->DistanceFieldInstanceIndices[0];
|
|
}
|
|
|
|
UploadObjectIndices.Add(UploadIdx);
|
|
|
|
const FBoxSphereBounds& Bounds = PrimitiveSceneInfo->Proxy->GetBounds();
|
|
const FBox BoxBound = Bounds.GetBox();
|
|
UploadObjectData.Add(FVector4(BoxBound.GetCenter(), Bounds.SphereRadius));
|
|
UploadObjectData.Add(FVector4(BoxBound.GetExtent(), 0.f));
|
|
|
|
const FMatrix& LocalToWorld = HeightFieldCompDesc.LocalToWorld;
|
|
check(LocalToWorld.GetMaximumAxisScale() > 0.f);
|
|
const FMatrix WorldToLocalT = LocalToWorld.Inverse().GetTransposed();
|
|
UploadObjectData.Add(*(const FVector4*)&WorldToLocalT.M[0]);
|
|
UploadObjectData.Add(*(const FVector4*)&WorldToLocalT.M[1]);
|
|
UploadObjectData.Add(*(const FVector4*)&WorldToLocalT.M[2]);
|
|
|
|
const FIntRect& HeightFieldRect = HeightFieldCompDesc.HeightfieldRect;
|
|
const float WorldToLocalScale = FMath::Min3(
|
|
WorldToLocalT.GetColumn(0).Size(),
|
|
WorldToLocalT.GetColumn(1).Size(),
|
|
WorldToLocalT.GetColumn(2).Size());
|
|
UploadObjectData.Add(FVector4(HeightFieldRect.Width(), HeightFieldRect.Height(), WorldToLocalScale, 0.f));
|
|
|
|
const FVector4& HeightFieldScaleBias = HeightFieldCompDesc.HeightfieldScaleBias;
|
|
check(HeightFieldScaleBias.Y >= 0.f && HeightFieldScaleBias.Z >= 0.f && HeightFieldScaleBias.W >= 0.f);
|
|
const FVector4 AllocationScaleBias = GHeightFieldTextureAtlas.GetAllocationScaleBias(Handle);
|
|
UploadObjectData.Add(FVector4(
|
|
FMath::Abs(HeightFieldScaleBias.X) * AllocationScaleBias.X,
|
|
HeightFieldScaleBias.Y * AllocationScaleBias.Y,
|
|
HeightFieldScaleBias.Z * AllocationScaleBias.X + AllocationScaleBias.Z,
|
|
HeightFieldScaleBias.W * AllocationScaleBias.Y + AllocationScaleBias.W));
|
|
|
|
FVector4 VisUVScaleBias(ForceInitToZero);
|
|
if (VisibilityTexture)
|
|
{
|
|
const uint32 VisHandle = GHFVisibilityTextureAtlas.GetAllocationHandle(VisibilityTexture);
|
|
if (VisHandle != INDEX_NONE)
|
|
{
|
|
const FVector4 ScaleBias = GHFVisibilityTextureAtlas.GetAllocationScaleBias(VisHandle);
|
|
VisUVScaleBias = FVector4(1.f / HeightFieldRect.Width() * ScaleBias.X, 1.f / HeightFieldRect.Height() * ScaleBias.Y, ScaleBias.Z, ScaleBias.W);
|
|
}
|
|
}
|
|
UploadObjectData.Add(VisUVScaleBias);
|
|
|
|
check(!(UploadObjectData.Num() % UploadHeightFieldObjectDataStride));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool bVerifySceneIntegrity = false;
|
|
|
|
void FDeferredShadingSceneRenderer::UpdateGlobalDistanceFieldObjectBuffers(FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData;
|
|
|
|
if (GDistanceFieldVolumeTextureAtlas.VolumeTextureRHI
|
|
&& (DistanceFieldSceneData.HasPendingOperations() ||
|
|
DistanceFieldSceneData.PendingThrottledOperations.Num() > 0 ||
|
|
DistanceFieldSceneData.AtlasGeneration != GDistanceFieldVolumeTextureAtlas.GetGeneration()))
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_UpdateObjectData);
|
|
// Multi-GPU support : Updating on all GPUs may be inefficient for AFR. Work is
|
|
// wasted for any objects that update on consecutive frames.
|
|
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
|
|
SCOPED_DRAW_EVENT(RHICmdList, UpdateSceneObjectData);
|
|
|
|
if (DistanceFieldSceneData.ObjectBuffers[DistanceFieldSceneData.ObjectBufferIndex] == nullptr)
|
|
{
|
|
DistanceFieldSceneData.ObjectBuffers[DistanceFieldSceneData.ObjectBufferIndex] = new FDistanceFieldObjectBuffers();
|
|
}
|
|
|
|
if (DistanceFieldSceneData.PendingAddOperations.Num() > 0)
|
|
{
|
|
DistanceFieldSceneData.PendingThrottledOperations.Reserve(DistanceFieldSceneData.PendingThrottledOperations.Num() + DistanceFieldSceneData.PendingAddOperations.Num());
|
|
}
|
|
|
|
DistanceFieldSceneData.PendingAddOperations.Append(DistanceFieldSceneData.PendingThrottledOperations);
|
|
DistanceFieldSceneData.PendingThrottledOperations.Reset();
|
|
|
|
if (DistanceFieldSceneData.AtlasGeneration != GDistanceFieldVolumeTextureAtlas.GetGeneration())
|
|
{
|
|
DistanceFieldSceneData.AtlasGeneration = GDistanceFieldVolumeTextureAtlas.GetGeneration();
|
|
|
|
for (int32 PrimitiveInstanceIndex = 0; PrimitiveInstanceIndex < DistanceFieldSceneData.PrimitiveInstanceMapping.Num(); PrimitiveInstanceIndex++)
|
|
{
|
|
FPrimitiveAndInstance& PrimitiveInstance = DistanceFieldSceneData.PrimitiveInstanceMapping[PrimitiveInstanceIndex];
|
|
|
|
// Queue an update of all primitives, since the atlas layout has changed
|
|
if (PrimitiveInstance.InstanceIndex == 0
|
|
&& !DistanceFieldSceneData.HasPendingRemovePrimitive(PrimitiveInstance.Primitive)
|
|
&& !DistanceFieldSceneData.PendingAddOperations.Contains(PrimitiveInstance.Primitive)
|
|
&& !DistanceFieldSceneData.PendingUpdateOperations.Contains(PrimitiveInstance.Primitive))
|
|
{
|
|
DistanceFieldSceneData.PendingUpdateOperations.Add(PrimitiveInstance.Primitive);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process removes before adds
|
|
UpdateGlobalDistanceFieldObjectRemoves(RHICmdList, Scene);
|
|
|
|
TArray<uint32> UploadObjectIndices;
|
|
TArray<FVector4> UploadObjectData;
|
|
|
|
if (DistanceFieldSceneData.PendingAddOperations.Num() > 0 || DistanceFieldSceneData.PendingUpdateOperations.Num() > 0)
|
|
{
|
|
TArray<FMatrix> ObjectLocalToWorldTransforms;
|
|
|
|
const int32 NumUploadOperations = DistanceFieldSceneData.PendingAddOperations.Num() + DistanceFieldSceneData.PendingUpdateOperations.Num();
|
|
UploadObjectData.Empty(NumUploadOperations * UploadObjectDataStride);
|
|
UploadObjectIndices.Empty(NumUploadOperations);
|
|
|
|
const int32 NumTexelsOneDimX = GDistanceFieldVolumeTextureAtlas.GetSizeX();
|
|
const int32 NumTexelsOneDimY = GDistanceFieldVolumeTextureAtlas.GetSizeY();
|
|
const int32 NumTexelsOneDimZ = GDistanceFieldVolumeTextureAtlas.GetSizeZ();
|
|
const FVector InvTextureDim(1.0f / NumTexelsOneDimX, 1.0f / NumTexelsOneDimY, 1.0f / NumTexelsOneDimZ);
|
|
|
|
int32 OriginalNumObjects = DistanceFieldSceneData.NumObjectsInBuffer;
|
|
|
|
bool bAnyViewEnabledDistanceCulling = !WITH_EDITOR;
|
|
#if WITH_EDITOR
|
|
for (const FViewInfo& ViewInfo : Views)
|
|
{
|
|
if (!ViewInfo.Family->EngineShowFlags.DistanceCulledPrimitives)
|
|
{
|
|
bAnyViewEnabledDistanceCulling = true;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (int32 UploadPrimitiveIndex = 0; UploadPrimitiveIndex < DistanceFieldSceneData.PendingAddOperations.Num(); UploadPrimitiveIndex++)
|
|
{
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo = DistanceFieldSceneData.PendingAddOperations[UploadPrimitiveIndex];
|
|
|
|
if (!ProcessPrimitiveUpdate(
|
|
true,
|
|
RHICmdList,
|
|
*this,
|
|
PrimitiveSceneInfo,
|
|
OriginalNumObjects,
|
|
InvTextureDim,
|
|
bAnyViewEnabledDistanceCulling,
|
|
ObjectLocalToWorldTransforms,
|
|
UploadObjectIndices,
|
|
UploadObjectData))
|
|
{
|
|
DistanceFieldSceneData.PendingThrottledOperations.Add(PrimitiveSceneInfo);
|
|
}
|
|
}
|
|
|
|
for (TSet<FPrimitiveSceneInfo*>::TIterator It(DistanceFieldSceneData.PendingUpdateOperations); It; ++It)
|
|
{
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo = *It;
|
|
|
|
ProcessPrimitiveUpdate(
|
|
false,
|
|
RHICmdList,
|
|
*this,
|
|
PrimitiveSceneInfo,
|
|
OriginalNumObjects,
|
|
InvTextureDim,
|
|
bAnyViewEnabledDistanceCulling,
|
|
ObjectLocalToWorldTransforms,
|
|
UploadObjectIndices,
|
|
UploadObjectData);
|
|
}
|
|
|
|
DistanceFieldSceneData.PendingAddOperations.Reset();
|
|
DistanceFieldSceneData.PendingUpdateOperations.Empty();
|
|
if (DistanceFieldSceneData.PendingThrottledOperations.Num() == 0)
|
|
{
|
|
DistanceFieldSceneData.PendingThrottledOperations.Empty();
|
|
}
|
|
|
|
FDistanceFieldObjectBuffers*& ObjectBuffers = DistanceFieldSceneData.ObjectBuffers[DistanceFieldSceneData.ObjectBufferIndex];
|
|
|
|
if (ObjectBuffers->MaxObjects < DistanceFieldSceneData.NumObjectsInBuffer)
|
|
{
|
|
if (ObjectBuffers->MaxObjects > 0)
|
|
{
|
|
// Realloc
|
|
FDistanceFieldObjectBuffers* NewObjectBuffers = new FDistanceFieldObjectBuffers();
|
|
NewObjectBuffers->MaxObjects = DistanceFieldSceneData.NumObjectsInBuffer * 5 / 4;
|
|
NewObjectBuffers->Initialize();
|
|
|
|
{
|
|
TShaderMapRef<TCopyObjectBufferCS<DFPT_SignedDistanceField>> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
|
|
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
|
|
ComputeShader->SetParameters(RHICmdList, *ObjectBuffers, *NewObjectBuffers, OriginalNumObjects);
|
|
|
|
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), FMath::DivideAndRoundUp<uint32>(OriginalNumObjects, UpdateObjectsGroupSize), 1, 1);
|
|
ComputeShader->UnsetParameters(RHICmdList, *NewObjectBuffers);
|
|
}
|
|
|
|
ObjectBuffers->Release();
|
|
delete ObjectBuffers;
|
|
ObjectBuffers = NewObjectBuffers;
|
|
}
|
|
else
|
|
{
|
|
// First time allocate
|
|
ObjectBuffers->MaxObjects = DistanceFieldSceneData.NumObjectsInBuffer * 5 / 4;
|
|
ObjectBuffers->Initialize();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UploadObjectIndices.Num() > 0)
|
|
{
|
|
if (UploadObjectIndices.Num() > GDistanceFieldUploadIndices.UploadIndices.MaxElements
|
|
// Shrink if very large
|
|
|| (GDistanceFieldUploadIndices.UploadIndices.MaxElements > 1000 && GDistanceFieldUploadIndices.UploadIndices.MaxElements > UploadObjectIndices.Num() * 2))
|
|
{
|
|
GDistanceFieldUploadIndices.UploadIndices.MaxElements = UploadObjectIndices.Num() * 5 / 4;
|
|
GDistanceFieldUploadIndices.UploadIndices.Release();
|
|
GDistanceFieldUploadIndices.UploadIndices.Initialize();
|
|
|
|
GDistanceFieldUploadData.UploadData.MaxElements = UploadObjectIndices.Num() * 5 / 4;
|
|
GDistanceFieldUploadData.UploadData.Release();
|
|
GDistanceFieldUploadData.UploadData.Initialize();
|
|
}
|
|
|
|
void* LockedBuffer = RHILockVertexBuffer(GDistanceFieldUploadIndices.UploadIndices.Buffer, 0, GDistanceFieldUploadIndices.UploadIndices.Buffer->GetSize(), RLM_WriteOnly);
|
|
const uint32 MemcpySize = UploadObjectIndices.GetTypeSize() * UploadObjectIndices.Num();
|
|
check(GDistanceFieldUploadIndices.UploadIndices.Buffer->GetSize() >= MemcpySize);
|
|
FPlatformMemory::Memcpy(LockedBuffer, UploadObjectIndices.GetData(), MemcpySize);
|
|
RHIUnlockVertexBuffer(GDistanceFieldUploadIndices.UploadIndices.Buffer);
|
|
|
|
LockedBuffer = RHILockVertexBuffer(GDistanceFieldUploadData.UploadData.Buffer, 0, GDistanceFieldUploadData.UploadData.Buffer->GetSize(), RLM_WriteOnly);
|
|
const uint32 MemcpySize2 = UploadObjectData.GetTypeSize() * UploadObjectData.Num();
|
|
check(GDistanceFieldUploadData.UploadData.Buffer->GetSize() >= MemcpySize2);
|
|
FPlatformMemory::Memcpy(LockedBuffer, UploadObjectData.GetData(), MemcpySize2);
|
|
RHIUnlockVertexBuffer(GDistanceFieldUploadData.UploadData.Buffer);
|
|
|
|
{
|
|
TShaderMapRef<TUploadObjectsToBufferCS<DFPT_SignedDistanceField>> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
|
|
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
|
|
ComputeShader->SetParameters(RHICmdList, Scene, UploadObjectIndices.Num(), GDistanceFieldUploadIndices.UploadIndices.BufferSRV, GDistanceFieldUploadData.UploadData.BufferSRV);
|
|
|
|
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), FMath::DivideAndRoundUp<uint32>(UploadObjectIndices.Num(), UpdateObjectsGroupSize), 1, 1);
|
|
ComputeShader->UnsetParameters(RHICmdList, Scene);
|
|
}
|
|
}
|
|
|
|
check(DistanceFieldSceneData.NumObjectsInBuffer == DistanceFieldSceneData.PrimitiveInstanceMapping.Num());
|
|
|
|
if (bVerifySceneIntegrity)
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_UpdateObjectData_VerifyIntegrity);
|
|
DistanceFieldSceneData.VerifyIntegrity();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::UpdateGlobalHeightFieldObjectBuffers(FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData;
|
|
|
|
if (GHeightFieldTextureAtlas.GetAtlasTexture()
|
|
&& (DistanceFieldSceneData.HasPendingHeightFieldOperations()
|
|
|| DistanceFieldSceneData.HeightFieldAtlasGeneration != GHeightFieldTextureAtlas.GetGeneration()
|
|
|| DistanceFieldSceneData.HFVisibilityAtlasGenerattion != GHFVisibilityTextureAtlas.GetGeneration()))
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_UpdateHeightFieldSceneObjectData);
|
|
SCOPED_DRAW_EVENT(RHICmdList, UpdateHeightFieldSceneObjectData);
|
|
|
|
if (!DistanceFieldSceneData.HeightFieldObjectBuffers)
|
|
{
|
|
AddOrRemoveSceneHeightFieldPrimitives(true);
|
|
|
|
for (int32 Idx = 0; Idx < DistanceFieldSceneData.HeightfieldPrimitives.Num(); ++Idx)
|
|
{
|
|
FPrimitiveSceneInfo* Primitive = DistanceFieldSceneData.HeightfieldPrimitives[Idx];
|
|
check(!DistanceFieldSceneData.PendingHeightFieldAddOps.Contains(Primitive));
|
|
DistanceFieldSceneData.PendingHeightFieldAddOps.Add(Primitive);
|
|
}
|
|
DistanceFieldSceneData.HeightfieldPrimitives.Reset();
|
|
DistanceFieldSceneData.HeightFieldObjectBuffers = new FHeightFieldObjectBuffers;
|
|
}
|
|
|
|
if (DistanceFieldSceneData.HeightFieldAtlasGeneration != GHeightFieldTextureAtlas.GetGeneration()
|
|
|| DistanceFieldSceneData.HFVisibilityAtlasGenerattion != GHFVisibilityTextureAtlas.GetGeneration())
|
|
{
|
|
DistanceFieldSceneData.HeightFieldAtlasGeneration = GHeightFieldTextureAtlas.GetGeneration();
|
|
DistanceFieldSceneData.HFVisibilityAtlasGenerattion = GHFVisibilityTextureAtlas.GetGeneration();
|
|
|
|
for (int32 Idx = 0; Idx < DistanceFieldSceneData.HeightfieldPrimitives.Num(); ++Idx)
|
|
{
|
|
FPrimitiveSceneInfo* Primitive = DistanceFieldSceneData.HeightfieldPrimitives[Idx];
|
|
|
|
if (!DistanceFieldSceneData.HasPendingRemoveHeightFieldPrimitive(Primitive)
|
|
&& !DistanceFieldSceneData.PendingHeightFieldAddOps.Contains(Primitive)
|
|
&& !DistanceFieldSceneData.PendingHeightFieldUpdateOps.Contains(Primitive))
|
|
{
|
|
DistanceFieldSceneData.PendingHeightFieldUpdateOps.Add(Primitive);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateGlobalHeightFieldObjectRemoves(RHICmdList, Scene);
|
|
|
|
if (DistanceFieldSceneData.PendingHeightFieldAddOps.Num() || DistanceFieldSceneData.PendingHeightFieldUpdateOps.Num())
|
|
{
|
|
const int32 NumAddOps = DistanceFieldSceneData.PendingHeightFieldAddOps.Num();
|
|
const int32 NumUpdateOps = DistanceFieldSceneData.PendingHeightFieldUpdateOps.Num();
|
|
const int32 NumUploadOps = NumAddOps + NumUpdateOps;
|
|
const int32 OriginalNumObjects = DistanceFieldSceneData.NumHeightFieldObjectsInBuffer;
|
|
TArray<uint32> UploadHeightFieldObjectIndices;
|
|
TArray<FVector4> UploadHeightFieldObjectData;
|
|
|
|
UploadHeightFieldObjectIndices.Empty(NumUploadOps);
|
|
UploadHeightFieldObjectData.Empty(NumUploadOps * UploadHeightFieldObjectDataStride);
|
|
|
|
for (int32 Idx = 0; Idx < NumAddOps; ++Idx)
|
|
{
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo = DistanceFieldSceneData.PendingHeightFieldAddOps[Idx];
|
|
ProcessHeightFieldPrimitiveUpdate(true, RHICmdList, Scene, PrimitiveSceneInfo, OriginalNumObjects, UploadHeightFieldObjectIndices, UploadHeightFieldObjectData);
|
|
}
|
|
|
|
for (int32 Idx = 0; Idx < NumUpdateOps; ++Idx)
|
|
{
|
|
FPrimitiveSceneInfo* PrimitiveSceneInfo = DistanceFieldSceneData.PendingHeightFieldUpdateOps[Idx];
|
|
ProcessHeightFieldPrimitiveUpdate(false, RHICmdList, Scene, PrimitiveSceneInfo, OriginalNumObjects, UploadHeightFieldObjectIndices, UploadHeightFieldObjectData);
|
|
}
|
|
|
|
DistanceFieldSceneData.PendingHeightFieldAddOps.Reset();
|
|
DistanceFieldSceneData.PendingHeightFieldUpdateOps.Empty();
|
|
|
|
FHeightFieldObjectBuffers*& ObjectBuffers = DistanceFieldSceneData.HeightFieldObjectBuffers;
|
|
|
|
if (ObjectBuffers->MaxObjects < DistanceFieldSceneData.NumHeightFieldObjectsInBuffer)
|
|
{
|
|
if (ObjectBuffers->MaxObjects > 0)
|
|
{
|
|
FHeightFieldObjectBuffers* NewObjectBuffers = new FHeightFieldObjectBuffers;
|
|
NewObjectBuffers->MaxObjects = DistanceFieldSceneData.NumHeightFieldObjectsInBuffer * 5 / 4;
|
|
NewObjectBuffers->Initialize();
|
|
|
|
TShaderMapRef<TCopyObjectBufferCS<DFPT_HeightField>> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
|
|
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
|
|
ComputeShader->SetParameters(RHICmdList, *ObjectBuffers, *NewObjectBuffers, OriginalNumObjects);
|
|
DispatchComputeShader(RHICmdList, ComputeShader, FMath::DivideAndRoundUp<uint32>(OriginalNumObjects, UpdateObjectsGroupSize), 1, 1);
|
|
ComputeShader->UnsetParameters(RHICmdList, *NewObjectBuffers);
|
|
|
|
ObjectBuffers->Release();
|
|
delete ObjectBuffers;
|
|
ObjectBuffers = NewObjectBuffers;
|
|
}
|
|
else
|
|
{
|
|
ObjectBuffers->MaxObjects = DistanceFieldSceneData.NumHeightFieldObjectsInBuffer * 5 / 4;
|
|
ObjectBuffers->Initialize();
|
|
}
|
|
}
|
|
|
|
const int32 NumObjectsToUpload = UploadHeightFieldObjectIndices.Num();
|
|
|
|
if (NumObjectsToUpload)
|
|
{
|
|
if (NumObjectsToUpload > GHeightFieldUploadIndices.UploadIndices.MaxElements
|
|
|| (GHeightFieldUploadIndices.UploadIndices.MaxElements > 1000 && GHeightFieldUploadIndices.UploadIndices.MaxElements > NumObjectsToUpload * 2))
|
|
{
|
|
GHeightFieldUploadIndices.UploadIndices.MaxElements = NumObjectsToUpload * 5 / 4;
|
|
GHeightFieldUploadIndices.UploadIndices.Release();
|
|
GHeightFieldUploadIndices.UploadIndices.Initialize();
|
|
|
|
GHeightFieldUploadData.UploadData.MaxElements = NumObjectsToUpload * 5 / 4;
|
|
GHeightFieldUploadData.UploadData.Release();
|
|
GHeightFieldUploadData.UploadData.Initialize();
|
|
}
|
|
|
|
void* LockedBuffer = RHILockVertexBuffer(GHeightFieldUploadIndices.UploadIndices.Buffer, 0, GHeightFieldUploadIndices.UploadIndices.Buffer->GetSize(), RLM_WriteOnly);
|
|
uint32 MemcpySize = UploadHeightFieldObjectIndices.GetTypeSize() * NumObjectsToUpload;
|
|
check(GHeightFieldUploadIndices.UploadIndices.Buffer->GetSize() >= MemcpySize);
|
|
FMemory::Memcpy(LockedBuffer, UploadHeightFieldObjectIndices.GetData(), MemcpySize);
|
|
RHIUnlockVertexBuffer(GHeightFieldUploadIndices.UploadIndices.Buffer);
|
|
|
|
LockedBuffer = RHILockVertexBuffer(GHeightFieldUploadData.UploadData.Buffer, 0, GHeightFieldUploadData.UploadData.Buffer->GetSize(), RLM_WriteOnly);
|
|
MemcpySize = UploadHeightFieldObjectData.GetTypeSize() * UploadHeightFieldObjectData.Num();
|
|
check(GHeightFieldUploadData.UploadData.Buffer->GetSize() >= MemcpySize);
|
|
FMemory::Memcpy(LockedBuffer, UploadHeightFieldObjectData.GetData(), MemcpySize);
|
|
RHIUnlockVertexBuffer(GHeightFieldUploadData.UploadData.Buffer);
|
|
|
|
TShaderMapRef<TUploadObjectsToBufferCS<DFPT_HeightField>> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
|
|
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
|
|
ComputeShader->SetParameters(RHICmdList, Scene, NumObjectsToUpload, GHeightFieldUploadIndices.UploadIndices.BufferSRV, GHeightFieldUploadData.UploadData.BufferSRV);
|
|
DispatchComputeShader(RHICmdList, ComputeShader, FMath::DivideAndRoundUp<uint32>(NumObjectsToUpload, UpdateObjectsGroupSize), 1, 1);
|
|
ComputeShader->UnsetParameters(RHICmdList, Scene);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::AddOrRemoveSceneHeightFieldPrimitives(bool bSkipAdd)
|
|
{
|
|
FDistanceFieldSceneData& SceneData = Scene->DistanceFieldSceneData;
|
|
|
|
if (SceneData.HeightFieldObjectBuffers)
|
|
{
|
|
delete SceneData.HeightFieldObjectBuffers;
|
|
SceneData.HeightFieldObjectBuffers = nullptr;
|
|
SceneData.NumHeightFieldObjectsInBuffer = 0;
|
|
SceneData.HeightFieldAtlasGeneration = 0;
|
|
SceneData.HFVisibilityAtlasGenerattion = 0;
|
|
}
|
|
|
|
TArray<int32, SceneRenderingAllocator> PendingRemoveIndices;
|
|
for (int32 Idx = 0; Idx < SceneData.PendingHeightFieldRemoveOps.Num(); ++Idx)
|
|
{
|
|
const FHeightFieldPrimitiveRemoveInfo& RemoveInfo = SceneData.PendingHeightFieldRemoveOps[Idx];
|
|
check(RemoveInfo.DistanceFieldInstanceIndices.Num() == 1);
|
|
PendingRemoveIndices.Add(RemoveInfo.DistanceFieldInstanceIndices[0]);
|
|
const FGlobalDFCacheType CacheType = RemoveInfo.bOftenMoving ? GDF_Full : GDF_MostlyStatic;
|
|
SceneData.PrimitiveModifiedBounds[CacheType].Add(RemoveInfo.SphereBound);
|
|
}
|
|
SceneData.PendingHeightFieldRemoveOps.Reset();
|
|
Algo::Sort(PendingRemoveIndices);
|
|
for (int32 Idx = PendingRemoveIndices.Num() - 1; Idx >= 0; --Idx)
|
|
{
|
|
const int32 RemoveIdx = PendingRemoveIndices[Idx];
|
|
const int32 LastObjectIdx = SceneData.HeightfieldPrimitives.Num() - 1;
|
|
if (RemoveIdx != LastObjectIdx)
|
|
{
|
|
SceneData.HeightfieldPrimitives[LastObjectIdx]->DistanceFieldInstanceIndices[0] = RemoveIdx;
|
|
}
|
|
SceneData.HeightfieldPrimitives.RemoveAtSwap(RemoveIdx);
|
|
}
|
|
|
|
if (!bSkipAdd)
|
|
{
|
|
for (int32 Idx = 0; Idx < SceneData.PendingHeightFieldAddOps.Num(); ++Idx)
|
|
{
|
|
FPrimitiveSceneInfo* Primitive = SceneData.PendingHeightFieldAddOps[Idx];
|
|
const int32 HFIdx = SceneData.HeightfieldPrimitives.Add(Primitive);
|
|
Primitive->DistanceFieldInstanceIndices.Empty(1);
|
|
Primitive->DistanceFieldInstanceIndices.Add(HFIdx);
|
|
const FGlobalDFCacheType CacheType = Primitive->Proxy->IsOftenMoving() ? GDF_Full : GDF_MostlyStatic;
|
|
const FBoxSphereBounds& Bounds = Primitive->Proxy->GetBounds();
|
|
SceneData.PrimitiveModifiedBounds[CacheType].Add(FVector4(Bounds.Origin, Bounds.SphereRadius));
|
|
}
|
|
SceneData.PendingHeightFieldAddOps.Reset();
|
|
}
|
|
|
|
SceneData.PendingHeightFieldUpdateOps.Empty();
|
|
}
|
|
|
|
FString GetObjectBufferMemoryString()
|
|
{
|
|
return FString::Printf(TEXT("Temp object buffers %.3fMb"),
|
|
(GDistanceFieldUploadIndices.UploadIndices.GetSizeBytes() + GDistanceFieldUploadData.UploadData.GetSizeBytes() + GDistanceFieldRemoveIndices.RemoveIndices.GetSizeBytes()) / 1024.0f / 1024.0f);
|
|
}
|