1302 lines
48 KiB
C++
1302 lines
48 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RayTracingScene.h"
|
|
|
|
#if RHI_RAYTRACING
|
|
|
|
#include "RayTracingInstanceBufferUtil.h"
|
|
#include "RenderCore.h"
|
|
#include "RayTracingDefinitions.h"
|
|
#include "RenderGraphBuilder.h"
|
|
#include "RenderGraphUtils.h"
|
|
#include "RaytracingOptions.h"
|
|
#include "PrimitiveSceneProxy.h"
|
|
#include "SceneUniformBuffer.h"
|
|
#include "SceneRendering.h"
|
|
#include "RayTracingInstanceCulling.h"
|
|
#include "Rendering/RayTracingGeometryManager.h"
|
|
|
|
static TAutoConsoleVariable<int32> CVarRayTracingSceneBuildMode(
|
|
TEXT("r.RayTracing.Scene.BuildMode"),
|
|
1,
|
|
TEXT("Controls the mode in which ray tracing scene is built:\n")
|
|
TEXT(" 0: Fast build\n")
|
|
TEXT(" 1: Fast trace (default)\n"),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability
|
|
);
|
|
|
|
static TAutoConsoleVariable<bool> CVarRayTracingSceneUseTracingFeedback(
|
|
TEXT("r.RayTracing.Scene.UseTracingFeedback"),
|
|
false,
|
|
TEXT("When set to true, will only schedule updates of dynamic geometry instances that were hit in the previous frame."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<bool> CVarRayTracingSceneBatchedBuild(
|
|
TEXT("r.RayTracing.Scene.BatchedBuild"),
|
|
true,
|
|
TEXT("Whether to batch TLAS builds. Should be kept enabled since batched builds reduce barriers on GPU."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<bool> CVarRayTracingSceneCompactInstances(
|
|
TEXT("r.RayTracing.Scene.CompactInstances"),
|
|
false,
|
|
TEXT("Whether to compact the instance buffer so it only contains active instances.\n")
|
|
TEXT("On platforms that don't support indirect TLAS build this requires doing a GPU->CPU readback, ")
|
|
TEXT("which lead so instances missing from TLAS due to the extra latency.\n")
|
|
TEXT("r.RayTracing.Scene.CompactInstances.Min and r.RayTracing.Scene.CompactInstances.Margin can be used to avoid those issues."),
|
|
ECVF_RenderThreadSafe | ECVF_Scalability
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRayTracingSceneCompactInstancesMin(
|
|
TEXT("r.RayTracing.Scene.CompactInstances.Min"),
|
|
0,
|
|
TEXT("Minimum of instances in the instance buffer when using compaction.\n")
|
|
TEXT("Should be set to the expected high water mark to avoid issues on platforms that don't support indirect TLAS build."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static TAutoConsoleVariable<int32> CVarRayTracingSceneCompactInstancesMargin(
|
|
TEXT("r.RayTracing.Scene.CompactInstances.Margin"),
|
|
5000,
|
|
TEXT("Margin applied on top of lastest number of active instances readback from GPU to avoid issues when number instances increases from frame to frame."),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
|
|
static bool GRayTracingSerializeSceneNextFrame = false;
|
|
|
|
static FAutoConsoleCommand RayTracingSerializeSceneCmd(
|
|
TEXT("r.RayTracing.Scene.SerializeOnce"),
|
|
TEXT("Serialize Ray Tracing Scene to disk."),
|
|
FConsoleCommandDelegate::CreateStatic([] { GRayTracingSerializeSceneNextFrame = true; }));
|
|
|
|
#endif
|
|
|
|
bool IsRayTracingFeedbackEnabled(const FSceneViewFamily& ViewFamily)
|
|
{
|
|
// TODO: For now Feedback is limited to inline passes
|
|
return !HasRayTracedOverlay(ViewFamily) && CVarRayTracingSceneUseTracingFeedback.GetValueOnRenderThread() && GRHISupportsInlineRayTracing;
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FBuildInstanceBufferPassParams, )
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, InstanceBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, HitGroupContributionsBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, OutputStats)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, InstanceExtraDataBuffer)
|
|
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene)
|
|
SHADER_PARAMETER_STRUCT_INCLUDE(FGPUSceneResourceParameters, GPUSceneParameters)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
const FRayTracingScene::FInstanceHandle FRayTracingScene::INVALID_INSTANCE_HANDLE = FInstanceHandle();
|
|
const FRayTracingScene::FViewHandle FRayTracingScene::INVALID_VIEW_HANDLE = FViewHandle();
|
|
|
|
using FInstanceBufferStats = uint32;
|
|
|
|
FRayTracingScene::FRayTracingScene()
|
|
{
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
Layers.AddDefaulted(NumLayers);
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
Layer.Name = FName(FString::Printf(TEXT("RayTracingScene_Layer%u"), LayerIndex));
|
|
}
|
|
}
|
|
|
|
FRayTracingScene::~FRayTracingScene()
|
|
{
|
|
ReleaseFeedbackReadbackBuffers();
|
|
ReleaseReadbackBuffers();
|
|
}
|
|
|
|
void FRayTracingScene::BuildInitializationData(bool bUseLightingChannels, bool bForceOpaque, bool bDisableTriangleCull)
|
|
{
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
for (int32 ViewIndex : ActiveViews)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
|
|
FRayTracingInstanceBufferBuilderInitializer Initializer;
|
|
Initializer.Instances = Layer.Instances;
|
|
Initializer.VisibleInstances = LayerView.VisibleInstances;
|
|
Initializer.PreViewTranslation = ViewParameters[ViewIndex].PreViewTranslation;
|
|
Initializer.bUseLightingChannels = bUseLightingChannels;
|
|
Initializer.bForceOpaque = bForceOpaque;
|
|
Initializer.bDisableTriangleCull = bDisableTriangleCull;
|
|
|
|
LayerView.InstanceBufferBuilder.Init(MoveTemp(Initializer));
|
|
}
|
|
}
|
|
|
|
bInitializationDataBuilt = true;
|
|
}
|
|
|
|
FRayTracingScene::FViewHandle FRayTracingScene::AddView(uint32 ViewKey)
|
|
{
|
|
if (ViewIndexMap.Contains(ViewKey))
|
|
{
|
|
const int32 ViewIndex = ViewIndexMap[ViewKey];
|
|
check(ActiveViews.IsValidIndex(ViewIndex) && ActiveViews[ViewIndex] == ViewIndex);
|
|
return FViewHandle(ViewIndex);
|
|
}
|
|
|
|
const int32 ViewIndex = ActiveViews.Add(0);
|
|
ActiveViews[ViewIndex] = ViewIndex;
|
|
|
|
if (ViewKey == 0)
|
|
{
|
|
// Transient View (eg: no ViewState) are removed at the end of the frame
|
|
TransientViewIndices.Add(ViewIndex);
|
|
}
|
|
else
|
|
{
|
|
ViewIndexMap.Add(ViewKey, ViewIndex);
|
|
}
|
|
|
|
if (ViewParameters.Num() < ViewIndex + 1)
|
|
{
|
|
ViewParameters.SetNum(ViewIndex + 1);
|
|
}
|
|
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
if (Layer.Views.Num() < ViewIndex + 1)
|
|
{
|
|
Layer.Views.SetNum(ViewIndex + 1);
|
|
}
|
|
}
|
|
|
|
return FViewHandle(ViewIndex);
|
|
}
|
|
|
|
void FRayTracingScene::RemoveView(uint32 ViewKey)
|
|
{
|
|
if (!ViewIndexMap.Contains(ViewKey))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int32 ViewIndex = ViewIndexMap[ViewKey];
|
|
check(ActiveViews.IsValidIndex(ViewIndex) && ActiveViews[ViewIndex] == ViewIndex);
|
|
|
|
// clear ViewIndex in Layers Views
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
Layer.Views[ViewIndex] = {};
|
|
}
|
|
|
|
ViewParameters[ViewIndex] = {};
|
|
|
|
ActiveViews.RemoveAt(ViewIndex);
|
|
ViewIndexMap.Remove(ViewKey);
|
|
}
|
|
|
|
void FRayTracingScene::SetViewParams(FViewHandle ViewHandle, const FViewMatrices& ViewMatrices, const FRayTracingCullingParameters& CullingParameters)
|
|
{
|
|
ViewParameters[ViewHandle].CullingParameters = &CullingParameters;
|
|
ViewParameters[ViewHandle].PreViewTranslation = ViewMatrices.GetPreViewTranslation();
|
|
}
|
|
|
|
void FRayTracingScene::Update(FRDGBuilder& GraphBuilder, FSceneUniformBuffer& SceneUniformBuffer, const FGPUScene* GPUScene, ERDGPassFlags ComputePassFlags)
|
|
{
|
|
// Round up buffer sizes to some multiple to avoid pathological growth reallocations.
|
|
static constexpr uint32 AllocationGranularity = 8 * 1024;
|
|
static constexpr uint64 BufferAllocationGranularity = 16 * 1024 * 1024;
|
|
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FRayTracingScene::Update);
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_RayTracingScene_Update);
|
|
|
|
const ERayTracingAccelerationStructureFlags BuildFlags = CVarRayTracingSceneBuildMode.GetValueOnRenderThread()
|
|
? ERayTracingAccelerationStructureFlags::FastTrace
|
|
: ERayTracingAccelerationStructureFlags::FastBuild;
|
|
|
|
checkf(bInitializationDataBuilt, TEXT("BuildInitializationData(...) must be called before Update(...)."));
|
|
|
|
bUsedThisFrame = true;
|
|
|
|
FRHICommandListBase& RHICmdList = GraphBuilder.RHICmdList;
|
|
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
const uint32 MaxNumViews = ActiveViews.GetMaxIndex();
|
|
|
|
FRDGBufferUAVRef InstanceStatsBufferUAV = nullptr;
|
|
{
|
|
// one counter per layer in the stats buffer
|
|
FRDGBufferDesc InstanceStatsBufferDesc = FRDGBufferDesc::CreateStructuredDesc(sizeof(FInstanceBufferStats), NumLayers * MaxNumViews);
|
|
InstanceStatsBufferDesc.Usage |= BUF_SourceCopy;
|
|
|
|
InstanceStatsBuffer = GraphBuilder.CreateBuffer(InstanceStatsBufferDesc, TEXT("FRayTracingScene::InstanceStatsBuffer"));
|
|
InstanceStatsBufferUAV = GraphBuilder.CreateUAV(InstanceStatsBuffer);
|
|
|
|
AddClearUAVPass(GraphBuilder, InstanceStatsBufferUAV, 0, ComputePassFlags);
|
|
}
|
|
|
|
const bool bCompactInstanceBuffer = CVarRayTracingSceneCompactInstances.GetValueOnRenderThread();
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
for (int32 ViewIndex : ActiveViews)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
|
|
uint32 NumNativeInstances = LayerView.InstanceBufferBuilder.GetMaxNumInstances();
|
|
|
|
if (bCompactInstanceBuffer)
|
|
{
|
|
NumNativeInstances = FMath::Max<uint32>(CVarRayTracingSceneCompactInstancesMin.GetValueOnRenderThread(), LayerView.NumActiveInstances + CVarRayTracingSceneCompactInstancesMargin.GetValueOnRenderThread());
|
|
NumNativeInstances = FMath::Min<uint32>(NumNativeInstances, LayerView.InstanceBufferBuilder.GetMaxNumInstances());
|
|
}
|
|
|
|
LayerView.MaxNumInstances = NumNativeInstances;
|
|
|
|
const uint32 NumNativeInstancesAligned = FMath::DivideAndRoundUp(FMath::Max(NumNativeInstances, 1U), AllocationGranularity) * AllocationGranularity;
|
|
|
|
{
|
|
FRayTracingSceneInitializer Initializer;
|
|
Initializer.DebugName = Layer.Name; // TODO: also include ViewIndex in the name
|
|
Initializer.MaxNumInstances = NumNativeInstances;
|
|
Initializer.BuildFlags = BuildFlags;
|
|
LayerView.RayTracingSceneRHI = RHICreateRayTracingScene(MoveTemp(Initializer));
|
|
}
|
|
|
|
FRayTracingAccelerationStructureSize SizeInfo = LayerView.RayTracingSceneRHI->GetSizeInfo();
|
|
SizeInfo.ResultSize = FMath::DivideAndRoundUp(FMath::Max(SizeInfo.ResultSize, 1ull), BufferAllocationGranularity) * BufferAllocationGranularity;
|
|
|
|
// Allocate GPU buffer if current one is too small or significantly larger than what we need.
|
|
if (!LayerView.RayTracingScenePooledBuffer.IsValid()
|
|
|| SizeInfo.ResultSize > LayerView.RayTracingScenePooledBuffer->GetSize()
|
|
|| SizeInfo.ResultSize < LayerView.RayTracingScenePooledBuffer->GetSize() / 2)
|
|
{
|
|
FRDGBufferDesc Desc = FRDGBufferDesc::CreateBufferDesc(1, uint32(SizeInfo.ResultSize));
|
|
Desc.Usage = EBufferUsageFlags::AccelerationStructure;
|
|
|
|
LayerView.RayTracingScenePooledBuffer = AllocatePooledBuffer(Desc, TEXT("FRayTracingScene::SceneBuffer"));
|
|
}
|
|
|
|
LayerView.RayTracingSceneBufferRDG = GraphBuilder.RegisterExternalBuffer(LayerView.RayTracingScenePooledBuffer);
|
|
LayerView.RayTracingSceneBufferSRV = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(LayerView.RayTracingSceneBufferRDG, LayerView.RayTracingSceneRHI, 0));
|
|
|
|
{
|
|
const uint64 ScratchAlignment = GRHIRayTracingScratchBufferAlignment;
|
|
FRDGBufferDesc ScratchBufferDesc;
|
|
ScratchBufferDesc.Usage = EBufferUsageFlags::RayTracingScratch | EBufferUsageFlags::StructuredBuffer;
|
|
ScratchBufferDesc.BytesPerElement = uint32(ScratchAlignment);
|
|
ScratchBufferDesc.NumElements = uint32(FMath::DivideAndRoundUp(SizeInfo.BuildScratchSize, ScratchAlignment));
|
|
|
|
LayerView.BuildScratchBuffer = GraphBuilder.CreateBuffer(ScratchBufferDesc, TEXT("FRayTracingScene::ScratchBuffer"));
|
|
}
|
|
|
|
{
|
|
FRDGBufferDesc InstanceBufferDesc;
|
|
InstanceBufferDesc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
InstanceBufferDesc.BytesPerElement = GRHIRayTracingInstanceDescriptorSize;
|
|
InstanceBufferDesc.NumElements = NumNativeInstancesAligned;
|
|
|
|
LayerView.InstanceBuffer = GraphBuilder.CreateBuffer(InstanceBufferDesc, TEXT("FRayTracingScene::InstanceBuffer"));
|
|
|
|
if (bCompactInstanceBuffer)
|
|
{
|
|
// need to clear since FRayTracingBuildInstanceBufferCS will only write active instances
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(LayerView.InstanceBuffer), 0, ComputePassFlags);
|
|
}
|
|
}
|
|
|
|
if(GRHIGlobals.RayTracing.RequiresSeparateHitGroupContributionsBuffer)
|
|
{
|
|
FRDGBufferDesc HitGroupContributionsDesc;
|
|
HitGroupContributionsDesc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
HitGroupContributionsDesc.BytesPerElement = 4;
|
|
HitGroupContributionsDesc.NumElements = NumNativeInstancesAligned;
|
|
|
|
LayerView.HitGroupContributionsBuffer = GraphBuilder.CreateBuffer(HitGroupContributionsDesc, TEXT("FRayTracingScene::HitGroupContributionsBuffer"));
|
|
}
|
|
|
|
// Feedback
|
|
if (bTracingFeedbackEnabled)
|
|
{
|
|
{
|
|
FRDGBufferDesc InstanceHitCountBufferDesc;
|
|
InstanceHitCountBufferDesc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
InstanceHitCountBufferDesc.BytesPerElement = sizeof(uint32);
|
|
InstanceHitCountBufferDesc.NumElements = NumNativeInstancesAligned;
|
|
|
|
LayerView.InstanceHitCountBuffer = GraphBuilder.CreateBuffer(InstanceHitCountBufferDesc, TEXT("FRayTracingScene::InstanceHitCount"));
|
|
LayerView.InstanceHitCountBufferUAV = GraphBuilder.CreateUAV(LayerView.InstanceHitCountBuffer, ERDGUnorderedAccessViewFlags::SkipBarrier);
|
|
AddClearUAVPass(GraphBuilder, LayerView.InstanceHitCountBufferUAV, 0, ComputePassFlags);
|
|
}
|
|
|
|
{
|
|
FRDGBufferDesc AccelerationStructureIndexBufferDesc;
|
|
AccelerationStructureIndexBufferDesc.Usage = EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
AccelerationStructureIndexBufferDesc.BytesPerElement = sizeof(uint32);
|
|
AccelerationStructureIndexBufferDesc.NumElements = NumNativeInstancesAligned;
|
|
|
|
LayerView.AccelerationStructureIndexBuffer = GraphBuilder.CreateBuffer(AccelerationStructureIndexBufferDesc, TEXT("FRayTracingScene::AccelerationStructureIndexBuffer"));
|
|
|
|
TConstArrayView<uint32> InstanceGeometryIndices = LayerView.InstanceBufferBuilder.GetInstanceGeometryIndices();
|
|
GraphBuilder.QueueBufferUpload(LayerView.AccelerationStructureIndexBuffer, InstanceGeometryIndices.GetData(), InstanceGeometryIndices.GetTypeSize() * InstanceGeometryIndices.Num());
|
|
}
|
|
}
|
|
|
|
FRDGBufferUAVRef InstanceExtraDataBufferUAV = nullptr;
|
|
if (bInstanceExtraDataBufferEnabled || bTracingFeedbackEnabled || bInstanceDebugDataEnabled)
|
|
{
|
|
FRDGBufferDesc InstanceExtraDataBufferDesc;
|
|
InstanceExtraDataBufferDesc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
InstanceExtraDataBufferDesc.BytesPerElement = sizeof(FRayTracingInstanceExtraData);
|
|
InstanceExtraDataBufferDesc.NumElements = FMath::Max(NumNativeInstances, 1u);
|
|
|
|
LayerView.InstanceExtraDataBuffer = GraphBuilder.CreateBuffer(InstanceExtraDataBufferDesc, TEXT("FRayTracingScene::InstanceExtraDataBuffer"));
|
|
InstanceExtraDataBufferUAV = GraphBuilder.CreateUAV(LayerView.InstanceExtraDataBuffer);
|
|
|
|
AddClearUAVPass(GraphBuilder, InstanceExtraDataBufferUAV, 0xFFFFFFFF, ComputePassFlags);
|
|
}
|
|
|
|
if (NumNativeInstances > 0)
|
|
{
|
|
// Fill instance upload buffer on separate thread since results are only needed in RHI thread
|
|
GraphBuilder.AddCommandListSetupTask([&InstanceBufferBuilder = LayerView.InstanceBufferBuilder](FRHICommandList& RHICmdList)
|
|
{
|
|
FTaskTagScope TaskTagScope(ETaskTag::EParallelRenderingThread);
|
|
|
|
InstanceBufferBuilder.FillRayTracingInstanceUploadBuffer(RHICmdList);
|
|
});
|
|
|
|
GraphBuilder.AddCommandListSetupTask([&InstanceBufferBuilder = LayerView.InstanceBufferBuilder](FRHICommandList& RHICmdList)
|
|
{
|
|
FTaskTagScope TaskTagScope(ETaskTag::EParallelRenderingThread);
|
|
|
|
InstanceBufferBuilder.FillAccelerationStructureAddressesBuffer(RHICmdList);
|
|
});
|
|
|
|
#if STATS
|
|
const bool bStatsEnabled = true;
|
|
#else
|
|
const bool bStatsEnabled = false;
|
|
#endif
|
|
|
|
{
|
|
FBuildInstanceBufferPassParams* PassParams = GraphBuilder.AllocParameters<FBuildInstanceBufferPassParams>();
|
|
PassParams->InstanceBuffer = GraphBuilder.CreateUAV(LayerView.InstanceBuffer);
|
|
|
|
if(GRHIGlobals.RayTracing.RequiresSeparateHitGroupContributionsBuffer)
|
|
{
|
|
PassParams->HitGroupContributionsBuffer = GraphBuilder.CreateUAV(LayerView.HitGroupContributionsBuffer);
|
|
}
|
|
|
|
PassParams->InstanceExtraDataBuffer = InstanceExtraDataBufferUAV;
|
|
PassParams->Scene = SceneUniformBuffer.GetBuffer(GraphBuilder);
|
|
PassParams->GPUSceneParameters = GPUScene->GetShaderParameters(GraphBuilder);
|
|
PassParams->OutputStats = bCompactInstanceBuffer || bStatsEnabled ? InstanceStatsBufferUAV : nullptr;
|
|
|
|
const uint32 OutputStatsOffset = LayerIndex * MaxNumViews + ViewIndex;
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("RayTracingBuildInstanceBuffer"),
|
|
PassParams,
|
|
ComputePassFlags,
|
|
[PassParams,
|
|
&InstanceBufferBuilder = LayerView.InstanceBufferBuilder,
|
|
OutputStatsOffset,
|
|
GPUScene,
|
|
CullingParameters = ViewParameters[ViewIndex].CullingParameters,
|
|
NumNativeInstances,
|
|
bCompactInstanceBuffer
|
|
](FRHICommandList& RHICmdList)
|
|
{
|
|
FGPUSceneResourceParametersRHI GPUSceneParameters;
|
|
GPUSceneParameters.CommonParameters = PassParams->GPUSceneParameters.CommonParameters;
|
|
GPUSceneParameters.GPUSceneInstanceSceneData = PassParams->GPUSceneParameters.GPUSceneInstanceSceneData->GetRHI();
|
|
GPUSceneParameters.GPUSceneInstancePayloadData = PassParams->GPUSceneParameters.GPUSceneInstancePayloadData->GetRHI();
|
|
GPUSceneParameters.GPUScenePrimitiveSceneData = PassParams->GPUSceneParameters.GPUScenePrimitiveSceneData->GetRHI();
|
|
GPUSceneParameters.GPUSceneLightmapData = PassParams->GPUSceneParameters.GPUSceneLightmapData->GetRHI();
|
|
GPUSceneParameters.GPUSceneLightData = PassParams->GPUSceneParameters.GPUSceneLightData->GetRHI();
|
|
|
|
InstanceBufferBuilder.BuildRayTracingInstanceBuffer(
|
|
RHICmdList,
|
|
GPUScene,
|
|
&GPUSceneParameters,
|
|
CullingParameters,
|
|
PassParams->InstanceBuffer->GetRHI(),
|
|
GRHIGlobals.RayTracing.RequiresSeparateHitGroupContributionsBuffer ? PassParams->HitGroupContributionsBuffer->GetRHI() : nullptr,
|
|
NumNativeInstances,
|
|
bCompactInstanceBuffer,
|
|
PassParams->OutputStats ? PassParams->OutputStats->GetRHI() : nullptr,
|
|
OutputStatsOffset,
|
|
PassParams->InstanceExtraDataBuffer ? PassParams->InstanceExtraDataBuffer->GetRHI() : nullptr);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Feedback
|
|
if (bTracingFeedbackEnabled)
|
|
{
|
|
FRDGBufferDesc GeometryHandleBufferDesc;
|
|
GeometryHandleBufferDesc.Usage = EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
GeometryHandleBufferDesc.BytesPerElement = sizeof(int32);
|
|
GeometryHandleBufferDesc.NumElements = FMath::Max(Layer.GeometryHandles.Num(), 1);
|
|
|
|
Layer.GeometryHandleBuffer = GraphBuilder.CreateBuffer(GeometryHandleBufferDesc, TEXT("FRayTracingScene::GeometryHandleBuffer"));
|
|
GraphBuilder.QueueBufferUpload(Layer.GeometryHandleBuffer, Layer.GeometryHandles.GetData(), Layer.GeometryHandles.GetTypeSize() * Layer.GeometryHandles.Num());
|
|
}
|
|
|
|
if (Layer.InstancesDebugData.Num() > 0)
|
|
{
|
|
check(Layer.InstancesDebugData.Num() == Layer.Instances.Num());
|
|
|
|
Layer.InstanceDebugBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("FRayTracingScene::InstanceDebugData"), Layer.InstancesDebugData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FRayTracingSceneBuildPassParams, )
|
|
RDG_BUFFER_ACCESS_ARRAY(TLASBuildBuffers)
|
|
RDG_BUFFER_ACCESS(DynamicGeometryScratchBuffer, ERHIAccess::UAVCompute)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FRayTracingSceneSerializePassParams, )
|
|
RDG_BUFFER_ACCESS(TLASBuffer, ERHIAccess::BVHRead)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FRayTracingScene::Build(FRDGBuilder& GraphBuilder, ERDGPassFlags ComputePassFlags, FRDGBufferRef DynamicGeometryScratchBuffer)
|
|
{
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
|
|
FRayTracingSceneBuildPassParams* PassParams = GraphBuilder.AllocParameters<FRayTracingSceneBuildPassParams>();
|
|
PassParams->DynamicGeometryScratchBuffer = DynamicGeometryScratchBuffer; // TODO: Is this necessary?
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
for (int32 ViewIndex : ActiveViews)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
|
|
PassParams->TLASBuildBuffers.Emplace(LayerView.BuildScratchBuffer, ERHIAccess::UAVCompute);
|
|
PassParams->TLASBuildBuffers.Emplace(LayerView.InstanceBuffer, ERHIAccess::SRVCompute);
|
|
|
|
if(GRHIGlobals.RayTracing.RequiresSeparateHitGroupContributionsBuffer)
|
|
{
|
|
PassParams->TLASBuildBuffers.Emplace(LayerView.HitGroupContributionsBuffer, ERHIAccess::SRVCompute);
|
|
}
|
|
|
|
PassParams->TLASBuildBuffers.Emplace(LayerView.RayTracingSceneBufferRDG, ERHIAccess::BVHWrite);
|
|
}
|
|
}
|
|
|
|
GraphBuilder.AddPass(RDG_EVENT_NAME("RayTracingBuildScene"), PassParams, ComputePassFlags,
|
|
[PassParams, this](FRHICommandList& RHICmdList)
|
|
{
|
|
const bool bUseBatchedBuild = CVarRayTracingSceneBatchedBuild.GetValueOnRenderThread();
|
|
|
|
TArray<FRayTracingSceneBuildParams> BatchedBuildParams;
|
|
BatchedBuildParams.Reserve(NumLayers); // TODO: should also take num views into account
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
for (int32 ViewIndex : ActiveViews)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
|
|
FRayTracingSceneBuildParams BuildParams;
|
|
BuildParams.Scene = LayerView.RayTracingSceneRHI;
|
|
BuildParams.ScratchBuffer = LayerView.BuildScratchBuffer->GetRHI();
|
|
BuildParams.ScratchBufferOffset = 0;
|
|
BuildParams.InstanceBuffer = LayerView.InstanceBuffer->GetRHI();
|
|
BuildParams.InstanceBufferOffset = 0;
|
|
|
|
if(GRHIGlobals.RayTracing.RequiresSeparateHitGroupContributionsBuffer)
|
|
{
|
|
check(LayerView.HitGroupContributionsBuffer);
|
|
BuildParams.HitGroupContributionsBuffer = LayerView.HitGroupContributionsBuffer->GetRHI();
|
|
BuildParams.HitGroupContributionsBufferOffset = 0;
|
|
}
|
|
|
|
BuildParams.NumInstances = LayerView.MaxNumInstances;
|
|
BuildParams.ReferencedGeometries = LayerView.InstanceBufferBuilder.GetReferencedGeometries();
|
|
|
|
RHICmdList.BindAccelerationStructureMemory(LayerView.RayTracingSceneRHI, LayerView.RayTracingSceneBufferRDG->GetRHI(), 0);
|
|
|
|
if (bUseBatchedBuild)
|
|
{
|
|
BatchedBuildParams.Add(BuildParams);
|
|
}
|
|
else
|
|
{
|
|
RHICmdList.BuildAccelerationStructure(BuildParams);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bUseBatchedBuild)
|
|
{
|
|
RHICmdList.BuildAccelerationStructures(BatchedBuildParams);
|
|
}
|
|
});
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
if (GRayTracingSerializeSceneNextFrame && GRHIGlobals.RayTracing.SupportsSerializeAccelerationStructure)
|
|
{
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
for (int32 ViewIndex : ActiveViews)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
|
|
FRayTracingSceneSerializePassParams* SerializePassParams = GraphBuilder.AllocParameters<FRayTracingSceneSerializePassParams>();
|
|
SerializePassParams->TLASBuffer = LayerView.RayTracingSceneBufferRDG;
|
|
|
|
GraphBuilder.AddPass(RDG_EVENT_NAME("RayTracingSerializeScene"), SerializePassParams, ERDGPassFlags::Readback,
|
|
[SerializePassParams, &Layer, &LayerView, ViewIndex](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FString Filename = FString::Printf(TEXT("%s_%d_(%s)"), *Layer.Name.ToString(), ViewIndex, *FDateTime::Now().ToString(TEXT("%Y%m%d_%H%M%S")));
|
|
FString RootPath = FPaths::ScreenShotDir() + TEXT("BVH/"); // Save BVH dumps to ScreenShot directory
|
|
FString OutputFilename = RootPath + Filename + TEXT(".bvh");
|
|
|
|
RHICmdList.SerializeAccelerationStructure(LayerView.RayTracingSceneRHI, *OutputFilename);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
GRayTracingSerializeSceneNextFrame = false;
|
|
#endif // !UE_BUILD_SHIPPING
|
|
}
|
|
|
|
struct FRayTracingProcessFeedbackCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FRayTracingProcessFeedbackCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FRayTracingProcessFeedbackCS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, GeometryHitCountBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWGeometryHandleBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, RWGeometryHandleAllocator)
|
|
SHADER_PARAMETER(uint32, NumGeometries)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static constexpr uint32 ThreadGroupSize = 64;
|
|
|
|
static inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), ThreadGroupSize);
|
|
|
|
// Force DXC to avoid shader reflection issues.
|
|
OutEnvironment.CompilerFlags.Add(CFLAG_ForceDXC);
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsRayTracingEnabledForProject(Parameters.Platform);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FRayTracingProcessFeedbackCS, "/Engine/Private/Raytracing/RayTracingFeedback.usf", "RayTracingProcessFeedbackCS", SF_Compute);
|
|
|
|
struct FRayTracingUpdateGeometryHitCountCS : public FGlobalShader
|
|
{
|
|
DECLARE_GLOBAL_SHADER(FRayTracingUpdateGeometryHitCountCS);
|
|
SHADER_USE_PARAMETER_STRUCT(FRayTracingUpdateGeometryHitCountCS, FGlobalShader);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, GeometryHandleBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, InstanceHitCountBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWGeometryHitCountBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWGeometryHandleBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, AccelerationStructureIndexBuffer)
|
|
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, InstanceExtraDataBuffer)
|
|
|
|
SHADER_PARAMETER(uint32, NumInstances)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
static constexpr uint32 ThreadGroupSize = 64;
|
|
|
|
static inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
|
|
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), ThreadGroupSize);
|
|
|
|
// Force DXC to avoid shader reflection issues.
|
|
OutEnvironment.CompilerFlags.Add(CFLAG_ForceDXC);
|
|
}
|
|
|
|
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
|
|
{
|
|
return IsRayTracingEnabledForProject(Parameters.Platform);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FRayTracingUpdateGeometryHitCountCS, "/Engine/Private/Raytracing/RayTracingFeedback.usf", "RayTracingUpdateGeometryHitCountCS", SF_Compute);
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FFeedbackReadbackPassParameters, )
|
|
RDG_BUFFER_ACCESS(HandleBuffer, ERHIAccess::CopySrc)
|
|
RDG_BUFFER_ACCESS(CountBuffer, ERHIAccess::CopySrc)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
void FRayTracingScene::FinishTracingFeedback(FRDGBuilder& GraphBuilder, ERDGPassFlags ComputePassFlags)
|
|
{
|
|
RDG_EVENT_SCOPE(GraphBuilder, "RayTracingScene::FinishTracingFeedback");
|
|
|
|
if (!bTracingFeedbackEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FLayer& Layer = Layers[0];
|
|
const FLayerView& LayerView = Layer.Views[0];
|
|
const uint32 NumGeometries = (uint32)LayerView.InstanceBufferBuilder.GetReferencedGeometries().Num();
|
|
const uint32 NumInstances = LayerView.InstanceBufferBuilder.GetMaxNumInstances();
|
|
|
|
if (NumGeometries == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FRDGBufferRef GeometryHandleBuffer;
|
|
|
|
FRDGBufferDesc GeometryHandleBufferDesc;
|
|
GeometryHandleBufferDesc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer | EBufferUsageFlags::SourceCopy;
|
|
GeometryHandleBufferDesc.BytesPerElement = sizeof(int32);
|
|
GeometryHandleBufferDesc.NumElements = NumGeometries;
|
|
|
|
GeometryHandleBuffer = GraphBuilder.CreateBuffer(GeometryHandleBufferDesc, TEXT("FRayTracingScene::GeometryHandleBuffer"));
|
|
|
|
// Update geometry hit count
|
|
FRDGBufferRef GeometryHitCountBuffer;
|
|
{
|
|
FRDGBufferDesc GeometryHitCountBufferDesc;
|
|
GeometryHitCountBufferDesc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
GeometryHitCountBufferDesc.BytesPerElement = sizeof(uint32);
|
|
GeometryHitCountBufferDesc.NumElements = NumGeometries;
|
|
|
|
GeometryHitCountBuffer = GraphBuilder.CreateBuffer(GeometryHitCountBufferDesc, TEXT("FRayTracingScene::GeometryHitCountBuffer"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(GeometryHitCountBuffer), 0, ComputePassFlags);
|
|
|
|
FRayTracingUpdateGeometryHitCountCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRayTracingUpdateGeometryHitCountCS::FParameters>();
|
|
|
|
PassParameters->GeometryHandleBuffer = GraphBuilder.CreateSRV(Layer.GeometryHandleBuffer);
|
|
PassParameters->AccelerationStructureIndexBuffer = GraphBuilder.CreateSRV(LayerView.AccelerationStructureIndexBuffer);
|
|
PassParameters->InstanceHitCountBuffer = GraphBuilder.CreateSRV(LayerView.InstanceHitCountBuffer);
|
|
PassParameters->RWGeometryHitCountBuffer = GraphBuilder.CreateUAV(GeometryHitCountBuffer);
|
|
PassParameters->RWGeometryHandleBuffer = GraphBuilder.CreateUAV(GeometryHandleBuffer);
|
|
PassParameters->InstanceExtraDataBuffer = GraphBuilder.CreateSRV(LayerView.InstanceExtraDataBuffer);
|
|
PassParameters->NumInstances = NumInstances;
|
|
|
|
const FIntVector GroupSize = FComputeShaderUtils::GetGroupCountWrapped(NumInstances, FRayTracingUpdateGeometryHitCountCS::ThreadGroupSize);
|
|
|
|
TShaderRef<FRayTracingUpdateGeometryHitCountCS> ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FRayTracingUpdateGeometryHitCountCS>();
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("FRayTracingScene::RayTracingUpdateGeometryHitCount"),
|
|
ComputePassFlags,
|
|
ComputeShader,
|
|
PassParameters,
|
|
GroupSize);
|
|
}
|
|
|
|
// Fill geometry handle buffer
|
|
FRDGBufferRef GeometryHandleAllocatorBuffer;
|
|
{
|
|
FRDGBufferDesc GeometryHandleAllocatorBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1);
|
|
GeometryHandleAllocatorBufferDesc.Usage = EBufferUsageFlags(GeometryHandleAllocatorBufferDesc.Usage | BUF_SourceCopy);
|
|
GeometryHandleAllocatorBuffer = GraphBuilder.CreateBuffer(GeometryHandleAllocatorBufferDesc, TEXT("FRayTracingScene::GeometryHandleAllocator"));
|
|
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(GeometryHandleAllocatorBuffer, PF_R32_UINT), 0, ComputePassFlags);
|
|
|
|
FRayTracingProcessFeedbackCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FRayTracingProcessFeedbackCS::FParameters>();
|
|
PassParameters->GeometryHitCountBuffer = GraphBuilder.CreateSRV(GeometryHitCountBuffer);
|
|
PassParameters->RWGeometryHandleBuffer = GraphBuilder.CreateUAV(GeometryHandleBuffer);
|
|
PassParameters->RWGeometryHandleAllocator = GraphBuilder.CreateUAV(GeometryHandleAllocatorBuffer, PF_R32_UINT);
|
|
PassParameters->NumGeometries = NumGeometries;
|
|
|
|
const FIntVector GroupSize = FComputeShaderUtils::GetGroupCountWrapped(NumGeometries, FRayTracingProcessFeedbackCS::ThreadGroupSize);
|
|
|
|
TShaderRef<FRayTracingProcessFeedbackCS> ComputeShader = GetGlobalShaderMap(GMaxRHIFeatureLevel)->GetShader<FRayTracingProcessFeedbackCS>();
|
|
FComputeShaderUtils::AddPass(
|
|
GraphBuilder,
|
|
RDG_EVENT_NAME("FRayTracingScene::FinishTracingFeedback"),
|
|
ComputePassFlags,
|
|
ComputeShader,
|
|
PassParameters,
|
|
GroupSize);
|
|
}
|
|
|
|
// Readback
|
|
// if necessary create readback buffers
|
|
if (FeedbackReadback.IsEmpty())
|
|
{
|
|
FeedbackReadback.SetNum(MaxReadbackBuffers);
|
|
|
|
for (uint32 Index = 0; Index < MaxReadbackBuffers; ++Index)
|
|
{
|
|
FeedbackReadback[Index].GeometryHandleReadbackBuffer = new FRHIGPUBufferReadback(TEXT("FRayTracingScene::FeedbackReadbackBuffer::GeometryHandles"));
|
|
FeedbackReadback[Index].GeometryCountReadbackBuffer = new FRHIGPUBufferReadback(TEXT("FRayTracingScene::FeedbackReadbackBuffer::GeometryCount"));
|
|
}
|
|
}
|
|
|
|
// process ready results
|
|
|
|
while (FeedbackReadbackNumPending > 0)
|
|
{
|
|
uint32 Index = (FeedbackReadbackWriteIndex + MaxReadbackBuffers - FeedbackReadbackNumPending) % MaxReadbackBuffers;
|
|
FRHIGPUBufferReadback* GeometryHandleReadbackBuffer = FeedbackReadback[Index].GeometryHandleReadbackBuffer;
|
|
FRHIGPUBufferReadback* GeometryCountReadbackBuffer = FeedbackReadback[Index].GeometryCountReadbackBuffer;
|
|
check(GeometryHandleReadbackBuffer->IsReady() == GeometryCountReadbackBuffer->IsReady());
|
|
if (GeometryHandleReadbackBuffer->IsReady() && GeometryCountReadbackBuffer->IsReady())
|
|
{
|
|
FeedbackReadbackNumPending--;
|
|
|
|
const uint32* GeometryCountPtr = (const uint32*)GeometryCountReadbackBuffer->Lock(sizeof(uint32));
|
|
const uint32 GeometryCount = GeometryCountPtr[0];
|
|
GeometryCountReadbackBuffer->Unlock();
|
|
|
|
const int32* GeometryHandlesPtr = (const int32*)GeometryHandleReadbackBuffer->Lock(sizeof(int32) * GeometryCount);
|
|
|
|
for (uint32 i = 0; i < GeometryCount; i++)
|
|
{
|
|
if (ensure(GeometryHandlesPtr[i] != INDEX_NONE))
|
|
{
|
|
GRayTracingGeometryManager->AddVisibleGeometry(GeometryHandlesPtr[i]);
|
|
}
|
|
}
|
|
|
|
GeometryHandleReadbackBuffer->Unlock();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//if (FeedbackReadbackNumPending < MaxReadbackBuffers) // TODO: need to prevent overwritng results?
|
|
{
|
|
// copy feedback to readback buffer
|
|
|
|
FFeedbackReadbackPassParameters* PassParameters = GraphBuilder.AllocParameters<FFeedbackReadbackPassParameters>();
|
|
PassParameters->HandleBuffer = GeometryHandleBuffer;
|
|
PassParameters->CountBuffer = GeometryHandleAllocatorBuffer;
|
|
|
|
GraphBuilder.AddPass(RDG_EVENT_NAME("FRayTracingScene::FeedbackReadback"), PassParameters, ERDGPassFlags::Readback,
|
|
[HandleReadbackBuffer = FeedbackReadback[FeedbackReadbackWriteIndex].GeometryHandleReadbackBuffer,
|
|
CountReadbackBuffer = FeedbackReadback[FeedbackReadbackWriteIndex].GeometryCountReadbackBuffer,
|
|
PassParameters]
|
|
(FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
HandleReadbackBuffer->EnqueueCopy(RHICmdList, PassParameters->HandleBuffer->GetRHI(), 0u);
|
|
CountReadbackBuffer->EnqueueCopy(RHICmdList, PassParameters->CountBuffer->GetRHI(), 0u);
|
|
});
|
|
|
|
FeedbackReadbackWriteIndex = (FeedbackReadbackWriteIndex + 1u) % MaxReadbackBuffers;
|
|
FeedbackReadbackNumPending = FMath::Min(FeedbackReadbackNumPending + 1u, MaxReadbackBuffers);
|
|
}
|
|
}
|
|
|
|
void FRayTracingScene::FinishStats(FRDGBuilder& GraphBuilder, ERDGPassFlags ComputePassFlags)
|
|
{
|
|
// if necessary create readback buffers
|
|
if (StatsReadback.IsEmpty())
|
|
{
|
|
StatsReadback.SetNum(MaxReadbackBuffers);
|
|
|
|
for (uint32 Index = 0; Index < MaxReadbackBuffers; ++Index)
|
|
{
|
|
StatsReadback[Index].ReadbackBuffer = new FRHIGPUBufferReadback(TEXT("FRayTracingScene::StatsReadbackBuffer"));
|
|
}
|
|
}
|
|
|
|
uint32 TotalNumNativeInstances = 0;
|
|
uint32 TotalNumActiveInstances = 0;
|
|
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
|
|
// process ready results
|
|
while (StatsReadbackNumPending > 0)
|
|
{
|
|
uint32 Index = (StatsReadbackWriteIndex + MaxReadbackBuffers - StatsReadbackNumPending) % MaxReadbackBuffers;
|
|
FStatsReadbackData& ReadbackData = StatsReadback[Index];
|
|
if (ReadbackData.ReadbackBuffer->IsReady())
|
|
{
|
|
StatsReadbackNumPending--;
|
|
|
|
auto ReadbackBufferPtr = (const FInstanceBufferStats*)ReadbackData.ReadbackBuffer->Lock(sizeof(FInstanceBufferStats) * NumLayers * ReadbackData.MaxNumViews);
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
for (int32 ViewIndex : ActiveViews)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
|
|
const uint32 LayerViewNumNativeInstances = LayerView.InstanceBufferBuilder.GetMaxNumInstances();
|
|
|
|
LayerView.NumActiveInstances = FMath::Min(ReadbackBufferPtr[LayerIndex * ReadbackData.MaxNumViews + ViewIndex], LayerViewNumNativeInstances);
|
|
|
|
TotalNumNativeInstances += LayerViewNumNativeInstances;
|
|
TotalNumActiveInstances += LayerView.NumActiveInstances;
|
|
}
|
|
}
|
|
|
|
ReadbackData.ReadbackBuffer->Unlock();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
SET_DWORD_STAT(STAT_RayTracingTotalInstances, TotalNumNativeInstances);
|
|
SET_DWORD_STAT(STAT_RayTracingActiveInstances, TotalNumActiveInstances);
|
|
|
|
// copy stats to readback buffer
|
|
if (InstanceStatsBuffer != nullptr && StatsReadbackNumPending < MaxReadbackBuffers)
|
|
{
|
|
AddReadbackBufferPass(GraphBuilder, RDG_EVENT_NAME("FRayTracingScene::StatsReadback"), InstanceStatsBuffer,
|
|
[&ReadbackData = StatsReadback[StatsReadbackWriteIndex], InstanceStatsBuffer = InstanceStatsBuffer](FRDGAsyncTask, FRHICommandList& RHICmdList)
|
|
{
|
|
ReadbackData.ReadbackBuffer->EnqueueCopy(RHICmdList, InstanceStatsBuffer->GetRHI(), 0u);
|
|
});
|
|
|
|
StatsReadback[StatsReadbackWriteIndex].MaxNumViews = ActiveViews.GetMaxIndex();
|
|
|
|
StatsReadbackWriteIndex = (StatsReadbackWriteIndex + 1u) % MaxReadbackBuffers;
|
|
StatsReadbackNumPending = FMath::Min(StatsReadbackNumPending + 1u, MaxReadbackBuffers);
|
|
}
|
|
}
|
|
|
|
void FRayTracingScene::PostRender(FRDGBuilder& GraphBuilder, ERDGPassFlags ComputePassFlags)
|
|
{
|
|
FinishTracingFeedback(GraphBuilder, ComputePassFlags);
|
|
FinishStats(GraphBuilder, ComputePassFlags);
|
|
}
|
|
|
|
bool FRayTracingScene::IsCreated() const
|
|
{
|
|
return bUsedThisFrame;
|
|
}
|
|
|
|
FRHIRayTracingScene* FRayTracingScene::GetRHIRayTracingScene(ERayTracingSceneLayer InLayer, FViewHandle InViewHandle) const
|
|
{
|
|
return Layers[uint8(InLayer)].Views[InViewHandle].RayTracingSceneRHI.GetReference();
|
|
}
|
|
|
|
FRHIRayTracingScene* FRayTracingScene::GetRHIRayTracingSceneChecked(ERayTracingSceneLayer InLayer, FViewHandle InViewHandle) const
|
|
{
|
|
FRHIRayTracingScene* Result = GetRHIRayTracingScene(InLayer, InViewHandle);
|
|
checkf(Result, TEXT("Ray tracing scene was not created. Perhaps Update() was not called."));
|
|
return Result;
|
|
}
|
|
|
|
FShaderResourceViewRHIRef FRayTracingScene::CreateLayerViewRHI(FRHICommandListBase& RHICmdList, ERayTracingSceneLayer InLayer, FViewHandle InViewHandle) const
|
|
{
|
|
const FLayerView& LayerView = Layers[uint8(InLayer)].Views[InViewHandle];
|
|
checkf(LayerView.RayTracingScenePooledBuffer, TEXT("Ray tracing scene was not created. Perhaps Update() was not called."));
|
|
return RHICmdList.CreateShaderResourceView(FShaderResourceViewInitializer(LayerView.RayTracingScenePooledBuffer->GetRHI(), LayerView.RayTracingSceneRHI, 0));
|
|
}
|
|
|
|
FRDGBufferSRVRef FRayTracingScene::GetLayerView(ERayTracingSceneLayer InLayer, FViewHandle InViewHandle) const
|
|
{
|
|
const FLayerView& LayerView = Layers[uint8(InLayer)].Views[InViewHandle];
|
|
checkf(LayerView.RayTracingSceneBufferSRV, TEXT("Ray tracing scene SRV was not created. Perhaps Update() was not called."));
|
|
return LayerView.RayTracingSceneBufferSRV;
|
|
}
|
|
|
|
FRDGBufferUAVRef FRayTracingScene::GetInstanceHitCountBufferUAV(ERayTracingSceneLayer InLayer, FViewHandle InViewHandle) const
|
|
{
|
|
return bTracingFeedbackEnabled ? Layers[uint8(InLayer)].Views[InViewHandle].InstanceHitCountBufferUAV : nullptr;
|
|
}
|
|
|
|
uint32 FRayTracingScene::GetNumNativeInstances(ERayTracingSceneLayer InLayer, FViewHandle InViewHandle) const
|
|
{
|
|
const FLayerView& LayerView = Layers[uint8(InLayer)].Views[InViewHandle];
|
|
checkf(bInitializationDataBuilt, TEXT("Must call BuildInitializationData() or Update() before using GetNumNativeInstances()."));
|
|
return LayerView.InstanceBufferBuilder.GetMaxNumInstances();
|
|
}
|
|
|
|
FRayTracingScene::FInstanceHandle FRayTracingScene::AddCachedInstance(FRayTracingGeometryInstance Instance, ERayTracingSceneLayer InLayer, const FPrimitiveSceneProxy* Proxy, bool bDynamic, int32 InGeometryHandle)
|
|
{
|
|
ensure(!bCachedInstancesLocked);
|
|
|
|
FLayer& Layer = Layers[uint8(InLayer)];
|
|
|
|
FRHIRayTracingGeometry* GeometryRHI = Instance.GeometryRHI;
|
|
|
|
uint32 InstanceIndex = UINT32_MAX;
|
|
|
|
if (Layer.CachedInstancesFreeList.IsEmpty())
|
|
{
|
|
InstanceIndex = Layer.Instances.Add(MoveTemp(Instance));
|
|
}
|
|
else
|
|
{
|
|
InstanceIndex = Layer.CachedInstancesFreeList.Pop(EAllowShrinking::No);
|
|
Layer.Instances[InstanceIndex] = MoveTemp(Instance);
|
|
}
|
|
|
|
++Layer.NumCachedInstances;
|
|
|
|
if (bTracingFeedbackEnabled)
|
|
{
|
|
int32& GeometryHandle = Layer.GeometryHandles.IsValidIndex(InstanceIndex) ? Layer.GeometryHandles[InstanceIndex] : Layer.GeometryHandles.AddDefaulted_GetRef();
|
|
GeometryHandle = InGeometryHandle;
|
|
check(Layer.Instances.Num() == Layer.GeometryHandles.Num());
|
|
}
|
|
|
|
if (bInstanceDebugDataEnabled)
|
|
{
|
|
FRayTracingInstanceDebugData& InstanceDebugData = Layer.InstancesDebugData.IsValidIndex(InstanceIndex) ? Layer.InstancesDebugData[InstanceIndex] : Layer.InstancesDebugData.AddDefaulted_GetRef();
|
|
InstanceDebugData.Flags = bDynamic ? 1 : 0;
|
|
InstanceDebugData.GeometryAddress = uint64(GeometryRHI);
|
|
InstanceDebugData.ProxyHash = Proxy ? Proxy->GetTypeHash() : 0;
|
|
|
|
check(Layer.Instances.Num() == Layer.InstancesDebugData.Num());
|
|
}
|
|
|
|
return { InLayer, InstanceIndex };
|
|
}
|
|
|
|
void FRayTracingScene::FreeCachedInstance(FInstanceHandle Handle)
|
|
{
|
|
ensure(!bCachedInstancesLocked);
|
|
|
|
if (!Handle.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FLayer& Layer = Layers[uint8(Handle.Layer)];
|
|
|
|
Layer.Instances[Handle.Index] = {};
|
|
Layer.CachedInstancesFreeList.Push(Handle.Index);
|
|
--Layer.NumCachedInstances;
|
|
}
|
|
|
|
void FRayTracingScene::FreeCachedInstance(uint32 PackedHandle)
|
|
{
|
|
if (PackedHandle == UINT32_MAX)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreeCachedInstance(FInstanceHandle(PackedHandle));
|
|
}
|
|
|
|
void FRayTracingScene::UpdateCachedInstanceGeometry(FInstanceHandle Handle, FRHIRayTracingGeometry* GeometryRHI, int32 InstanceContributionToHitGroupIndex)
|
|
{
|
|
FLayer& Layer = Layers[uint8(Handle.Layer)];
|
|
Layer.Instances[Handle.Index].GeometryRHI = GeometryRHI;
|
|
Layer.Instances[Handle.Index].InstanceContributionToHitGroupIndex = InstanceContributionToHitGroupIndex;
|
|
|
|
if (bInstanceDebugDataEnabled)
|
|
{
|
|
FRayTracingInstanceDebugData& InstanceDebugData = Layer.InstancesDebugData[Handle.Index];
|
|
InstanceDebugData.GeometryAddress = uint64(GeometryRHI);
|
|
}
|
|
}
|
|
|
|
void FRayTracingScene::UpdateCachedInstanceGeometry(uint32 PackedHandle, FRHIRayTracingGeometry* GeometryRHI, int32 InstanceContributionToHitGroupIndex)
|
|
{
|
|
UpdateCachedInstanceGeometry(FInstanceHandle(PackedHandle), GeometryRHI, InstanceContributionToHitGroupIndex);
|
|
}
|
|
|
|
FRHIRayTracingGeometry* FRayTracingScene::GetCachedInstanceGeometry(FInstanceHandle Handle) const
|
|
{
|
|
const FLayer& Layer = Layers[uint8(Handle.Layer)];
|
|
return Layer.Instances[Handle.Index].GeometryRHI;
|
|
}
|
|
|
|
FRHIRayTracingGeometry* FRayTracingScene::GetCachedInstanceGeometry(uint32 PackedHandle) const
|
|
{
|
|
return GetCachedInstanceGeometry(FInstanceHandle(PackedHandle));
|
|
}
|
|
|
|
FRayTracingScene::FInstanceHandle FRayTracingScene::AddTransientInstance(FRayTracingGeometryInstance Instance, ERayTracingSceneLayer InLayer, FViewHandle InViewHandle, const FPrimitiveSceneProxy* Proxy, bool bDynamic, int32 GeometryHandle)
|
|
{
|
|
ensure(bCachedInstancesLocked);
|
|
|
|
FLayer& Layer = Layers[uint8(InLayer)];
|
|
|
|
FRHIRayTracingGeometry* GeometryRHI = Instance.GeometryRHI;
|
|
|
|
const uint32 InstanceIndex = Layer.Instances.Add(MoveTemp(Instance));
|
|
|
|
if (bTracingFeedbackEnabled)
|
|
{
|
|
Layer.GeometryHandles.Add(GeometryHandle);
|
|
check(Layer.Instances.Num() == Layer.GeometryHandles.Num());
|
|
}
|
|
|
|
if (bInstanceDebugDataEnabled)
|
|
{
|
|
FRayTracingInstanceDebugData& InstanceDebugData = Layer.InstancesDebugData.AddDefaulted_GetRef();
|
|
InstanceDebugData.Flags = bDynamic ? 1 : 0;
|
|
InstanceDebugData.GeometryAddress = uint64(GeometryRHI);
|
|
InstanceDebugData.ProxyHash = Proxy ? Proxy->GetTypeHash() : 0;
|
|
|
|
check(Layer.Instances.Num() == Layer.InstancesDebugData.Num());
|
|
}
|
|
|
|
FLayerView& LayerView = Layer.Views[InViewHandle];
|
|
|
|
int32 MinNumInstances = InstanceIndex + 1;
|
|
if (LayerView.VisibleInstances.Num() < MinNumInstances)
|
|
{
|
|
LayerView.VisibleInstances.SetNum(MinNumInstances, false);
|
|
}
|
|
|
|
LayerView.VisibleInstances[InstanceIndex] = true;
|
|
|
|
return { InLayer, InstanceIndex };
|
|
}
|
|
|
|
void FRayTracingScene::MarkInstanceVisible(FInstanceHandle Handle, FViewHandle InViewHandle)
|
|
{
|
|
FLayer& Layer = Layers[uint8(Handle.Layer)];
|
|
|
|
check(Layer.Instances[Handle.Index].GeometryRHI != nullptr);
|
|
|
|
FLayerView& LayerView = Layer.Views[InViewHandle];
|
|
|
|
int32 MinNumInstances = Handle.Index + 1;
|
|
if (LayerView.VisibleInstances.Num() < MinNumInstances)
|
|
{
|
|
LayerView.VisibleInstances.SetNum(MinNumInstances, false);
|
|
}
|
|
|
|
LayerView.VisibleInstances[Handle.Index] = true;
|
|
}
|
|
|
|
void FRayTracingScene::MarkInstanceVisible(uint32 Handle, FViewHandle InViewHandle)
|
|
{
|
|
MarkInstanceVisible(FInstanceHandle(ERayTracingSceneLayer(Handle >> 24), Handle & 0xFFFFFF), InViewHandle);
|
|
}
|
|
|
|
uint32 FRayTracingScene::FLayer::GetCachedInstanceSectionSize()
|
|
{
|
|
return NumCachedInstances + CachedInstancesFreeList.Num();
|
|
}
|
|
|
|
void FRayTracingScene::Reset()
|
|
{
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
|
|
for (int32 ViewIndex : TransientViewIndices)
|
|
{
|
|
check(ActiveViews.IsValidIndex(ViewIndex) && ActiveViews[ViewIndex] == ViewIndex);
|
|
|
|
// clear ViewIndex in Layers Views
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
Layer.Views[ViewIndex] = {};
|
|
}
|
|
|
|
ActiveViews.RemoveAt(ViewIndex);
|
|
ViewParameters[ViewIndex] = {};
|
|
}
|
|
|
|
TransientViewIndices.Empty();
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
const uint32 CachedInstanceSectionSize = Layer.GetCachedInstanceSectionSize();
|
|
|
|
Layer.Instances.SetNum(CachedInstanceSectionSize);
|
|
Layer.InstancesDebugData.SetNum(bInstanceDebugDataEnabled ? CachedInstanceSectionSize : 0);
|
|
|
|
Layer.GeometryHandleBuffer = nullptr;
|
|
Layer.GeometryHandles.SetNum(bTracingFeedbackEnabled ? CachedInstanceSectionSize : 0);
|
|
|
|
Layer.InstanceDebugBuffer = nullptr;
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Layer.Views.Num(); ++ViewIndex)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
|
|
LayerView.VisibleInstances.Reset();
|
|
LayerView.VisibleInstances.SetNum(CachedInstanceSectionSize, false);
|
|
|
|
LayerView.InstanceBufferBuilder = {};
|
|
|
|
LayerView.RayTracingSceneRHI = nullptr;
|
|
LayerView.RayTracingSceneBufferRDG = nullptr;
|
|
LayerView.RayTracingSceneBufferSRV = nullptr;
|
|
|
|
LayerView.InstanceBuffer = nullptr;
|
|
LayerView.HitGroupContributionsBuffer = nullptr;
|
|
LayerView.BuildScratchBuffer = nullptr;
|
|
LayerView.InstanceExtraDataBuffer = nullptr;
|
|
|
|
LayerView.InstanceHitCountBuffer = nullptr;
|
|
LayerView.InstanceHitCountBufferUAV = nullptr;
|
|
LayerView.AccelerationStructureIndexBuffer = nullptr;
|
|
}
|
|
}
|
|
|
|
InstanceStatsBuffer = nullptr;
|
|
|
|
GeometriesToBuild.Reset();
|
|
|
|
Allocator.Flush();
|
|
|
|
bUsesLightingChannels = false;
|
|
|
|
check(InitTask.IsCompleted());
|
|
InitTask = {};
|
|
|
|
bCachedInstancesLocked = false;
|
|
}
|
|
|
|
void FRayTracingScene::EndFrame()
|
|
{
|
|
Reset();
|
|
|
|
// Release the resources if ray tracing wasn't used
|
|
if (!bUsedThisFrame)
|
|
{
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Layer.Views.Num(); ++ViewIndex)
|
|
{
|
|
FLayerView& LayerView = Layer.Views[ViewIndex];
|
|
LayerView.RayTracingScenePooledBuffer = nullptr;
|
|
}
|
|
}
|
|
|
|
GeometriesToBuild.Empty();
|
|
|
|
ReleaseFeedbackReadbackBuffers();
|
|
ReleaseReadbackBuffers();
|
|
}
|
|
|
|
bUsedThisFrame = false;
|
|
bInitializationDataBuilt = false;
|
|
}
|
|
|
|
bool FRayTracingScene::SetInstanceExtraDataBufferEnabled(bool bInEnabled)
|
|
{
|
|
bInstanceExtraDataBufferEnabled = bInEnabled;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FRayTracingScene::SetTracingFeedbackEnabled(bool bInEnabled)
|
|
{
|
|
bool bChanged = (bTracingFeedbackEnabled != bInEnabled);
|
|
|
|
bTracingFeedbackEnabled = bInEnabled;
|
|
|
|
if (bChanged)
|
|
{
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
|
|
if (bTracingFeedbackEnabled)
|
|
{
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
Layer.GeometryHandles.SetNum(Layer.Instances.Num());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
Layer.GeometryHandles.Empty();
|
|
}
|
|
}
|
|
}
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
bool FRayTracingScene::SetInstanceDebugDataEnabled(bool bInEnabled)
|
|
{
|
|
bool bChanged = (bInstanceDebugDataEnabled != bInEnabled);
|
|
|
|
bInstanceDebugDataEnabled = bInEnabled;
|
|
|
|
if (bChanged)
|
|
{
|
|
const uint8 NumLayers = uint8(ERayTracingSceneLayer::NUM);
|
|
|
|
if (bInstanceDebugDataEnabled)
|
|
{
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
Layer.InstancesDebugData.SetNum(Layer.Instances.Num());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FLayer& Layer = Layers[LayerIndex];
|
|
Layer.InstancesDebugData.Empty();
|
|
}
|
|
}
|
|
}
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
void FRayTracingScene::ReleaseReadbackBuffers()
|
|
{
|
|
for (auto& ReadbackData : StatsReadback)
|
|
{
|
|
delete ReadbackData.ReadbackBuffer;
|
|
}
|
|
|
|
StatsReadback.Empty();
|
|
|
|
StatsReadbackWriteIndex = 0;
|
|
StatsReadbackNumPending = 0;
|
|
}
|
|
|
|
void FRayTracingScene::ReleaseFeedbackReadbackBuffers()
|
|
{
|
|
for (auto& ReadbackBuffer : FeedbackReadback)
|
|
{
|
|
delete ReadbackBuffer.GeometryHandleReadbackBuffer;
|
|
delete ReadbackBuffer.GeometryCountReadbackBuffer;
|
|
}
|
|
FeedbackReadback.Empty();
|
|
|
|
FeedbackReadbackWriteIndex = 0;
|
|
FeedbackReadbackNumPending = 0;
|
|
}
|
|
|
|
#endif // RHI_RAYTRACING
|