Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/DistanceFieldObjectManagement.cpp
Luke Thatcher 42f1c1f96b Copying //UE4/Dev-Console to //UE4/Dev-Main (Source: //UE4/Dev-Console @ 3483086)
#lockdown Nick.Penwarden
#rb none

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3389969 on 2017/04/12 by Guillaume.Abadie

	Implements FDebug::DumpStackTraceToLog() debugging utility.

Change 3391579 on 2017/04/12 by Joe.Barnes

	Fix minor spacing issue.

Change 3402629 on 2017/04/20 by Ben.Marsh

	Build: Remove job to populate the DDC. Trying out a new system for this.

Change 3417501 on 2017/05/01 by Joe.Barnes

	IWYU - Missing #include files.

Change 3419927 on 2017/05/02 by Joe.Barnes

	- Support custom LOD for shadow map generation only (r.ForceLODShadow)
	- New #define to expose forceLOD and forceLODShadow also in Test/Ship builds (#define EXPOSE_FORCE_LOD 1 in RenderCore.cpp). Not exposed by default.

Change 3420964 on 2017/05/03 by Jonathan.Fitzpatrick

	Fixed null dereference of LineBatcher when using DrawDebugSphere and DrawDebugAltCone
	#jira UE-30213

Change 3423470 on 2017/05/04 by Luke.Thatcher

	[CONSOLE] [STREAMS] [^] Merging //UE4/Dev-Main (CL 3391974) to Dev-Console (//UE4/Dev-Console)
	 - Compile errors in Switch, to be fixed after check-in.

Change 3430410 on 2017/05/09 by Ben.Woodhouse

	Fix uninitialized local variable causing crashes in Test

	#jira UE-44832

Change 3430506 on 2017/05/09 by Josh.Adams

	- Fixed up the editor platforms' method of loading TargetSettings objects so that we don't need any manual parsing of .ini files to fill out the class defaults

Change 3434035 on 2017/05/10 by Ben.Woodhouse

	Integrate updated FortGPUTestbed from Fortnite/Main

Change 3437046 on 2017/05/12 by Joe.Barnes

	Fix for clang producing a warning when not all specializations of a templated function are marked FORCEINLINE.
	Also, switch a specialization of BlendTransform() from a function with a check to just a declaration so compiler will catch error instead of a runtime catch.

Change 3437259 on 2017/05/12 by Joe.Barnes

	Fix for clang producing a warning when not all specializations of a templated function are marked FORCEINLINE.
	Also, switch a specialization of BlendTransform() from a function with a check to just a declaration so compiler will catch error instead of a runtime catch.

Change 3440758 on 2017/05/16 by Ben.Woodhouse

	Simple threaded CSV Profiler
	To capture:
	- On the commandline, add -csvCaptureFrames=N to capture N frames from startup
	- On the console, use csvprofile start, csvprofile stop or csvprofile frames=N to capture a fixed number of frames
	- Instrument with CSV_SCOPED_STAT(statname), CSV_CUSTOM_STAT(statname,value).

	CSV capture is enabled in all builds except shipping
	- Please do not check in the instrumentation √ we don╞t want to pollute the engine with lots of additional instrumentation. We may add some minimal level of instrumentation at some point

Change 3440954 on 2017/05/16 by Josh.Adams

	- Cleaned up some DeviceProfiles in BaseDP.ini

Change 3443778 on 2017/05/17 by Ben.Woodhouse

	Aliasing for transient resources + new high level API

	Changelists integrated:
	3368830
	3368887
	3377762
	3377763
	3379513
	3381840
	3382046
	3382138
	3385390
	3385391
	3385531
	3396613
	3388752
	3396756
	3397007
	3397059
	3397780
	3397883
	3401716
	3415179

Change 3451460 on 2017/05/22 by Ben.Woodhouse

	Fix editor crash (NULL dereference of ScreenSpaceShadowTexture) when moving the camera around in tm-shadermodels, probably fallout from the VRAM aliasing merge. Not sure if this is the correct fix, but it prevents the crash for now

Change 3451601 on 2017/05/22 by Josh.Adams

	- Track idle time from MaxTickRate, so that stat unit is accurate on Game: thread

Change 3452025 on 2017/05/22 by Ben.Woodhouse

	Integrate (as edit) CL 3378734 (editor crash fix)
	Also add a check for null in LightFunctionRendering.cpp

Change 3452389 on 2017/05/22 by Josh.Adams

	- Replaced POCulturePluralForms with a static array, instead of TMapBuilder (was blowing stack or similar on Switch Debug). Code courtesy of Jamie Dale.

Change 3452758 on 2017/05/22 by Joe.Barnes

	Add FindFirstClearBit() and FindFirstSetBit() to TStaticBitArray.

Change 3455889 on 2017/05/23 by Ben.Woodhouse

	Integrate from //UE4/Main/...@3453436 to //UE4/Dev-Console/...

Change 3458654 on 2017/05/25 by Joe.Conley

	Attempting to fix Static Analysis warning.

Change 3462710 on 2017/05/26 by Ben.Woodhouse

	Integrate from //UE4/Main/...@3461688 to //UE4/Dev-Console/...

Change 3471711 on 2017/06/02 by Jonathan.Fitzpatrick

	Updating MallocProfiler to use the accessor for OnOutOfMemory delegate to conform with change made in CL 3415996.

Change 3473813 on 2017/06/05 by Ben.Woodhouse

	Fix streaming visibility logic bug reported on UDN

	#jira UE-43892

Change 3475298 on 2017/06/06 by Luke.Thatcher

	[CONSOLE] [!] Fix RHITransitionResources crash with more than 16 textures.
	 - Old command had a fixed sized array of 16 textures that would overflow.
	 - New command allocates an array of texture pointers inline in the command list, so any number is supported.

	#jira UE-45625

Change 3476776 on 2017/06/06 by Ben.Woodhouse

	Integrate from //UE4/Main/...@3475908

Change 3479083 on 2017/06/07 by Ben.Woodhouse

	Integrate as edit CL 3467315 from dev-animphys:
	From Alexis Matte.
	Ensure SectionMap is fixed up for old entries that are no longer valid.

	#JIRA UE-45438

	#jira UE-45735

Change 3480576 on 2017/06/08 by Ben.Woodhouse

	Integrate from //UE4/Main/...@3480024 to //UE4/Dev-Console/...

[CL 3483258 by Luke Thatcher in Main branch]
2017-06-09 17:44:13 -04:00

1224 lines
50 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
DistanceFieldSurfaceCacheLighting.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
int32 FDistanceFieldObjectBuffers::ObjectDataStride = 18;
int32 FDistanceFieldCulledObjectBuffers::ObjectDataStride = 16;
int32 FDistanceFieldCulledObjectBuffers::ObjectBoxBoundsStride = 5;
// In float4's. Must match corresponding usf definition
int32 UploadObjectDataStride = 1 + FDistanceFieldObjectBuffers::ObjectDataStride;
class FDistanceFieldUploadDataResource : public FRenderResource
{
public:
FCPUUpdatedBuffer UploadData;
FDistanceFieldUploadDataResource()
{
UploadData.Format = PF_A32B32G32R32F;
UploadData.Stride = UploadObjectDataStride;
}
virtual void InitDynamicRHI() override
{
UploadData.Initialize();
}
virtual void ReleaseDynamicRHI() override
{
UploadData.Release();
}
};
TGlobalResource<FDistanceFieldUploadDataResource> GDistanceFieldUploadData;
class FDistanceFieldUploadIndicesResource : public FRenderResource
{
public:
FCPUUpdatedBuffer UploadIndices;
FDistanceFieldUploadIndicesResource()
{
UploadIndices.Format = PF_R32_UINT;
UploadIndices.Stride = 1;
}
virtual void InitDynamicRHI() override
{
UploadIndices.Initialize();
}
virtual void ReleaseDynamicRHI() override
{
UploadIndices.Release();
}
};
TGlobalResource<FDistanceFieldUploadIndicesResource> GDistanceFieldUploadIndices;
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;
const uint32 UpdateObjectsGroupSize = 64;
class FUploadObjectsToBufferCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FUploadObjectsToBufferCS,Global)
public:
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Platform);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment);
OutEnvironment.SetDefine(TEXT("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
}
FUploadObjectsToBufferCS(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);
}
FUploadObjectsToBufferCS()
{
}
void SetParameters(FRHICommandList& RHICmdList, const FScene* Scene, uint32 NumUploadOperationsValue, FShaderResourceViewRHIParamRef InUploadOperationIndices, FShaderResourceViewRHIParamRef InUploadOperationData)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
SetShaderValue(RHICmdList, ShaderRHI, NumUploadOperations, NumUploadOperationsValue);
SetSRVParameter(RHICmdList, ShaderRHI, UploadOperationIndices, InUploadOperationIndices);
SetSRVParameter(RHICmdList, ShaderRHI, UploadOperationData, InUploadOperationData);
ObjectBufferParameters.Set(RHICmdList, ShaderRHI, *(Scene->DistanceFieldSceneData.ObjectBuffers), Scene->DistanceFieldSceneData.NumObjectsInBuffer, true);
}
void UnsetParameters(FRHICommandList& RHICmdList, const FScene* Scene)
{
ObjectBufferParameters.UnsetParameters(RHICmdList, GetComputeShader(), *(Scene->DistanceFieldSceneData.ObjectBuffers), true);
const FDistanceFieldObjectBuffers& ObjectBuffers = *(Scene->DistanceFieldSceneData.ObjectBuffers);
FUnorderedAccessViewRHIParamRef OutUAVs[2];
OutUAVs[0] = ObjectBuffers.Bounds.UAV;
OutUAVs[1] = ObjectBuffers.Data.UAV;
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, ARRAY_COUNT(OutUAVs));
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << NumUploadOperations;
Ar << UploadOperationIndices;
Ar << UploadOperationData;
Ar << ObjectBufferParameters;
return bShaderHasOutdatedParameters;
}
private:
FShaderParameter NumUploadOperations;
FShaderResourceParameter UploadOperationIndices;
FShaderResourceParameter UploadOperationData;
FDistanceFieldObjectBufferParameters ObjectBufferParameters;
};
IMPLEMENT_SHADER_TYPE(,FUploadObjectsToBufferCS,TEXT("DistanceFieldObjectCulling"),TEXT("UploadObjectsToBufferCS"),SF_Compute);
class FCopyObjectBufferCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCopyObjectBufferCS,Global)
public:
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Platform);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment);
OutEnvironment.SetDefine(TEXT("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
}
FCopyObjectBufferCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
CopyObjectBounds.Bind(Initializer.ParameterMap, TEXT("CopyObjectBounds"));
CopyObjectData.Bind(Initializer.ParameterMap, TEXT("CopyObjectData"));
ObjectBufferParameters.Bind(Initializer.ParameterMap);
}
FCopyObjectBufferCS()
{
}
void SetParameters(FRHICommandList& RHICmdList, FDistanceFieldObjectBuffers& ObjectBuffersSource, FDistanceFieldObjectBuffers& ObjectBuffersDest, int32 NumObjectsValue)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
FUnorderedAccessViewRHIParamRef OutUAVs[2];
OutUAVs[0] = ObjectBuffersDest.Bounds.UAV;
OutUAVs[1] = ObjectBuffersDest.Data.UAV;
RHICmdList.TransitionResources(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, ARRAY_COUNT(OutUAVs));
CopyObjectBounds.SetBuffer(RHICmdList, ShaderRHI, ObjectBuffersDest.Bounds);
CopyObjectData.SetBuffer(RHICmdList, ShaderRHI, ObjectBuffersDest.Data);
ObjectBufferParameters.Set(RHICmdList, ShaderRHI, ObjectBuffersSource, NumObjectsValue);
}
void UnsetParameters(FRHICommandList& RHICmdList, FDistanceFieldObjectBuffers& ObjectBuffersDest)
{
ObjectBufferParameters.UnsetParameters(RHICmdList, GetComputeShader(), ObjectBuffersDest);
CopyObjectBounds.UnsetUAV(RHICmdList, GetComputeShader());
CopyObjectData.UnsetUAV(RHICmdList, GetComputeShader());
FUnorderedAccessViewRHIParamRef OutUAVs[2];
OutUAVs[0] = ObjectBuffersDest.Bounds.UAV;
OutUAVs[1] = ObjectBuffersDest.Data.UAV;
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, ARRAY_COUNT(OutUAVs));
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << CopyObjectBounds;
Ar << CopyObjectData;
Ar << ObjectBufferParameters;
return bShaderHasOutdatedParameters;
}
private:
FRWShaderParameter CopyObjectBounds;
FRWShaderParameter CopyObjectData;
FDistanceFieldObjectBufferParameters ObjectBufferParameters;
};
IMPLEMENT_SHADER_TYPE(,FCopyObjectBufferCS,TEXT("DistanceFieldObjectCulling"),TEXT("CopyObjectBufferCS"),SF_Compute);
class FCopySurfelBufferCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCopySurfelBufferCS,Global)
public:
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldGI(Platform);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment);
OutEnvironment.SetDefine(TEXT("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
}
FCopySurfelBufferCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
CopyInterpolatedVertexData.Bind(Initializer.ParameterMap, TEXT("CopyInterpolatedVertexData"));
CopySurfelData.Bind(Initializer.ParameterMap, TEXT("CopySurfelData"));
SurfelBufferParameters.Bind(Initializer.ParameterMap);
NumSurfels.Bind(Initializer.ParameterMap, TEXT("NumSurfels"));
}
FCopySurfelBufferCS()
{
}
void SetParameters(FRHICommandList& RHICmdList, const FSurfelBuffers& SurfelBuffersSource, const FInstancedSurfelBuffers& InstancedSurfelBuffersSource, FSurfelBuffers& SurfelBuffersDest, int32 NumSurfelsValue)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
FUnorderedAccessViewRHIParamRef OutUAVs[2];
OutUAVs[0] = SurfelBuffersDest.InterpolatedVertexData.UAV;
OutUAVs[1] = SurfelBuffersDest.Surfels.UAV;
RHICmdList.TransitionResources(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, ARRAY_COUNT(OutUAVs));
CopyInterpolatedVertexData.SetBuffer(RHICmdList, ShaderRHI, SurfelBuffersDest.InterpolatedVertexData);
CopySurfelData.SetBuffer(RHICmdList, ShaderRHI, SurfelBuffersDest.Surfels);
SurfelBufferParameters.Set(RHICmdList, ShaderRHI, SurfelBuffersSource, InstancedSurfelBuffersSource);
SetShaderValue(RHICmdList, ShaderRHI, NumSurfels, NumSurfelsValue);
}
void UnsetParameters(FRHICommandList& RHICmdList, FSurfelBuffers& SurfelBuffersDest)
{
SurfelBufferParameters.UnsetParameters(RHICmdList, GetComputeShader());
CopyInterpolatedVertexData.UnsetUAV(RHICmdList, GetComputeShader());
CopySurfelData.UnsetUAV(RHICmdList, GetComputeShader());
FUnorderedAccessViewRHIParamRef OutUAVs[2];
OutUAVs[0] = SurfelBuffersDest.InterpolatedVertexData.UAV;
OutUAVs[1] = SurfelBuffersDest.Surfels.UAV;
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, OutUAVs, ARRAY_COUNT(OutUAVs));
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << CopyInterpolatedVertexData;
Ar << CopySurfelData;
Ar << SurfelBufferParameters;
Ar << NumSurfels;
return bShaderHasOutdatedParameters;
}
private:
FRWShaderParameter CopyInterpolatedVertexData;
FRWShaderParameter CopySurfelData;
FSurfelBufferParameters SurfelBufferParameters;
FShaderParameter NumSurfels;
};
IMPLEMENT_SHADER_TYPE(,FCopySurfelBufferCS,TEXT("SurfelTree"),TEXT("CopySurfelBufferCS"),SF_Compute);
class FCopyVPLFluxBufferCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCopyVPLFluxBufferCS,Global)
public:
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Platform);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment);
OutEnvironment.SetDefine(TEXT("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
}
FCopyVPLFluxBufferCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
CopyVPLFlux.Bind(Initializer.ParameterMap, TEXT("CopyVPLFlux"));
SurfelBufferParameters.Bind(Initializer.ParameterMap);
NumSurfels.Bind(Initializer.ParameterMap, TEXT("NumSurfels"));
}
FCopyVPLFluxBufferCS()
{
}
void SetParameters(FRHICommandList& RHICmdList, const FSurfelBuffers& SurfelBuffersSource, const FInstancedSurfelBuffers& InstancedSurfelBuffersSource, FInstancedSurfelBuffers& InstancedSurfelBuffersDest, int32 NumSurfelsValue)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
RHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, InstancedSurfelBuffersDest.VPLFlux.UAV);
CopyVPLFlux.SetBuffer(RHICmdList, ShaderRHI, InstancedSurfelBuffersDest.VPLFlux);
SurfelBufferParameters.Set(RHICmdList, ShaderRHI, SurfelBuffersSource, InstancedSurfelBuffersSource);
SetShaderValue(RHICmdList, ShaderRHI, NumSurfels, NumSurfelsValue);
}
void UnsetParameters(FRHICommandList& RHICmdList, FInstancedSurfelBuffers& InstancedSurfelBuffersDest)
{
SurfelBufferParameters.UnsetParameters(RHICmdList, GetComputeShader());
CopyVPLFlux.UnsetUAV(RHICmdList, GetComputeShader());
RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToCompute, InstancedSurfelBuffersDest.VPLFlux.UAV);
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << CopyVPLFlux;
Ar << SurfelBufferParameters;
Ar << NumSurfels;
return bShaderHasOutdatedParameters;
}
private:
FRWShaderParameter CopyVPLFlux;
FSurfelBufferParameters SurfelBufferParameters;
FShaderParameter NumSurfels;
};
IMPLEMENT_SHADER_TYPE(,FCopyVPLFluxBufferCS,TEXT("SurfelTree"),TEXT("CopyVPLFluxBufferCS"),SF_Compute);
template<bool bRemoveFromSameBuffer>
class TRemoveObjectsFromBufferCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(TRemoveObjectsFromBufferCS,Global)
public:
static bool ShouldCache(EShaderPlatform Platform)
{
return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldAO(Platform);
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment);
OutEnvironment.SetDefine(TEXT("UPDATEOBJECTS_THREADGROUP_SIZE"), UpdateObjectsGroupSize);
OutEnvironment.SetDefine(TEXT("REMOVE_FROM_SAME_BUFFER"), bRemoveFromSameBuffer);
}
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,
FShaderResourceViewRHIParamRef InRemoveOperationIndices,
FShaderResourceViewRHIParamRef InObjectBounds2,
FShaderResourceViewRHIParamRef InObjectData2)
{
FComputeShaderRHIParamRef ShaderRHI = GetComputeShader();
SetShaderValue(RHICmdList, ShaderRHI, NumRemoveOperations, NumRemoveOperationsValue);
SetSRVParameter(RHICmdList, ShaderRHI, RemoveOperationIndices, InRemoveOperationIndices);
ObjectBufferParameters.Set(RHICmdList, ShaderRHI, *(Scene->DistanceFieldSceneData.ObjectBuffers), Scene->DistanceFieldSceneData.NumObjectsInBuffer, true);
SetSRVParameter(RHICmdList, ShaderRHI, ObjectBounds2, InObjectBounds2);
SetSRVParameter(RHICmdList, ShaderRHI, ObjectData2, InObjectData2);
}
void UnsetParameters(FRHICommandList& RHICmdList, const FScene* Scene)
{
ObjectBufferParameters.UnsetParameters(RHICmdList, GetComputeShader(), *(Scene->DistanceFieldSceneData.ObjectBuffers), true);
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << NumRemoveOperations;
Ar << RemoveOperationIndices;
Ar << ObjectBufferParameters;
Ar << ObjectBounds2;
Ar << ObjectData2;
return bShaderHasOutdatedParameters;
}
private:
FShaderParameter NumRemoveOperations;
FShaderResourceParameter RemoveOperationIndices;
FDistanceFieldObjectBufferParameters ObjectBufferParameters;
FShaderResourceParameter ObjectBounds2;
FShaderResourceParameter ObjectData2;
};
IMPLEMENT_SHADER_TYPE(template<>,TRemoveObjectsFromBufferCS<true>,TEXT("DistanceFieldObjectCulling"),TEXT("RemoveObjectsFromBufferCS"),SF_Compute);
IMPLEMENT_SHADER_TYPE(template<>,TRemoveObjectsFromBufferCS<false>,TEXT("DistanceFieldObjectCulling"),TEXT("RemoveObjectsFromBufferCS"),SF_Compute);
void FSurfelBufferAllocator::RemovePrimitive(const FPrimitiveSceneInfo* Primitive)
{
FPrimitiveSurfelAllocation Allocation;
if (Allocations.RemoveAndCopyValue(Primitive, Allocation))
{
bool bMergedWithExisting = false;
FPrimitiveSurfelFreeEntry FreeEntry(Allocation.Offset, Allocation.GetTotalNumSurfels());
// Note: only does one merge
//@todo - keep free list sorted then can binary search
for (int32 FreeIndex = 0; FreeIndex < FreeList.Num(); FreeIndex++)
{
if (FreeList[FreeIndex].Offset == FreeEntry.Offset + FreeEntry.NumSurfels)
{
FreeList[FreeIndex].Offset = FreeEntry.Offset;
FreeList[FreeIndex].NumSurfels += FreeEntry.NumSurfels;
bMergedWithExisting = true;
break;
}
else if (FreeList[FreeIndex].Offset + FreeList[FreeIndex].NumSurfels == FreeEntry.Offset)
{
FreeList[FreeIndex].NumSurfels += FreeEntry.NumSurfels;
bMergedWithExisting = true;
break;
}
}
if (!bMergedWithExisting)
{
FreeList.Add(FreeEntry);
}
}
}
void FSurfelBufferAllocator::AddPrimitive(const FPrimitiveSceneInfo* PrimitiveSceneInfo, int32 PrimitiveLOD0Surfels, int32 PrimitiveNumSurfels, int32 NumInstances)
{
int32 BestFreeAllocationIndex = -1;
for (int32 FreeIndex = 0; FreeIndex < FreeList.Num(); FreeIndex++)
{
const FPrimitiveSurfelFreeEntry& CurrentFreeEntry = FreeList[FreeIndex];
if (CurrentFreeEntry.NumSurfels >= PrimitiveNumSurfels * NumInstances
&& (BestFreeAllocationIndex == -1
|| CurrentFreeEntry.NumSurfels < FreeList[BestFreeAllocationIndex].NumSurfels))
{
BestFreeAllocationIndex = FreeIndex;
}
}
if (BestFreeAllocationIndex != -1)
{
FPrimitiveSurfelFreeEntry FreeEntry = FreeList[BestFreeAllocationIndex];
if (FreeEntry.NumSurfels == PrimitiveNumSurfels * NumInstances)
{
// Existing allocation matches exactly, remove it from the free list
FreeList.RemoveAtSwap(BestFreeAllocationIndex);
}
else
{
// Replace with the remaining free range
FreeList[BestFreeAllocationIndex] = FPrimitiveSurfelFreeEntry(FreeEntry.Offset + PrimitiveNumSurfels * NumInstances, FreeEntry.NumSurfels - PrimitiveNumSurfels * NumInstances);
}
Allocations.Add(PrimitiveSceneInfo, FPrimitiveSurfelAllocation(FreeEntry.Offset, PrimitiveLOD0Surfels, PrimitiveNumSurfels, NumInstances));
}
else
{
// Add a new allocation to the end of the buffer
Allocations.Add(PrimitiveSceneInfo, FPrimitiveSurfelAllocation(NumSurfelsInBuffer, PrimitiveLOD0Surfels, PrimitiveNumSurfels, NumInstances));
NumSurfelsInBuffer += PrimitiveNumSurfels * NumInstances;
}
}
void UpdateGlobalDistanceFieldObjectRemoves(FRHICommandListImmediate& RHICmdList, FScene* Scene)
{
FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData;
TArray<FIntRect> RemoveObjectIndices;
FDistanceFieldObjectBuffers* TemporaryCopySourceBuffers = NULL;
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;
DistanceFieldSceneData.SurfelAllocations.RemovePrimitive(Primitive);
DistanceFieldSceneData.InstancedSurfelAllocations.RemovePrimitive(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();
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();
// Have to copy the object data to allow parallel removing
TemporaryCopySourceBuffers = DistanceFieldSceneData.ObjectBuffers;
DistanceFieldSceneData.ObjectBuffers = new FDistanceFieldObjectBuffers();
DistanceFieldSceneData.ObjectBuffers->MaxObjects = TemporaryCopySourceBuffers->MaxObjects;
DistanceFieldSceneData.ObjectBuffers->Initialize();
TArray<FPrimitiveAndInstance> 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)
{
check(!TemporaryCopySourceBuffers);
TShaderMapRef<TRemoveObjectsFromBufferCS<true> > ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, RemoveObjectIndices.Num(), GDistanceFieldRemoveIndices.RemoveIndices.BufferSRV, NULL, NULL);
DispatchComputeShader(RHICmdList, *ComputeShader, FMath::DivideAndRoundUp<uint32>(RemoveObjectIndices.Num(), UpdateObjectsGroupSize), 1, 1);
ComputeShader->UnsetParameters(RHICmdList, Scene);
}
else
{
check(TemporaryCopySourceBuffers);
TShaderMapRef<TRemoveObjectsFromBufferCS<false> > ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, RemoveObjectIndices.Num(), GDistanceFieldRemoveIndices.RemoveIndices.BufferSRV, TemporaryCopySourceBuffers->Bounds.SRV, TemporaryCopySourceBuffers->Data.SRV);
DispatchComputeShader(RHICmdList, *ComputeShader, FMath::DivideAndRoundUp<uint32>(RemoveObjectIndices.Num(), UpdateObjectsGroupSize), 1, 1);
ComputeShader->UnsetParameters(RHICmdList, Scene);
TemporaryCopySourceBuffers->Release();
delete TemporaryCopySourceBuffers;
}
}
}
}
}
/** Gathers the information needed to represent a single object's distance field and appends it to the upload buffers. */
void ProcessPrimitiveUpdate(
bool bIsAddOperation,
FRHICommandListImmediate& RHICmdList,
FSceneRenderer& SceneRenderer,
FPrimitiveSceneInfo* PrimitiveSceneInfo,
int32 OriginalNumObjects,
FVector InvTextureDim,
bool bPrepareForDistanceFieldGI,
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;
PrimitiveSceneInfo->Proxy->GetDistancefieldAtlasData(LocalVolumeBounds, DistanceMinMax, BlockMin, BlockSize, bBuiltAsIfTwoSided, bMeshWasPlane, SelfShadowBias, ObjectLocalToWorldTransforms);
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)
{
FPrimitiveSurfelAllocation Allocation;
FPrimitiveSurfelAllocation InstancedAllocation;
if (bPrepareForDistanceFieldGI)
{
const FPrimitiveSurfelAllocation* AllocationPtr = Scene->DistanceFieldSceneData.SurfelAllocations.FindAllocation(PrimitiveSceneInfo);
const FPrimitiveSurfelAllocation* InstancedAllocationPtr = Scene->DistanceFieldSceneData.InstancedSurfelAllocations.FindAllocation(PrimitiveSceneInfo);
if (AllocationPtr)
{
checkSlow(InstancedAllocationPtr && InstancedAllocationPtr->NumInstances == ObjectLocalToWorldTransforms.Num());
Allocation = *AllocationPtr;
InstancedAllocation = *InstancedAllocationPtr;
extern void GenerateSurfelRepresentation(FRHICommandListImmediate& RHICmdList, FSceneRenderer& Renderer, FViewInfo& View, FPrimitiveSceneInfo* PrimitiveSceneInfo, const FMatrix& Instance0Transform, FPrimitiveSurfelAllocation& Allocation);
// @todo - support surfel generation without a view
GenerateSurfelRepresentation(RHICmdList, SceneRenderer, SceneRenderer.Views[0], PrimitiveSceneInfo, ObjectLocalToWorldTransforms[0], Allocation);
if (Allocation.NumSurfels == 0)
{
InstancedAllocation.NumSurfels = 0;
InstancedAllocation.NumInstances = 0;
InstancedAllocation.NumLOD0 = 0;
}
}
}
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 WorldToVolume = UniformScaleVolumeToWorld.Inverse();
// WorldToVolume
UploadObjectData.Add(*(FVector4*)&WorldToVolume.M[0]);
UploadObjectData.Add(*(FVector4*)&WorldToVolume.M[1]);
UploadObjectData.Add(*(FVector4*)&WorldToVolume.M[2]);
UploadObjectData.Add(*(FVector4*)&WorldToVolume.M[3]);
// Clamp to texel center by subtracting a half texel in the [-1,1] position space
// LocalPositionExtent
UploadObjectData.Add(FVector4(LocalPositionExtent - InvBlockSize, 0));
// 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));
// DistanceFieldMAD
// [0, 1] -> [MinVolumeDistance, MaxVolumeDistance]
UploadObjectData.Add(FVector4(DistanceMinMax.Y - DistanceMinMax.X, DistanceMinMax.X, 0, 0));
UploadObjectData.Add(*(FVector4*)&UniformScaleVolumeToWorld.M[0]);
UploadObjectData.Add(*(FVector4*)&UniformScaleVolumeToWorld.M[1]);
UploadObjectData.Add(*(FVector4*)&UniformScaleVolumeToWorld.M[2]);
UploadObjectData.Add(*(FVector4*)&LocalToWorld.M[0]);
UploadObjectData.Add(*(FVector4*)&LocalToWorld.M[1]);
UploadObjectData.Add(*(FVector4*)&LocalToWorld.M[2]);
UploadObjectData.Add(*(FVector4*)&LocalToWorld.M[3]);
UploadObjectData.Add(FVector4(Allocation.Offset, Allocation.NumLOD0, Allocation.NumSurfels, InstancedAllocation.Offset + InstancedAllocation.NumSurfels * TransformIndex));
UploadObjectData.Add(FVector4(LocalVolumeBounds.Min, 0));
// Box bounds
const float OftenMovingWSign = CacheType == GDF_Full ? 1.0f : -1.0f;
UploadObjectData.Add(FVector4(LocalVolumeBounds.Max, OftenMovingWSign));
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);
}
}
}
void FDeferredShadingSceneRenderer::UpdateGlobalDistanceFieldObjectBuffers(FRHICommandListImmediate& RHICmdList)
{
FDistanceFieldSceneData& DistanceFieldSceneData = Scene->DistanceFieldSceneData;
if (GDistanceFieldVolumeTextureAtlas.VolumeTextureRHI
&& (DistanceFieldSceneData.HasPendingOperations() || DistanceFieldSceneData.AtlasGeneration != GDistanceFieldVolumeTextureAtlas.GetGeneration()))
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_UpdateObjectData);
SCOPED_DRAW_EVENT(RHICmdList, UpdateSceneObjectData);
if (!DistanceFieldSceneData.ObjectBuffers)
{
DistanceFieldSceneData.ObjectBuffers = new FDistanceFieldObjectBuffers();
}
if (!DistanceFieldSceneData.SurfelBuffers)
{
DistanceFieldSceneData.SurfelBuffers = new FSurfelBuffers();
}
if (!DistanceFieldSceneData.InstancedSurfelBuffers)
{
DistanceFieldSceneData.InstancedSurfelBuffers = new FInstancedSurfelBuffers();
}
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, as the adds will overwrite primitive allocation info in DistanceFieldSceneData.SurfelAllocations
UpdateGlobalDistanceFieldObjectRemoves(RHICmdList, Scene);
extern int32 GVPLMeshGlobalIllumination;
TArray<uint32> UploadObjectIndices;
TArray<FVector4> UploadObjectData;
const bool bPrepareForDistanceFieldGI = GVPLMeshGlobalIllumination && SupportsDistanceFieldGI(Scene->GetFeatureLevel(), Scene->GetShaderPlatform());
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;
int32 OriginalNumSurfels = DistanceFieldSceneData.SurfelAllocations.GetNumSurfelsInBuffer();
int32 OriginalNumInstancedSurfels = DistanceFieldSceneData.InstancedSurfelAllocations.GetNumSurfelsInBuffer();
if (bPrepareForDistanceFieldGI)
{
for (int32 UploadPrimitiveIndex = 0; UploadPrimitiveIndex < DistanceFieldSceneData.PendingAddOperations.Num(); UploadPrimitiveIndex++)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = DistanceFieldSceneData.PendingAddOperations[UploadPrimitiveIndex];
int32 NumInstances = 0;
float BoundsSurfaceArea = 0;
PrimitiveSceneInfo->Proxy->GetDistanceFieldInstanceInfo(NumInstances, BoundsSurfaceArea);
extern void ComputeNumSurfels(float BoundsSurfaceArea, int32& PrimitiveNumSurfels, int32& PrimitiveLOD0Surfels);
int32 PrimitiveNumSurfels;
int32 PrimitiveLOD0Surfels;
ComputeNumSurfels(BoundsSurfaceArea, PrimitiveNumSurfels, PrimitiveLOD0Surfels);
if (PrimitiveNumSurfels > 0 && NumInstances > 0)
{
const int32 PrimitiveTotalNumSurfels = PrimitiveNumSurfels * NumInstances;
if (PrimitiveNumSurfels > 5000)
{
UE_LOG(LogDistanceField,Warning,TEXT("Primitive %s %s used %u Surfels"), *PrimitiveSceneInfo->Proxy->GetOwnerName().ToString(), *PrimitiveSceneInfo->Proxy->GetResourceName().ToString(), PrimitiveNumSurfels);
}
DistanceFieldSceneData.SurfelAllocations.AddPrimitive(PrimitiveSceneInfo, PrimitiveLOD0Surfels, PrimitiveNumSurfels, 1);
DistanceFieldSceneData.InstancedSurfelAllocations.AddPrimitive(PrimitiveSceneInfo, PrimitiveLOD0Surfels, PrimitiveNumSurfels, NumInstances);
}
}
if (DistanceFieldSceneData.SurfelBuffers->MaxSurfels < DistanceFieldSceneData.SurfelAllocations.GetNumSurfelsInBuffer())
{
if (DistanceFieldSceneData.SurfelBuffers->MaxSurfels > 0)
{
// Realloc
FSurfelBuffers* NewSurfelBuffers = new FSurfelBuffers();
NewSurfelBuffers->MaxSurfels = DistanceFieldSceneData.SurfelAllocations.GetNumSurfelsInBuffer() * 5 / 4;
NewSurfelBuffers->Initialize();
{
TShaderMapRef<FCopySurfelBufferCS> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, *(DistanceFieldSceneData.SurfelBuffers), *(DistanceFieldSceneData.InstancedSurfelBuffers), *NewSurfelBuffers, OriginalNumSurfels);
DispatchComputeShader(RHICmdList, *ComputeShader, FMath::DivideAndRoundUp<uint32>(OriginalNumSurfels, UpdateObjectsGroupSize), 1, 1);
ComputeShader->UnsetParameters(RHICmdList, *NewSurfelBuffers);
}
DistanceFieldSceneData.SurfelBuffers->Release();
delete DistanceFieldSceneData.SurfelBuffers;
DistanceFieldSceneData.SurfelBuffers = NewSurfelBuffers;
}
else
{
// First time allocate
DistanceFieldSceneData.SurfelBuffers->MaxSurfels = DistanceFieldSceneData.SurfelAllocations.GetNumSurfelsInBuffer() * 5 / 4;
DistanceFieldSceneData.SurfelBuffers->Initialize();
}
}
if (DistanceFieldSceneData.InstancedSurfelBuffers->MaxSurfels < DistanceFieldSceneData.InstancedSurfelAllocations.GetNumSurfelsInBuffer())
{
if (DistanceFieldSceneData.InstancedSurfelBuffers->MaxSurfels > 0)
{
// Realloc
FInstancedSurfelBuffers* NewInstancedSurfelBuffers = new FInstancedSurfelBuffers();
NewInstancedSurfelBuffers->MaxSurfels = DistanceFieldSceneData.InstancedSurfelAllocations.GetNumSurfelsInBuffer() * 5 / 4;
NewInstancedSurfelBuffers->Initialize();
{
TShaderMapRef<FCopyVPLFluxBufferCS> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, *(DistanceFieldSceneData.SurfelBuffers), *(DistanceFieldSceneData.InstancedSurfelBuffers), *NewInstancedSurfelBuffers, OriginalNumInstancedSurfels);
DispatchComputeShader(RHICmdList, *ComputeShader, FMath::DivideAndRoundUp<uint32>(OriginalNumInstancedSurfels, UpdateObjectsGroupSize), 1, 1);
ComputeShader->UnsetParameters(RHICmdList, *NewInstancedSurfelBuffers);
}
DistanceFieldSceneData.InstancedSurfelBuffers->Release();
delete DistanceFieldSceneData.InstancedSurfelBuffers;
DistanceFieldSceneData.InstancedSurfelBuffers = NewInstancedSurfelBuffers;
}
else
{
// First time allocate
DistanceFieldSceneData.InstancedSurfelBuffers->MaxSurfels = DistanceFieldSceneData.InstancedSurfelAllocations.GetNumSurfelsInBuffer() * 5 / 4;
DistanceFieldSceneData.InstancedSurfelBuffers->Initialize();
}
}
}
for (int32 UploadPrimitiveIndex = 0; UploadPrimitiveIndex < DistanceFieldSceneData.PendingAddOperations.Num(); UploadPrimitiveIndex++)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = DistanceFieldSceneData.PendingAddOperations[UploadPrimitiveIndex];
ProcessPrimitiveUpdate(
true,
RHICmdList,
*this,
PrimitiveSceneInfo,
OriginalNumObjects,
InvTextureDim,
bPrepareForDistanceFieldGI,
ObjectLocalToWorldTransforms,
UploadObjectIndices,
UploadObjectData);
}
for (TSet<FPrimitiveSceneInfo*>::TIterator It(DistanceFieldSceneData.PendingUpdateOperations); It; ++It)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = *It;
ProcessPrimitiveUpdate(
false,
RHICmdList,
*this,
PrimitiveSceneInfo,
OriginalNumObjects,
InvTextureDim,
bPrepareForDistanceFieldGI,
ObjectLocalToWorldTransforms,
UploadObjectIndices,
UploadObjectData);
}
DistanceFieldSceneData.PendingAddOperations.Reset();
DistanceFieldSceneData.PendingUpdateOperations.Empty();
if (DistanceFieldSceneData.ObjectBuffers->MaxObjects < DistanceFieldSceneData.NumObjectsInBuffer)
{
if (DistanceFieldSceneData.ObjectBuffers->MaxObjects > 0)
{
// Realloc
FDistanceFieldObjectBuffers* NewObjectBuffers = new FDistanceFieldObjectBuffers();
NewObjectBuffers->MaxObjects = DistanceFieldSceneData.NumObjectsInBuffer * 5 / 4;
NewObjectBuffers->Initialize();
{
TShaderMapRef<FCopyObjectBufferCS> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, *(DistanceFieldSceneData.ObjectBuffers), *NewObjectBuffers, OriginalNumObjects);
DispatchComputeShader(RHICmdList, *ComputeShader, FMath::DivideAndRoundUp<uint32>(OriginalNumObjects, UpdateObjectsGroupSize), 1, 1);
ComputeShader->UnsetParameters(RHICmdList, *NewObjectBuffers);
}
DistanceFieldSceneData.ObjectBuffers->Release();
delete DistanceFieldSceneData.ObjectBuffers;
DistanceFieldSceneData.ObjectBuffers = NewObjectBuffers;
}
else
{
// First time allocate
DistanceFieldSceneData.ObjectBuffers->MaxObjects = DistanceFieldSceneData.NumObjectsInBuffer * 5 / 4;
DistanceFieldSceneData.ObjectBuffers->Initialize();
}
}
}
if (UploadObjectIndices.Num() > 0)
{
if (UploadObjectIndices.Num() > GDistanceFieldUploadIndices.UploadIndices.MaxElements)
{
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<FUploadObjectsToBufferCS> ComputeShader(GetGlobalShaderMap(Scene->GetFeatureLevel()));
RHICmdList.SetComputeShader(ComputeShader->GetComputeShader());
ComputeShader->SetParameters(RHICmdList, Scene, UploadObjectIndices.Num(), GDistanceFieldUploadIndices.UploadIndices.BufferSRV, GDistanceFieldUploadData.UploadData.BufferSRV);
DispatchComputeShader(RHICmdList, *ComputeShader, FMath::DivideAndRoundUp<uint32>(UploadObjectIndices.Num(), UpdateObjectsGroupSize), 1, 1);
ComputeShader->UnsetParameters(RHICmdList, Scene);
}
}
check(DistanceFieldSceneData.NumObjectsInBuffer == DistanceFieldSceneData.PrimitiveInstanceMapping.Num());
DistanceFieldSceneData.VerifyIntegrity();
}
}
FString GetObjectBufferMemoryString()
{
return FString::Printf(TEXT("Temp object buffers %.3fMb"),
(GDistanceFieldUploadIndices.UploadIndices.GetSizeBytes() + GDistanceFieldUploadData.UploadData.GetSizeBytes() + GDistanceFieldRemoveIndices.RemoveIndices.GetSizeBytes()) / 1024.0f / 1024.0f);
}