You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Decals materials are evaluated using callable shaders in PathTracingKernel. - Decals are culled using a 2D grid similar to the existing light grid. - In order to correctly handle decal blending order, decals are sorted using the same logic as the rasterizer on CPU. The compute shader that builds the decal grid maintains the correct order. - Decal materials are wrapped in FRayTracingDecalMaterialShader. The instance parameters of each decal are bound using uniform buffers. #preflight 628f3fed2f2409bc1e7a6414 #rb Yuriy.ODonnell, chris.kulla, Jeremy.Moore [CL 20377336 by tiago costa in ue5-main branch]
304 lines
12 KiB
C++
304 lines
12 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"
|
|
|
|
BEGIN_SHADER_PARAMETER_STRUCT(FBuildInstanceBufferPassParams, )
|
|
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, InstanceBuffer)
|
|
END_SHADER_PARAMETER_STRUCT()
|
|
|
|
FRayTracingScene::FRayTracingScene()
|
|
{
|
|
|
|
}
|
|
|
|
FRayTracingScene::~FRayTracingScene()
|
|
{
|
|
WaitForTasks();
|
|
}
|
|
|
|
void FRayTracingScene::Create(FRDGBuilder& GraphBuilder, const FGPUScene& GPUScene, const FViewMatrices& ViewMatrices)
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(FRayTracingScene_BeginCreate);
|
|
|
|
// 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;
|
|
|
|
WaitForTasks();
|
|
|
|
static const uint8 NumLayers = uint32(ERayTracingSceneLayer::NUM);
|
|
|
|
FRayTracingSceneWithGeometryInstances SceneWithGeometryInstances = CreateRayTracingSceneWithGeometryInstances(
|
|
Instances,
|
|
NumLayers,
|
|
RAY_TRACING_NUM_SHADER_SLOTS,
|
|
RAY_TRACING_NUM_MISS_SHADER_SLOTS,
|
|
NumCallableShaderSlots);
|
|
|
|
RayTracingSceneRHI = SceneWithGeometryInstances.Scene;
|
|
|
|
const FRayTracingSceneInitializer2& SceneInitializer = RayTracingSceneRHI->GetInitializer();
|
|
|
|
const uint32 NumNativeInstances = SceneWithGeometryInstances.NumNativeGPUSceneInstances + SceneWithGeometryInstances.NumNativeGPUInstances + SceneWithGeometryInstances.NumNativeCPUInstances;
|
|
const uint32 NumNativeInstancesAligned = FMath::DivideAndRoundUp(FMath::Max(NumNativeInstances, 1U), AllocationGranularity) * AllocationGranularity;
|
|
const uint32 NumTransformsAligned = FMath::DivideAndRoundUp(FMath::Max(SceneWithGeometryInstances.NumNativeCPUInstances, 1U), AllocationGranularity) * AllocationGranularity;
|
|
|
|
FRayTracingAccelerationStructureSize SizeInfo = 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 (!RayTracingSceneBuffer.IsValid()
|
|
|| SizeInfo.ResultSize > RayTracingSceneBuffer->GetSize()
|
|
|| SizeInfo.ResultSize < RayTracingSceneBuffer->GetSize() / 2)
|
|
{
|
|
FRHIResourceCreateInfo CreateInfo(TEXT("FRayTracingScene::SceneBuffer"));
|
|
RayTracingSceneBuffer = RHICreateBuffer(uint32(SizeInfo.ResultSize), EBufferUsageFlags::AccelerationStructure, 0, ERHIAccess::BVHWrite, CreateInfo);
|
|
}
|
|
|
|
LayerSRVs.SetNum(NumLayers);
|
|
|
|
for (uint32 LayerIndex = 0; LayerIndex < NumLayers; ++LayerIndex)
|
|
{
|
|
FShaderResourceViewInitializer ViewInitializer(RayTracingSceneBuffer, RayTracingSceneRHI->GetLayerBufferOffset(LayerIndex), 0);
|
|
LayerSRVs[LayerIndex] = RHICreateShaderResourceView(ViewInitializer);
|
|
}
|
|
|
|
{
|
|
const uint64 ScratchAlignment = GRHIRayTracingScratchBufferAlignment;
|
|
FRDGBufferDesc ScratchBufferDesc;
|
|
ScratchBufferDesc.Usage = EBufferUsageFlags::RayTracingScratch | EBufferUsageFlags::StructuredBuffer;
|
|
ScratchBufferDesc.BytesPerElement = uint32(ScratchAlignment);
|
|
ScratchBufferDesc.NumElements = uint32(FMath::DivideAndRoundUp(SizeInfo.BuildScratchSize, ScratchAlignment));
|
|
|
|
BuildScratchBuffer = GraphBuilder.CreateBuffer(ScratchBufferDesc, TEXT("FRayTracingScene::ScratchBuffer"));
|
|
}
|
|
|
|
{
|
|
FRDGBufferDesc InstanceBufferDesc;
|
|
InstanceBufferDesc.Usage = EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource | EBufferUsageFlags::StructuredBuffer;
|
|
InstanceBufferDesc.BytesPerElement = GRHIRayTracingInstanceDescriptorSize;
|
|
InstanceBufferDesc.NumElements = NumNativeInstancesAligned;
|
|
|
|
InstanceBuffer = GraphBuilder.CreateBuffer(InstanceBufferDesc, TEXT("FRayTracingScene::InstanceBuffer"));
|
|
}
|
|
|
|
{
|
|
// Round to PoT to avoid resizing too often
|
|
const uint32 NumGeometries = FMath::RoundUpToPowerOfTwo(SceneInitializer.ReferencedGeometries.Num());
|
|
const uint32 AccelerationStructureAddressesBufferSize = NumGeometries * sizeof(FRayTracingAccelerationStructureAddress);
|
|
|
|
if (AccelerationStructureAddressesBuffer.NumBytes < AccelerationStructureAddressesBufferSize)
|
|
{
|
|
// Need to pass "BUF_MultiGPUAllocate", as virtual addresses are different per GPU
|
|
AccelerationStructureAddressesBuffer.Initialize(
|
|
TEXT("FRayTracingScene::AccelerationStructureAddressesBuffer"), AccelerationStructureAddressesBufferSize, BUF_Volatile | BUF_MultiGPUAllocate);
|
|
}
|
|
}
|
|
|
|
{
|
|
// Create/resize instance upload buffer (if necessary)
|
|
const uint32 UploadBufferSize = NumNativeInstancesAligned * sizeof(FRayTracingInstanceDescriptorInput);
|
|
|
|
if (!InstanceUploadBuffer.IsValid()
|
|
|| UploadBufferSize > InstanceUploadBuffer->GetSize()
|
|
|| UploadBufferSize < InstanceUploadBuffer->GetSize() / 2)
|
|
{
|
|
FRHIResourceCreateInfo CreateInfo(TEXT("FRayTracingScene::InstanceUploadBuffer"));
|
|
InstanceUploadBuffer = RHICreateStructuredBuffer(sizeof(FRayTracingInstanceDescriptorInput), UploadBufferSize, BUF_ShaderResource | BUF_Volatile, CreateInfo);
|
|
InstanceUploadSRV = RHICreateShaderResourceView(InstanceUploadBuffer);
|
|
}
|
|
}
|
|
|
|
{
|
|
const uint32 UploadBufferSize = NumTransformsAligned * sizeof(FVector4f) * 3;
|
|
|
|
// Create/resize transform upload buffer (if necessary)
|
|
if (!TransformUploadBuffer.IsValid()
|
|
|| UploadBufferSize > TransformUploadBuffer->GetSize()
|
|
|| UploadBufferSize < TransformUploadBuffer->GetSize() / 2)
|
|
{
|
|
FRHIResourceCreateInfo CreateInfo(TEXT("FRayTracingScene::TransformUploadBuffer"));
|
|
TransformUploadBuffer = RHICreateStructuredBuffer(sizeof(FVector4f), UploadBufferSize, BUF_ShaderResource | BUF_Volatile, CreateInfo);
|
|
TransformUploadSRV = RHICreateShaderResourceView(TransformUploadBuffer);
|
|
}
|
|
}
|
|
|
|
if (NumNativeInstances > 0)
|
|
{
|
|
const uint32 InstanceUploadBytes = NumNativeInstances * sizeof(FRayTracingInstanceDescriptorInput);
|
|
const uint32 TransformUploadBytes = SceneWithGeometryInstances.NumNativeCPUInstances * 3 * sizeof(FVector4f);
|
|
|
|
FRayTracingInstanceDescriptorInput* InstanceUploadData = (FRayTracingInstanceDescriptorInput*)RHILockBuffer(InstanceUploadBuffer, 0, InstanceUploadBytes, RLM_WriteOnly);
|
|
FVector4f* TransformUploadData = (FVector4f*)RHILockBuffer(TransformUploadBuffer, 0, TransformUploadBytes, RLM_WriteOnly);
|
|
|
|
// Fill instance upload buffer on separate thread since results are only needed in RHI thread
|
|
FillInstanceUploadBufferTask = FFunctionGraphTask::CreateAndDispatchWhenReady(
|
|
[InstanceUploadData = MakeArrayView(InstanceUploadData, NumNativeInstances),
|
|
TransformUploadData = MakeArrayView(TransformUploadData, SceneWithGeometryInstances.NumNativeCPUInstances * 3),
|
|
NumNativeGPUSceneInstances = SceneWithGeometryInstances.NumNativeGPUSceneInstances,
|
|
NumNativeCPUInstances = SceneWithGeometryInstances.NumNativeCPUInstances,
|
|
Instances = MakeArrayView(Instances),
|
|
InstanceGeometryIndices = MoveTemp(SceneWithGeometryInstances.InstanceGeometryIndices),
|
|
BaseUploadBufferOffsets = MoveTemp(SceneWithGeometryInstances.BaseUploadBufferOffsets),
|
|
RayTracingSceneRHI = RayTracingSceneRHI,
|
|
PreViewTranslation = ViewMatrices.GetPreViewTranslation()]()
|
|
{
|
|
FTaskTagScope TaskTagScope(ETaskTag::EParallelRenderingThread);
|
|
FillRayTracingInstanceUploadBuffer(
|
|
RayTracingSceneRHI,
|
|
PreViewTranslation,
|
|
Instances,
|
|
InstanceGeometryIndices,
|
|
BaseUploadBufferOffsets,
|
|
NumNativeGPUSceneInstances,
|
|
NumNativeCPUInstances,
|
|
InstanceUploadData,
|
|
TransformUploadData);
|
|
}, TStatId(), nullptr, ENamedThreads::AnyThread);
|
|
|
|
FBuildInstanceBufferPassParams* PassParams = GraphBuilder.AllocParameters<FBuildInstanceBufferPassParams>();
|
|
PassParams->InstanceBuffer = GraphBuilder.CreateUAV(InstanceBuffer);
|
|
|
|
const FLargeWorldRenderPosition AbsoluteViewOrigin(ViewMatrices.GetViewOrigin());
|
|
const FVector ViewTileOffset = AbsoluteViewOrigin.GetTileOffset();
|
|
|
|
GraphBuilder.AddPass(
|
|
RDG_EVENT_NAME("BuildTLASInstanceBuffer"),
|
|
PassParams,
|
|
ERDGPassFlags::Compute,
|
|
[PassParams,
|
|
this,
|
|
GPUScene = &GPUScene,
|
|
ViewTilePosition = AbsoluteViewOrigin.GetTile(),
|
|
RelativePreViewTranslation = ViewMatrices.GetPreViewTranslation() + ViewTileOffset,
|
|
&SceneInitializer,
|
|
NumNativeGPUSceneInstances = SceneWithGeometryInstances.NumNativeGPUSceneInstances,
|
|
NumNativeCPUInstances = SceneWithGeometryInstances.NumNativeCPUInstances,
|
|
GPUInstances = MoveTemp(SceneWithGeometryInstances.GPUInstances)
|
|
](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
WaitForTasks();
|
|
RHICmdList.UnlockBuffer(InstanceUploadBuffer);
|
|
RHICmdList.UnlockBuffer(TransformUploadBuffer);
|
|
|
|
// Pull this out here, because command list playback (where the lambda is executed) doesn't update the GPU mask
|
|
FRHIGPUMask IterateGPUMasks = RHICmdList.GetGPUMask();
|
|
|
|
RHICmdList.EnqueueLambda([BufferRHIRef = AccelerationStructureAddressesBuffer.Buffer, &SceneInitializer, IterateGPUMasks](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(GetAccelerationStructuresAddresses);
|
|
|
|
for (uint32 GPUIndex : IterateGPUMasks)
|
|
{
|
|
FRayTracingAccelerationStructureAddress* AddressesPtr = (FRayTracingAccelerationStructureAddress*)RHICmdList.LockBufferMGPU(
|
|
BufferRHIRef,
|
|
GPUIndex,
|
|
0,
|
|
SceneInitializer.ReferencedGeometries.Num() * sizeof(FRayTracingAccelerationStructureAddress), RLM_WriteOnly);
|
|
|
|
const uint32 NumGeometries = SceneInitializer.ReferencedGeometries.Num();
|
|
for (uint32 GeometryIndex = 0; GeometryIndex < NumGeometries; ++GeometryIndex)
|
|
{
|
|
AddressesPtr[GeometryIndex] = SceneInitializer.ReferencedGeometries[GeometryIndex]->GetAccelerationStructureAddress(GPUIndex);
|
|
}
|
|
|
|
RHICmdList.UnlockBufferMGPU(BufferRHIRef, GPUIndex);
|
|
}
|
|
});
|
|
|
|
BuildRayTracingInstanceBuffer(
|
|
RHICmdList,
|
|
GPUScene,
|
|
ViewTilePosition,
|
|
FVector3f(RelativePreViewTranslation),
|
|
PassParams->InstanceBuffer->GetRHI(),
|
|
InstanceUploadSRV,
|
|
AccelerationStructureAddressesBuffer.SRV,
|
|
TransformUploadSRV,
|
|
NumNativeGPUSceneInstances,
|
|
NumNativeCPUInstances,
|
|
GPUInstances);
|
|
});
|
|
}
|
|
}
|
|
|
|
void FRayTracingScene::WaitForTasks() const
|
|
{
|
|
if (FillInstanceUploadBufferTask.IsValid())
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(WaitForRayTracingSceneFillInstanceUploadBuffer);
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(WaitForRayTracingSceneFillInstanceUploadBuffer);
|
|
FTaskGraphInterface::Get().WaitUntilTaskCompletes(FillInstanceUploadBufferTask, ENamedThreads::GetRenderThread_Local());
|
|
|
|
FillInstanceUploadBufferTask = {};
|
|
}
|
|
}
|
|
|
|
bool FRayTracingScene::IsCreated() const
|
|
{
|
|
return RayTracingSceneRHI.IsValid();
|
|
}
|
|
|
|
FRHIRayTracingScene* FRayTracingScene::GetRHIRayTracingScene() const
|
|
{
|
|
return RayTracingSceneRHI.GetReference();
|
|
}
|
|
|
|
FRHIRayTracingScene* FRayTracingScene::GetRHIRayTracingSceneChecked() const
|
|
{
|
|
FRHIRayTracingScene* Result = GetRHIRayTracingScene();
|
|
checkf(Result, TEXT("Ray tracing scene was not created. Perhaps Create() was not called."));
|
|
return Result;
|
|
}
|
|
|
|
FRHIBuffer* FRayTracingScene::GetBufferChecked() const
|
|
{
|
|
checkf(RayTracingSceneBuffer.IsValid(), TEXT("Ray tracing scene buffer was not created. Perhaps Create() was not called."));
|
|
return RayTracingSceneBuffer.GetReference();
|
|
}
|
|
|
|
FRHIShaderResourceView* FRayTracingScene::GetLayerSRVChecked(ERayTracingSceneLayer Layer) const
|
|
{
|
|
checkf(LayerSRVs[uint8(Layer)].IsValid(), TEXT("Ray tracing scene SRV was not created. Perhaps Create() was not called."));
|
|
return LayerSRVs[uint8(Layer)].GetReference();
|
|
}
|
|
|
|
void FRayTracingScene::Reset()
|
|
{
|
|
WaitForTasks();
|
|
|
|
Instances.Reset();
|
|
NumCallableShaderSlots = 0;
|
|
CallableCommands.Reset();
|
|
UniformBuffers.Reset();
|
|
GeometriesToBuild.Reset();
|
|
UsedCoarseMeshStreamingHandles.Reset();
|
|
|
|
Allocator.Flush();
|
|
|
|
BuildScratchBuffer = nullptr;
|
|
}
|
|
|
|
void FRayTracingScene::ResetAndReleaseResources()
|
|
{
|
|
Reset();
|
|
|
|
Instances.Empty();
|
|
CallableCommands.Empty();
|
|
UniformBuffers.Empty();
|
|
GeometriesToBuild.Empty();
|
|
UsedCoarseMeshStreamingHandles.Empty();
|
|
RayTracingSceneBuffer = nullptr;
|
|
RayTracingSceneRHI = nullptr;
|
|
}
|
|
|
|
#endif // RHI_RAYTRACING
|