2022-12-02 15:09:41 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "RayTracingGeometry.h"
2023-01-27 14:54:10 -05:00
# include "DynamicRHI.h"
2023-01-27 14:17:39 -05:00
# include "HAL/IConsoleManager.h"
2022-12-02 15:09:41 -05:00
# include "RayTracingGeometryManager.h"
# include "RenderUtils.h"
2022-12-07 00:05:42 -05:00
# include "RHIResourceUpdates.h"
2023-01-27 14:54:10 -05:00
# include "RHITextureReference.h" // IWYU pragma: keep
2022-12-02 15:09:41 -05:00
int32 GVarDebugForceRuntimeBLAS = 0 ;
FAutoConsoleVariableRef CVarDebugForceRuntimeBLAS (
TEXT ( " r.Raytracing.DebugForceRuntimeBLAS " ) ,
GVarDebugForceRuntimeBLAS ,
TEXT ( " Force building BLAS at runtime. " ) ,
ECVF_ReadOnly ) ;
FRayTracingGeometry : : FRayTracingGeometry ( ) = default ;
/** Destructor. */
FRayTracingGeometry : : ~ FRayTracingGeometry ( ) = default ;
# if RHI_RAYTRACING
2022-12-07 00:05:42 -05:00
void FRayTracingGeometry : : InitRHIForStreaming ( FRHIRayTracingGeometry * IntermediateGeometry , FRHIResourceUpdateBatcher & Batcher )
{
EnumAddFlags ( GeometryState , EGeometryStateFlags : : StreamedIn ) ;
if ( RayTracingGeometryRHI & & IntermediateGeometry )
{
Batcher . QueueUpdateRequest ( RayTracingGeometryRHI , IntermediateGeometry ) ;
EnumAddFlags ( GeometryState , EGeometryStateFlags : : Valid ) ;
}
}
void FRayTracingGeometry : : ReleaseRHIForStreaming ( FRHIResourceUpdateBatcher & Batcher )
{
Initializer = { } ;
RemoveBuildRequest ( ) ;
EnumRemoveFlags ( GeometryState , EGeometryStateFlags : : StreamedIn ) ;
if ( RayTracingGeometryRHI )
{
Batcher . QueueUpdateRequest ( RayTracingGeometryRHI , nullptr ) ;
EnumRemoveFlags ( GeometryState , EGeometryStateFlags : : Valid ) ;
}
}
2022-12-02 15:09:41 -05:00
void FRayTracingGeometry : : CreateRayTracingGeometryFromCPUData ( TResourceArray < uint8 > & OfflineData )
{
check ( OfflineData . Num ( ) = = 0 | | Initializer . OfflineData = = nullptr ) ;
if ( OfflineData . Num ( ) )
{
Initializer . OfflineData = & OfflineData ;
}
if ( GVarDebugForceRuntimeBLAS & & Initializer . OfflineData ! = nullptr )
{
Initializer . OfflineData - > Discard ( ) ;
Initializer . OfflineData = nullptr ;
}
SetRequiresBuild ( Initializer . OfflineData = = nullptr ) ;
RayTracingGeometryRHI = RHICreateRayTracingGeometry ( Initializer ) ;
}
void FRayTracingGeometry : : RequestBuildIfNeeded ( ERTAccelerationStructureBuildPriority InBuildPriority )
{
RayTracingGeometryRHI - > SetInitializer ( Initializer ) ;
if ( GetRequiresBuild ( ) )
{
RayTracingBuildRequestIndex = GRayTracingGeometryManager . RequestBuildAccelerationStructure ( this , InBuildPriority ) ;
SetRequiresBuild ( false ) ;
}
}
void FRayTracingGeometry : : InitRHIForDynamicRayTracing ( )
{
check ( GetRayTracingMode ( ) = = ERayTracingMode : : Dynamic ) ;
// Streaming BLAS needs special handling to not get their "streaming" type wiped out as it will cause issues down the line.
// We only have to do this if the geometry was marked to be streamed in.
// In that case we will recreate the geometry as-if it was streamed in.
if ( EnumHasAnyFlags ( GeometryState , FRayTracingGeometry : : EGeometryStateFlags : : StreamedIn ) )
{
// When a mesh is streamed in (FStaticMeshStreamIn::DoFinishUpdate) we update the geometry initializer using just streamed in VB/IB.
// That initializer sets a Rendering type but RHI object was created as StreamingDestination and we have a mismatch between geometry initializer and RHI initializer.
// It's not an issue unless we try to initialize the geometry again using the geometry's initializer.
// We need the current geometry and RHI object to be StreamingDestination so the streaming continues to work.
Initializer . Type = ERayTracingGeometryInitializerType : : StreamingDestination ;
// Creating RHI with StreamingDestination type will only initialize RHI object but will not created the underlying BLAS buffers.
InitRHI ( ) ;
// Here we simulate geometry streaming: create geometry with StreamingSource type to allocate BLAS buffers (1) and swap it with the current geometry (2).
// Follows the same pattern as: (1) FStaticMeshStreamIn::CreateBuffers_* (2) FStaticMeshStreamIn::DoFinishUpdate
// There is no other way to initialize BLAS buffers for the geometry that has a StreamingDestination type.
{
TRHIResourceUpdateBatcher < 1 > Batcher ;
FRayTracingGeometryInitializer IntermediateInitializer = Initializer ;
IntermediateInitializer . Type = ERayTracingGeometryInitializerType : : StreamingSource ;
FRayTracingGeometryRHIRef IntermediateRayTracingGeometry = RHICreateRayTracingGeometry ( IntermediateInitializer ) ;
InitRHIForStreaming ( IntermediateRayTracingGeometry , Batcher ) ;
// When Batcher goes out of scope it will add commands to copy the BLAS buffers on RHI thread.
// We need to do it before we build the current geometry (also on RHI thread).
}
RequestBuildIfNeeded ( ERTAccelerationStructureBuildPriority : : Normal ) ;
}
else
{
InitRHI ( ) ;
}
}
void FRayTracingGeometry : : CreateRayTracingGeometry ( ERTAccelerationStructureBuildPriority InBuildPriority )
{
// Release previous RHI object if any
ReleaseRHI ( ) ;
check ( RawData . Num ( ) = = 0 | | Initializer . OfflineData = = nullptr ) ;
if ( RawData . Num ( ) )
{
Initializer . OfflineData = & RawData ;
}
if ( GVarDebugForceRuntimeBLAS & & Initializer . OfflineData ! = nullptr )
{
Initializer . OfflineData - > Discard ( ) ;
Initializer . OfflineData = nullptr ;
}
bool bAllSegmentsAreValid = Initializer . Segments . Num ( ) > 0 | | Initializer . OfflineData ;
for ( const FRayTracingGeometrySegment & Segment : Initializer . Segments )
{
if ( ! Segment . VertexBuffer )
{
bAllSegmentsAreValid = false ;
break ;
}
}
const bool bWithoutNativeResource = Initializer . Type = = ERayTracingGeometryInitializerType : : StreamingDestination ;
if ( bAllSegmentsAreValid )
{
// Only geometries with StreamingDestination type are initially created in invalid state until they are streamed in (see InitRHIForStreaming).
if ( bWithoutNativeResource )
{
EnumRemoveFlags ( GeometryState , EGeometryStateFlags : : Valid ) ;
}
else
{
EnumAddFlags ( GeometryState , EGeometryStateFlags : : Valid ) ;
}
if ( IsRayTracingEnabled ( ) )
{
RayTracingGeometryRHI = RHICreateRayTracingGeometry ( Initializer ) ;
}
// Register the geometry if it wasn't registered before and it's not using custom path
const bool bRegisterGeometry = ( RayTracingGeometryHandle = = INDEX_NONE ) & & ( InBuildPriority ! = ERTAccelerationStructureBuildPriority : : Immediate ) ;
if ( bRegisterGeometry )
{
RayTracingGeometryHandle = GRayTracingGeometryManager . RegisterRayTracingGeometry ( this ) ;
}
if ( Initializer . OfflineData = = nullptr )
{
// Request build if not skip
if ( InBuildPriority ! = ERTAccelerationStructureBuildPriority : : Skip )
{
if ( IsRayTracingEnabled ( ) )
{
RayTracingBuildRequestIndex = GRayTracingGeometryManager . RequestBuildAccelerationStructure ( this , InBuildPriority ) ;
}
SetRequiresBuild ( false ) ;
}
else
{
SetRequiresBuild ( true ) ;
}
}
else
{
SetRequiresBuild ( false ) ;
// Offline data ownership is transferred to the RHI, which discards it after use.
// It is no longer valid to use it after this point.
Initializer . OfflineData = nullptr ;
}
}
}
bool FRayTracingGeometry : : IsValid ( ) const
{
return RayTracingGeometryRHI ! = nullptr & & Initializer . TotalPrimitiveCount > 0 & & EnumHasAnyFlags ( GeometryState , EGeometryStateFlags : : Valid ) ;
}
void FRayTracingGeometry : : InitRHI ( )
{
if ( ! IsRayTracingAllowed ( ) )
return ;
ERTAccelerationStructureBuildPriority BuildPriority = Initializer . Type ! = ERayTracingGeometryInitializerType : : Rendering
? ERTAccelerationStructureBuildPriority : : Skip
: ERTAccelerationStructureBuildPriority : : Normal ;
CreateRayTracingGeometry ( BuildPriority ) ;
}
void FRayTracingGeometry : : ReleaseRHI ( )
{
RemoveBuildRequest ( ) ;
RayTracingGeometryRHI . SafeRelease ( ) ;
if ( RayTracingGeometryHandle ! = INDEX_NONE )
{
GRayTracingGeometryManager . ReleaseRayTracingGeometryHandle ( RayTracingGeometryHandle ) ;
RayTracingGeometryHandle = INDEX_NONE ;
}
}
void FRayTracingGeometry : : RemoveBuildRequest ( )
{
if ( HasPendingBuildRequest ( ) )
{
GRayTracingGeometryManager . RemoveBuildRequest ( RayTracingBuildRequestIndex ) ;
RayTracingBuildRequestIndex = INDEX_NONE ;
}
}
void FRayTracingGeometry : : ReleaseResource ( )
{
// Release any resource references held by the initializer.
// This includes index and vertex buffers used for building the BLAS.
Initializer = FRayTracingGeometryInitializer { } ;
FRenderResource : : ReleaseResource ( ) ;
}
void FRayTracingGeometry : : BoostBuildPriority ( float InBoostValue ) const
{
check ( HasPendingBuildRequest ( ) ) ;
GRayTracingGeometryManager . BoostPriority ( RayTracingBuildRequestIndex , InBoostValue ) ;
}
# endif // RHI_RAYTRACING