1019 lines
40 KiB
C++
1019 lines
40 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RayTracingDynamicGeometryUpdateManager.h"
|
|
#include "MeshMaterialShader.h"
|
|
#include "DataDrivenShaderPlatformInfo.h"
|
|
#include "ScenePrivate.h"
|
|
#include "RayTracingInstance.h"
|
|
#include "RayTracingGeometry.h"
|
|
#include "RenderGraphBuilder.h"
|
|
#include "PSOPrecacheMaterial.h"
|
|
#include "PSOPrecacheValidation.h"
|
|
#include "Rendering/RayTracingGeometryManager.h"
|
|
|
|
#if RHI_RAYTRACING
|
|
|
|
#include "Materials/MaterialRenderProxy.h"
|
|
|
|
DECLARE_GPU_STAT(RayTracingDynamicGeometry);
|
|
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Ray tracing dynamic build primitives"), STAT_RayTracingDynamicBuildPrimitives, STATGROUP_SceneRendering);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Ray tracing dynamic update primitives"), STAT_RayTracingDynamicUpdatePrimitives, STATGROUP_SceneRendering);
|
|
DECLARE_DWORD_COUNTER_STAT(TEXT("Ray tracing dynamic skipped primitives"), STAT_RayTracingDynamicSkippedPrimitives, STATGROUP_SceneRendering);
|
|
|
|
static int32 GRTDynGeomSharedVertexBufferSizeInMB = 4;
|
|
static FAutoConsoleVariableRef CVarRTDynGeomSharedVertexBufferSizeInMB(
|
|
TEXT("r.RayTracing.DynamicGeometry.SharedVertexBufferSizeInMB"),
|
|
GRTDynGeomSharedVertexBufferSizeInMB,
|
|
TEXT("Size of the a single shared vertex buffer used during the BLAS update of dynamic geometries (default 4MB)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static int32 GRTDynGeomSharedVertexBufferGarbageCollectLatency = 30;
|
|
static FAutoConsoleVariableRef CVarRTDynGeomSharedVertexBufferGarbageCollectLatency(
|
|
TEXT("r.RayTracing.DynamicGeometry.SharedVertexBufferGarbageCollectLatency"),
|
|
GRTDynGeomSharedVertexBufferGarbageCollectLatency,
|
|
TEXT("Amount of update cycles before a heap is deleted when not used (default 30)."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRTDynGeomMaxUpdatePrimitivesPerFrame(
|
|
TEXT("r.RayTracing.DynamicGeometry.MaxUpdatePrimitivesPerFrame"),
|
|
-1,
|
|
TEXT("Sets the dynamic ray tracing acceleration structure build budget in terms of maximum number of updated triangles per frame (<= 0 then disabled and all acceleration structures are updated - default)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRTDynGeomForceBuildMaxUpdatePrimitivesPerFrame(
|
|
TEXT("r.RayTracing.DynamicGeometry.ForceBuild.MaxPrimitivesPerFrame"),
|
|
0,
|
|
TEXT("Sets the dynamic ray tracing acceleration structure build budget in terms of maximum number of triangles that are rebuild per frame (default 0)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRTDynGeomForceBuildMinUpdatesSinceLastBuild(
|
|
TEXT("r.RayTracing.DynamicGeometry.ForceBuild.MinUpdatesSinceLastBuild"),
|
|
-1,
|
|
TEXT("Sets minimum number of updates before the dynamic geometry acceleration structure will be considered for rebuild (default INT_MAX)"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
class FRayTracingDynamicGeometryConverterCS : public FMeshMaterialShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FRayTracingDynamicGeometryConverterCS, MeshMaterial);
|
|
|
|
class FVertexMask : SHADER_PERMUTATION_BOOL("USE_VERTEX_MASK");
|
|
|
|
using FPermutationDomain = TShaderPermutationDomain<FVertexMask>;
|
|
|
|
public:
|
|
FRayTracingDynamicGeometryConverterCS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{
|
|
PassUniformBuffer.Bind(Initializer.ParameterMap, FSceneTextureUniformParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName());
|
|
|
|
RWVertexPositions.Bind(Initializer.ParameterMap, TEXT("RWVertexPositions"));
|
|
UsingIndirectDraw.Bind(Initializer.ParameterMap, TEXT("UsingIndirectDraw"));
|
|
NumVertices.Bind(Initializer.ParameterMap, TEXT("NumVertices"));
|
|
MaxNumThreads.Bind(Initializer.ParameterMap, TEXT("MaxNumThreads"));
|
|
MinVertexIndex.Bind(Initializer.ParameterMap, TEXT("MinVertexIndex"));
|
|
PrimitiveId.Bind(Initializer.ParameterMap, TEXT("PrimitiveId"));
|
|
OutputVertexBaseIndex.Bind(Initializer.ParameterMap, TEXT("OutputVertexBaseIndex"));
|
|
bApplyWorldPositionOffset.Bind(Initializer.ParameterMap, TEXT("bApplyWorldPositionOffset"));
|
|
InstanceId.Bind(Initializer.ParameterMap, TEXT("InstanceId"));
|
|
WorldToInstance.Bind(Initializer.ParameterMap, TEXT("WorldToInstance"));
|
|
IndexBuffer.Bind(Initializer.ParameterMap, TEXT("IndexBuffer"));
|
|
IndexBufferOffset.Bind(Initializer.ParameterMap, TEXT("IndexBufferOffset"));
|
|
}
|
|
|
|
FRayTracingDynamicGeometryConverterCS() = default;
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
const FPermutationDomain PermutationVector(Parameters.PermutationId);
|
|
|
|
if (!IsRayTracingEnabledForProject(Parameters.Platform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!Parameters.VertexFactoryType->SupportsRayTracingDynamicGeometry())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (PermutationVector.Get<FVertexMask>())
|
|
{
|
|
return Parameters.MaterialParameters.BlendMode == BLEND_Masked;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
OutEnvironment.SetDefine(TEXT("SCENE_TEXTURES_DISABLED"), 1);
|
|
OutEnvironment.SetDefine(TEXT("RAYTRACING_DYNAMIC_GEOMETRY_CONVERTER"), 1);
|
|
}
|
|
|
|
void GetShaderBindings(
|
|
const FScene* Scene,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
const FMeshMaterialShaderElementData& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings) const
|
|
{
|
|
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, ShaderElementData, ShaderBindings);
|
|
}
|
|
|
|
void GetElementShaderBindings(
|
|
const FShaderMapPointerTable& PointerTable,
|
|
const FScene* Scene,
|
|
const FSceneView* ViewIfDynamicMeshCommand,
|
|
const FVertexFactory* VertexFactory,
|
|
const EVertexInputStreamType InputStreamType,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMeshBatch& MeshBatch,
|
|
const FMeshBatchElement& BatchElement,
|
|
const FMeshMaterialShaderElementData& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings,
|
|
FVertexInputStreamArray& VertexStreams) const
|
|
{
|
|
FMeshMaterialShader::GetElementShaderBindings(PointerTable, Scene, ViewIfDynamicMeshCommand, VertexFactory, InputStreamType, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, ShaderBindings, VertexStreams);
|
|
}
|
|
|
|
LAYOUT_FIELD(FShaderResourceParameter, RWVertexPositions);
|
|
LAYOUT_FIELD(FShaderParameter, UsingIndirectDraw);
|
|
LAYOUT_FIELD(FShaderParameter, MaxNumThreads);
|
|
LAYOUT_FIELD(FShaderParameter, NumVertices);
|
|
LAYOUT_FIELD(FShaderParameter, MinVertexIndex);
|
|
LAYOUT_FIELD(FShaderParameter, PrimitiveId);
|
|
LAYOUT_FIELD(FShaderParameter, bApplyWorldPositionOffset);
|
|
LAYOUT_FIELD(FShaderParameter, OutputVertexBaseIndex);
|
|
LAYOUT_FIELD(FShaderParameter, InstanceId);
|
|
LAYOUT_FIELD(FShaderParameter, WorldToInstance);
|
|
LAYOUT_FIELD(FShaderResourceParameter, IndexBuffer);
|
|
LAYOUT_FIELD(FShaderParameter, IndexBufferOffset);
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(, FRayTracingDynamicGeometryConverterCS, TEXT("/Engine/Private/RayTracing/RayTracingDynamicMesh.usf"), TEXT("RayTracingDynamicGeometryConverterCS"), SF_Compute);
|
|
|
|
static const TCHAR* RayTracingDynamicGeometryPSOCollectorName = TEXT("RayTracingDynamicGeometry");
|
|
|
|
class FRayTracingDynamicGeometryPSOCollector : public IPSOCollector
|
|
{
|
|
public:
|
|
FRayTracingDynamicGeometryPSOCollector(ERHIFeatureLevel::Type InFeatureLevel) :
|
|
IPSOCollector(FPSOCollectorCreateManager::GetIndex(GetFeatureLevelShadingPath(InFeatureLevel), RayTracingDynamicGeometryPSOCollectorName)),
|
|
FeatureLevel(InFeatureLevel)
|
|
{
|
|
}
|
|
|
|
virtual void CollectPSOInitializers(
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
const FMaterial& Material,
|
|
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
|
|
const FPSOPrecacheParams& PreCacheParams,
|
|
TArray<FPSOPrecacheData>& PSOInitializers
|
|
) override final;
|
|
|
|
private:
|
|
|
|
ERHIFeatureLevel::Type FeatureLevel;
|
|
};
|
|
|
|
|
|
void FRayTracingDynamicGeometryPSOCollector::CollectPSOInitializers(
|
|
const FSceneTexturesConfig& SceneTexturesConfig,
|
|
const FMaterial& Material,
|
|
const FPSOPrecacheVertexFactoryData& VertexFactoryData,
|
|
const FPSOPrecacheParams& PreCacheParams,
|
|
TArray<FPSOPrecacheData>& PSOInitializers
|
|
)
|
|
{
|
|
if (!VertexFactoryData.VertexFactoryType->SupportsRayTracingDynamicGeometry())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FMaterialShaderTypes ShaderTypes;
|
|
ShaderTypes.AddShaderType<FRayTracingDynamicGeometryConverterCS>();
|
|
|
|
FMaterialShaders MaterialShaders;
|
|
if (!Material.TryGetShaders(ShaderTypes, VertexFactoryData.VertexFactoryType, MaterialShaders))
|
|
{
|
|
return;
|
|
}
|
|
|
|
TShaderRef<FRayTracingDynamicGeometryConverterCS> Shader;
|
|
if (!MaterialShaders.TryGetShader(SF_Compute, Shader))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FPSOPrecacheData RTPrecacheData;
|
|
RTPrecacheData.Type = FPSOPrecacheData::EType::Compute;
|
|
RTPrecacheData.SetComputeShader(Shader);
|
|
#if PSO_PRECACHING_VALIDATE
|
|
RTPrecacheData.PSOCollectorIndex = PSOCollectorIndex;
|
|
RTPrecacheData.VertexFactoryType = VertexFactoryData.VertexFactoryType;
|
|
#endif // PSO_PRECACHING_VALIDATE
|
|
PSOInitializers.Add(MoveTemp(RTPrecacheData));
|
|
}
|
|
|
|
IPSOCollector* CreateRayTracingDynamicGeometryPSOCollector(ERHIFeatureLevel::Type FeatureLevel)
|
|
{
|
|
return new FRayTracingDynamicGeometryPSOCollector(FeatureLevel);
|
|
}
|
|
FRegisterPSOCollectorCreateFunction RegisterRayTracingDynamicGeometryPSOCollector(&CreateRayTracingDynamicGeometryPSOCollector, EShadingPath::Deferred, RayTracingDynamicGeometryPSOCollectorName);
|
|
|
|
FRayTracingDynamicGeometryUpdateManager::FRayTracingDynamicGeometryUpdateManager()
|
|
{
|
|
}
|
|
|
|
FRayTracingDynamicGeometryUpdateManager::~FRayTracingDynamicGeometryUpdateManager()
|
|
{
|
|
for (FVertexPositionBuffer* Buffer : VertexPositionBuffers)
|
|
{
|
|
delete Buffer;
|
|
}
|
|
VertexPositionBuffers.Empty();
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::Clear()
|
|
{
|
|
DispatchCommandsPerView = {};
|
|
|
|
// Clear working arrays - keep max size allocated
|
|
BuildParams.Empty(BuildParams.Max());
|
|
Segments.Empty(Segments.Max());
|
|
|
|
DynamicGeometryBuilds.Empty(DynamicGeometryBuilds.Max());
|
|
DynamicGeometryUpdates.Empty(DynamicGeometryUpdates.Max());
|
|
|
|
ScratchBufferSize = 0;
|
|
}
|
|
|
|
int64 FRayTracingDynamicGeometryUpdateManager::BeginUpdate()
|
|
{
|
|
check(DispatchCommandsPerView.IsEmpty());
|
|
check(BuildParams.IsEmpty());
|
|
check(Segments.IsEmpty());
|
|
check(ReferencedUniformBuffers.IsEmpty());
|
|
check(DynamicGeometryBuilds.IsEmpty());
|
|
check(DynamicGeometryUpdates.IsEmpty());
|
|
|
|
// Vertex buffer data can be immediatly reused the next frame, because it's already 'consumed' for building the AccelerationStructure data
|
|
// Garbage collect unused buffers for n generations
|
|
for (int32 BufferIndex = 0; BufferIndex < VertexPositionBuffers.Num(); ++BufferIndex)
|
|
{
|
|
FVertexPositionBuffer* Buffer = VertexPositionBuffers[BufferIndex];
|
|
Buffer->UsedSize = 0;
|
|
|
|
if (Buffer->LastUsedGenerationID + GRTDynGeomSharedVertexBufferGarbageCollectLatency <= SharedBufferGenerationID)
|
|
{
|
|
VertexPositionBuffers.RemoveAtSwap(BufferIndex);
|
|
delete Buffer;
|
|
BufferIndex--;
|
|
}
|
|
}
|
|
|
|
// Increment generation ID used for validation
|
|
SharedBufferGenerationID++;
|
|
|
|
return SharedBufferGenerationID;
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::AddDynamicGeometryToUpdate(
|
|
FRHICommandListBase& RHICmdList,
|
|
const FScene* Scene,
|
|
const FSceneView* View,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FRayTracingDynamicGeometryUpdateParams& UpdateParams,
|
|
uint32 PrimitiveId
|
|
)
|
|
{
|
|
FRayTracingGeometry& Geometry = *UpdateParams.Geometry;
|
|
|
|
uint32 NumVertices = UpdateParams.NumVertices;
|
|
uint32 VertexBufferSize = UpdateParams.VertexBufferSize;
|
|
|
|
if (UpdateParams.bAlphaMasked)
|
|
{
|
|
check(UpdateParams.IndexBuffer);
|
|
check(!UpdateParams.bUsingIndirectDraw);
|
|
|
|
NumVertices = UpdateParams.NumTriangles * 3;
|
|
VertexBufferSize = NumVertices * (uint32)sizeof(FVector3f);
|
|
}
|
|
|
|
FRWBuffer* RWBuffer = UpdateParams.Buffer;
|
|
uint32 VertexBufferOffset = 0;
|
|
bool bUseSharedVertexBuffer = false;
|
|
|
|
if (ReferencedUniformBuffers.Num() == 0 || ReferencedUniformBuffers.Last() != View->ViewUniformBuffer)
|
|
{
|
|
// Keep ViewUniformBuffer alive until EndUpdate()
|
|
ReferencedUniformBuffers.Add(View->ViewUniformBuffer);
|
|
}
|
|
|
|
FRayTracingDynamicGeometryBuildParams GeometryBuildParams;
|
|
GeometryBuildParams.ViewUniformBuffer = View->ViewUniformBuffer;
|
|
GeometryBuildParams.DispatchCommands.Reserve(UpdateParams.MeshBatches.Num());
|
|
|
|
// Only update when we have mesh batches
|
|
if (!UpdateParams.MeshBatches.IsEmpty())
|
|
{
|
|
// If update params didn't provide a buffer then use a shared vertex position buffer
|
|
if (RWBuffer == nullptr)
|
|
{
|
|
RWBuffer = AllocateSharedBuffer(RHICmdList, VertexBufferSize, VertexBufferOffset);
|
|
bUseSharedVertexBuffer = true;
|
|
}
|
|
check(IsAligned(VertexBufferOffset, RHI_RAW_VIEW_ALIGNMENT));
|
|
|
|
AddDispatchCommands(RHICmdList, Scene, View, PrimitiveSceneProxy, UpdateParams, PrimitiveId, RWBuffer, NumVertices, VertexBufferOffset, VertexBufferSize, GeometryBuildParams);
|
|
}
|
|
|
|
bool bRefit = true;
|
|
|
|
// Optionally resize the buffer when not shared (could also be lazy allocated and still empty)
|
|
if (!bUseSharedVertexBuffer && RWBuffer && RWBuffer->NumBytes != VertexBufferSize)
|
|
{
|
|
RWBuffer->Initialize(RHICmdList, TEXT("FRayTracingDynamicGeometryUpdateManager::RayTracingDynamicVertexBuffer"), sizeof(float), VertexBufferSize / sizeof(float), PF_R32_FLOAT, BUF_UnorderedAccess | BUF_ShaderResource);
|
|
bRefit = false;
|
|
}
|
|
|
|
if (!Geometry.IsValid() || Geometry.IsEvicted())
|
|
{
|
|
bRefit = false;
|
|
}
|
|
|
|
if (!Geometry.Initializer.bAllowUpdate)
|
|
{
|
|
bRefit = false;
|
|
}
|
|
|
|
check(Geometry.IsInitialized());
|
|
|
|
if (Geometry.Initializer.TotalPrimitiveCount != UpdateParams.NumTriangles && UpdateParams.NumTriangles > 0)
|
|
{
|
|
checkf(Geometry.Initializer.Segments.Num() <= 1, TEXT("Dynamic ray tracing geometry '%s' has an unexpected number of segments."), *Geometry.Initializer.DebugName.ToString());
|
|
Geometry.Initializer.TotalPrimitiveCount = UpdateParams.NumTriangles;
|
|
Geometry.Initializer.Segments.Empty();
|
|
FRayTracingGeometrySegment Segment;
|
|
Segment.NumPrimitives = UpdateParams.NumTriangles;
|
|
Segment.MaxVertices = NumVertices;
|
|
Geometry.Initializer.Segments.Add(Segment);
|
|
bRefit = false;
|
|
}
|
|
|
|
if (UpdateParams.bAlphaMasked)
|
|
{
|
|
Geometry.Initializer.IndexBuffer = nullptr;
|
|
}
|
|
|
|
if (RWBuffer)
|
|
{
|
|
for (FRayTracingGeometrySegment& Segment : Geometry.Initializer.Segments)
|
|
{
|
|
Segment.VertexBuffer = RWBuffer->Buffer;
|
|
Segment.VertexBufferOffset = VertexBufferOffset;
|
|
}
|
|
}
|
|
|
|
if (!bRefit)
|
|
{
|
|
checkf(Geometry.RawData.IsEmpty() && Geometry.Initializer.OfflineData == nullptr, TEXT("Dynamic geometry is not expected to have offline acceleration structure data"));
|
|
Geometry.CreateRayTracingGeometry(RHICmdList, ERTAccelerationStructureBuildPriority::Skip);
|
|
}
|
|
|
|
EAccelerationStructureBuildMode BuildMode = Geometry.GetRequiresBuild()
|
|
? EAccelerationStructureBuildMode::Build
|
|
: EAccelerationStructureBuildMode::Update;
|
|
|
|
GeometryBuildParams.Geometry = UpdateParams.Geometry;
|
|
|
|
if (bUseSharedVertexBuffer)
|
|
{
|
|
GeometryBuildParams.SegmentOffset = Segments.Num();
|
|
Segments.Append(Geometry.Initializer.Segments);
|
|
}
|
|
|
|
Geometry.SetRequiresBuild(false);
|
|
|
|
if (BuildMode == EAccelerationStructureBuildMode::Build)
|
|
{
|
|
DynamicGeometryBuilds.Add(GeometryBuildParams);
|
|
}
|
|
else
|
|
{
|
|
DynamicGeometryUpdates.Add(GeometryBuildParams);
|
|
}
|
|
|
|
if (bUseSharedVertexBuffer)
|
|
{
|
|
Geometry.DynamicGeometrySharedBufferGenerationID = SharedBufferGenerationID;
|
|
}
|
|
else
|
|
{
|
|
Geometry.DynamicGeometrySharedBufferGenerationID = FRayTracingGeometry::NonSharedVertexBuffers;
|
|
}
|
|
}
|
|
|
|
FRWBuffer* FRayTracingDynamicGeometryUpdateManager::AllocateSharedBuffer(FRHICommandListBase& RHICmdList, uint32 VertexBufferSize, uint32& OutVertexBufferOffset)
|
|
{
|
|
FVertexPositionBuffer* VertexPositionBuffer = nullptr;
|
|
for (FVertexPositionBuffer* Buffer : VertexPositionBuffers)
|
|
{
|
|
if (Buffer->RWBuffer.NumBytes >= (VertexBufferSize + Buffer->UsedSize))
|
|
{
|
|
VertexPositionBuffer = Buffer;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Allocate a new buffer?
|
|
if (VertexPositionBuffer == nullptr)
|
|
{
|
|
VertexPositionBuffer = new FVertexPositionBuffer;
|
|
VertexPositionBuffers.Add(VertexPositionBuffer);
|
|
|
|
static const uint32 VertexBufferCacheSize = GRTDynGeomSharedVertexBufferSizeInMB * 1024 * 1024;
|
|
uint32 AllocationSize = FMath::Max(VertexBufferCacheSize, VertexBufferSize);
|
|
|
|
VertexPositionBuffer->RWBuffer.Initialize(RHICmdList, TEXT("FRayTracingDynamicGeometryUpdateManager::RayTracingDynamicVertexBuffer"), sizeof(float), AllocationSize / sizeof(float), PF_R32_FLOAT, BUF_UnorderedAccess | BUF_ShaderResource);
|
|
VertexPositionBuffer->UsedSize = 0;
|
|
}
|
|
|
|
// Update the last used generation ID
|
|
VertexPositionBuffer->LastUsedGenerationID = SharedBufferGenerationID;
|
|
|
|
// Get the offset and update used size
|
|
OutVertexBufferOffset = VertexPositionBuffer->UsedSize;
|
|
VertexPositionBuffer->UsedSize += VertexBufferSize;
|
|
|
|
// Make sure vertex buffer offset is aligned to 16 (required for Raw SRV views)
|
|
VertexPositionBuffer->UsedSize = Align(VertexPositionBuffer->UsedSize, RHI_RAW_VIEW_ALIGNMENT);
|
|
|
|
return &VertexPositionBuffer->RWBuffer;
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::AddDispatchCommands(
|
|
FRHICommandListBase& RHICmdList,
|
|
const FScene* Scene,
|
|
const FSceneView* View,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FRayTracingDynamicGeometryUpdateParams& UpdateParams,
|
|
uint32 PrimitiveId,
|
|
FRWBuffer* RWBuffer,
|
|
uint32 NumVertices,
|
|
uint32 VertexBufferOffset,
|
|
uint32 VertexBufferSize,
|
|
FRayTracingDynamicGeometryUpdateManager::FRayTracingDynamicGeometryBuildParams& GeometryBuildParams)
|
|
{
|
|
const int32 PSOCollectorIndex = FPSOCollectorCreateManager::GetIndex(EShadingPath::Deferred, RayTracingDynamicGeometryPSOCollectorName);
|
|
|
|
for (const FMeshBatch& MeshBatch : UpdateParams.MeshBatches)
|
|
{
|
|
if (!ensureMsgf(MeshBatch.VertexFactory->GetType()->SupportsRayTracingDynamicGeometry(),
|
|
TEXT("FRayTracingDynamicGeometryConverterCS doesn't support %s. Skipping rendering of %s. This can happen when the skinning cache runs out of space and falls back to GPUSkinVertexFactory."),
|
|
MeshBatch.VertexFactory->GetType()->GetName(), *PrimitiveSceneProxy->GetOwnerName().ToString()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FMaterialRenderProxy* MaterialRenderProxyPtr = MeshBatch.MaterialRenderProxy;
|
|
while (MaterialRenderProxyPtr)
|
|
{
|
|
const FMaterial* MaterialPtr = MaterialRenderProxyPtr->GetMaterialNoFallback(Scene->GetFeatureLevel());
|
|
if (MaterialPtr && MaterialPtr->GetRenderingThreadShaderMap())
|
|
{
|
|
const FMaterial& Material = *MaterialPtr;
|
|
const FMaterialRenderProxy& MaterialRenderProxy = *MaterialRenderProxyPtr;
|
|
|
|
auto* MaterialInterface = Material.GetMaterialInterface();
|
|
|
|
FMeshComputeDispatchCommand DispatchCmd;
|
|
|
|
FRayTracingDynamicGeometryConverterCS::FPermutationDomain PermutationVectorCS;
|
|
PermutationVectorCS.Set<FRayTracingDynamicGeometryConverterCS::FVertexMask>(UpdateParams.bAlphaMasked);
|
|
|
|
FMaterialShaderTypes ShaderTypes;
|
|
ShaderTypes.AddShaderType<FRayTracingDynamicGeometryConverterCS>(PermutationVectorCS.ToDimensionValueId());
|
|
|
|
FMaterialShaders MaterialShaders;
|
|
if (Material.TryGetShaders(ShaderTypes, MeshBatch.VertexFactory->GetType(), MaterialShaders))
|
|
{
|
|
TShaderRef<FRayTracingDynamicGeometryConverterCS> Shader;
|
|
MaterialShaders.TryGetShader(SF_Compute, Shader);
|
|
|
|
FMeshProcessorShaders MeshProcessorShaders;
|
|
MeshProcessorShaders.ComputeShader = Shader;
|
|
|
|
DispatchCmd.MaterialShader = Shader;
|
|
FMeshDrawShaderBindings& ShaderBindings = DispatchCmd.ShaderBindings;
|
|
ShaderBindings.Initialize(MeshProcessorShaders);
|
|
|
|
FMeshMaterialShaderElementData ShaderElementData;
|
|
ShaderElementData.InitializeMeshMaterialData(View, PrimitiveSceneProxy, MeshBatch, -1, false);
|
|
|
|
FMeshDrawSingleShaderBindings SingleShaderBindings = ShaderBindings.GetSingleShaderBindings(SF_Compute);
|
|
Shader->GetShaderBindings(Scene, Scene->GetFeatureLevel(), PrimitiveSceneProxy, MaterialRenderProxy, Material, ShaderElementData, SingleShaderBindings);
|
|
|
|
FVertexInputStreamArray DummyArray;
|
|
FMeshMaterialShader::GetElementShaderBindings(Shader, Scene, View, MeshBatch.VertexFactory, EVertexInputStreamType::Default, Scene->GetFeatureLevel(), PrimitiveSceneProxy, MeshBatch, MeshBatch.Elements[0], ShaderElementData, SingleShaderBindings, DummyArray);
|
|
|
|
DispatchCmd.TargetBuffer = RWBuffer;
|
|
|
|
// Setup the loose parameters directly on the binding
|
|
uint32 OutputVertexBaseIndex = VertexBufferOffset / sizeof(float);
|
|
uint32 MinVertexIndex = MeshBatch.Elements[0].MinVertexIndex;
|
|
uint32 NumCPUVertices = NumVertices;
|
|
if (MeshBatch.Elements[0].MinVertexIndex < MeshBatch.Elements[0].MaxVertexIndex)
|
|
{
|
|
NumCPUVertices = 1 + MeshBatch.Elements[0].MaxVertexIndex - MeshBatch.Elements[0].MinVertexIndex;
|
|
}
|
|
|
|
const uint32 VertexBufferNumElements = VertexBufferSize / sizeof(FVector3f) - MinVertexIndex;
|
|
if (!ensureMsgf(NumCPUVertices <= VertexBufferNumElements,
|
|
TEXT("%s: Vertex buffer contains %d vertices, but RayTracingDynamicGeometryConverterCS dispatch command expects at least %d."),
|
|
*PrimitiveSceneProxy->GetOwnerName().ToString(), VertexBufferNumElements, NumCPUVertices))
|
|
{
|
|
NumCPUVertices = VertexBufferNumElements;
|
|
}
|
|
|
|
DispatchCmd.NumCPUVertices = NumCPUVertices;
|
|
|
|
SingleShaderBindings.Add(Shader->UsingIndirectDraw, UpdateParams.bUsingIndirectDraw ? 1 : 0);
|
|
SingleShaderBindings.Add(Shader->NumVertices, NumCPUVertices);
|
|
SingleShaderBindings.Add(Shader->MinVertexIndex, MinVertexIndex);
|
|
SingleShaderBindings.Add(Shader->PrimitiveId, PrimitiveId);
|
|
SingleShaderBindings.Add(Shader->OutputVertexBaseIndex, OutputVertexBaseIndex);
|
|
SingleShaderBindings.Add(Shader->bApplyWorldPositionOffset, UpdateParams.bApplyWorldPositionOffset ? 1 : 0);
|
|
SingleShaderBindings.Add(Shader->InstanceId, UpdateParams.InstanceId);
|
|
SingleShaderBindings.Add(Shader->WorldToInstance, UpdateParams.WorldToInstance);
|
|
|
|
if (UpdateParams.bAlphaMasked)
|
|
{
|
|
FRHIBuffer* IndexBufferRHI = UpdateParams.IndexBuffer;
|
|
|
|
const uint32 IndexStride = IndexBufferRHI->GetStride();
|
|
const uint32 NumTriangles = UpdateParams.NumTriangles;
|
|
const uint32 IndexBufferOffset = UpdateParams.Geometry->Initializer.IndexBufferOffset / IndexStride + MeshBatch.Elements[0].FirstIndex;
|
|
|
|
SingleShaderBindings.Add(Shader->IndexBuffer,
|
|
RHICmdList.CreateShaderResourceView(IndexBufferRHI,
|
|
FRHIViewDesc::CreateBufferSRV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(IndexStride == 4 ? PF_R32_UINT : PF_R16_UINT))
|
|
);
|
|
|
|
SingleShaderBindings.Add(Shader->MaxNumThreads, NumTriangles);
|
|
SingleShaderBindings.Add(Shader->IndexBufferOffset, IndexBufferOffset);
|
|
|
|
DispatchCmd.NumThreads = NumTriangles;
|
|
}
|
|
else
|
|
{
|
|
SingleShaderBindings.Add(Shader->MaxNumThreads, NumCPUVertices);
|
|
SingleShaderBindings.Add(Shader->IndexBufferOffset, 0);
|
|
|
|
DispatchCmd.NumThreads = NumVertices;
|
|
}
|
|
|
|
#if MESH_DRAW_COMMAND_DEBUG_DATA
|
|
ShaderBindings.Finalize(&MeshProcessorShaders);
|
|
#endif
|
|
|
|
#if PSO_PRECACHING_VALIDATE
|
|
FRHIComputeShader* ComputeShader = DispatchCmd.MaterialShader.GetComputeShader();
|
|
if (ComputeShader != nullptr)
|
|
{
|
|
EPSOPrecacheResult PSOPrecacheResult = PipelineStateCache::CheckPipelineStateInCache(ComputeShader);
|
|
PSOCollectorStats::CheckComputePipelineStateInCache(*ComputeShader, PSOPrecacheResult, &MaterialRenderProxy, PSOCollectorIndex);
|
|
}
|
|
#endif
|
|
|
|
GeometryBuildParams.DispatchCommands.Add(DispatchCmd);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
MaterialRenderProxyPtr = MaterialRenderProxyPtr->GetFallback(Scene->GetFeatureLevel());
|
|
}
|
|
}
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FRayTracingDynamicGeometryUpdatePassParams, )
|
|
RDG_BUFFER_ACCESS(DynamicGeometryScratchBuffer, ERHIAccess::UAVCompute)
|
|
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::ScheduleUpdates(bool bUseTracingFeedback)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FRayTracingDynamicGeometryUpdateManager::Update);
|
|
|
|
// Early out of nothing to do
|
|
const int32 TotalNumGeometryBuilds = DynamicGeometryBuilds.Num() + DynamicGeometryUpdates.Num();
|
|
if (TotalNumGeometryBuilds == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
checkf(DispatchCommandsPerView.IsEmpty(), TEXT("DispatchCommandsPerView is not empty. Previous frame updates were not dispatched."));
|
|
checkf(BuildParams.IsEmpty(), TEXT("BuildParams is not empty. Previous frame updates were not dispatched."));
|
|
|
|
// reserve for worst case (might be wasteful if there are too many views)
|
|
for (FRHIUniformBuffer* ViewUniformBuffer : ReferencedUniformBuffers)
|
|
{
|
|
TArray<FMeshComputeDispatchCommand>& ViewDispatchCommands = DispatchCommandsPerView.FindOrAdd(ViewUniformBuffer);
|
|
ViewDispatchCommands.Reserve(TotalNumGeometryBuilds);
|
|
}
|
|
|
|
BuildParams.Reserve(TotalNumGeometryBuilds);
|
|
|
|
FRayTracingGeometrySegment* SegmentData = Segments.GetData();
|
|
|
|
const uint32 ScratchAlignment = GRHIRayTracingScratchBufferAlignment;
|
|
|
|
uint32 BLASScratchSize = 0;
|
|
int32 NumBuildPrimitives = 0;
|
|
|
|
auto AddGeometryBuildParamsToBuildList = [this, SegmentData, &BLASScratchSize](const FRayTracingDynamicGeometryBuildParams& InBuildParams)
|
|
{
|
|
FRHIRayTracingGeometry* RayTracingGeometry = InBuildParams.Geometry->GetRHI();
|
|
|
|
const uint32 ScratchAlignment = GRHIRayTracingScratchBufferAlignment;
|
|
const uint32 NumBuildPrimitives = InBuildParams.Geometry->Initializer.TotalPrimitiveCount;
|
|
|
|
const uint32 ScratchSize = RayTracingGeometry->GetSizeInfo().BuildScratchSize;
|
|
BLASScratchSize = Align(BLASScratchSize + ScratchSize, ScratchAlignment);
|
|
|
|
InBuildParams.Geometry->NumUpdatesSinceLastBuild = 0;
|
|
InBuildParams.Geometry->SetRequiresUpdate(false);
|
|
|
|
FRayTracingGeometryBuildParams RTGeoBuildParams;
|
|
RTGeoBuildParams.Geometry = RayTracingGeometry;
|
|
RTGeoBuildParams.BuildMode = EAccelerationStructureBuildMode::Build;
|
|
|
|
if (InBuildParams.SegmentOffset >= 0)
|
|
{
|
|
RTGeoBuildParams.Segments = MakeArrayView(&SegmentData[InBuildParams.SegmentOffset], InBuildParams.Geometry->Initializer.Segments.Num());
|
|
}
|
|
else
|
|
{
|
|
RTGeoBuildParams.Segments = InBuildParams.Geometry->Initializer.Segments;
|
|
}
|
|
|
|
BuildParams.Add(MoveTemp(RTGeoBuildParams));
|
|
|
|
if (!InBuildParams.DispatchCommands.IsEmpty())
|
|
{
|
|
DispatchCommandsPerView[InBuildParams.ViewUniformBuffer].Append(InBuildParams.DispatchCommands);
|
|
}
|
|
};
|
|
|
|
for (const FRayTracingDynamicGeometryBuildParams& Build : DynamicGeometryBuilds)
|
|
{
|
|
AddGeometryBuildParamsToBuildList(Build);
|
|
NumBuildPrimitives += Build.Geometry->Initializer.TotalPrimitiveCount;
|
|
}
|
|
|
|
const uint32 MaxUpdatePrimitivesPerFrame = uint32(CVarRTDynGeomMaxUpdatePrimitivesPerFrame.GetValueOnRenderThread());
|
|
const uint32 MaxForceBuildPrimitivesPerFrame = uint32(CVarRTDynGeomForceBuildMaxUpdatePrimitivesPerFrame.GetValueOnRenderThread());
|
|
const uint32 MinUpdatesSinceLastBuild = uint32(CVarRTDynGeomForceBuildMinUpdatesSinceLastBuild.GetValueOnRenderThread());
|
|
|
|
uint32 NumUpdatedPrimitives = 0;
|
|
uint32 NumForceBuildPrimitives = 0;
|
|
|
|
const bool bNeedsSorting = (int32(MaxUpdatePrimitivesPerFrame) != -1) || (MaxForceBuildPrimitivesPerFrame != 0);
|
|
if (bNeedsSorting)
|
|
{
|
|
DynamicGeometryUpdates.Sort([](const FRayTracingDynamicGeometryBuildParams& InLHS, const FRayTracingDynamicGeometryBuildParams& InRHS)
|
|
{
|
|
if (InLHS.Geometry->LastUpdatedFrame == InRHS.Geometry->LastUpdatedFrame)
|
|
{
|
|
return InLHS.Geometry->NumUpdatesSinceLastBuild > InRHS.Geometry->NumUpdatesSinceLastBuild;
|
|
}
|
|
|
|
return InLHS.Geometry->LastUpdatedFrame < InRHS.Geometry->LastUpdatedFrame;
|
|
});
|
|
}
|
|
|
|
for (const FRayTracingDynamicGeometryBuildParams& Update : DynamicGeometryUpdates)
|
|
{
|
|
FRHIRayTracingGeometry* RayTracingGeometry = Update.Geometry->GetRHI();
|
|
const uint32 TotalPrimitiveCount = Update.Geometry->Initializer.TotalPrimitiveCount;
|
|
|
|
if (bUseTracingFeedback && !GRayTracingGeometryManager->IsGeometryVisible(Update.Geometry->GetGeometryHandle()))
|
|
{
|
|
INC_DWORD_STAT_BY(STAT_RayTracingDynamicSkippedPrimitives, TotalPrimitiveCount);
|
|
continue;
|
|
}
|
|
|
|
if (MaxForceBuildPrimitivesPerFrame > 0)
|
|
{
|
|
if (Update.Geometry->NumUpdatesSinceLastBuild > MinUpdatesSinceLastBuild && NumForceBuildPrimitives <= MaxForceBuildPrimitivesPerFrame)
|
|
{
|
|
AddGeometryBuildParamsToBuildList(Update);
|
|
NumBuildPrimitives += TotalPrimitiveCount;
|
|
NumForceBuildPrimitives += TotalPrimitiveCount;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Update.Geometry->LastUpdatedFrame = GFrameCounterRenderThread;
|
|
Update.Geometry->NumUpdatesSinceLastBuild++;
|
|
Update.Geometry->SetRequiresUpdate(false);
|
|
|
|
NumUpdatedPrimitives += TotalPrimitiveCount;
|
|
|
|
const uint32 ScratchSize = RayTracingGeometry->GetSizeInfo().UpdateScratchSize;
|
|
BLASScratchSize = Align(BLASScratchSize + ScratchSize, ScratchAlignment);
|
|
|
|
FRayTracingGeometryBuildParams BuildParam;
|
|
BuildParam.Geometry = RayTracingGeometry;
|
|
BuildParam.BuildMode = EAccelerationStructureBuildMode::Update;
|
|
if (Update.SegmentOffset >= 0)
|
|
{
|
|
BuildParam.Segments = MakeArrayView(&SegmentData[Update.SegmentOffset], Update.Geometry->Initializer.Segments.Num());
|
|
}
|
|
else
|
|
{
|
|
BuildParam.Segments = Update.Geometry->Initializer.Segments;
|
|
}
|
|
BuildParams.Add(MoveTemp(BuildParam));
|
|
|
|
if (!Update.DispatchCommands.IsEmpty())
|
|
{
|
|
DispatchCommandsPerView[Update.ViewUniformBuffer].Append(Update.DispatchCommands);
|
|
}
|
|
|
|
if (NumUpdatedPrimitives > MaxUpdatePrimitivesPerFrame)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
INC_DWORD_STAT_BY(STAT_RayTracingDynamicUpdatePrimitives, NumUpdatedPrimitives);
|
|
INC_DWORD_STAT_BY(STAT_RayTracingDynamicBuildPrimitives, NumBuildPrimitives);
|
|
|
|
ScratchBufferSize = BLASScratchSize;
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::Update(const FViewInfo& View)
|
|
{
|
|
ScheduleUpdates(View.bRayTracingFeedbackEnabled);
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::AddDynamicGeometryUpdatePass(FRDGBuilder& GraphBuilder, ERDGPassFlags ComputePassFlags, const TRDGUniformBufferRef<FSceneUniformParameters>& SceneUB, bool bUseTracingFeedback, ERHIPipeline ResourceAccessPipelines, FRDGBufferRef& OutDynamicGeometryScratchBuffer)
|
|
{
|
|
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
|
|
RDG_EVENT_SCOPE_STAT(GraphBuilder, RayTracingDynamicGeometry, "RayTracingDynamicGeometry");
|
|
RDG_GPU_STAT_SCOPE(GraphBuilder, RayTracingDynamicGeometry);
|
|
|
|
ScheduleUpdates(bUseTracingFeedback);
|
|
|
|
const uint32 ScratchAlignment = GRHIRayTracingScratchBufferAlignment;
|
|
const uint32 BLASScratchSize = ScratchBufferSize;
|
|
|
|
if (BLASScratchSize > 0)
|
|
{
|
|
FRDGBufferDesc ScratchBufferDesc;
|
|
ScratchBufferDesc.Usage = EBufferUsageFlags::RayTracingScratch | EBufferUsageFlags::StructuredBuffer;
|
|
ScratchBufferDesc.BytesPerElement = ScratchAlignment;
|
|
ScratchBufferDesc.NumElements = FMath::DivideAndRoundUp(BLASScratchSize, ScratchAlignment);
|
|
|
|
OutDynamicGeometryScratchBuffer = GraphBuilder.CreateBuffer(ScratchBufferDesc, TEXT("DynamicGeometry.BLASSharedScratchBuffer"));
|
|
}
|
|
|
|
const ERHIPipeline SrcResourceAccessPipelines = ComputePassFlags == ERDGPassFlags::AsyncCompute ? ERHIPipeline::AsyncCompute : ERHIPipeline::Graphics;
|
|
|
|
for(TPair<FRHIUniformBuffer*, TArray<FMeshComputeDispatchCommand>>& ViewDispatchCommands : DispatchCommandsPerView)
|
|
{
|
|
if (ViewDispatchCommands.Value.IsEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FRayTracingDynamicGeometryUpdatePassParams* PassParams = GraphBuilder.AllocParameters<FRayTracingDynamicGeometryUpdatePassParams>();
|
|
PassParams->View.View = TUniformBufferRef<FViewUniformShaderParameters>(ViewDispatchCommands.Key);
|
|
PassParams->View.InstancedView = TUniformBufferRef<FInstancedViewUniformShaderParameters>(nullptr); // TODO: Is this necessary?
|
|
PassParams->Scene = SceneUB;
|
|
|
|
// DynamicGeometryScratchBuffer is not directly used in this pass but set so RDG orders passes correctly
|
|
// (TODO: this might also prevent dispatches for different views from overlapping, so investigate better solution)
|
|
PassParams->DynamicGeometryScratchBuffer = OutDynamicGeometryScratchBuffer;
|
|
|
|
GraphBuilder.AddPass(RDG_EVENT_NAME("RayTracingDynamicUpdate"), PassParams, ComputePassFlags | ERDGPassFlags::NeverCull,
|
|
[this, SrcResourceAccessPipelines, ResourceAccessPipelines, DispatchCommands = MakeArrayView(ViewDispatchCommands.Value)](FRHICommandList& RHICmdList)
|
|
{
|
|
DispatchUpdates(RHICmdList, DispatchCommands, SrcResourceAccessPipelines, ResourceAccessPipelines);
|
|
});
|
|
}
|
|
|
|
if (BuildParams.Num() > 0)
|
|
{
|
|
FRayTracingDynamicGeometryUpdatePassParams* PassParams = GraphBuilder.AllocParameters<FRayTracingDynamicGeometryUpdatePassParams>();
|
|
PassParams->View = {};
|
|
PassParams->Scene = nullptr;
|
|
PassParams->DynamicGeometryScratchBuffer = OutDynamicGeometryScratchBuffer;
|
|
|
|
GraphBuilder.AddPass(RDG_EVENT_NAME("RayTracingDynamicUpdateBuild"), PassParams, ComputePassFlags | ERDGPassFlags::NeverCull,
|
|
[this, PassParams](FRHICommandList& RHICmdList)
|
|
{
|
|
// Can't use parallel command list because we have to make sure we are not building BVH data
|
|
// on the same RTGeometry on multiple threads at the same time. Ideally move the build
|
|
// requests over to the RaytracingGeometry manager so they can be correctly scheduled
|
|
// with other build requests in the engine (see UE-106982)
|
|
SCOPED_DRAW_EVENT(RHICmdList, Build);
|
|
|
|
FRHIBufferRange ScratchBufferRange;
|
|
ScratchBufferRange.Buffer = PassParams->DynamicGeometryScratchBuffer ? PassParams->DynamicGeometryScratchBuffer->GetRHI() : nullptr;
|
|
ScratchBufferRange.Offset = 0;
|
|
RHICmdList.BuildAccelerationStructures(BuildParams, ScratchBufferRange);
|
|
});
|
|
}
|
|
|
|
// TODO: Is it safe to use a regular task that waits on FRDGBuilder::GetAsyncExecuteTask() here instead?
|
|
// which would allow the passes above to be tagged with FRDGAsyncTask
|
|
GraphBuilder.AddPostExecuteCallback([this]
|
|
{
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
EndUpdate();
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
});
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::AddDynamicGeometryUpdatePass(const FViewInfo& View, FRDGBuilder& GraphBuilder, ERDGPassFlags ComputePassFlags, ERHIPipeline ResourceAccessPipelines, FRDGBufferRef& OutDynamicGeometryScratchBuffer)
|
|
{
|
|
AddDynamicGeometryUpdatePass(GraphBuilder, ComputePassFlags, View.GetSceneUniforms().GetBuffer(GraphBuilder), View.bRayTracingFeedbackEnabled, ResourceAccessPipelines, OutDynamicGeometryScratchBuffer);
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::DispatchUpdates(FRHICommandList& RHICmdList, TConstArrayView<FMeshComputeDispatchCommand> DispatchCommands, ERHIPipeline SrcResourceAccessPipelines, ERHIPipeline DstResourceAccessPipelines)
|
|
{
|
|
if (DispatchCommands.Num() > 0)
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, RayTracingDynamicGeometryUpdate);
|
|
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(SortDispatchCommands);
|
|
|
|
// This can be optimized by using sorted insert or using map on shaders
|
|
// There are only a handful of unique shaders and a few target buffers so we want to swap state as little as possible
|
|
// to reduce RHI thread overhead
|
|
DispatchCommands.Sort([](const FMeshComputeDispatchCommand& InLHS, const FMeshComputeDispatchCommand& InRHS)
|
|
{
|
|
if (InLHS.MaterialShader.GetComputeShader() != InRHS.MaterialShader.GetComputeShader())
|
|
return InLHS.MaterialShader.GetComputeShader() < InRHS.MaterialShader.GetComputeShader();
|
|
|
|
return InLHS.TargetBuffer < InRHS.TargetBuffer;
|
|
});
|
|
}
|
|
|
|
FMemMark Mark(FMemStack::Get());
|
|
|
|
TArray<FRHITransitionInfo, TMemStackAllocator<>> TransitionsBefore, TransitionsAfter;
|
|
TArray<FRHIUnorderedAccessView*, TMemStackAllocator<>> OverlapUAVs;
|
|
TransitionsBefore.Reserve(DispatchCommands.Num());
|
|
TransitionsAfter.Reserve(DispatchCommands.Num());
|
|
OverlapUAVs.Reserve(DispatchCommands.Num());
|
|
const FRWBuffer* LastBuffer = nullptr;
|
|
TSet<const FRWBuffer*> TransitionedBuffers;
|
|
for (const FMeshComputeDispatchCommand& Cmd : DispatchCommands)
|
|
{
|
|
if (Cmd.TargetBuffer == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
FRHIUnorderedAccessView* UAV = Cmd.TargetBuffer->UAV.GetReference();
|
|
|
|
// The list is sorted by TargetBuffer, so we can remove duplicates by simply looking at the previous value we've processed.
|
|
if (LastBuffer == Cmd.TargetBuffer)
|
|
{
|
|
// This UAV is used by more than one dispatch, so tell the RHI it's OK to overlap the dispatches, because
|
|
// we're updating disjoint regions.
|
|
if (OverlapUAVs.Num() == 0 || OverlapUAVs.Last() != UAV)
|
|
{
|
|
OverlapUAVs.Add(UAV);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
LastBuffer = Cmd.TargetBuffer;
|
|
|
|
// In case different shaders use different TargetBuffer we want to add transition only once
|
|
bool bAlreadyInSet = false;
|
|
TransitionedBuffers.FindOrAdd(LastBuffer, &bAlreadyInSet);
|
|
if (!bAlreadyInSet)
|
|
{
|
|
// Looks like the resource can get here in either UAVCompute or SRVMask mode, so we'll have to use Unknown until we can have better tracking.
|
|
TransitionsBefore.Add(FRHITransitionInfo(UAV, ERHIAccess::Unknown, ERHIAccess::UAVCompute));
|
|
TransitionsAfter.Add(FRHITransitionInfo(UAV, ERHIAccess::UAVCompute, ERHIAccess::SRVMask));
|
|
}
|
|
}
|
|
|
|
{
|
|
FRHIComputeShader* CurrentShader = nullptr;
|
|
FRWBuffer* CurrentBuffer = nullptr;
|
|
|
|
// Transition to writeable for each cmd list and enable UAV overlap, because several dispatches can update non-overlapping portions of the same buffer.
|
|
// Mark as no fence because these resources have been fenced already at the beginning of the frame by RDG
|
|
RHICmdList.Transition(TransitionsBefore, ERHITransitionCreateFlags::AllowDecayPipelines);
|
|
RHICmdList.BeginUAVOverlap(OverlapUAVs);
|
|
|
|
// Cache the bound uniform buffers because a lot are the same between dispatches
|
|
FShaderBindingState ShaderBindingState;
|
|
|
|
for (const FMeshComputeDispatchCommand& Cmd : DispatchCommands)
|
|
{
|
|
const TShaderRef<FRayTracingDynamicGeometryConverterCS>& Shader = Cmd.MaterialShader;
|
|
FRHIComputeShader* ComputeShader = Shader.GetComputeShader();
|
|
if (CurrentShader != ComputeShader)
|
|
{
|
|
SetComputePipelineState(RHICmdList, ComputeShader);
|
|
CurrentBuffer = nullptr;
|
|
CurrentShader = ComputeShader;
|
|
|
|
// Reset binding state
|
|
ShaderBindingState = FShaderBindingState();
|
|
}
|
|
|
|
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
|
|
|
|
FRWBuffer* TargetBuffer = Cmd.TargetBuffer;
|
|
|
|
// Always rebind the target buffer because we have to make sure that the bindless index is always written
|
|
// otherwise it might miss in the cbuffer data
|
|
//if (CurrentBuffer != TargetBuffer)
|
|
{
|
|
CurrentBuffer = TargetBuffer;
|
|
|
|
SetUAVParameter(BatchedParameters, Shader->RWVertexPositions, Cmd.TargetBuffer->UAV);
|
|
}
|
|
|
|
Cmd.ShaderBindings.SetParameters(BatchedParameters, &ShaderBindingState);
|
|
RHICmdList.SetBatchedShaderParameters(CurrentShader, BatchedParameters);
|
|
|
|
const FIntVector NumWrappedThreadGroups = FComputeShaderUtils::GetGroupCountWrapped(Cmd.NumThreads, 64);
|
|
RHICmdList.DispatchComputeShader(NumWrappedThreadGroups.X, NumWrappedThreadGroups.Y, NumWrappedThreadGroups.Z);
|
|
}
|
|
|
|
// Make sure buffers are readable again and disable UAV overlap.
|
|
RHICmdList.EndUAVOverlap(OverlapUAVs);
|
|
|
|
// Transition to SRV state and mark readable on requested pipelines
|
|
RHICmdList.Transition(TransitionsAfter, SrcResourceAccessPipelines, DstResourceAccessPipelines);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::DispatchUpdates(FRHICommandList& RHICmdList, FRHIBuffer* ScratchBuffer, ERHIPipeline SrcResourceAccessPipelines, ERHIPipeline DstResourceAccessPipelines)
|
|
{
|
|
for (TPair<FRHIUniformBuffer*, TArray<FMeshComputeDispatchCommand>>& ViewDispatchCommands : DispatchCommandsPerView)
|
|
{
|
|
DispatchUpdates(RHICmdList, ViewDispatchCommands.Value, SrcResourceAccessPipelines, DstResourceAccessPipelines);
|
|
}
|
|
|
|
if (BuildParams.Num() > 0)
|
|
{
|
|
// Can't use parallel command list because we have to make sure we are not building BVH data
|
|
// on the same RTGeometry on multiple threads at the same time. Ideally move the build
|
|
// requests over to the RaytracingGeometry manager so they can be correctly scheduled
|
|
// with other build requests in the engine (see UE-106982)
|
|
SCOPED_DRAW_EVENT(RHICmdList, Build);
|
|
|
|
FRHIBufferRange ScratchBufferRange;
|
|
ScratchBufferRange.Buffer = ScratchBuffer;
|
|
ScratchBufferRange.Offset = 0;
|
|
RHICmdList.BuildAccelerationStructures(BuildParams, ScratchBufferRange);
|
|
}
|
|
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
EndUpdate();
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
}
|
|
|
|
void FRayTracingDynamicGeometryUpdateManager::EndUpdate()
|
|
{
|
|
ReferencedUniformBuffers.Empty(ReferencedUniformBuffers.Max());
|
|
|
|
Clear();
|
|
}
|
|
|
|
uint32 FRayTracingDynamicGeometryUpdateManager::ComputeScratchBufferSize()
|
|
{
|
|
return ScratchBufferSize;
|
|
}
|
|
|
|
#undef USE_RAY_TRACING_DYNAMIC_GEOMETRY_PARALLEL_COMMAND_LISTS
|
|
|
|
#endif // RHI_RAYTRACING
|