2022-06-22 18:47:11 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
# include "ProfilingDebugging/RealtimeGPUProfiler.h"
2023-01-03 20:13:41 -05:00
# include "RenderGraphAllocator.h"
# include "RenderGraphFwd.h"
Merging //UE5/Dev-ParallelRendering/... (up to CL 30965645) to //UE5/Main/... (base CL 30962637)
Significant refactor of RHI command list management and submission, and RHI breadcrumbs / RenderGraph (RDG) scopes, to allow for parallel translation of most RHI command lists.
See individual changelists in //UE5/Dev-ParallelRendering for details. A summary of the changes is as follows:
This work's primary goal was to allow as many RHI command lists as possible to be parallel translated, to make more efficient use of many-core systems. To achieve this:
- The submission code paths for the immediate and parallel RHI command lists have been merged into a single function: FRHICommandListExecutor::Submit().
- A "dispatch thread" (which is simply a series of chained task graph tasks) is used to decide which command lists are batched together in a single parallel translate job.
- Individual command lists can disable parallel translate, which forces them to be executed on the RHI thread. This happens automatically if an RHI command list performs an operation that is not thread safe (e.g. buffer lock, or low-level resource transition).
One of the primary blockers for parallel translation was the RHI breadcrumb system, and the way RDG builds scopes. This was also refactored to remove these limitations:
- RDG could only push/pop events on the immediate command list, which resulted in parallel and immediate work being interleaved, breaking any opportunity for parallelism.
- Platform RHI implementations of breadcrumbs (e.g. in D3D12 RHI) was not correct across multiple RHI contexts. Push/pop operations aren't necessarily balanced within any one RHI context given that RDG builds "parallel pass sets" containing arbitrary ranges of renderer passes.
A summary of the new RHI breadcrumb system is as follows:
- A tree of breadcrumb nodes is built by the render thread and RDG. Each node contains the node name, and pointers to the parent and next nodes. When fully built, the nodes form a depth-first linked list which is used for traversing the tree for GPU crash debugging.
- The memory for breadcrumb nodes is provided by ref-counted allocator objects. These allocators are pipelined through the RHI, allowing the platform RHI implementation to extend their lifetime for GPU crash debugging purposes.
- RHIPushEvent / RHIPopEvent have been removed, replaced with RHIBeginBreadcrumbGPU / RHIEndBreadcrumbGPU. Platform RHIs implement these functions to perform GPU immediate writes using the unique ID of each node, for tracking GPU progress.
- Format string arguments are captured by-value to remove the cost of string formatting while building the breadcrumb tree. String formatting only occurs when the actual formatted string is required (e.g. during GPU crash breadcrumb stack traversal, or when calling platform GPU profiling APIs).
RenderGraph scopes have been simplified:
- The separate scope trees / arrays of ops have been combined. There is now a single tree of RDG scopes containing all types.
- Each RDG pass holds a pointer to the scope it was created under.
- BeginCPU / EndCPU is called on each RDG scope as the various RDG threads enter / exit them. This allows us to mark-up each worker thread with the relevant Unreal Insights scopes.
Other changes include:
- Fixes for bugs uncovered when parallel translate was enabled.
- Adjusted platform affinities necessary due to the new layout of thread tasks in the renderer.
- Refactored RHI draw call stats to better fit the new pipeline design.
#rb jeannoe.morissette, zach.bethel
#jira UE-139543
[CL 30973133 by Luke Thatcher in ue5-main branch]
2024-01-29 12:47:28 -05:00
# include "RHIBreadcrumbs.h"
2022-06-22 18:47:11 -04:00
/** DEFINES */
/** Whether render graph debugging is enabled. */
# define RDG_ENABLE_DEBUG (!UE_BUILD_SHIPPING && !UE_BUILD_TEST)
/** Performs the operation if RDG_ENABLE_DEBUG is enabled. Useful for one-line checks without explicitly wrapping in #if. */
# if RDG_ENABLE_DEBUG
# define IF_RDG_ENABLE_DEBUG(Op) Op
# else
# define IF_RDG_ENABLE_DEBUG(Op)
# endif
/** Whether render graph debugging is enabled and we are compiling with the engine. */
# define RDG_ENABLE_DEBUG_WITH_ENGINE (RDG_ENABLE_DEBUG && WITH_ENGINE)
/** Whether render graph insight tracing is enabled. */
# define RDG_ENABLE_TRACE UE_TRACE_ENABLED && !IS_PROGRAM && !UE_BUILD_SHIPPING
# if RDG_ENABLE_TRACE
# define IF_RDG_ENABLE_TRACE(Op) Op
# else
# define IF_RDG_ENABLE_TRACE(Op)
# endif
/** Allows to dump all RDG resources of a frame. */
# define RDG_DUMP_RESOURCES (WITH_DUMPGPU)
/** The type of GPU events the render graph system supports.
* RDG_EVENTS = = 0 means there is no string processing at all .
* RDG_EVENTS = = 1 means the format component of the event name is stored as a const TCHAR * .
* RDG_EVENTS = = 2 means string formatting is evaluated and stored in an FString .
*/
# define RDG_EVENTS_NONE 0
# define RDG_EVENTS_STRING_REF 1
# define RDG_EVENTS_STRING_COPY 2
/** Whether render graph GPU events are enabled. */
# if WITH_PROFILEGPU
2022-09-14 00:02:33 -04:00
# if UE_BUILD_TEST || UE_BUILD_SHIPPING
2022-08-30 02:23:50 -04:00
# define RDG_EVENTS RDG_EVENTS_STRING_REF
# else
# define RDG_EVENTS RDG_EVENTS_STRING_COPY
# endif
Merging //UE5/Dev-ParallelRendering/... (up to CL 30965645) to //UE5/Main/... (base CL 30962637)
Significant refactor of RHI command list management and submission, and RHI breadcrumbs / RenderGraph (RDG) scopes, to allow for parallel translation of most RHI command lists.
See individual changelists in //UE5/Dev-ParallelRendering for details. A summary of the changes is as follows:
This work's primary goal was to allow as many RHI command lists as possible to be parallel translated, to make more efficient use of many-core systems. To achieve this:
- The submission code paths for the immediate and parallel RHI command lists have been merged into a single function: FRHICommandListExecutor::Submit().
- A "dispatch thread" (which is simply a series of chained task graph tasks) is used to decide which command lists are batched together in a single parallel translate job.
- Individual command lists can disable parallel translate, which forces them to be executed on the RHI thread. This happens automatically if an RHI command list performs an operation that is not thread safe (e.g. buffer lock, or low-level resource transition).
One of the primary blockers for parallel translation was the RHI breadcrumb system, and the way RDG builds scopes. This was also refactored to remove these limitations:
- RDG could only push/pop events on the immediate command list, which resulted in parallel and immediate work being interleaved, breaking any opportunity for parallelism.
- Platform RHI implementations of breadcrumbs (e.g. in D3D12 RHI) was not correct across multiple RHI contexts. Push/pop operations aren't necessarily balanced within any one RHI context given that RDG builds "parallel pass sets" containing arbitrary ranges of renderer passes.
A summary of the new RHI breadcrumb system is as follows:
- A tree of breadcrumb nodes is built by the render thread and RDG. Each node contains the node name, and pointers to the parent and next nodes. When fully built, the nodes form a depth-first linked list which is used for traversing the tree for GPU crash debugging.
- The memory for breadcrumb nodes is provided by ref-counted allocator objects. These allocators are pipelined through the RHI, allowing the platform RHI implementation to extend their lifetime for GPU crash debugging purposes.
- RHIPushEvent / RHIPopEvent have been removed, replaced with RHIBeginBreadcrumbGPU / RHIEndBreadcrumbGPU. Platform RHIs implement these functions to perform GPU immediate writes using the unique ID of each node, for tracking GPU progress.
- Format string arguments are captured by-value to remove the cost of string formatting while building the breadcrumb tree. String formatting only occurs when the actual formatted string is required (e.g. during GPU crash breadcrumb stack traversal, or when calling platform GPU profiling APIs).
RenderGraph scopes have been simplified:
- The separate scope trees / arrays of ops have been combined. There is now a single tree of RDG scopes containing all types.
- Each RDG pass holds a pointer to the scope it was created under.
- BeginCPU / EndCPU is called on each RDG scope as the various RDG threads enter / exit them. This allows us to mark-up each worker thread with the relevant Unreal Insights scopes.
Other changes include:
- Fixes for bugs uncovered when parallel translate was enabled.
- Adjusted platform affinities necessary due to the new layout of thread tasks in the renderer.
- Refactored RHI draw call stats to better fit the new pipeline design.
#rb jeannoe.morissette, zach.bethel
#jira UE-139543
[CL 30973133 by Luke Thatcher in ue5-main branch]
2024-01-29 12:47:28 -05:00
# elif WITH_RHI_BREADCRUMBS
2022-06-22 18:47:11 -04:00
# define RDG_EVENTS RDG_EVENTS_STRING_REF
# else
# define RDG_EVENTS RDG_EVENTS_NONE
# endif
Visualize Texture: Performance and feature upgrades.
* Visualize texture system starts out in an inactive state until a command is issued, avoiding overhead of tracking views and scene textures, saving 1.4% on the render thread.
* Visualization overhead eliminated for views besides the one currently being visualized.
* Support for visualization of textures from scene captures, via "view=N" option (specifying the unique ID of the view), with "view=?" displaying a list of views for reference.
* Improved visualization for cube maps. PIP uses 2:1 aspect for the longitudinal render to match resource viewer display, and pixel perfect option shows tiled flat cube map faces (actual pixels) rather than running a projection.
* Padding for scene or screen pass textures is removed in the visualization -- the padding otherwise shows up as garbage or blank space.
To remove scene texture padding, it's necessary to add a field to RDG textures to provide an option to track the viewport sizes that were rendered for a given texture. If not set, the assumption is the whole texture was rendered. The field is set for FSceneTextures and FScreenPassTexture, covering the vast majority of cases, plus the denoiser was spot fixed -- worst case if any other cases are missed, you still see the padding. You can tell padding was present when visualizing by contrasting the texture size with the viewport size.
Padding was always a potential issue for the visualizer, but is exacerbated by scene captures, as the padded scene textures are set to a size that's a union of the main view and any scene captures. Padding is also exacerbated by dynamic resolution scaling, as the buffers will be padded to the maximum resolution. For example, a cube map rendering at 512x512 will have 93% of the pixel area as padding if the front buffer is at 1440p, or the default dynamic resolution setup will have 70% of the pixels as padding at minimum res.
#rb Jason.Nadro
[CL 31160232 by jason hoerner in ue5-main branch]
2024-02-03 16:07:46 -05:00
# define SUPPORTS_VISUALIZE_TEXTURE (WITH_ENGINE && (!UE_BUILD_SHIPPING || WITH_EDITOR))
2024-09-03 11:39:06 -04:00
/** An RDG pass execution lambda MAY be executed in a parallel task IF the lambda references a non-immediate command list AND the builder flags are set to execute in parallel.
* By default , if a pass executes in parallel , the task will be awaited at the end of FRDGBuilder : : Execute ( ) . This behavior may be overridden by tagging the lambda with FRDGAsyncTask as the
* first argument . A tagged lambda , when executed in parallel , is NOT awaited at the end of FRDGBuilder : : Execute ( ) . Instead , the task is recorded as an outstanding RHI command list task
* ( which share semantics with mesh passes or other parallel command list tasks ) and can be manually awaited by calling FRDGBuilder : : WaitForAsyncExecuteTasks ( ) or formed into a task
* graph with FRDGBuilder : : GetAsyncExecuteTask ( ) ( both static methods ) . The lifetime of RDG allocations is tied to these tasks and RDG will not release any memory or allocated objects
* until the last task completes , even though the FRDGBuilder instance itself may go out of scope and destruct .
*
* Consider the following examples :
*
* // Builder is marked as supporting parallel execute.
* FRDGBuilder GraphBuilder ( RDG_EVENT_NAME ( " MyBuilder " ) , ERDGBuilderFlags : : Parallel )
*
* GraphBuilder . AddPass ( RDG_EVENT_NAME ( " ... " ) , PassParameters , PassFlags , [ . . . ] ( FRHICommandList & RHICmdList )
* {
* // This will execute in parallel and is awaited by RDG on the render thread at the end of FRDGBuilder::Execute().
* } ) ;
*
* GraphBuilder . AddPass ( RDG_EVENT_NAME ( " ... " ) , PassParameters , PassFlags , [ . . . ] ( FRHICommandListImmediate & RHICmdList )
* {
* // This will execute inline on the render thread, because the immediate command list is referenced.
* } ) ;
*
* FMyObject * Object = GraphBuilder . AllocObject < FMyObject > ( ) ;
*
* GraphBuilder . AddPass ( RDG_EVENT_NAME ( " ... " ) , PassParameters , PassFlags , [ Object ] ( FRDGAsyncTask , FRHICommandList & RHICmdList )
* {
* // This will execute in parallel and is NOT awaited at the end of FRDGBuilder::Execute(). Accessing 'Object' is safe.
* } ) ;
*
* GraphBuilder . Execute ( ) ;
*
* Tasks can be synced in a few different ways . RDG async execute tasks are chained , so syncing the last batch will sync ALL prior batches .
*
* // This will sync all RDG async execute tasks.
* RHICmdList . ImmediateFlush ( EImmediateFlushType : : WaitForOutstandingTasksOnly ) ;
*
* // This will also sync all RDG async execute tasks.
* FRDGBuilder : : WaitForAsyncExecuteTasks ( ) ;
*
* // Launch a task that will do something when RDG async execute tasks complete.
* UE : : Tasks : : Launch ( UE_SOURCE_LOCATION , [ . . . ] { . . . } , FRDGBuilder : : GetAsyncExecuteTask ( ) ) ;
*/
struct FRDGAsyncTask { } ;
2022-06-22 18:47:11 -04:00
/** ENUMS */
enum class ERDGBuilderFlags
{
None = 0 ,
2024-07-16 20:58:37 -04:00
/** Allows the builder to parallelize AddSetupPass calls. Without this flag, setup passes run serially. */
ParallelSetup = 1 < < 0 ,
/** Allows the builder to parallelize compilation of the graph. Without this flag, all passes execute on the render thread. */
ParallelCompile = 1 < < 1 ,
2022-06-22 18:47:11 -04:00
/** Allows the builder to parallelize execution of passes. Without this flag, all passes execute on the render thread. */
2024-07-16 20:58:37 -04:00
ParallelExecute = 1 < < 2 ,
Parallel = ParallelSetup | ParallelCompile | ParallelExecute ,
AllowParallelExecute UE_DEPRECATED ( 5.5 , " Use ERDDGBuilderFlags::Parallel instead. " ) = Parallel ,
2022-06-22 18:47:11 -04:00
} ;
ENUM_CLASS_FLAGS ( ERDGBuilderFlags ) ;
/** Flags to annotate a pass with when calling AddPass. */
2022-11-21 17:16:53 -05:00
enum class ERDGPassFlags : uint16
2022-06-22 18:47:11 -04:00
{
/** Pass doesn't have any inputs or outputs tracked by the graph. This may only be used by the parameterless AddPass function. */
None = 0 ,
/** Pass uses rasterization on the graphics pipe. */
Raster = 1 < < 0 ,
/** Pass uses compute on the graphics pipe. */
Compute = 1 < < 1 ,
/** Pass uses compute on the async compute pipe. */
AsyncCompute = 1 < < 2 ,
/** Pass uses copy commands on the graphics pipe. */
Copy = 1 < < 3 ,
/** Pass (and its producers) will never be culled. Necessary if outputs cannot be tracked by the graph. */
NeverCull = 1 < < 4 ,
/** Render pass begin / end is skipped and left to the user. Only valid when combined with 'Raster'. Disables render pass merging for the pass. */
SkipRenderPass = 1 < < 5 ,
/** Pass will never have its render pass merged with other passes. */
NeverMerge = 1 < < 6 ,
/** Pass will never run off the render thread. */
NeverParallel = 1 < < 7 ,
/** Pass uses copy commands but writes to a staging resource. */
Readback = Copy | NeverCull
} ;
ENUM_CLASS_FLAGS ( ERDGPassFlags ) ;
/** Flags to annotate a render graph buffer. */
enum class ERDGBufferFlags : uint8
{
None = 0 ,
/** Tag the buffer to survive through frame, that is important for multi GPU alternate frame rendering. */
MultiFrame = 1 < < 0 ,
/** The buffer is ignored by RDG tracking and will never be transitioned. Use the flag when registering a buffer with no writable GPU flags.
* Write access is not allowed for the duration of the graph . This flag is intended as an optimization to cull out tracking of read - only
* buffers that are used frequently throughout the graph . Note that it ' s the user ' s responsibility to ensure the resource is in the correct
* readable state for use with RDG passes , as RDG does not know the exact state of the resource .
*/
SkipTracking = 1 < < 1 ,
/** When set, RDG will perform its first barrier without splitting. Practically, this means the resource is left in its initial state
* until the first pass it ' s used within the graph . Without this flag , the resource is split - transitioned at the start of the graph .
*/
ForceImmediateFirstBarrier = 1 < < 2 ,
} ;
ENUM_CLASS_FLAGS ( ERDGBufferFlags ) ;
/** Flags to annotate a render graph texture. */
enum class ERDGTextureFlags : uint8
{
None = 0 ,
/** Tag the texture to survive through frame, that is important for multi GPU alternate frame rendering. */
MultiFrame = 1 < < 0 ,
/** The buffer is ignored by RDG tracking and will never be transitioned. Use the flag when registering a buffer with no writable GPU flags.
* Write access is not allowed for the duration of the graph . This flag is intended as an optimization to cull out tracking of read - only
* buffers that are used frequently throughout the graph . Note that it ' s the user ' s responsibility to ensure the resource is in the correct
* readable state for use with RDG passes , as RDG does not know the exact state of the resource .
*/
SkipTracking = 1 < < 1 ,
/** When set, RDG will perform its first barrier without splitting. Practically, this means the resource is left in its initial state
* until the first pass it ' s used within the graph . Without this flag , the resource is split - transitioned at the start of the graph .
*/
ForceImmediateFirstBarrier = 1 < < 2 ,
/** Prevents metadata decompression on this texture. */
MaintainCompression = 1 < < 3 ,
} ;
ENUM_CLASS_FLAGS ( ERDGTextureFlags ) ;
2024-07-16 20:58:37 -04:00
enum class ERDGSetupTaskWaitPoint : uint8
{
/** (Default) Setup task is synced prior to compilation. Use this mode if task mutates RDG resources (e.g. RDG buffer upload contents, buffer size callbacks, etc) */
Compile = 0 ,
/** Setup task is synced prior to execution. Use this mode if your task is stalling in RDG and doesn't affect RDG compilation in any way. */
Execute = 1 ,
MAX
} ;
2022-06-22 18:47:11 -04:00
/** Flags to annotate a view with when calling CreateUAV. */
enum class ERDGUnorderedAccessViewFlags : uint8
{
None = 0 ,
// The view will not perform UAV barriers between consecutive usage.
SkipBarrier = 1 < < 0
} ;
ENUM_CLASS_FLAGS ( ERDGUnorderedAccessViewFlags ) ;
/** The set of concrete parent resource types. */
enum class ERDGViewableResourceType : uint8
{
Texture ,
Buffer ,
MAX
} ;
/** The set of concrete view types. */
enum class ERDGViewType : uint8
{
TextureUAV ,
TextureSRV ,
BufferUAV ,
BufferSRV ,
MAX
} ;
inline ERDGViewableResourceType GetParentType ( ERDGViewType ViewType )
{
switch ( ViewType )
{
case ERDGViewType : : TextureUAV :
case ERDGViewType : : TextureSRV :
return ERDGViewableResourceType : : Texture ;
case ERDGViewType : : BufferUAV :
case ERDGViewType : : BufferSRV :
return ERDGViewableResourceType : : Buffer ;
}
checkNoEntry ( ) ;
return ERDGViewableResourceType : : MAX ;
}
enum class ERDGResourceExtractionFlags : uint8
{
None = 0 ,
// Allows the resource to remain transient. Only use this flag if you intend to register the resource back
// into the graph and release the reference. This should not be used if the resource is cached for a long
// period of time.
AllowTransient = 1 ,
} ;
ENUM_CLASS_FLAGS ( ERDGResourceExtractionFlags ) ;
enum class ERDGInitialDataFlags : uint8
{
/** Specifies the default behavior, which is to make a copy of the initial data for replay when
* the graph is executed . The user does not need to preserve lifetime of the data pointer .
*/
None = 0 ,
/** Specifies that the user will maintain ownership of the data until the graph is executed. The
* upload pass will only use a reference to store the data . Use caution with this flag since graph
* execution is deferred ! Useful to avoid the copy if the initial data lifetime is guaranteed to
* outlive the graph .
*/
NoCopy = 1 < < 0
} ;
ENUM_CLASS_FLAGS ( ERDGInitialDataFlags )
enum class ERDGPooledBufferAlignment : uint8
{
// The buffer size is not aligned.
None ,
// The buffer size is aligned up to the next page size.
Page ,
// The buffer size is aligned up to the next power of two.
PowerOfTwo
} ;
/** Returns the equivalent parent resource type for a view type. */
inline ERDGViewableResourceType GetViewableResourceType ( ERDGViewType ViewType )
{
switch ( ViewType )
{
case ERDGViewType : : TextureUAV :
case ERDGViewType : : TextureSRV :
return ERDGViewableResourceType : : Texture ;
case ERDGViewType : : BufferUAV :
case ERDGViewType : : BufferSRV :
return ERDGViewableResourceType : : Buffer ;
default :
checkNoEntry ( ) ;
return ERDGViewableResourceType : : MAX ;
}
}
using ERDGTextureMetaDataAccess = ERHITextureMetaDataAccess ;
/** Returns the associated FRHITransitionInfo plane index. */
inline int32 GetResourceTransitionPlaneForMetadataAccess ( ERDGTextureMetaDataAccess Metadata )
{
switch ( Metadata )
{
case ERDGTextureMetaDataAccess : : CompressedSurface :
case ERDGTextureMetaDataAccess : : HTile :
case ERDGTextureMetaDataAccess : : Depth :
return FRHITransitionInfo : : kDepthPlaneSlice ;
case ERDGTextureMetaDataAccess : : Stencil :
return FRHITransitionInfo : : kStencilPlaneSlice ;
default :
return 0 ;
}
}
/** HANDLE UTILITIES */
/** Handle helper class for internal tracking of RDG types. */
2024-02-12 16:47:42 -05:00
// Disable false positive buffer overrun warning during pgo linking step
PRAGMA_DISABLE_BUFFER_OVERRUN_WARNING
2022-06-22 18:47:11 -04:00
template < typename LocalObjectType , typename LocalIndexType >
class TRDGHandle
{
public :
using ObjectType = LocalObjectType ;
using IndexType = LocalIndexType ;
static const TRDGHandle Null ;
TRDGHandle ( ) = default ;
2024-03-05 12:58:21 -05:00
explicit inline TRDGHandle ( uint32 InIndex )
2022-06-22 18:47:11 -04:00
{
check ( InIndex > = 0 & & InIndex < = kNullIndex ) ;
Index = ( IndexType ) InIndex ;
}
FORCEINLINE IndexType GetIndex ( ) const { check ( IsValid ( ) ) ; return Index ; }
FORCEINLINE IndexType GetIndexUnchecked ( ) const { return Index ; }
FORCEINLINE bool IsNull ( ) const { return Index = = kNullIndex ; }
FORCEINLINE bool IsValid ( ) const { return Index ! = kNullIndex ; }
FORCEINLINE bool operator = = ( TRDGHandle Other ) const { return Index = = Other . Index ; }
FORCEINLINE bool operator ! = ( TRDGHandle Other ) const { return Index ! = Other . Index ; }
FORCEINLINE bool operator < = ( TRDGHandle Other ) const { check ( IsValid ( ) & & Other . IsValid ( ) ) ; return Index < = Other . Index ; }
FORCEINLINE bool operator > = ( TRDGHandle Other ) const { check ( IsValid ( ) & & Other . IsValid ( ) ) ; return Index > = Other . Index ; }
FORCEINLINE bool operator < ( TRDGHandle Other ) const { check ( IsValid ( ) & & Other . IsValid ( ) ) ; return Index < Other . Index ; }
FORCEINLINE bool operator > ( TRDGHandle Other ) const { check ( IsValid ( ) & & Other . IsValid ( ) ) ; return Index > Other . Index ; }
FORCEINLINE TRDGHandle & operator + = ( int32 Increment )
{
check ( int64 ( Index + Increment ) < = int64 ( kNullIndex ) ) ;
Index + = ( IndexType ) Increment ;
return * this ;
}
FORCEINLINE TRDGHandle & operator - = ( int32 Decrement )
{
check ( int64 ( Index - Decrement ) > 0 ) ;
Index - = ( IndexType ) Decrement ;
return * this ;
}
FORCEINLINE TRDGHandle operator - ( int32 Subtract ) const
{
TRDGHandle Handle = * this ;
Handle - = Subtract ;
return Handle ;
}
FORCEINLINE TRDGHandle operator + ( int32 Add ) const
{
TRDGHandle Handle = * this ;
Handle + = Add ;
return Handle ;
}
FORCEINLINE TRDGHandle & operator + + ( )
{
check ( IsValid ( ) ) ;
+ + Index ;
return * this ;
}
FORCEINLINE TRDGHandle & operator - - ( )
{
check ( IsValid ( ) ) ;
- - Index ;
return * this ;
}
// Returns the min of two pass handles. Returns null if both are null; returns the valid handle if one is null.
FORCEINLINE static TRDGHandle Min ( TRDGHandle A , TRDGHandle B )
{
// If either index is null is will fail the comparison.
return A . Index < B . Index ? A : B ;
}
// Returns the max of two pass handles. Returns null if both are null; returns the valid handle if one is null.
FORCEINLINE static TRDGHandle Max ( TRDGHandle A , TRDGHandle B )
{
// If either index is null, it will wrap around to 0 and fail the comparison.
return ( IndexType ) ( A . Index + 1 ) > ( IndexType ) ( B . Index + 1 ) ? A : B ;
}
private :
static const IndexType kNullIndex = TNumericLimits < IndexType > : : Max ( ) ;
IndexType Index = kNullIndex ;
2022-11-01 15:50:27 -04:00
friend FORCEINLINE uint32 GetTypeHash ( TRDGHandle Handle )
{
return Handle . GetIndex ( ) ;
}
} ;
2024-02-12 16:47:42 -05:00
PRAGMA_ENABLE_BUFFER_OVERRUN_WARNING
2022-06-22 18:47:11 -04:00
enum class ERDGHandleRegistryDestructPolicy
{
Registry ,
Allocator ,
Never
} ;
/** Helper handle registry class for internal tracking of RDG types. */
template < typename LocalHandleType , ERDGHandleRegistryDestructPolicy DestructPolicy = ERDGHandleRegistryDestructPolicy : : Registry >
class TRDGHandleRegistry
{
public :
using HandleType = LocalHandleType ;
using ObjectType = typename HandleType : : ObjectType ;
using IndexType = typename HandleType : : IndexType ;
2022-07-07 11:29:48 -04:00
TRDGHandleRegistry ( ) = default ;
TRDGHandleRegistry ( const TRDGHandleRegistry & ) = delete ;
TRDGHandleRegistry ( TRDGHandleRegistry & & ) = default ;
TRDGHandleRegistry & operator = ( TRDGHandleRegistry & & ) = default ;
TRDGHandleRegistry & operator = ( const TRDGHandleRegistry & ) = delete ;
~ TRDGHandleRegistry ( )
{
Clear ( ) ;
}
2022-06-22 18:47:11 -04:00
void Insert ( ObjectType * Object )
{
Array . Emplace ( Object ) ;
Object - > Handle = Last ( ) ;
}
template < typename DerivedType = ObjectType , class . . . TArgs >
DerivedType * Allocate ( FRDGAllocator & Allocator , TArgs & & . . . Args )
{
static_assert ( TIsDerivedFrom < DerivedType , ObjectType > : : Value , " You must specify a type that derives from ObjectType " ) ;
DerivedType * Object ;
if ( DestructPolicy = = ERDGHandleRegistryDestructPolicy : : Allocator )
{
Object = Allocator . Alloc < DerivedType > ( Forward < TArgs > ( Args ) . . . ) ;
}
else
{
Object = Allocator . AllocNoDestruct < DerivedType > ( Forward < TArgs > ( Args ) . . . ) ;
}
Insert ( Object ) ;
return Object ;
}
void Clear ( )
{
if ( DestructPolicy = = ERDGHandleRegistryDestructPolicy : : Registry )
{
for ( int32 Index = Array . Num ( ) - 1 ; Index > = 0 ; - - Index )
{
Array [ Index ] - > ~ ObjectType ( ) ;
}
}
Array . Empty ( ) ;
}
template < typename FunctionType >
void Enumerate ( FunctionType Function )
{
for ( ObjectType * Object : Array )
{
Function ( Object ) ;
}
}
template < typename FunctionType >
void Enumerate ( FunctionType Function ) const
{
for ( const ObjectType * Object : Array )
{
Function ( Object ) ;
}
}
FORCEINLINE const ObjectType * Get ( HandleType Handle ) const
{
return Array [ Handle . GetIndex ( ) ] ;
}
FORCEINLINE ObjectType * Get ( HandleType Handle )
{
return Array [ Handle . GetIndex ( ) ] ;
}
FORCEINLINE const ObjectType * operator [ ] ( HandleType Handle ) const
{
return Get ( Handle ) ;
}
FORCEINLINE ObjectType * operator [ ] ( HandleType Handle )
{
return Get ( Handle ) ;
}
FORCEINLINE HandleType Begin ( ) const
{
return HandleType ( 0 ) ;
}
FORCEINLINE HandleType End ( ) const
{
return HandleType ( Array . Num ( ) ) ;
}
FORCEINLINE HandleType Last ( ) const
{
return HandleType ( Array . Num ( ) - 1 ) ;
}
FORCEINLINE int32 Num ( ) const
{
return Array . Num ( ) ;
}
private :
TArray < ObjectType * , FRDGArrayAllocator > Array ;
} ;
/** Specialization of bit array with compile-time type checking for handles and a pre-configured allocator. */
template < typename HandleType >
class TRDGHandleBitArray : public TBitArray < FRDGBitArrayAllocator >
{
using Base = TBitArray < FRDGBitArrayAllocator > ;
public :
using Base : : Base ;
FORCEINLINE FBitReference operator [ ] ( HandleType Handle )
{
return Base : : operator [ ] ( Handle . GetIndex ( ) ) ;
}
FORCEINLINE const FConstBitReference operator [ ] ( HandleType Handle ) const
{
return Base : : operator [ ] ( Handle . GetIndex ( ) ) ;
}
} ;
/** Esoteric helper class which accumulates handles and will return a valid handle only if a single unique
* handle was added . Otherwise , it returns null until reset . This helper is chiefly used to track UAVs
* tagged as ' no UAV barrier ' ; such that a UAV barrier is issued only if a unique no - barrier UAV is used
* on a pass . Intended for internal use only .
*/
template < typename HandleType >
class TRDGHandleUniqueFilter
{
public :
TRDGHandleUniqueFilter ( ) = default ;
TRDGHandleUniqueFilter ( HandleType InHandle )
{
AddHandle ( InHandle ) ;
}
void Reset ( )
{
Handle = HandleType : : Null ;
}
void AddHandle ( HandleType InHandle )
{
2024-08-19 12:34:52 -04:00
checkf ( InHandle ! = NotUniqueHandle , TEXT ( " Overflowed TRDGHandleUniqueFilter " ) ) ;
2022-06-22 18:47:11 -04:00
if ( Handle ! = InHandle & & InHandle . IsValid ( ) )
{
2024-08-19 12:34:52 -04:00
Handle = Handle . IsNull ( ) ? InHandle : NotUniqueHandle ;
2022-06-22 18:47:11 -04:00
}
}
HandleType GetUniqueHandle ( ) const
{
2024-08-19 12:34:52 -04:00
return Handle ! = NotUniqueHandle ? Handle : HandleType : : Null ;
2022-06-22 18:47:11 -04:00
}
private :
2024-08-19 12:34:52 -04:00
static const HandleType NotUniqueHandle ;
2022-06-22 18:47:11 -04:00
HandleType Handle ;
} ;
template < typename ObjectType , typename IndexType >
const TRDGHandle < ObjectType , IndexType > TRDGHandle < ObjectType , IndexType > : : Null ;
2024-08-19 12:34:52 -04:00
template < typename HandleType >
const HandleType TRDGHandleUniqueFilter < HandleType > : : NotUniqueHandle ( TNumericLimits < typename HandleType : : IndexType > : : Max ( ) - 1 ) ;
2022-06-22 18:47:11 -04:00
struct FRDGTextureDesc : public FRHITextureDesc
{
static FRDGTextureDesc Create2D (
FIntPoint Size
, EPixelFormat Format
, FClearValueBinding ClearValue
, ETextureCreateFlags Flags
, uint8 NumMips = 1
, uint8 NumSamples = 1
, uint32 ExtData = 0
)
{
2022-09-30 11:36:06 -04:00
const uint16 Depth = 1 ;
const uint16 ArraySize = 1 ;
2022-06-22 18:47:11 -04:00
return FRDGTextureDesc ( ETextureDimension : : Texture2D , Flags , Format , ClearValue , { Size . X , Size . Y } , Depth , ArraySize , NumMips , NumSamples , ExtData ) ;
}
static FRDGTextureDesc Create2DArray (
FIntPoint Size
, EPixelFormat Format
, FClearValueBinding ClearValue
, ETextureCreateFlags Flags
, uint16 ArraySize
, uint8 NumMips = 1
, uint8 NumSamples = 1
, uint32 ExtData = 0
)
{
2022-09-30 11:36:06 -04:00
const uint16 Depth = 1 ;
2022-06-22 18:47:11 -04:00
return FRDGTextureDesc ( ETextureDimension : : Texture2DArray , Flags , Format , ClearValue , { Size . X , Size . Y } , Depth , ArraySize , NumMips , NumSamples , ExtData ) ;
}
static FRDGTextureDesc Create3D (
FIntVector Size
, EPixelFormat Format
, FClearValueBinding ClearValue
, ETextureCreateFlags Flags
, uint8 NumMips = 1
, uint32 ExtData = 0
)
{
2022-09-30 11:36:06 -04:00
const uint16 ArraySize = 1 ;
const uint8 LocalNumSamples = 1 ;
2022-06-22 18:47:11 -04:00
checkf ( Size . Z < = TNumericLimits < decltype ( FRDGTextureDesc : : Depth ) > : : Max ( ) , TEXT ( " Depth parameter (Size.Z) exceeds valid range " ) ) ;
2022-09-30 11:36:06 -04:00
return FRDGTextureDesc ( ETextureDimension : : Texture3D , Flags , Format , ClearValue , { Size . X , Size . Y } , ( uint16 ) Size . Z , ArraySize , NumMips , LocalNumSamples , ExtData ) ;
2022-06-22 18:47:11 -04:00
}
static FRDGTextureDesc CreateCube (
uint32 Size
, EPixelFormat Format
, FClearValueBinding ClearValue
, ETextureCreateFlags Flags
, uint8 NumMips = 1
, uint8 NumSamples = 1
, uint32 ExtData = 0
)
{
checkf ( Size < = ( uint32 ) TNumericLimits < int32 > : : Max ( ) , TEXT ( " Size parameter exceeds valid range " ) ) ;
2022-09-30 11:36:06 -04:00
const uint16 Depth = 1 ;
const uint16 ArraySize = 1 ;
2022-06-22 18:47:11 -04:00
return FRDGTextureDesc ( ETextureDimension : : TextureCube , Flags , Format , ClearValue , { ( int32 ) Size , ( int32 ) Size } , Depth , ArraySize , NumMips , NumSamples , ExtData ) ;
}
static FRDGTextureDesc CreateCubeArray (
uint32 Size
, EPixelFormat Format
, FClearValueBinding ClearValue
, ETextureCreateFlags Flags
, uint16 ArraySize
, uint8 NumMips = 1
, uint8 NumSamples = 1
, uint32 ExtData = 0
)
{
checkf ( Size < = ( uint32 ) TNumericLimits < int32 > : : Max ( ) , TEXT ( " Size parameter exceeds valid range " ) ) ;
2022-09-30 11:36:06 -04:00
const uint16 Depth = 1 ;
2022-06-22 18:47:11 -04:00
return FRDGTextureDesc ( ETextureDimension : : TextureCubeArray , Flags , Format , ClearValue , { ( int32 ) Size , ( int32 ) Size } , Depth , ArraySize , NumMips , NumSamples , ExtData ) ;
}
FRDGTextureDesc ( ) = default ;
FRDGTextureDesc (
ETextureDimension InDimension
, ETextureCreateFlags InFlags
, EPixelFormat InFormat
, FClearValueBinding InClearValue
, FIntPoint InExtent
, uint16 InDepth
, uint16 InArraySize
, uint8 InNumMips
, uint8 InNumSamples
, uint32 InExtData
)
: FRHITextureDesc ( InDimension , InFlags , InFormat , InClearValue , InExtent , InDepth , InArraySize , InNumMips , InNumSamples , InExtData )
{
}
} ;
/** FORWARD DECLARATIONS */
class FRDGBlackboard ;
class FRDGAsyncComputeBudgetScopeGuard ;
class FRDGScopedCsvStatExclusive ;
class FRDGScopedCsvStatExclusiveConditional ;
class FRDGBarrierBatch ;
class FRDGBarrierBatchBegin ;
class FRDGBarrierBatchEnd ;
class FRDGBarrierValidation ;
class FRDGEventName ;
class FRDGUserValidation ;
class FRDGViewableResource ;
2024-03-05 12:58:21 -05:00
using FRDGPassHandle = TRDGHandle < FRDGPass , uint32 > ;
2022-06-22 18:47:11 -04:00
using FRDGPassRegistry = TRDGHandleRegistry < FRDGPassHandle > ;
using FRDGPassHandleArray = TArray < FRDGPassHandle , TInlineAllocator < 4 , FRDGArrayAllocator > > ;
using FRDGPassBitArray = TRDGHandleBitArray < FRDGPassHandle > ;
2024-03-05 12:58:21 -05:00
using FRDGUniformBufferHandle = TRDGHandle < FRDGUniformBuffer , uint32 > ;
2022-06-22 18:47:11 -04:00
using FRDGUniformBufferRegistry = TRDGHandleRegistry < FRDGUniformBufferHandle > ;
using FRDGUniformBufferBitArray = TRDGHandleBitArray < FRDGUniformBufferHandle > ;
2024-03-05 12:58:21 -05:00
using FRDGViewHandle = TRDGHandle < FRDGView , uint32 > ;
2022-06-22 18:47:11 -04:00
using FRDGViewRegistry = TRDGHandleRegistry < FRDGViewHandle , ERDGHandleRegistryDestructPolicy : : Never > ;
using FRDGViewUniqueFilter = TRDGHandleUniqueFilter < FRDGViewHandle > ;
2024-01-22 11:04:48 -05:00
using FRDGViewBitArray = TRDGHandleBitArray < FRDGViewHandle > ;
2022-06-22 18:47:11 -04:00
2024-03-05 12:58:21 -05:00
using FRDGTextureHandle = TRDGHandle < FRDGTexture , uint32 > ;
2022-06-22 18:47:11 -04:00
using FRDGTextureRegistry = TRDGHandleRegistry < FRDGTextureHandle , ERDGHandleRegistryDestructPolicy : : Never > ;
using FRDGTextureBitArray = TRDGHandleBitArray < FRDGTextureHandle > ;
2024-03-05 12:58:21 -05:00
using FRDGBufferHandle = TRDGHandle < FRDGBuffer , uint32 > ;
2024-08-19 12:34:52 -04:00
using FRDGBufferReservedCommitHandle = TRDGHandle < FRDGBuffer , uint16 > ;
2022-06-22 18:47:11 -04:00
using FRDGBufferRegistry = TRDGHandleRegistry < FRDGBufferHandle , ERDGHandleRegistryDestructPolicy : : Registry > ;
using FRDGBufferBitArray = TRDGHandleBitArray < FRDGBufferHandle > ;
class FRDGBufferPool ;
class FRDGTransientRenderTarget ;
using FRDGPassHandlesByPipeline = TRHIPipelineArray < FRDGPassHandle > ;
using FRDGPassesByPipeline = TRHIPipelineArray < FRDGPass * > ;
class FRDGTrace ;
class FRDGResourceDumpContext ;
using FRDGBufferNumElementsCallback = TFunction < uint32 ( ) > ;
using FRDGBufferInitialDataCallback = TFunction < const void * ( ) > ;
using FRDGBufferInitialDataSizeCallback = TFunction < uint64 ( ) > ;
template < typename ArrayType ,
typename ArrayTypeNoRef = std : : remove_reference_t < ArrayType > ,
2023-01-27 14:11:42 -05:00
typename = typename TEnableIf < TIsTArray_V < ArrayTypeNoRef > > : : Type > using TRDGBufferArrayCallback = TFunction < const ArrayType & ( ) > ;
2022-06-22 18:47:11 -04:00
using FRDGBufferInitialDataFreeCallback = TFunction < void ( const void * InData ) > ;
2023-08-28 11:28:40 -04:00
using FRDGBufferInitialDataFillCallback = TFunction < void ( void * InData , uint32 InDataSize ) > ;
2022-06-22 18:47:11 -04:00
using FRDGDispatchGroupCountCallback = TFunction < FIntVector ( ) > ;