2019-07-16 13:08:56 -04:00
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
# include "RenderGraphValidation.h"
# if RDG_ENABLE_DEBUG
2019-09-14 09:45:25 -04:00
/** Validates that we are only executing a single render graph instance in the callstack. Used to catch if a
* user creates a second FRDGBuilder instance inside of a pass that is executing .
*/
static bool GRDGInExecutePassScope = false ;
2019-07-16 13:08:56 -04:00
/** This utility class tracks read / write events on individual resources within a pass in order to ensure
* that sub - resources are not being simultaneously bound for read / write . The RHI currently only supports
* disjoint read - write access on a texture for mip - map generation purposes .
*/
class FRDGPassResourceValidator
{
public :
static const uint32 AllMipLevels = ~ 0 ;
FRDGPassResourceValidator ( const FRDGPass & InPass )
: Pass ( InPass )
, bIsGenerateMips ( InPass . IsGenerateMips ( ) )
{ }
2019-09-14 09:45:25 -04:00
~ FRDGPassResourceValidator ( )
{
for ( const auto & KeyValue : BufferAccessMap )
{
const FRDGBufferRef Buffer = KeyValue . Key ;
const FBufferAccess & BufferAccess = KeyValue . Value ;
ensureMsgf ( ! ( BufferAccess . Bits . Read & & BufferAccess . Bits . Write ) ,
TEXT ( " Attempting to use buffer '%s' for both SRV and UAV access in pass '%s'. Only one usage at a time is allowed. " ) ,
Buffer - > Name , Pass . GetName ( ) ) ;
}
for ( const auto & KeyValue : TextureAccessMap )
{
const FRDGTextureRef Texture = KeyValue . Key ;
const FTextureAccess & TextureAccess = KeyValue . Value ;
// A generate mips pass requires both read / write access to the same texture for barriers to work correctly.
if ( bIsGenerateMips )
{
const bool bWriteWithoutRead = TextureAccess . IsAnyWrite ( ) & & ! TextureAccess . IsAnyRead ( ) ;
ensureMsgf ( ! bWriteWithoutRead ,
TEXT ( " Attempting to use texture '%s' for write but not read access in pass '%s' which is marked with 'GenerateMips'. This kind of pass only supports textures for simultaneous read and write. " ) ,
Texture - > Name , Pass . GetName ( ) ) ;
}
// A normal pass requires only read OR write access to the same resource for barriers to work correctly.
else
{
const bool bReadAndWrite = TextureAccess . IsAnyRead ( ) & & TextureAccess . IsAnyWrite ( ) ;
ensureMsgf ( ! bReadAndWrite ,
TEXT ( " Attempting to use texture '%s' for both read and write access in pass '%s' which is NOT marked with 'GenerateMips'. You must specify GenerateMips in your pass flags for this to be valid. " ) ,
Texture - > Name , Pass . GetName ( ) ) ;
}
}
}
2019-07-16 13:08:56 -04:00
void Read ( FRDGBufferRef Buffer )
{
check ( Buffer ) ;
ensureMsgf ( ! bIsGenerateMips ,
2019-09-14 09:45:25 -04:00
TEXT ( " Attempting to read from Buffer %s on Pass %s which is marked as 'GenerateMips'. Only textures are supported on this type of pass. " ) ,
2019-07-16 13:08:56 -04:00
Buffer - > Name , Pass . GetName ( ) ) ;
FBufferAccess & BufferAccess = BufferAccessMap . FindOrAdd ( Buffer ) ;
2019-09-14 09:45:25 -04:00
BufferAccess . Bits . Read = 1 ;
2019-07-16 13:08:56 -04:00
}
void Write ( FRDGBufferRef Buffer )
{
check ( Buffer ) ;
ensureMsgf ( ! bIsGenerateMips ,
2019-09-14 09:45:25 -04:00
TEXT ( " Attempting to write to Buffer %s on Pass %s which is marked as 'GenerateMips'. Only textures are supported on this type of pass. " ) ,
2019-07-16 13:08:56 -04:00
Buffer - > Name , Pass . GetName ( ) ) ;
FBufferAccess & BufferAccess = BufferAccessMap . FindOrAdd ( Buffer ) ;
2019-09-14 09:45:25 -04:00
BufferAccess . Bits . Write = 1 ;
2019-07-16 13:08:56 -04:00
}
void Read ( FRDGTextureRef Texture , uint32 MipLevel = AllMipLevels )
{
check ( Texture ) ;
const uint32 MipMask = ( MipLevel = = AllMipLevels ) ? AllMipLevels : ( 1 < < MipLevel ) ;
FTextureAccess & TextureAccess = TextureAccessMap . FindOrAdd ( Texture ) ;
TextureAccess . SetAllRead ( MipMask ) ;
}
2019-09-14 09:45:25 -04:00
void Write ( FRDGTextureRef Texture , uint32 MipLevel = AllMipLevels )
2019-07-16 13:08:56 -04:00
{
check ( Texture ) ;
const uint32 MipMask = 1 < < MipLevel ;
FTextureAccess & TextureAccess = TextureAccessMap . FindOrAdd ( Texture ) ;
TextureAccess . SetAllWrite ( MipMask ) ;
}
private :
struct FBufferAccess
{
2019-09-14 09:45:25 -04:00
union
{
uint8 PackedData = 0 ;
struct
{
uint8 Read : 1 ;
uint8 Write : 1 ;
} Bits ;
} ;
2019-07-16 13:08:56 -04:00
FORCEINLINE bool operator ! = ( FBufferAccess Other ) const
{
2019-09-14 09:45:25 -04:00
return PackedData ! = Other . PackedData ;
2019-07-16 13:08:56 -04:00
}
FORCEINLINE bool operator < ( FBufferAccess Other ) const
{
2019-09-14 09:45:25 -04:00
return PackedData < Other . PackedData ;
2019-07-16 13:08:56 -04:00
}
} ;
struct FTextureAccess
{
FTextureAccess ( ) = default ;
union
{
uint64 PackedData = 0 ;
struct
{
uint32 Read ;
uint32 Write ;
} Mips ;
} ;
2019-09-14 09:45:25 -04:00
FORCEINLINE bool IsAnyRead ( uint32 MipMask = AllMipLevels ) const
2019-07-16 13:08:56 -04:00
{
return ( Mips . Read & MipMask ) ! = 0 ;
}
2019-09-14 09:45:25 -04:00
FORCEINLINE bool IsAnyWrite ( uint32 MipMask = AllMipLevels ) const
2019-07-16 13:08:56 -04:00
{
return ( Mips . Write & MipMask ) ! = 0 ;
}
2019-09-14 09:45:25 -04:00
FORCEINLINE void SetAllRead ( uint32 MipMask = AllMipLevels )
2019-07-16 13:08:56 -04:00
{
Mips . Read | = MipMask ;
}
2019-09-14 09:45:25 -04:00
FORCEINLINE void SetAllWrite ( uint32 MipMask = AllMipLevels )
2019-07-16 13:08:56 -04:00
{
Mips . Write | = MipMask ;
}
FORCEINLINE bool operator ! = ( FTextureAccess Other ) const
{
return PackedData ! = Other . PackedData ;
}
FORCEINLINE bool operator < ( FTextureAccess Other ) const
{
return PackedData < Other . PackedData ;
}
} ;
TMap < FRDGBufferRef , FBufferAccess , SceneRenderingSetAllocator > BufferAccessMap ;
TMap < FRDGTextureRef , FTextureAccess , SceneRenderingSetAllocator > TextureAccessMap ;
const FRDGPass & Pass ;
const bool bIsGenerateMips ;
} ;
FRDGUserValidation : : ~ FRDGUserValidation ( )
{
checkf ( bHasExecuted , TEXT ( " Render graph execution is required to ensure consistency with immediate mode. " ) ) ;
}
void FRDGUserValidation : : ExecuteGuard ( const TCHAR * Operation , const TCHAR * ResourceName )
{
checkf ( ! bHasExecuted , TEXT ( " Render graph operation '%s' with resource '%s' must be performed prior to graph execution. " ) , Operation , ResourceName ) ;
}
2019-09-14 09:45:25 -04:00
void FRDGUserValidation : : ValidateCreateTexture ( FRDGTextureRef Texture )
2019-07-16 13:08:56 -04:00
{
2019-09-14 09:45:25 -04:00
check ( Texture ) ;
if ( IsRDGDebugEnabled ( ) )
{
TrackedTextures . Add ( Texture ) ;
}
2019-07-16 13:08:56 -04:00
}
2019-09-14 09:45:25 -04:00
void FRDGUserValidation : : ValidateCreateBuffer ( FRDGBufferRef Buffer )
2019-07-16 13:08:56 -04:00
{
2019-09-14 09:45:25 -04:00
check ( Buffer ) ;
if ( IsRDGDebugEnabled ( ) )
{
TrackedBuffers . Add ( Buffer ) ;
}
2019-07-16 13:08:56 -04:00
}
2019-09-14 09:45:25 -04:00
void FRDGUserValidation : : ValidateCreateExternalTexture ( FRDGTextureRef Texture )
{
ValidateCreateTexture ( Texture ) ;
Texture - > MarkAsExternal ( ) ;
}
void FRDGUserValidation : : ValidateCreateExternalBuffer ( FRDGBufferRef Buffer )
{
ValidateCreateBuffer ( Buffer ) ;
Buffer - > MarkAsExternal ( ) ;
}
void FRDGUserValidation : : ValidateExtractResource ( FRDGParentResourceRef Resource )
2019-07-16 13:08:56 -04:00
{
check ( Resource ) ;
checkf ( Resource - > HasBeenProduced ( ) ,
TEXT ( " Unable to queue the extraction of the resource %s because it has not been produced by any pass. " ) ,
Resource - > Name ) ;
/** Increment pass access counts for externally registered buffers and textures to avoid
* emitting a ' produced but never used ' warning . We don ' t have the history of registered
* resources to be able to emit a proper warning .
*/
Resource - > PassAccessCount + + ;
}
2019-09-14 09:45:25 -04:00
void FRDGUserValidation : : RemoveUnusedWarning ( FRDGParentResourceRef Resource )
2019-07-16 13:08:56 -04:00
{
check ( Resource ) ;
2019-09-14 09:45:25 -04:00
// Removes 'produced but not used' warning.
2019-07-16 13:08:56 -04:00
Resource - > PassAccessCount + + ;
2019-09-14 09:45:25 -04:00
// Removes 'not used' warning.
Resource - > bIsActuallyUsedByPass = true ;
2019-07-16 13:08:56 -04:00
}
void FRDGUserValidation : : ValidateAllocPassParameters ( const void * Parameters )
{
check ( Parameters ) ;
AllocatedUnusedPassParameters . Add ( Parameters ) ;
}
2019-09-14 09:45:25 -04:00
void FRDGUserValidation : : ValidateAddPass ( const FRDGPass * Pass , bool bSkipPassAccessMarking )
2019-07-16 13:08:56 -04:00
{
checkf ( ! bHasExecuted , TEXT ( " Render graph pass %s needs to be added before the builder execution. " ) , Pass - > GetName ( ) ) ;
{
const void * ParameterStructData = Pass - > GetParameters ( ) . GetContents ( ) ;
/** The lifetime of each pass parameter structure must extend until deferred pass execution; therefore, it needs to be
* allocated with FRDGBuilder : : AllocParameters ( ) . Also , all references held by the parameter structure are released
* immediately after pass execution , so a pass parameter struct instance must be 1 - to - 1 with a pass instance ( i . e . one
* per AddPass ( ) call ) .
*/
checkf (
AllocatedUnusedPassParameters . Contains ( ParameterStructData ) ,
TEXT ( " The pass parameter structure has not been allocated for correct life time FRDGBuilder::AllocParameters() or has already " )
TEXT ( " been used by another previous FRDGBuilder::AddPass(). " ) ) ;
AllocatedUnusedPassParameters . Remove ( ParameterStructData ) ;
}
2019-09-14 09:45:25 -04:00
const auto MarkAsProduced = [ Pass , bSkipPassAccessMarking ] ( FRDGParentResourceRef Resource )
{
if ( ! bSkipPassAccessMarking )
{
Resource - > MarkAsProducedBy ( Pass ) ;
Resource - > PassAccessCount + + ;
}
} ;
const auto MarkAsConsumed = [ Pass , bSkipPassAccessMarking ] ( FRDGParentResourceRef Resource )
{
if ( ! bSkipPassAccessMarking )
{
Resource - > PassAccessCount + + ;
}
} ;
2019-07-16 13:08:56 -04:00
const FRenderTargetBindingSlots * RenderTargetBindingSlots = nullptr ;
const TCHAR * PassName = Pass - > GetName ( ) ;
2019-09-14 09:45:25 -04:00
const bool bIsRaster = Pass - > IsRaster ( ) ;
2019-07-16 13:08:56 -04:00
const bool bIsCompute = Pass - > IsCompute ( ) ;
2019-09-14 09:45:25 -04:00
const bool bIsCopy = Pass - > IsCopy ( ) ;
2019-07-16 13:08:56 -04:00
const bool bIsGeneratingMips = Pass - > IsGenerateMips ( ) ;
2019-09-14 09:45:25 -04:00
/** Validate that the user set the correct pass flags. */
{
uint32 Count = 0 ;
Count + = bIsRaster ? 1 : 0 ;
Count + = bIsCompute ? 1 : 0 ;
Count + = bIsCopy ? 1 : 0 ;
checkf ( Count = = 1 ,
TEXT ( " Pass %s must be declared as either Raster, Compute, or Copy. These flags cannot be combined. " ) ,
PassName ) ;
if ( bIsGeneratingMips )
{
checkf ( bIsRaster | | bIsCompute ,
TEXT ( " Pass %s is declared as generating mips. This is only supported by Raster or Compute passes. " ) ,
PassName ) ;
}
}
2019-07-16 13:08:56 -04:00
FRDGPassResourceValidator PassResourceValidator ( * Pass ) ;
FRDGPassParameterStruct ParameterStruct = Pass - > GetParameters ( ) ;
const uint32 ParameterCount = ParameterStruct . GetParameterCount ( ) ;
for ( uint32 ParameterIndex = 0 ; ParameterIndex < ParameterCount ; + + ParameterIndex )
{
FRDGPassParameter Parameter = ParameterStruct . GetParameter ( ParameterIndex ) ;
switch ( Parameter . GetType ( ) )
{
case UBMT_RDG_TEXTURE :
{
if ( FRDGTextureRef Texture = Parameter . GetAsTexture ( ) )
{
PassResourceValidator . Read ( Texture ) ;
2019-09-14 09:45:25 -04:00
ensureMsgf ( Texture - > HasBeenProduced ( ) ,
TEXT ( " Pass %s has a dependency on the texture %s that has never been produced. " ) ,
2019-07-16 13:08:56 -04:00
PassName , Texture - > Name ) ;
2019-09-14 09:45:25 -04:00
MarkAsConsumed ( Texture ) ;
2019-07-16 13:08:56 -04:00
}
}
break ;
case UBMT_RDG_TEXTURE_SRV :
{
if ( FRDGTextureSRVRef SRV = Parameter . GetAsTextureSRV ( ) )
{
2019-09-14 09:45:25 -04:00
FRDGTextureRef Texture = SRV - > GetParent ( ) ;
2019-07-16 13:08:56 -04:00
PassResourceValidator . Read ( Texture , SRV - > Desc . MipLevel ) ;
2019-09-14 09:45:25 -04:00
ensureMsgf ( Texture - > HasBeenProduced ( ) ,
TEXT ( " Pass %s has a dependency on the texture %s that has never been produced. " ) ,
2019-07-16 13:08:56 -04:00
PassName , Texture - > Name ) ;
2019-09-14 09:45:25 -04:00
MarkAsConsumed ( Texture ) ;
2019-07-16 13:08:56 -04:00
}
}
break ;
case UBMT_RDG_TEXTURE_UAV :
{
if ( FRDGTextureUAVRef UAV = Parameter . GetAsTextureUAV ( ) )
{
2019-09-14 09:45:25 -04:00
FRDGTextureRef Texture = UAV - > GetParent ( ) ;
2019-07-16 13:08:56 -04:00
PassResourceValidator . Write ( Texture , UAV - > Desc . MipLevel ) ;
2019-09-14 09:45:25 -04:00
MarkAsProduced ( Texture ) ;
2019-07-16 13:08:56 -04:00
}
}
break ;
case UBMT_RDG_BUFFER :
{
if ( FRDGBufferRef Buffer = Parameter . GetAsBuffer ( ) )
{
PassResourceValidator . Read ( Buffer ) ;
2019-09-14 09:45:25 -04:00
ensureMsgf ( Buffer - > HasBeenProduced ( ) ,
TEXT ( " Pass %s has a dependency on the buffer %s that has never been produced. " ) ,
2019-07-16 13:08:56 -04:00
PassName , Buffer - > Name ) ;
2019-09-14 09:45:25 -04:00
MarkAsConsumed ( Buffer ) ;
2019-07-16 13:08:56 -04:00
}
}
break ;
case UBMT_RDG_BUFFER_SRV :
{
if ( FRDGBufferSRVRef SRV = Parameter . GetAsBufferSRV ( ) )
{
2019-09-14 09:45:25 -04:00
FRDGBufferRef Buffer = SRV - > GetParent ( ) ;
2019-07-16 13:08:56 -04:00
PassResourceValidator . Read ( Buffer ) ;
2019-09-14 09:45:25 -04:00
ensureMsgf ( Buffer - > HasBeenProduced ( ) ,
TEXT ( " Pass %s has a dependency on the buffer %s that has never been produced. " ) ,
2019-07-16 13:08:56 -04:00
PassName , SRV - > Desc . Buffer - > Name ) ;
2019-09-14 09:45:25 -04:00
MarkAsConsumed ( Buffer ) ;
2019-07-16 13:08:56 -04:00
}
}
break ;
case UBMT_RDG_BUFFER_UAV :
{
if ( FRDGBufferUAVRef UAV = Parameter . GetAsBufferUAV ( ) )
{
2019-09-14 09:45:25 -04:00
FRDGBufferRef Buffer = UAV - > GetParent ( ) ;
2019-07-16 13:08:56 -04:00
PassResourceValidator . Write ( Buffer ) ;
2019-09-14 09:45:25 -04:00
MarkAsProduced ( Buffer ) ;
}
}
break ;
case UBMT_RDG_TEXTURE_COPY_DEST :
{
if ( FRDGTextureRef Texture = Parameter . GetAsTexture ( ) )
{
PassResourceValidator . Write ( Texture ) ;
MarkAsProduced ( Texture ) ;
}
}
break ;
case UBMT_RDG_BUFFER_COPY_DEST :
{
if ( FRDGBufferRef Buffer = Parameter . GetAsBuffer ( ) )
{
PassResourceValidator . Write ( Buffer ) ;
MarkAsProduced ( Buffer ) ;
2019-07-16 13:08:56 -04:00
}
}
break ;
case UBMT_RENDER_TARGET_BINDING_SLOTS :
{
if ( ! RenderTargetBindingSlots )
{
RenderTargetBindingSlots = & Parameter . GetAsRenderTargetBindingSlots ( ) ;
}
else if ( IsRDGDebugEnabled ( ) )
{
2019-09-14 09:45:25 -04:00
EmitRDGWarningf ( TEXT ( " Pass %s have duplicated render target binding slots. " ) , PassName ) ;
2019-07-16 13:08:56 -04:00
}
}
break ;
}
}
/** Validate that raster passes have render target binding slots and compute passes don't. */
if ( RenderTargetBindingSlots )
{
2019-09-14 09:45:25 -04:00
checkf ( bIsRaster , TEXT ( " Pass '%s' has render target binding slots but is flagged as 'Compute'. " ) , PassName ) ;
2019-07-16 13:08:56 -04:00
}
else
{
2019-09-14 09:45:25 -04:00
checkf ( ! bIsRaster , TEXT ( " Pass '%s' is missing render target binding slots. Set the 'Compute' or 'Copy' flag if render targets are not required. " ) , PassName ) ;
2019-07-16 13:08:56 -04:00
}
/** Validate render target / depth stencil binding usage. */
if ( RenderTargetBindingSlots )
{
const auto & RenderTargets = RenderTargetBindingSlots - > Output ;
{
const auto & DepthStencil = RenderTargetBindingSlots - > DepthStencil ;
if ( FRDGTextureRef Texture = DepthStencil . GetTexture ( ) )
{
2019-09-25 13:55:37 -04:00
//ensureMsgf(Texture->Desc.NumSamples == 1,
// TEXT("Pass '%s' uses an MSAA depth-stencil render target. This is not yet supported."),
// Pass->GetName());
2019-07-16 13:08:56 -04:00
ensureMsgf ( ! bIsGeneratingMips ,
TEXT ( " Pass '%s' is marked to generate mips but has a depth stencil texture. This is not supported. " ) ,
Pass - > GetName ( ) ) ;
2019-09-14 09:45:25 -04:00
checkf (
Texture - > Desc . TargetableFlags & TexCreate_DepthStencilTargetable ,
TEXT ( " Pass '%s' attempted to bind texture '%s' as a depth stencil render target, but the texture has not been created with TexCreate_DepthStencilTargetable. " ) ,
PassName , Texture - > Name ) ;
2019-07-16 13:08:56 -04:00
// Depth stencil only supports one mip, since there isn't actually a way to select the mip level.
const uint32 MipLevel = 0 ;
check ( Texture - > Desc . NumMips = = 1 ) ;
if ( DepthStencil . GetDepthStencilAccess ( ) . IsAnyWrite ( ) )
{
PassResourceValidator . Write ( Texture , MipLevel ) ;
2019-09-14 09:45:25 -04:00
MarkAsProduced ( Texture ) ;
2019-07-16 13:08:56 -04:00
}
else
{
PassResourceValidator . Read ( Texture , MipLevel ) ;
2019-09-14 09:45:25 -04:00
MarkAsConsumed ( Texture ) ;
2019-07-16 13:08:56 -04:00
}
}
}
const uint32 RenderTargetCount = RenderTargets . Num ( ) ;
{
/** Tracks the number of contiguous, non-null textures in the render target output array. */
uint32 ValidRenderTargetCount = 0 ;
for ( uint32 RenderTargetIndex = 0 ; RenderTargetIndex < RenderTargetCount ; + + RenderTargetIndex )
{
const FRenderTargetBinding & RenderTarget = RenderTargets [ RenderTargetIndex ] ;
if ( FRDGTextureRef Texture = RenderTarget . GetTexture ( ) )
{
PassResourceValidator . Write ( Texture , RenderTarget . GetMipIndex ( ) ) ;
const bool bIsLoadAction = RenderTarget . GetLoadAction ( ) = = ERenderTargetLoadAction : : ELoad ;
2019-07-23 17:49:47 -04:00
ensureMsgf (
Texture - > Desc . TargetableFlags & TexCreate_RenderTargetable ,
TEXT ( " Pass '%s' attempted to bind texture '%s' as a render target, but the texture has not been created with TexCreate_RenderTargetable. " ) ,
PassName , Texture - > Name ) ;
/** Validate that load action is correct. We can only load contents if a pass previously produced something. */
{
const bool bIsLoadActionInvalid = bIsLoadAction & & ! Texture - > HasBeenProduced ( ) ;
checkf (
! bIsLoadActionInvalid ,
TEXT ( " Pass '%s' attempted to bind texture '%s' as a render target with the 'Load' action specified, but the texture has not been produced yet. The render target must use either 'Clear' or 'NoAction' action instead. " ) ,
PassName ,
Texture - > Name ) ;
}
/** Validate that any previously produced texture contents are loaded. This occurs if the user failed to specify a load action
* on a texture that was produced by a previous pass , effectively losing that data . This can also happen if the user ' re - uses '
* a texture for some other purpose . The latter is considered bad practice , since it increases memory pressure on the render
* target pool . Instead , the user should create a new texture instance . An exception to this rule are untracked render targets ,
* which are not actually managed by the render target pool and likely represent the frame buffer .
*/
{
2019-09-14 09:45:25 -04:00
// Ignore external textures which are always marked as produced. We don't need to enforce this warning on them.
const bool bHasBeenProduced = Texture - > HasBeenProduced ( ) & & ! Texture - > IsExternal ( ) ;
2019-07-23 17:49:47 -04:00
// We only validate single-mip textures since we don't track production at the subresource level.
2019-09-14 09:45:25 -04:00
const bool bFailedToLoadProducedContent = ! bIsLoadAction & & bHasBeenProduced & & Texture - > Desc . NumMips = = 1 ;
2019-07-23 17:49:47 -04:00
// Untracked render targets aren't actually managed by the render target pool.
const bool bIsUntrackedRenderTarget = Texture - > PooledRenderTarget & & ! Texture - > PooledRenderTarget - > IsTracked ( ) ;
ensureMsgf ( ! bFailedToLoadProducedContent | | bIsUntrackedRenderTarget ,
TEXT ( " Pass '%s' attempted to bind texture '%s' as a render target without the 'Load' action specified, despite a prior pass having produced it. It's invalid to completely clobber the contents of a resource. Create a new texture instance instead. " ) ,
PassName ,
Texture - > Name ) ;
}
2019-07-16 13:08:56 -04:00
/** Mark the pass as a producer for render targets with a store action. */
2019-09-14 09:45:25 -04:00
MarkAsProduced ( Texture ) ;
2019-07-16 13:08:56 -04:00
}
else
{
/** Found end of contiguous interval of valid render targets. */
ValidRenderTargetCount = RenderTargetIndex ;
break ;
}
}
/** Validate that no holes exist in the render target output array. Render targets must be bound contiguously. */
for ( uint32 RenderTargetIndex = ValidRenderTargetCount ; RenderTargetIndex < RenderTargetCount ; + + RenderTargetIndex )
{
const FRenderTargetBinding & RenderTarget = RenderTargets [ RenderTargetIndex ] ;
checkf ( RenderTarget . GetTexture ( ) = = nullptr , TEXT ( " Render targets must be packed. No empty spaces in the array. " ) ) ;
}
}
}
}
void FRDGUserValidation : : ValidateExecuteBegin ( )
{
checkf ( ! bHasExecuted , TEXT ( " Render graph execution should only happen once to ensure consistency with immediate mode. " ) ) ;
/** FRDGBuilder::AllocParameters() allocates shader parameter structure for the lifetime until pass execution.
* But they are allocated on a FMemStack for CPU performance , and are destructed immediately after pass execution .
* Therefore allocating pass parameter unused by a FRDGBuilder : : AddPass ( ) can lead on a memory leak of RHI resources
* that have been reference in the parameter structure .
*/
checkf (
AllocatedUnusedPassParameters . Num ( ) = = 0 ,
TEXT ( " %i pass parameter structure has been allocated with FRDGBuilder::AllocParameters(), but has not be used by a " )
TEXT ( " FRDGBuilder::AddPass() that can cause RHI resource leak. " ) , AllocatedUnusedPassParameters . Num ( ) ) ;
}
void FRDGUserValidation : : ValidateExecuteEnd ( )
{
bHasExecuted = true ;
2019-09-14 09:45:25 -04:00
if ( IsRDGDebugEnabled ( ) )
2019-07-16 13:08:56 -04:00
{
2019-09-14 09:45:25 -04:00
auto ValidateResourceAtExecuteEnd = [ ] ( const FRDGParentResourceRef Resource )
2019-07-16 13:08:56 -04:00
{
2019-09-14 09:45:25 -04:00
check ( Resource - > ReferenceCount = = 0 ) ;
2019-07-16 13:08:56 -04:00
2019-09-14 09:45:25 -04:00
const bool bProducedButNeverUsed = Resource - > PassAccessCount = = 1 & & Resource - > FirstProducer ;
if ( bProducedButNeverUsed )
{
check ( Resource - > HasBeenProduced ( ) ) ;
EmitRDGWarningf (
TEXT ( " Resource %s has been produced by the pass %s, but never used by another pass. " ) ,
Resource - > Name , Resource - > FirstProducer - > GetName ( ) ) ;
}
} ;
for ( const FRDGTextureRef Texture : TrackedTextures )
{
ValidateResourceAtExecuteEnd ( Texture ) ;
bool bHasBeenProducedByGraph = ! Texture - > IsExternal ( ) & & Texture - > PassAccessCount > 0 ;
if ( bHasBeenProducedByGraph & & ! Texture - > bHasNeededUAV & & ( Texture - > Desc . TargetableFlags & TexCreate_UAV ) )
{
EmitRDGWarningf (
TEXT ( " Resource %s first produced by the pass %s had the TexCreate_UAV flag, but no UAV has been used. " ) ,
Texture - > Name , Texture - > FirstProducer - > GetName ( ) ) ;
}
if ( bHasBeenProducedByGraph & & ! Texture - > bHasBeenBoundAsRenderTarget & & ( Texture - > Desc . TargetableFlags & TexCreate_RenderTargetable ) )
{
EmitRDGWarningf (
TEXT ( " Resource %s first produced by the pass %s had the TexCreate_RenderTargetable flag, but has never been bound as a render target of a pass. " ) ,
Texture - > Name , Texture - > FirstProducer - > GetName ( ) ) ;
}
}
for ( const FRDGBufferRef Buffer : TrackedBuffers )
{
ValidateResourceAtExecuteEnd ( Buffer ) ;
2019-07-16 13:08:56 -04:00
}
}
2019-09-14 09:45:25 -04:00
TrackedTextures . Empty ( ) ;
TrackedBuffers . Empty ( ) ;
2019-07-16 13:08:56 -04:00
}
void FRDGUserValidation : : ValidateExecutePassBegin ( const FRDGPass * Pass )
{
2019-09-14 09:45:25 -04:00
checkf ( ! GRDGInExecutePassScope , TEXT ( " Render graph is being executed recursively. This usually means a separate FRDGBuilder instance was created inside of an executing pass. " ) ) ;
GRDGInExecutePassScope = true ;
2019-07-16 13:08:56 -04:00
SetAllowRHIAccess ( Pass , true ) ;
2019-09-14 09:45:25 -04:00
if ( IsRDGDebugEnabled ( ) )
{
FRDGPassParameterStruct ParameterStruct = Pass - > GetParameters ( ) ;
const uint32 ParameterCount = ParameterStruct . GetParameterCount ( ) ;
for ( uint32 ParameterIndex = 0 ; ParameterIndex < ParameterCount ; + + ParameterIndex )
{
FRDGPassParameter Parameter = ParameterStruct . GetParameter ( ParameterIndex ) ;
if ( Parameter . GetType ( ) = = UBMT_RDG_TEXTURE_UAV )
{
if ( FRDGTextureUAVRef UAV = Parameter . GetAsTextureUAV ( ) )
{
FRDGTextureRef Texture = UAV - > Desc . Texture ;
Texture - > bHasNeededUAV = true ;
}
}
else if ( Parameter . GetType ( ) = = UBMT_RENDER_TARGET_BINDING_SLOTS )
{
const FRenderTargetBindingSlots & RenderTargetBindingSlots = Parameter . GetAsRenderTargetBindingSlots ( ) ;
const auto & RenderTargets = RenderTargetBindingSlots . Output ;
const auto & DepthStencil = RenderTargetBindingSlots . DepthStencil ;
const uint32 RenderTargetCount = RenderTargets . Num ( ) ;
for ( uint32 RenderTargetIndex = 0 ; RenderTargetIndex < RenderTargetCount ; RenderTargetIndex + + )
{
const FRenderTargetBinding & RenderTarget = RenderTargets [ RenderTargetIndex ] ;
if ( FRDGTextureRef Texture = RenderTarget . GetTexture ( ) )
{
Texture - > bHasBeenBoundAsRenderTarget = true ;
}
else
{
break ;
}
}
if ( FRDGTextureRef Texture = DepthStencil . GetTexture ( ) )
{
Texture - > bHasBeenBoundAsRenderTarget = true ;
}
}
}
}
2019-07-16 13:08:56 -04:00
}
void FRDGUserValidation : : ValidateExecutePassEnd ( const FRDGPass * Pass )
{
SetAllowRHIAccess ( Pass , false ) ;
FRDGPassParameterStruct ParameterStruct = Pass - > GetParameters ( ) ;
const uint32 ParameterCount = ParameterStruct . GetParameterCount ( ) ;
if ( IsRDGDebugEnabled ( ) )
{
uint32 TrackedResourceCount = 0 ;
uint32 UsedResourceCount = 0 ;
for ( uint32 ParameterIndex = 0 ; ParameterIndex < ParameterCount ; + + ParameterIndex )
{
FRDGPassParameter Parameter = ParameterStruct . GetParameter ( ParameterIndex ) ;
if ( Parameter . IsResource ( ) )
{
if ( FRDGResourceRef Resource = Parameter . GetAsResource ( ) )
{
TrackedResourceCount + + ;
UsedResourceCount + = Resource - > bIsActuallyUsedByPass ? 1 : 0 ;
}
}
}
if ( TrackedResourceCount ! = UsedResourceCount )
{
FString WarningMessage = FString : : Printf (
2019-09-14 09:45:25 -04:00
TEXT ( " '%d' of the '%d' resources of the pass '%s' were not actually used. " ) ,
2019-07-16 13:08:56 -04:00
TrackedResourceCount - UsedResourceCount , TrackedResourceCount , Pass - > GetName ( ) ) ;
for ( uint32 ParameterIndex = 0 ; ParameterIndex < ParameterCount ; + + ParameterIndex )
{
FRDGPassParameter Parameter = ParameterStruct . GetParameter ( ParameterIndex ) ;
if ( Parameter . IsResource ( ) )
{
if ( const FRDGResourceRef Resource = Parameter . GetAsResource ( ) )
{
if ( ! Resource - > bIsActuallyUsedByPass )
{
WarningMessage + = FString : : Printf ( TEXT ( " \n %s " ) , Resource - > Name ) ;
}
}
}
}
EmitRDGWarning ( WarningMessage ) ;
}
}
for ( uint32 ParameterIndex = 0 ; ParameterIndex < ParameterCount ; + + ParameterIndex )
{
FRDGPassParameter Parameter = ParameterStruct . GetParameter ( ParameterIndex ) ;
if ( Parameter . IsResource ( ) )
{
if ( const FRDGResourceRef Resource = Parameter . GetAsResource ( ) )
{
Resource - > bIsActuallyUsedByPass = false ;
}
}
}
2019-09-14 09:45:25 -04:00
GRDGInExecutePassScope = false ;
2019-07-16 13:08:56 -04:00
}
void FRDGUserValidation : : SetAllowRHIAccess ( const FRDGPass * Pass , bool bAllowAccess )
{
FRDGPassParameterStruct ParameterStruct = Pass - > GetParameters ( ) ;
const uint32 ParameterCount = ParameterStruct . GetParameterCount ( ) ;
for ( uint32 ParameterIndex = 0 ; ParameterIndex < ParameterCount ; + + ParameterIndex )
{
FRDGPassParameter Parameter = ParameterStruct . GetParameter ( ParameterIndex ) ;
if ( Parameter . IsResource ( ) )
{
if ( FRDGResourceRef Resource = Parameter . GetAsResource ( ) )
{
Resource - > bAllowRHIAccess = bAllowAccess ;
}
}
2019-09-14 09:45:25 -04:00
else if ( Parameter . IsRenderTargetBindingSlots ( ) )
2019-07-16 13:08:56 -04:00
{
const FRenderTargetBindingSlots & RenderTargetBindingSlots = Parameter . GetAsRenderTargetBindingSlots ( ) ;
const auto & RenderTargets = RenderTargetBindingSlots . Output ;
const auto & DepthStencil = RenderTargetBindingSlots . DepthStencil ;
const uint32 RenderTargetCount = RenderTargets . Num ( ) ;
for ( uint32 RenderTargetIndex = 0 ; RenderTargetIndex < RenderTargetCount ; RenderTargetIndex + + )
{
const FRenderTargetBinding & RenderTarget = RenderTargets [ RenderTargetIndex ] ;
if ( FRDGTextureRef Texture = RenderTarget . GetTexture ( ) )
{
Texture - > bAllowRHIAccess = bAllowAccess ;
}
else
{
break ;
}
}
if ( FRDGTextureRef Texture = DepthStencil . GetTexture ( ) )
{
Texture - > bAllowRHIAccess = bAllowAccess ;
}
}
}
}
# endif