2021-12-03 02:41:52 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "RenderGraph.h"
# include "HAL/PlatformFileManager.h"
2021-12-03 16:04:00 -05:00
# include "HAL/PlatformMisc.h"
2021-12-03 02:41:52 -05:00
# include "Misc/FileHelper.h"
# include "Misc/WildcardString.h"
2021-12-03 16:04:00 -05:00
# include "Misc/OutputDeviceRedirector.h"
# include "Runtime/Launch/Resources/Version.h"
2021-12-03 02:41:52 -05:00
# include "BuildSettings.h"
# include "Serialization/JsonTypes.h"
# include "Serialization/JsonReader.h"
# include "Serialization/JsonSerializer.h"
2021-12-03 16:04:00 -05:00
# include "GenericPlatform/GenericPlatformCrashContext.h"
# include "GenericPlatform/GenericPlatformDriver.h"
# include "RenderGraphUtils.h"
# include "GlobalShader.h"
2021-12-03 16:04:39 -05:00
# include "RHIValidation.h"
2021-12-03 02:41:52 -05:00
# if RDG_DUMP_RESOURCES
namespace
{
static TAutoConsoleVariable < FString > GDumpGPURootCVar (
TEXT ( " r.DumpGPU.Root " ) ,
TEXT ( " * " ) ,
TEXT ( " Allows to filter the tree when using r.DumpGPU command, the pattern match is case sensitive. " ) ,
ECVF_Default ) ;
2021-12-03 16:04:00 -05:00
static TAutoConsoleVariable < int32 > GDumpTextureCVar (
TEXT ( " r.DumpGPU.Texture " ) , 2 ,
TEXT ( " Whether to dump textures. \n " )
TEXT ( " 0: Ignores all textures \n " )
TEXT ( " 1: Dump only textures' descriptors \n " )
TEXT ( " 2: Dump textures' descriptors and binaries (default) " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > GDumpBufferCVar (
TEXT ( " r.DumpGPU.Buffer " ) , 2 ,
TEXT ( " Whether to dump buffer. \n " )
TEXT ( " 0: Ignores all buffers \n " )
TEXT ( " 1: Dump only buffers' descriptors \n " )
TEXT ( " 2: Dump buffers' descriptors and binaries (default) " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > GDumpGPUPassParameters (
TEXT ( " r.DumpGPU.PassParameters " ) , 1 ,
TEXT ( " Whether to dump the pass parameters. " ) ,
ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > GDumpGPUDraws (
TEXT ( " r.DumpGPU.Draws " ) , 0 ,
TEXT ( " Whether to dump resource after each individual draw call (disabled by default). " ) ,
ECVF_Default ) ;
2022-01-07 08:18:56 -05:00
static TAutoConsoleVariable < int32 > GDumpGPUMask (
TEXT ( " r.DumpGPU.Mask " ) , 1 ,
TEXT ( " Whether to include GPU mask in the name of each Pass (has no effect unless system has multiple GPUs). " ) ,
ECVF_Default ) ;
2021-12-03 16:04:00 -05:00
static TAutoConsoleVariable < int32 > GDumpExploreCVar (
TEXT ( " r.DumpGPU.Explore " ) , 1 ,
TEXT ( " Whether to open file explorer to where the GPU dump on completion (enabled by default). " ) ,
ECVF_Default ) ;
static TAutoConsoleVariable < int32 > GDumpRenderingConsoleVariablesCVar (
TEXT ( " r.DumpGPU.ConsoleVariables " ) , 1 ,
TEXT ( " Whether to dump rendering console variables (enabled by default). " ) ,
ECVF_Default ) ;
2022-03-15 15:53:25 -04:00
static TAutoConsoleVariable < int32 > GDumpGPUCompressResources (
TEXT ( " r.DumpGPU.CompressResources " ) , 0 ,
TEXT ( " Whether to compress resource binary. \n " )
TEXT ( " 0: Disabled (default) \n " )
TEXT ( " 1: Zlib \n " )
TEXT ( " 2: GZip " ) ,
ECVF_Default ) ;
2021-12-03 16:04:00 -05:00
static TAutoConsoleVariable < int32 > GDumpTestEnableDiskWrite (
TEXT ( " r.DumpGPU.Test.EnableDiskWrite " ) , 1 ,
TEXT ( " Master switch whether any files should be written to disk, used for r.DumpGPU automation tests to not fill up workers' hard drive. " ) ,
ECVF_Default ) ;
2021-12-03 16:04:47 -05:00
static TAutoConsoleVariable < int32 > GDumpTestPrettifyResourceFileNames (
TEXT ( " r.DumpGPU.Test.PrettifyResourceFileNames " ) , 0 ,
TEXT ( " Whether the resource file names should include resource name. May increase the likelyness of running into Windows' filepath limit. " ) ,
ECVF_RenderThreadSafe ) ;
2021-12-03 16:04:00 -05:00
static TAutoConsoleVariable < FString > GDumpGPUDirectoryCVar (
TEXT ( " r.DumpGPU.Directory " ) , TEXT ( " " ) ,
TEXT ( " Directory to dump to. " ) ,
ECVF_Default ) ;
// Although this cvar does not seams used in the C++ code base, it is dumped by DumpRenderingCVarsToCSV() and used by GPUDumpViewer.html.
static TAutoConsoleVariable < FString > GDumpGPUVisualizeResource (
TEXT ( " r.DumpGPU.Viewer.Visualize " ) , TEXT ( " " ) ,
TEXT ( " Name of RDG output resource to automatically open in the dump viewer. " ) ,
ECVF_Default ) ;
class FDumpTextureCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER ( FDumpTextureCS ) ;
SHADER_USE_PARAMETER_STRUCT ( FDumpTextureCS , FGlobalShader ) ;
static inline bool IsSupported ( const FStaticShaderPlatform ShaderPlatform )
{
return RHISupportsComputeShaders ( ShaderPlatform ) ;
}
enum class ETextureType
{
Texture2DFloatNoMSAA ,
Texture2DUintNoMSAA ,
Texture2DDepthStencilNoMSAA ,
MAX
} ;
class FTextureTypeDim : SHADER_PERMUTATION_ENUM_CLASS ( " DIM_TEXTURE_TYPE " , ETextureType ) ;
using FPermutationDomain = TShaderPermutationDomain < FTextureTypeDim > ;
static bool ShouldCompilePermutation ( const FGlobalShaderPermutationParameters & Parameters )
{
return IsSupported ( Parameters . Platform ) ;
}
BEGIN_SHADER_PARAMETER_STRUCT ( FParameters , )
SHADER_PARAMETER_SRV ( Texture2D , Texture )
SHADER_PARAMETER_UAV ( RWTexture2D , StagingOutput )
END_SHADER_PARAMETER_STRUCT ( )
} ;
IMPLEMENT_GLOBAL_SHADER ( FDumpTextureCS , " /Engine/Private/Tools/DumpTexture.usf " , " MainCS " , SF_Compute ) ;
2021-12-03 02:41:52 -05:00
BEGIN_SHADER_PARAMETER_STRUCT ( FDumpTexturePass , )
2021-12-03 16:04:00 -05:00
SHADER_PARAMETER_RDG_TEXTURE_SRV ( Texture2D , Texture )
RDG_TEXTURE_ACCESS_DYNAMIC ( TextureAccess )
2021-12-03 02:41:52 -05:00
END_SHADER_PARAMETER_STRUCT ( )
BEGIN_SHADER_PARAMETER_STRUCT ( FDumpBufferPass , )
RDG_BUFFER_ACCESS ( Buffer , ERHIAccess : : CopySrc )
END_SHADER_PARAMETER_STRUCT ( )
struct FRDGResourceDumpContext
{
2021-12-03 16:04:00 -05:00
static constexpr const TCHAR * kBaseDir = TEXT ( " Base/ " ) ;
static constexpr const TCHAR * kPassesDir = TEXT ( " Passes/ " ) ;
static constexpr const TCHAR * kResourcesDir = TEXT ( " Resources/ " ) ;
static constexpr const TCHAR * kStructuresDir = TEXT ( " Structures/ " ) ;
static constexpr const TCHAR * kStructuresMetadataDir = TEXT ( " StructuresMetadata/ " ) ;
bool bEnableDiskWrite = false ;
2022-03-15 15:53:25 -04:00
FName ResourceCompressionName ;
2021-12-03 02:41:52 -05:00
FString DumpingDirectoryPath ;
FDateTime Time ;
2021-12-03 16:04:00 -05:00
FGenericPlatformMemoryConstants MemoryConstants ;
FGenericPlatformMemoryStats MemoryStats ;
int32 ResourcesDumpPasses = 0 ;
int32 ResourcesDumpExecutedPasses = 0 ;
2021-12-03 02:41:52 -05:00
int32 PassesCount = 0 ;
2021-12-03 10:04:40 -05:00
TMap < const FRDGResource * , const FRDGPass * > LastResourceVersion ;
2021-12-03 16:04:00 -05:00
TSet < const void * > IsDumpedToDisk ;
// Pass being dumping individual draws
const FRDGPass * DrawDumpingPass = nullptr ;
int32 DrawDumpCount = 0 ;
2021-12-03 02:41:52 -05:00
2022-03-15 13:52:36 -04:00
int32 MetadataFilesOpened = 0 ;
int64 MetadataFilesWriteBytes = 0 ;
int32 ResourceBinaryFilesOpened = 0 ;
int64 ResourceBinaryWriteBytes = 0 ;
2022-03-15 15:53:25 -04:00
int64 ResourceBinaryUncompressedBytes = 0 ;
2022-03-15 13:52:36 -04:00
enum class ETimingBucket : uint8
{
RHIReadbackCommands ,
RHIReleaseResources ,
GPUWait ,
2022-03-15 15:53:25 -04:00
CPUPostProcessing ,
CPUCompression ,
2022-03-15 13:52:36 -04:00
MetadataFileWrite ,
ResourceBinaryFileWrite ,
MAX
} ;
mutable double TimingBucket [ int32 ( ETimingBucket : : MAX ) ] ;
class FTimeBucketMeasure
{
public :
FTimeBucketMeasure ( FRDGResourceDumpContext * InDumpCtx , ETimingBucket InBucket )
: DumpCtx ( InDumpCtx )
, Start ( FPlatformTime : : Cycles64 ( ) )
, Bucket ( InBucket )
{ }
~ FTimeBucketMeasure ( )
{
uint64 End = FPlatformTime : : Cycles64 ( ) ;
DumpCtx - > TimingBucket [ int32 ( Bucket ) ] + = FPlatformTime : : ToSeconds64 ( FMath : : Max ( End - Start , uint64 ( 0 ) ) ) ;
}
protected :
FRDGResourceDumpContext * const DumpCtx ;
private :
const uint64 Start ;
const ETimingBucket Bucket ;
} ;
class FFileWriteCtx : public FTimeBucketMeasure
{
public :
FFileWriteCtx ( FRDGResourceDumpContext * InDumpCtx , ETimingBucket InBucket , int64 WriteSizeBytes )
: FTimeBucketMeasure ( InDumpCtx , InBucket )
{
if ( InBucket = = ETimingBucket : : MetadataFileWrite )
{
DumpCtx - > MetadataFilesOpened + = 1 ;
DumpCtx - > MetadataFilesWriteBytes + = WriteSizeBytes ;
}
else if ( InBucket = = ETimingBucket : : ResourceBinaryFileWrite )
{
DumpCtx - > ResourceBinaryFilesOpened + = 1 ;
DumpCtx - > ResourceBinaryWriteBytes + = WriteSizeBytes ;
}
else
{
unimplemented ( ) ;
}
}
} ;
2021-12-03 02:41:52 -05:00
bool bShowInExplore = false ;
2022-03-15 13:52:36 -04:00
FRDGResourceDumpContext ( )
{
for ( int32 i = 0 ; i < int32 ( ETimingBucket : : MAX ) ; i + + )
{
TimingBucket [ i ] = 0.0f ;
}
}
2021-12-03 02:41:52 -05:00
bool IsDumpingFrame ( ) const
{
2021-12-03 16:04:00 -05:00
check ( IsInRenderingThread ( ) | | IsInParallelRenderingThread ( ) ) ;
2021-12-03 02:41:52 -05:00
return ! DumpingDirectoryPath . IsEmpty ( ) ;
}
2021-12-03 16:04:00 -05:00
FString GetDumpFullPath ( const FString & DumpRelativeFileName ) const
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
check ( bEnableDiskWrite ) ;
return DumpingDirectoryPath / DumpRelativeFileName ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
bool DumpStringToFile ( FStringView OutputString , const FString & FileName , uint32 WriteFlags = FILEWRITE_None )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
// Make it has if the write happened and was successful.
if ( ! bEnableDiskWrite )
{
return true ;
}
FString FullPath = GetDumpFullPath ( FileName ) ;
2022-03-15 13:52:36 -04:00
FFileWriteCtx WriteCtx ( this , ETimingBucket : : MetadataFileWrite , OutputString . Len ( ) ) ;
2021-12-03 16:04:00 -05:00
return FFileHelper : : SaveStringToFile (
OutputString , * FullPath ,
FFileHelper : : EEncodingOptions : : AutoDetect , & IFileManager : : Get ( ) , WriteFlags ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
bool DumpJsonToFile ( const TSharedPtr < FJsonObject > & JsonObject , const FString & FileName , uint32 WriteFlags = FILEWRITE_None )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
FString OutputString ;
TSharedRef < TJsonWriter < TCHAR , TPrettyJsonPrintPolicy < TCHAR > > > Writer = TJsonWriterFactory < TCHAR , TPrettyJsonPrintPolicy < TCHAR > > : : Create ( & OutputString ) ;
FJsonSerializer : : Serialize ( JsonObject . ToSharedRef ( ) , Writer ) ;
return DumpStringToFile ( OutputString , FileName , WriteFlags ) ;
}
bool DumpBinaryToFile ( TArrayView < const uint8 > ArrayView , const FString & FileName )
{
// Make it has if the write happened and was successful.
if ( ! bEnableDiskWrite )
{
return true ;
}
FString FullPath = GetDumpFullPath ( FileName ) ;
2022-03-15 13:52:36 -04:00
FFileWriteCtx WriteCtx ( this , ETimingBucket : : ResourceBinaryFileWrite , ArrayView . Num ( ) ) ;
2021-12-03 16:04:00 -05:00
return FFileHelper : : SaveArrayToFile ( ArrayView , * FullPath ) ;
}
2022-03-15 15:53:25 -04:00
bool DumpBinaryToFile ( const uint8 * Data , int64 DataByteSize , const FString & FileName )
2021-12-03 17:00:32 -05:00
{
// Make it has if the write happened and was successful.
if ( ! bEnableDiskWrite )
{
return true ;
}
FString FullPath = GetDumpFullPath ( FileName ) ;
2022-03-15 15:53:25 -04:00
FFileWriteCtx WriteCtx ( this , ETimingBucket : : ResourceBinaryFileWrite , DataByteSize ) ;
2022-03-15 13:52:36 -04:00
2022-03-15 15:53:25 -04:00
TUniquePtr < FArchive > Ar = TUniquePtr < FArchive > ( IFileManager : : Get ( ) . CreateFileWriter ( * FullPath , /* WriteFlags = */ 0 ) ) ;
if ( ! Ar )
{
return false ;
}
Ar - > Serialize ( ( void * ) Data , DataByteSize ) ;
// Always explicitly close to catch errors from flush/close
Ar - > Close ( ) ;
return ! Ar - > IsError ( ) & & ! Ar - > IsCriticalError ( ) ;
}
bool IsResourceBinaryCompressable ( int64 UncompressedSize ) const
{
return ! ResourceCompressionName . IsNone ( ) & & ( UncompressedSize < ( int64 ( 1 ) < < int64 ( 32 ) ) ) ;
}
bool DumpResourceBinaryToFile ( const uint8 * UncompressedData , int64 UncompressedSize , const FString & FileName )
{
ResourceBinaryUncompressedBytes + = UncompressedSize ;
if ( ! IsResourceBinaryCompressable ( UncompressedSize ) )
{
return DumpBinaryToFile ( UncompressedData , UncompressedSize , FileName ) ;
}
int32 CompressedSize ;
TArray64 < uint8 > CompressedArray ;
{
FTimeBucketMeasure TimeBucketMeasure ( this , ETimingBucket : : CPUCompression ) ;
int32 MaxCompressedSize = FCompression : : CompressMemoryBound ( ResourceCompressionName , UncompressedSize , COMPRESS_BiasSize ) ;
CompressedArray . SetNumUninitialized ( MaxCompressedSize ) ;
CompressedSize = MaxCompressedSize ;
bool bSuccess = FCompression : : CompressMemory (
ResourceCompressionName ,
/* out */ CompressedArray . GetData ( ) , /* out */ CompressedSize ,
UncompressedData , UncompressedSize ,
COMPRESS_BiasSize ) ;
}
return DumpBinaryToFile ( CompressedArray . GetData ( ) , CompressedSize , FileName ) ;
2021-12-03 17:00:32 -05:00
}
bool IsUnsafeToDumpResource ( SIZE_T ResourceByteSize , float DumpMemoryMultiplier ) const
2021-12-03 16:04:00 -05:00
{
uint64 AproximatedStagingMemoryRequired = uint64 ( double ( ResourceByteSize ) * DumpMemoryMultiplier ) ;
2022-03-15 15:53:25 -04:00
if ( IsResourceBinaryCompressable ( ResourceByteSize ) )
{
AproximatedStagingMemoryRequired + = FCompression : : CompressMemoryBound ( ResourceCompressionName , ResourceByteSize , COMPRESS_BiasSize ) ;
}
2021-12-03 16:04:00 -05:00
uint64 MaxMemoryAvailable = FMath : : Min ( MemoryStats . AvailablePhysical , MemoryStats . AvailableVirtual ) ;
return AproximatedStagingMemoryRequired > MaxMemoryAvailable ;
}
template < typename T >
static uint64 PtrToUint ( const T * Ptr )
{
return static_cast < uint64 > ( reinterpret_cast < size_t > ( Ptr ) ) ;
}
template < typename T >
static FString PtrToString ( const T * Ptr )
{
return FString : : Printf ( TEXT ( " %016x " ) , PtrToUint ( Ptr ) ) ;
}
static FString GetUniqueResourceName ( const FRDGResource * Resource )
{
2021-12-03 16:04:47 -05:00
if ( GDumpTestPrettifyResourceFileNames . GetValueOnRenderThread ( ) )
{
FString UniqueResourceName = FString : : Printf ( TEXT ( " %s.%016x " ) , Resource - > Name , PtrToUint ( Resource ) ) ;
UniqueResourceName . ReplaceInline ( TEXT ( " / " ) , TEXT ( " " ) ) ;
UniqueResourceName . ReplaceInline ( TEXT ( " \\ " ) , TEXT ( " " ) ) ;
return UniqueResourceName ;
}
return PtrToString ( Resource ) ;
2021-12-03 16:04:00 -05:00
}
static FString GetUniqueSubResourceName ( FRDGTextureSRVDesc SubResourceDesc )
{
check ( SubResourceDesc . NumMipLevels = = 1 ) ;
FString UniqueResourceName = GetUniqueResourceName ( SubResourceDesc . Texture ) ;
if ( SubResourceDesc . Format = = PF_X24_G8 )
{
return FString : : Printf ( TEXT ( " %s.stencil " ) , * UniqueResourceName ) ;
}
return FString : : Printf ( TEXT ( " %s.mip%d " ) , * UniqueResourceName , SubResourceDesc . MipLevel ) ;
}
void ReleaseRHIResources ( FRHICommandListImmediate & RHICmdList )
{
2022-03-15 13:52:36 -04:00
FTimeBucketMeasure TimeBucketMeasure ( this , ETimingBucket : : RHIReleaseResources ) ;
2021-12-03 16:04:00 -05:00
// Flush the RHI resource memory so the readback memory can be fully reused in the next resource dump.
{
RHICmdList . SubmitCommandsAndFlushGPU ( ) ;
RHICmdList . BlockUntilGPUIdle ( ) ;
FRHIResource : : FlushPendingDeletes ( RHICmdList ) ;
RHICmdList . FlushResources ( ) ;
RHICmdList . ImmediateFlush ( EImmediateFlushType : : FlushRHIThreadFlushResources ) ;
}
}
void UpdatePassProgress ( )
{
ResourcesDumpExecutedPasses + + ;
if ( ResourcesDumpExecutedPasses % 10 = = 0 )
{
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped %d / %d resources " ) , ResourcesDumpExecutedPasses , ResourcesDumpPasses ) ;
}
}
void DumpRenderingCVarsToCSV ( ) const
{
FString FileName = GetDumpFullPath ( FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " ConsoleVariables.csv " ) ) ;
TUniquePtr < FArchive > Ar = TUniquePtr < FArchive > ( IFileManager : : Get ( ) . CreateFileWriter ( * FileName ) ) ;
auto OnConsoleVariable = [ & Ar ] ( const TCHAR * CVarName , IConsoleObject * ConsoleObj )
{
if ( ConsoleObj - > TestFlags ( ECVF_Unregistered ) )
{
return ;
}
const IConsoleVariable * CVar = ConsoleObj - > AsVariable ( ) ;
if ( ! CVar )
{
return ;
}
EConsoleVariableFlags CVarFlags = CVar - > GetFlags ( ) ;
const TCHAR * Type = nullptr ;
if ( CVar - > IsVariableBool ( ) )
{
Type = TEXT ( " bool " ) ;
}
else if ( CVar - > IsVariableInt ( ) )
{
Type = TEXT ( " int32 " ) ;
}
else if ( CVar - > IsVariableFloat ( ) )
{
Type = TEXT ( " float " ) ;
}
else if ( CVar - > IsVariableString ( ) )
{
Type = TEXT ( " FString " ) ;
}
else
{
return ;
}
const TCHAR * SetByName = TEXT ( " " ) ;
switch ( CVarFlags & ECVF_SetByMask )
{
case ECVF_SetByConstructor :
{
SetByName = TEXT ( " Constructor " ) ;
break ;
}
case ECVF_SetByScalability :
{
SetByName = TEXT ( " Scalability " ) ;
break ;
}
case ECVF_SetByGameSetting :
{
SetByName = TEXT ( " GameSetting " ) ;
break ;
}
case ECVF_SetByProjectSetting :
{
SetByName = TEXT ( " ProjectSetting " ) ;
break ;
}
case ECVF_SetBySystemSettingsIni :
{
SetByName = TEXT ( " SystemSettingsIni " ) ;
break ;
}
case ECVF_SetByDeviceProfile :
{
SetByName = TEXT ( " DeviceProfile " ) ;
break ;
}
case ECVF_SetByConsoleVariablesIni :
{
SetByName = TEXT ( " ConsoleVariablesIni " ) ;
break ;
}
case ECVF_SetByCommandline :
{
SetByName = TEXT ( " Commandline " ) ;
break ;
}
case ECVF_SetByCode :
{
SetByName = TEXT ( " Code " ) ;
break ;
}
case ECVF_SetByConsole :
{
SetByName = TEXT ( " Console " ) ;
break ;
}
default :
unimplemented ( ) ;
}
FString Value = CVar - > GetString ( ) ;
FString CSVLine = FString : : Printf ( TEXT ( " %s,%s,%s,%s \n " ) , CVarName , Type , SetByName , * Value ) ;
FStringView CSVLineView ( CSVLine ) ;
auto Src = StringCast < ANSICHAR > ( CSVLineView . GetData ( ) , CSVLineView . Len ( ) ) ;
Ar - > Serialize ( ( ANSICHAR * ) Src . Get ( ) , Src . Length ( ) * sizeof ( ANSICHAR ) ) ;
} ;
bool bSuccess = false ;
if ( Ar )
{
{
FString CSVLine = TEXT ( " CVar,Type,SetBy,Value \n " ) ;
FStringView CSVLineView ( CSVLine ) ;
auto Src = StringCast < ANSICHAR > ( CSVLineView . GetData ( ) , CSVLineView . Len ( ) ) ;
Ar - > Serialize ( ( ANSICHAR * ) Src . Get ( ) , Src . Length ( ) * sizeof ( ANSICHAR ) ) ;
}
if ( GDumpRenderingConsoleVariablesCVar . GetValueOnGameThread ( ) ! = 0 )
{
2022-02-02 05:28:48 -05:00
IConsoleManager : : Get ( ) . ForEachConsoleObjectThatStartsWith ( FConsoleObjectVisitor : : CreateLambda ( OnConsoleVariable ) , TEXT ( " " ) ) ;
2021-12-03 16:04:00 -05:00
}
else
{
IConsoleManager : : Get ( ) . ForEachConsoleObjectThatStartsWith ( FConsoleObjectVisitor : : CreateLambda ( OnConsoleVariable ) , TEXT ( " r.DumpGPU. " ) ) ;
}
// Always explicitly close to catch errors from flush/close
Ar - > Close ( ) ;
bSuccess = ! Ar - > IsError ( ) & & ! Ar - > IsCriticalError ( ) ;
}
if ( bSuccess )
{
UE_LOG ( LogRendererCore , Display , TEXT ( " DumpGPU dumped rendering cvars to %s. " ) , * FileName ) ;
}
else
{
UE_LOG ( LogRendererCore , Error , TEXT ( " DumpGPU had a file error when dumping rendering cvars to %s. " ) , * FileName ) ;
}
}
template < typename T >
bool IsDumped ( const T * Ptr ) const
{
return IsDumpedToDisk . Contains ( static_cast < const void * > ( Ptr ) ) ;
}
template < typename T >
void SetDumped ( const T * Ptr )
{
check ( ! IsDumped ( Ptr ) ) ;
if ( IsDumpedToDisk . Num ( ) % 1024 = = 0 )
{
IsDumpedToDisk . Reserve ( IsDumpedToDisk . Num ( ) + 1024 ) ;
}
IsDumpedToDisk . Add ( static_cast < const void * > ( Ptr ) ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 10:04:40 -05:00
void GetResourceDumpInfo (
const FRDGPass * Pass ,
const FRDGResource * Resource ,
bool bIsOutputResource ,
bool * bOutDumpResourceInfos ,
2021-12-03 16:04:00 -05:00
bool * bOutDumpResourceBinary )
2021-12-03 10:04:40 -05:00
{
* bOutDumpResourceInfos = false ;
2021-12-03 16:04:00 -05:00
* bOutDumpResourceBinary = bIsOutputResource ;
2021-12-03 10:04:40 -05:00
2021-12-03 16:04:00 -05:00
//if (bIsOutputResource)
//{
// *OutResourceVersionDumpName = GetResourceVersionDumpName(Pass, Resource);
//}
2021-12-03 10:04:40 -05:00
if ( ! LastResourceVersion . Contains ( Resource ) )
{
// First time we ever see this resource, so dump it's info to disk
* bOutDumpResourceInfos = true ;
// If not an output, it might be a resource undumped by r.DumpGPU.Root or external texture so still dump it as v0.
if ( ! bIsOutputResource )
{
2021-12-03 16:04:00 -05:00
* bOutDumpResourceBinary = true ;
2021-12-03 10:04:40 -05:00
}
if ( LastResourceVersion . Num ( ) % 1024 = = 0 )
{
LastResourceVersion . Reserve ( LastResourceVersion . Num ( ) + 1024 ) ;
}
LastResourceVersion . Add ( Resource , Pass ) ;
}
else
{
LastResourceVersion [ Resource ] = Pass ;
}
}
2021-12-03 16:04:00 -05:00
FString ToJson ( EPixelFormat Format )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
FString PixelFormat = GPixelFormats [ Format ] . Name ;
if ( ! PixelFormat . StartsWith ( TEXT ( " PF_ " ) ) )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
PixelFormat = FString : : Printf ( TEXT ( " PF_%s " ) , GPixelFormats [ Format ] . Name ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
return PixelFormat ;
}
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
TSharedPtr < FJsonObject > ToJson ( const FShaderParametersMetadata : : FMember & Member )
{
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " Name " ) , Member . GetName ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " ShaderType " ) , Member . GetShaderType ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " FileLine " ) , Member . GetFileLine ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " Offset " ) , Member . GetOffset ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " BaseType " ) , GetUniformBufferBaseTypeString ( Member . GetBaseType ( ) ) ) ;
JsonObject - > SetNumberField ( TEXT ( " Precision " ) , Member . GetPrecision ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " NumRows " ) , Member . GetNumRows ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " NumColumns " ) , Member . GetNumColumns ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " NumElements " ) , Member . GetNumElements ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " StructMetadata " ) , * PtrToString ( Member . GetStructMetadata ( ) ) ) ;
return JsonObject ;
}
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
TSharedPtr < FJsonObject > ToJson ( const FShaderParametersMetadata * Metadata )
{
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " StructTypeName " ) , Metadata - > GetStructTypeName ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " ShaderVariableName " ) , Metadata - > GetShaderVariableName ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " FileName " ) , Metadata - > GetFileName ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " FileLine " ) , Metadata - > GetFileLine ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " Size " ) , Metadata - > GetSize ( ) ) ;
//JsonObject->SetNumberField(TEXT("UseCase"), Metadata->GetUseCase());
{
TArray < TSharedPtr < FJsonValue > > Members ;
for ( const FShaderParametersMetadata : : FMember & Member : Metadata - > GetMembers ( ) )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
Members . Add ( MakeShared < FJsonValueObject > ( ToJson ( Member ) ) ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
JsonObject - > SetArrayField ( TEXT ( " Members " ) , Members ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
return JsonObject ;
}
2021-12-03 10:04:40 -05:00
2022-01-31 12:53:35 -05:00
TSharedPtr < FJsonObject > ToJson ( const FString & UniqueResourceName , const TCHAR * Name , const FRDGTextureDesc & Desc )
2021-12-03 16:04:00 -05:00
{
FString PixelFormat = ToJson ( Desc . Format ) ;
int32 ResourceByteSize = Desc . Extent . X * Desc . Extent . Y * Desc . Depth * Desc . ArraySize * Desc . NumSamples * GPixelFormats [ Desc . Format ] . BlockBytes ;
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " Name " ) , Name ) ;
2022-01-31 12:53:35 -05:00
JsonObject - > SetStringField ( TEXT ( " UniqueResourceName " ) , UniqueResourceName ) ;
2021-12-03 16:04:00 -05:00
JsonObject - > SetNumberField ( TEXT ( " ByteSize " ) , ResourceByteSize ) ;
JsonObject - > SetStringField ( TEXT ( " Desc " ) , TEXT ( " FRDGTextureDesc " ) ) ;
JsonObject - > SetStringField ( TEXT ( " Type " ) , GetTextureDimensionString ( Desc . Dimension ) ) ;
JsonObject - > SetStringField ( TEXT ( " Format " ) , * PixelFormat ) ;
JsonObject - > SetNumberField ( TEXT ( " ExtentX " ) , Desc . Extent . X ) ;
JsonObject - > SetNumberField ( TEXT ( " ExtentY " ) , Desc . Extent . Y ) ;
JsonObject - > SetNumberField ( TEXT ( " Depth " ) , Desc . Depth ) ;
JsonObject - > SetNumberField ( TEXT ( " ArraySize " ) , Desc . ArraySize ) ;
JsonObject - > SetNumberField ( TEXT ( " NumMips " ) , Desc . NumMips ) ;
JsonObject - > SetNumberField ( TEXT ( " NumSamples " ) , Desc . NumSamples ) ;
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
TArray < TSharedPtr < FJsonValue > > FlagsNames ;
for ( uint64 BitId = 0 ; BitId < uint64 ( 8 * sizeof ( ETextureCreateFlags ) ) ; BitId + + )
{
ETextureCreateFlags Flag = ETextureCreateFlags ( uint64 ( 1 ) < < BitId ) ;
if ( EnumHasAnyFlags ( Desc . Flags , Flag ) )
{
FlagsNames . Add ( MakeShareable ( new FJsonValueString ( GetTextureCreateFlagString ( Flag ) ) ) ) ;
}
}
JsonObject - > SetArrayField ( TEXT ( " Flags " ) , FlagsNames ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
return JsonObject ;
}
2022-01-31 12:53:35 -05:00
TSharedPtr < FJsonObject > ToJson ( const FString & UniqueResourceName , const TCHAR * Name , const FRDGBufferDesc & Desc , int32 ResourceByteSize )
2021-12-03 16:04:00 -05:00
{
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " Name " ) , Name ) ;
2022-01-31 12:53:35 -05:00
JsonObject - > SetStringField ( TEXT ( " UniqueResourceName " ) , UniqueResourceName ) ;
2021-12-03 16:04:00 -05:00
JsonObject - > SetNumberField ( TEXT ( " ByteSize " ) , ResourceByteSize ) ;
JsonObject - > SetStringField ( TEXT ( " Desc " ) , TEXT ( " FRDGBufferDesc " ) ) ;
JsonObject - > SetStringField ( TEXT ( " Type " ) , GetBufferUnderlyingTypeName ( Desc . UnderlyingType ) ) ;
JsonObject - > SetNumberField ( TEXT ( " BytesPerElement " ) , Desc . BytesPerElement ) ;
JsonObject - > SetNumberField ( TEXT ( " NumElements " ) , Desc . NumElements ) ;
JsonObject - > SetStringField ( TEXT ( " Metadata " ) , * PtrToString ( Desc . Metadata ) ) ;
{
TArray < TSharedPtr < FJsonValue > > UsageNames ;
for ( uint64 BitId = 0 ; BitId < uint64 ( 8 * sizeof ( EBufferUsageFlags ) ) ; BitId + + )
{
EBufferUsageFlags Flag = EBufferUsageFlags ( uint64 ( 1 ) < < BitId ) ;
if ( EnumHasAnyFlags ( Desc . Usage , Flag ) )
{
UsageNames . Add ( MakeShareable ( new FJsonValueString ( GetBufferUsageFlagString ( Flag ) ) ) ) ;
}
}
JsonObject - > SetArrayField ( TEXT ( " Usage " ) , UsageNames ) ;
}
return JsonObject ;
}
void Dump ( const FShaderParametersMetadata * Metadata )
{
if ( IsDumped ( Metadata ) )
2021-12-03 02:41:52 -05:00
{
return ;
}
2021-12-03 16:04:00 -05:00
TSharedPtr < FJsonObject > JsonObject = ToJson ( Metadata ) ;
FString JsonPath = kStructuresMetadataDir / PtrToString ( Metadata ) + TEXT ( " .json " ) ;
DumpJsonToFile ( JsonObject , JsonPath ) ;
SetDumped ( Metadata ) ;
// Dump dependencies
Metadata - > IterateStructureMetadataDependencies (
[ & ] ( const FShaderParametersMetadata * Struct )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
if ( Struct )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
Dump ( Struct ) ;
}
} ) ;
}
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
struct FTextureSubresourceDumpDesc
{
FIntPoint SubResourceExtent = FIntPoint ( 0 , 0 ) ;
2021-12-03 17:00:32 -05:00
SIZE_T ByteSize = 0 ;
2021-12-03 16:04:00 -05:00
bool bPreprocessForStaging = false ;
FDumpTextureCS : : ETextureType DumpTextureType = FDumpTextureCS : : ETextureType : : MAX ;
EPixelFormat PreprocessedPixelFormat ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
bool IsDumpSupported ( ) const
{
2021-12-03 17:00:32 -05:00
return ByteSize ! = SIZE_T ( 0 ) ;
2021-12-03 16:04:00 -05:00
}
} ;
FTextureSubresourceDumpDesc TranslateSubresourceDumpDesc ( FRDGTextureSRVDesc SubresourceDesc )
{
const FRDGTextureDesc & Desc = SubresourceDesc . Texture - > Desc ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
FTextureSubresourceDumpDesc SubresourceDumpDesc ;
SubresourceDumpDesc . PreprocessedPixelFormat = Desc . Format ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
bool bIsUnsupported = false ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
if ( GPixelFormats [ Desc . Format ] . BlockSizeX ! = 1 | |
GPixelFormats [ Desc . Format ] . BlockSizeY ! = 1 | |
GPixelFormats [ Desc . Format ] . BlockSizeZ ! = 1 )
{
bIsUnsupported = true ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
if ( ! bIsUnsupported & & Desc . IsTexture2D ( ) & & ! Desc . IsMultisample ( ) & & ! Desc . IsTextureArray ( ) )
{
SubresourceDumpDesc . SubResourceExtent . X = Desc . Extent . X > > SubresourceDesc . MipLevel ;
SubresourceDumpDesc . SubResourceExtent . Y = Desc . Extent . Y > > SubresourceDesc . MipLevel ;
if ( IsUintFormat ( Desc . Format ) | | IsSintFormat ( Desc . Format ) )
{
SubresourceDumpDesc . DumpTextureType = FDumpTextureCS : : ETextureType : : Texture2DUintNoMSAA ;
}
else
{
SubresourceDumpDesc . DumpTextureType = FDumpTextureCS : : ETextureType : : Texture2DFloatNoMSAA ;
}
if ( SubresourceDesc . Format = = PF_X24_G8 )
{
SubresourceDumpDesc . PreprocessedPixelFormat = PF_R8_UINT ;
SubresourceDumpDesc . DumpTextureType = FDumpTextureCS : : ETextureType : : Texture2DDepthStencilNoMSAA ;
}
else if ( Desc . Format = = PF_DepthStencil )
{
SubresourceDumpDesc . PreprocessedPixelFormat = PF_R32_FLOAT ;
SubresourceDumpDesc . DumpTextureType = FDumpTextureCS : : ETextureType : : Texture2DFloatNoMSAA ;
}
else if ( Desc . Format = = PF_ShadowDepth )
{
SubresourceDumpDesc . PreprocessedPixelFormat = PF_R32_FLOAT ;
SubresourceDumpDesc . DumpTextureType = FDumpTextureCS : : ETextureType : : Texture2DFloatNoMSAA ;
}
else if ( Desc . Format = = PF_D24 )
{
SubresourceDumpDesc . PreprocessedPixelFormat = PF_R32_FLOAT ;
SubresourceDumpDesc . DumpTextureType = FDumpTextureCS : : ETextureType : : Texture2DFloatNoMSAA ;
}
}
2021-12-03 17:00:32 -05:00
SubresourceDumpDesc . ByteSize = SIZE_T ( SubresourceDumpDesc . SubResourceExtent . X ) * SIZE_T ( SubresourceDumpDesc . SubResourceExtent . Y ) * SIZE_T ( GPixelFormats [ SubresourceDumpDesc . PreprocessedPixelFormat ] . BlockBytes ) ;
2021-12-03 16:04:00 -05:00
// Whether the subresource need preprocessing pass before copy into staging.
2022-02-15 12:06:09 -05:00
SubresourceDumpDesc . bPreprocessForStaging = SubresourceDumpDesc . PreprocessedPixelFormat ! = Desc . Format | | SubresourceDesc . Texture - > Desc . NumMips > 1 ;
2021-12-03 16:04:00 -05:00
return SubresourceDumpDesc ;
}
void DumpTextureSubResource (
FRHICommandListImmediate & RHICmdList ,
const TCHAR * TextureDebugName ,
FRHITexture * Texture ,
FRHIShaderResourceView * SubResourceSRV ,
const FTextureSubresourceDumpDesc & SubresourceDumpDesc ,
const FString & DumpFilePath )
{
check ( IsInRenderingThread ( ) ) ;
// Preprocess
FTextureRHIRef StagingSrcTexture ;
2022-01-11 09:24:45 -05:00
EPixelFormat PreprocessedPixelFormat = SubresourceDumpDesc . PreprocessedPixelFormat ;
SIZE_T SubresourceByteSize = SubresourceDumpDesc . ByteSize ;
2022-03-15 13:52:36 -04:00
FTextureRHIRef StagingTexture ;
2021-12-03 16:04:00 -05:00
{
2022-03-15 13:52:36 -04:00
FTimeBucketMeasure TimeBucketMeasure ( this , ETimingBucket : : RHIReadbackCommands ) ;
if ( SubresourceDumpDesc . bPreprocessForStaging )
2022-01-11 09:24:45 -05:00
{
2022-03-15 13:52:36 -04:00
// Some RHIs (GL) only support 32Bit single channel images as CS output
if ( IsOpenGLPlatform ( GMaxRHIShaderPlatform ) & &
GPixelFormats [ PreprocessedPixelFormat ] . NumComponents = = 1 & &
GPixelFormats [ PreprocessedPixelFormat ] . BlockBytes < 4 )
{
SubresourceByteSize * = ( 4 / GPixelFormats [ PreprocessedPixelFormat ] . BlockBytes ) ;
PreprocessedPixelFormat = PF_R32_UINT ;
}
2022-01-11 09:24:45 -05:00
2022-03-15 13:52:36 -04:00
{
FRHIResourceCreateInfo CreateInfo ( TEXT ( " DumpGPU.PreprocessTexture " ) ) ;
StagingSrcTexture = RHICreateTexture2D (
SubresourceDumpDesc . SubResourceExtent . X ,
SubresourceDumpDesc . SubResourceExtent . Y ,
( uint8 ) PreprocessedPixelFormat ,
/* NumMips = */ 1 ,
/* NumSamples = */ 1 ,
TexCreate_UAV | TexCreate_ShaderResource | TexCreate_HideInVisualizeTexture ,
CreateInfo ) ;
}
FUnorderedAccessViewRHIRef StagingOutput = RHICreateUnorderedAccessView ( StagingSrcTexture , /* MipLevel = */ 0 ) ;
RHICmdList . Transition ( FRHITransitionInfo ( StagingSrcTexture , ERHIAccess : : Unknown , ERHIAccess : : UAVCompute ) ) ;
FDumpTextureCS : : FPermutationDomain PermutationVector ;
PermutationVector . Set < FDumpTextureCS : : FTextureTypeDim > ( SubresourceDumpDesc . DumpTextureType ) ;
TShaderMapRef < FDumpTextureCS > ComputeShader ( GetGlobalShaderMap ( GMaxRHIShaderPlatform ) , PermutationVector ) ;
FDumpTextureCS : : FParameters ShaderParameters ;
ShaderParameters . Texture = SubResourceSRV ;
ShaderParameters . StagingOutput = StagingOutput ;
FComputeShaderUtils : : Dispatch (
RHICmdList ,
ComputeShader ,
ShaderParameters ,
FComputeShaderUtils : : GetGroupCount ( SubresourceDumpDesc . SubResourceExtent , 8 ) ) ;
RHICmdList . Transition ( FRHITransitionInfo ( StagingSrcTexture , ERHIAccess : : UAVCompute , ERHIAccess : : CopySrc ) ) ;
}
else
2021-12-03 16:04:00 -05:00
{
2022-03-15 13:52:36 -04:00
StagingSrcTexture = Texture ;
}
// Copy the texture for CPU readback
{
FRHIResourceCreateInfo CreateInfo ( TEXT ( " DumpGPU.StagingTexture " ) ) ;
StagingTexture = RHICreateTexture2D (
2021-12-03 16:04:00 -05:00
SubresourceDumpDesc . SubResourceExtent . X ,
SubresourceDumpDesc . SubResourceExtent . Y ,
2022-01-11 09:24:45 -05:00
( uint8 ) PreprocessedPixelFormat ,
2021-12-03 16:04:00 -05:00
/* NumMips = */ 1 ,
/* NumSamples = */ 1 ,
2022-03-15 13:52:36 -04:00
TexCreate_CPUReadback | TexCreate_HideInVisualizeTexture ,
2021-12-03 16:04:00 -05:00
CreateInfo ) ;
2022-03-15 13:52:36 -04:00
RHICmdList . Transition ( FRHITransitionInfo ( StagingTexture , ERHIAccess : : Unknown , ERHIAccess : : CopyDest ) ) ;
// Ensure this copy call does not perform any transitions. We're handling them manually.
FResolveParams ResolveParams ;
ResolveParams . SourceAccessFinal = ERHIAccess : : Unknown ;
ResolveParams . DestAccessFinal = ERHIAccess : : Unknown ;
// Transfer memory GPU -> CPU
RHICmdList . CopyToResolveTarget ( StagingSrcTexture , StagingTexture , ResolveParams ) ;
RHICmdList . Transition ( FRHITransitionInfo ( StagingTexture , ERHIAccess : : CopyDest , ERHIAccess : : CPURead ) ) ;
2021-12-03 16:04:00 -05:00
}
}
// Submit to GPU and wait for completion.
static const FName FenceName ( TEXT ( " DumpGPU.TextureFence " ) ) ;
FGPUFenceRHIRef Fence = RHICreateGPUFence ( FenceName ) ;
{
2022-03-15 13:52:36 -04:00
FTimeBucketMeasure TimeBucketMeasure ( this , ETimingBucket : : GPUWait ) ;
2021-12-03 16:04:00 -05:00
Fence - > Clear ( ) ;
RHICmdList . WriteGPUFence ( Fence ) ;
RHICmdList . SubmitCommandsAndFlushGPU ( ) ;
RHICmdList . BlockUntilGPUIdle ( ) ;
}
void * Content = nullptr ;
int32 RowPitchInPixels = 0 ;
int32 ColumnPitchInPixels = 0 ;
UE5_RELEASE: MGPU, numerous fixes to get EngineTest AFR, and Virtual Production City map to run with multiple GPUs. Ultimately there were 3 crash sources (RayTracing, Nanite, Distance Field streaming), each of which required a couple fixes, plus infrastructure to support those fixes...
There remain significant visual artifacts in the Virtual Production City map. Lumen has serious issues with multiple views in both single and multi-GPU modes -- I think Lumen data needs to be split per view family to solve this. There is some corrupt geometry in the second view, which may be Nanite or instance rendering related (or something else entirely). To narrow down these issues, I think I'm going to need to extend the DumpGPU feature to be able to do more effective MGPU graphical debugging, since none of PIX, RenderDoc, or NSight work. But at least it doesn't crash now...
Full list of changes:
* CVAR (DC.MultiGPUMode) to override multi-GPU mode for Display Cluster, debug feature copied over from 4.27.
* Barrier and synchronization fixes for RHITransferTextures copied over 4.27. Future work will make RDG handle multi-GPU transitions more seamlessly...
* CVAR (DC.ForceCrossGPUCopy) to force expensive full synchronization and copy of resources cross GPU at the end of each view family render (for debugging). RHITransferTextures upgraded to support copying things besides 2D textures, including other texture resources and buffers.
* AFR temporal fixes from a previous CL (which I moved from my single GPU to multi GPU PC), now improved to avoid some validation asserts in Debug builds (pass inputs not declared, GetParent()->GetRHI() not working because parent not declared to pass).
* Ray tracing (hang): acceleration buffers are branched per GPU, as GPU virtual addresses for resources internally referenced by these buffers may vary per GPU. Needed to add infrastructure to support buffers that duplicate memory per GPU, rather than using driver aliasing of the underlying resource.
* Ray tracing (hang): some buffer bindings weren't using a proper GPU index.
* Nanite (hang): Force initial clear of Nanite.MainAndPostNodesAndClusterBatchesBuffer to run on all GPUs. Solves GPU hang in shadow rendering the first frame (due to shadow rendering running across all GPUs), and later random hangs in view rendering.
* Distance field streaming (assert): GPU readback staging buffers need to be branched per GPU, as the underlying class is single device. GPU readback buffers and textures properly take into account the GPU they were last written on when locking and unlocking. Includes handling an edge case where a write can be queued when a lock is active, due to the deferred way commands are played back in the render graph.
* Distance field streaming (assert): UAV clear wasn't taking into account GPU index.
* GPU scene update needs to run across all GPUs.
* Fix for "DumpGPU" command to avoid assert with MGPU -- arbitrarily pick a GPU (last index) when the GPU mask contains multiple bits. Hope to improve this in the future, but it works.
#rnx
#rb mihnea.balta juan.canada tiago.costa kenzo.terelst
#jira none
#preflight 61ba7edbdc58e54b3318fdf5
#ROBOMERGE-AUTHOR: jason.hoerner
#ROBOMERGE-SOURCE: CL 18472819 in //UE5/Release-5.0/... via CL 18473380
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v899-18417669)
[CL 18473412 by jason hoerner in ue5-release-engine-test branch]
2021-12-15 23:12:04 -05:00
// jhoerner_todo 12/9/2021: pick arbitrary GPU out of mask to avoid assert. Eventually want to dump results for all GPUs, but
// I need to understand how to modify the dumping logic, and this works for now (usually when debugging, the bugs happen on
// secondary GPUs, so I figure the last index is most useful if we need to pick one). I also would like the dump to include
// information about the GPUMask for each pass, and perhaps have the dump include the final state of all external resources
// modified by the graph (especially useful for MGPU, where we are concerned about cross-view or cross-frame state).
uint32 GPUIndex = RHICmdList . GetGPUMask ( ) . GetLastIndex ( ) ;
RHICmdList . MapStagingSurface ( StagingTexture , Fence . GetReference ( ) , Content , RowPitchInPixels , ColumnPitchInPixels , GPUIndex ) ;
2021-12-03 16:04:00 -05:00
if ( Content )
{
2021-12-03 17:00:32 -05:00
TArray64 < uint8 > Array ;
2021-12-03 16:04:00 -05:00
{
2022-03-15 15:53:25 -04:00
FTimeBucketMeasure TimeBucketMeasure ( this , ETimingBucket : : CPUPostProcessing ) ;
2021-12-03 16:04:00 -05:00
2022-03-15 13:52:36 -04:00
Array . SetNumUninitialized ( SubresourceByteSize ) ;
2021-12-03 16:04:00 -05:00
2022-03-15 13:52:36 -04:00
SIZE_T BytePerPixel = SIZE_T ( GPixelFormats [ PreprocessedPixelFormat ] . BlockBytes ) ;
2021-12-03 16:04:00 -05:00
2022-03-15 13:52:36 -04:00
const uint8 * SrcData = static_cast < const uint8 * > ( Content ) ;
for ( int32 y = 0 ; y < SubresourceDumpDesc . SubResourceExtent . Y ; y + + )
2022-01-11 09:24:45 -05:00
{
2022-03-15 13:52:36 -04:00
// Flip the data to be bottom left corner for the WebGL viewer.
const uint8 * SrcPos = SrcData + SIZE_T ( SubresourceDumpDesc . SubResourceExtent . Y - 1 - y ) * SIZE_T ( RowPitchInPixels ) * BytePerPixel ;
uint8 * DstPos = ( & Array [ 0 ] ) + SIZE_T ( y ) * SIZE_T ( SubresourceDumpDesc . SubResourceExtent . X ) * BytePerPixel ;
FPlatformMemory : : Memmove ( DstPos , SrcPos , SIZE_T ( SubresourceDumpDesc . SubResourceExtent . X ) * BytePerPixel ) ;
2022-01-11 09:24:45 -05:00
}
2022-03-15 13:52:36 -04:00
RHICmdList . UnmapStagingSurface ( StagingTexture , GPUIndex ) ;
if ( PreprocessedPixelFormat ! = SubresourceDumpDesc . PreprocessedPixelFormat )
{
// Convert 32Bit values back to 16 or 8bit
const int32 DstPixelNumBytes = GPixelFormats [ SubresourceDumpDesc . PreprocessedPixelFormat ] . BlockBytes ;
const uint32 * SrcData32 = ( const uint32 * ) Array . GetData ( ) ;
uint8 * DstData8 = Array . GetData ( ) ;
uint16 * DstData16 = ( uint16 * ) Array . GetData ( ) ;
for ( int32 Index = 0 ; Index < Array . Num ( ) / 4 ; Index + + )
{
uint32 Value32 = SrcData32 [ Index ] ;
if ( DstPixelNumBytes = = 2 )
{
DstData16 [ Index ] = ( uint16 ) Value32 ;
}
else
{
DstData8 [ Index ] = ( uint8 ) Value32 ;
}
}
Array . SetNum ( Array . Num ( ) / ( 4 / DstPixelNumBytes ) ) ;
}
2022-01-11 09:24:45 -05:00
}
2022-03-15 15:53:25 -04:00
DumpResourceBinaryToFile ( Array . GetData ( ) , Array . Num ( ) , DumpFilePath ) ;
2021-12-03 16:04:00 -05:00
}
else
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " RHICmdList.MapStagingSurface() to dump texture %s failed. " ) , TextureDebugName ) ;
}
}
void DumpDrawTextureSubResource (
FRHICommandList & RHICmdList ,
FRDGTextureSRVDesc SubresourceDesc ,
ERHIAccess RHIAccessState )
{
check ( IsInRenderingThread ( ) ) ;
FRHICommandListImmediate & RHICmdListImmediate = FRHICommandListExecutor : : GetImmediateCommandList ( ) ;
check ( & RHICmdListImmediate = = & RHICmdList ) ;
const FString UniqueResourceSubResourceName = GetUniqueSubResourceName ( SubresourceDesc ) ;
const FTextureSubresourceDumpDesc SubresourceDumpDesc = TranslateSubresourceDumpDesc ( SubresourceDesc ) ;
if ( ! SubresourceDumpDesc . IsDumpSupported ( ) )
{
return ;
}
FRHITexture * RHITexture = SubresourceDesc . Texture - > GetRHI ( ) ;
FShaderResourceViewRHIRef SubResourceSRV ;
if ( SubresourceDumpDesc . bPreprocessForStaging )
{
SubResourceSRV = RHICreateShaderResourceView ( RHITexture , FRHITextureSRVCreateInfo ( SubresourceDesc ) ) ;
RHICmdListImmediate . Transition ( FRHITransitionInfo ( RHITexture , ERHIAccess : : Unknown , ERHIAccess : : SRVCompute ) ) ;
}
else
{
RHICmdListImmediate . Transition ( FRHITransitionInfo ( RHITexture , RHIAccessState , ERHIAccess : : CopySrc ) ) ;
}
FString DumpFilePath = kResourcesDir / FString : : Printf (
TEXT ( " %s.v%016x.d%d.bin " ) ,
* UniqueResourceSubResourceName ,
PtrToUint ( DrawDumpingPass ) ,
DrawDumpCount ) ;
DumpTextureSubResource (
RHICmdListImmediate ,
SubresourceDesc . Texture - > Name ,
RHITexture ,
SubResourceSRV ,
SubresourceDumpDesc ,
DumpFilePath ) ;
if ( SubresourceDumpDesc . bPreprocessForStaging )
{
RHICmdListImmediate . Transition ( FRHITransitionInfo ( RHITexture , ERHIAccess : : SRVCompute , RHIAccessState ) ) ;
}
else
{
RHICmdListImmediate . Transition ( FRHITransitionInfo ( RHITexture , ERHIAccess : : CopySrc , RHIAccessState ) ) ;
}
SubResourceSRV = nullptr ;
ReleaseRHIResources ( RHICmdListImmediate ) ;
}
void AddDumpTextureSubResourcePass (
FRDGBuilder & GraphBuilder ,
TArray < TSharedPtr < FJsonValue > > & InputResourceNames ,
TArray < TSharedPtr < FJsonValue > > & OutputResourceNames ,
const FRDGPass * Pass ,
FRDGTextureSRVDesc SubresourceDesc ,
bool bIsOutputResource )
{
int32 DumpTextureMode = GDumpTextureCVar . GetValueOnRenderThread ( ) ;
if ( DumpTextureMode = = 0 )
{
return ;
}
const FRDGTextureDesc & Desc = SubresourceDesc . Texture - > Desc ;
const FString UniqueResourceName = GetUniqueResourceName ( SubresourceDesc . Texture ) ;
const FString UniqueResourceSubResourceName = GetUniqueSubResourceName ( SubresourceDesc ) ;
const FTextureSubresourceDumpDesc SubresourceDumpDesc = TranslateSubresourceDumpDesc ( SubresourceDesc ) ;
if ( bIsOutputResource )
{
OutputResourceNames . AddUnique ( MakeShareable ( new FJsonValueString ( UniqueResourceSubResourceName ) ) ) ;
}
else
{
InputResourceNames . AddUnique ( MakeShareable ( new FJsonValueString ( UniqueResourceSubResourceName ) ) ) ;
}
bool bDumpResourceInfos ;
bool bDumpResourceBinary ;
GetResourceDumpInfo ( Pass , SubresourceDesc . Texture , bIsOutputResource , & bDumpResourceInfos , & bDumpResourceBinary ) ;
// Dump the information of the texture to json file.
if ( bDumpResourceInfos )
{
2022-01-31 12:53:35 -05:00
TSharedPtr < FJsonObject > JsonObject = ToJson ( UniqueResourceName , SubresourceDesc . Texture - > Name , Desc ) ;
DumpJsonToFile ( JsonObject , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " ResourceDescs.json " ) , FILEWRITE_Append ) ;
2021-12-03 16:04:00 -05:00
}
if ( ! SubresourceDumpDesc . IsDumpSupported ( ) )
{
return ;
}
// Early return if this resource shouldn't be dumped.
if ( ! bDumpResourceBinary | | DumpTextureMode ! = 2 )
{
return ;
}
FString DumpFilePath = kResourcesDir / FString : : Printf ( TEXT ( " %s.v%016x.bin " ) , * UniqueResourceSubResourceName , PtrToUint ( bIsOutputResource ? Pass : nullptr ) ) ;
// Verify there is enough available memory to dump the resource.
if ( IsUnsafeToDumpResource ( SubresourceDumpDesc . ByteSize , 2.2f + ( SubresourceDumpDesc . bPreprocessForStaging ? 1.0f : 0.0f ) ) )
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Not dumping %s because of insuficient memory available for staging texture. " ) , * DumpFilePath ) ;
return ;
}
// Dump the resource's binary to a .bin file.
{
FDumpTexturePass * PassParameters = GraphBuilder . AllocParameters < FDumpTexturePass > ( ) ;
if ( SubresourceDumpDesc . bPreprocessForStaging )
{
if ( ! ( SubresourceDesc . Texture - > Desc . Flags & TexCreate_ShaderResource ) )
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Not dumping %s because requires copy to staging texture using compute, but is missing TexCreate_ShaderResource. " ) , * UniqueResourceSubResourceName ) ;
return ;
}
if ( ! FDumpTextureCS : : IsSupported ( GMaxRHIShaderPlatform ) )
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Not dumping %s because FDumpTextureCS compute shader is not supported. " ) , * UniqueResourceSubResourceName ) ;
return ;
}
PassParameters - > Texture = GraphBuilder . CreateSRV ( SubresourceDesc ) ;
}
else
{
PassParameters - > TextureAccess = FRDGTextureAccess ( SubresourceDesc . Texture , ERHIAccess : : CopySrc ) ;
}
GraphBuilder . AddPass (
RDG_EVENT_NAME ( " RDG DumpTexture(%s -> %s) %dx%d " ,
SubresourceDesc . Texture - > Name , * DumpFilePath ,
SubresourceDumpDesc . SubResourceExtent . X , SubresourceDumpDesc . SubResourceExtent . Y ) ,
PassParameters ,
( SubresourceDumpDesc . bPreprocessForStaging ? ERDGPassFlags : : Compute : ERDGPassFlags : : Copy ) | ERDGPassFlags : : NeverCull ,
[
PassParameters , this , DumpFilePath ,
SubresourceDesc , SubresourceDumpDesc ]
( FRHICommandListImmediate & RHICmdList )
{
this - > DumpTextureSubResource (
RHICmdList ,
SubresourceDesc . Texture - > Name ,
SubresourceDumpDesc . bPreprocessForStaging ? nullptr : SubresourceDesc . Texture - > GetRHI ( ) ,
SubresourceDumpDesc . bPreprocessForStaging ? PassParameters - > Texture - > GetRHI ( ) : nullptr ,
SubresourceDumpDesc ,
DumpFilePath ) ;
this - > ReleaseRHIResources ( RHICmdList ) ;
this - > UpdatePassProgress ( ) ;
} ) ;
ResourcesDumpPasses + + ;
}
}
void AddDumpTexturePasses (
FRDGBuilder & GraphBuilder ,
TArray < TSharedPtr < FJsonValue > > & InputResourceNames ,
TArray < TSharedPtr < FJsonValue > > & OutputResourceNames ,
const FRDGPass * Pass ,
FRDGTextureSRVDesc SubresourceRangeDesc ,
bool bIsOutputResource )
{
if ( SubresourceRangeDesc . Format = = PF_X24_G8 )
{
AddDumpTextureSubResourcePass (
GraphBuilder ,
InputResourceNames ,
OutputResourceNames ,
Pass ,
SubresourceRangeDesc ,
bIsOutputResource ) ;
}
else
{
for ( int32 MipLevel = SubresourceRangeDesc . MipLevel ; MipLevel < ( SubresourceRangeDesc . MipLevel + SubresourceRangeDesc . NumMipLevels ) ; MipLevel + + )
{
FRDGTextureSRVDesc SubresourceDesc = FRDGTextureSRVDesc : : CreateForMipLevel ( SubresourceRangeDesc . Texture , MipLevel ) ;
AddDumpTextureSubResourcePass (
GraphBuilder ,
InputResourceNames ,
OutputResourceNames ,
Pass ,
SubresourceDesc ,
bIsOutputResource ) ;
}
}
2021-12-03 02:41:52 -05:00
}
void AddDumpBufferPass (
FRDGBuilder & GraphBuilder ,
TArray < TSharedPtr < FJsonValue > > & InputResourceNames ,
TArray < TSharedPtr < FJsonValue > > & OutputResourceNames ,
const FRDGPass * Pass ,
FRDGBuffer * Buffer ,
bool bIsOutputResource )
{
2021-12-03 16:04:00 -05:00
int32 DumpBufferMode = GDumpTextureCVar . GetValueOnRenderThread ( ) ;
if ( DumpBufferMode = = 0 )
{
return ;
}
FString UniqueResourceName = GetUniqueResourceName ( Buffer ) ;
2021-12-03 02:41:52 -05:00
if ( bIsOutputResource )
{
OutputResourceNames . AddUnique ( MakeShareable ( new FJsonValueString ( UniqueResourceName ) ) ) ;
}
else
{
InputResourceNames . AddUnique ( MakeShareable ( new FJsonValueString ( UniqueResourceName ) ) ) ;
}
const FRDGBufferDesc & Desc = Buffer - > Desc ;
2021-12-03 16:04:00 -05:00
const int32 ByteSize = Desc . GetTotalNumBytes ( ) ;
2021-12-03 02:41:52 -05:00
2021-12-03 10:04:40 -05:00
bool bDumpResourceInfos ;
2021-12-03 16:04:00 -05:00
bool bDumpResourceBinary ;
GetResourceDumpInfo ( Pass , Buffer , bIsOutputResource , & bDumpResourceInfos , & bDumpResourceBinary ) ;
2021-12-03 10:04:40 -05:00
2021-12-03 02:41:52 -05:00
// Dump the information of the buffer to json file.
2021-12-03 10:04:40 -05:00
if ( bDumpResourceInfos )
2021-12-03 02:41:52 -05:00
{
2022-01-31 12:53:35 -05:00
TSharedPtr < FJsonObject > JsonObject = ToJson ( UniqueResourceName , Buffer - > Name , Desc , ByteSize ) ;
DumpJsonToFile ( JsonObject , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " ResourceDescs.json " ) , FILEWRITE_Append ) ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
if ( Desc . Metadata & & DumpBufferMode = = 2 )
{
Dump ( Desc . Metadata ) ;
}
2021-12-03 02:41:52 -05:00
}
if ( Desc . UnderlyingType = = FRDGBufferDesc : : EUnderlyingType : : AccelerationStructure )
{
return ;
}
// Dump the resource's binary to a .bin file.
2021-12-03 16:04:00 -05:00
if ( bDumpResourceBinary & & DumpBufferMode = = 2 )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
FString DumpFilePath = kResourcesDir / FString : : Printf ( TEXT ( " %s.v%016x.bin " ) , * UniqueResourceName , PtrToUint ( bIsOutputResource ? Pass : nullptr ) ) ;
if ( IsUnsafeToDumpResource ( ByteSize , 1.2f ) )
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Not dumping %s because of insuficient memory available for staging buffer. " ) , * DumpFilePath ) ;
return ;
}
2021-12-03 02:41:52 -05:00
FDumpBufferPass * PassParameters = GraphBuilder . AllocParameters < FDumpBufferPass > ( ) ;
PassParameters - > Buffer = Buffer ;
GraphBuilder . AddPass (
RDG_EVENT_NAME ( " RDG DumpBuffer(%s -> %s) " , Buffer - > Name , * DumpFilePath ) ,
PassParameters ,
ERDGPassFlags : : Readback ,
2021-12-03 16:04:00 -05:00
[ this , DumpFilePath , Buffer , ByteSize ] ( FRHICommandListImmediate & RHICmdList )
2021-12-03 02:41:52 -05:00
{
check ( IsInRenderingThread ( ) ) ;
2022-03-15 13:52:36 -04:00
FStagingBufferRHIRef StagingBuffer ;
{
FTimeBucketMeasure TimeBucketMeasure ( this , ETimingBucket : : RHIReadbackCommands ) ;
StagingBuffer = RHICreateStagingBuffer ( ) ;
2021-12-03 02:41:52 -05:00
2022-03-15 13:52:36 -04:00
// Transfer memory GPU -> CPU
RHICmdList . CopyToStagingBuffer ( Buffer - > GetRHI ( ) , StagingBuffer , 0 , ByteSize ) ;
}
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
// Submit to GPU and wait for completion.
static const FName FenceName ( TEXT ( " DumpGPU.BufferFence " ) ) ;
FGPUFenceRHIRef Fence = RHICreateGPUFence ( FenceName ) ;
{
2022-03-15 13:52:36 -04:00
FTimeBucketMeasure TimeBucketMeasure ( this , ETimingBucket : : GPUWait ) ;
2021-12-03 16:04:00 -05:00
Fence - > Clear ( ) ;
RHICmdList . WriteGPUFence ( Fence ) ;
RHICmdList . SubmitCommandsAndFlushGPU ( ) ;
RHICmdList . BlockUntilGPUIdle ( ) ;
}
void * Content = RHICmdList . LockStagingBuffer ( StagingBuffer , Fence . GetReference ( ) , 0 , ByteSize ) ;
2021-12-03 02:41:52 -05:00
if ( Content )
{
2022-03-15 15:53:25 -04:00
DumpResourceBinaryToFile ( reinterpret_cast < const uint8 * > ( Content ) , ByteSize , DumpFilePath ) ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
RHICmdList . UnlockStagingBuffer ( StagingBuffer ) ;
2021-12-03 02:41:52 -05:00
}
else
{
2021-12-03 16:04:39 -05:00
UE_LOG ( LogRendererCore , Warning , TEXT ( " RHICmdList.LockStagingBuffer() to dump buffer %s failed. " ) , Buffer - > Name ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
StagingBuffer = nullptr ;
Fence = nullptr ;
this - > ReleaseRHIResources ( RHICmdList ) ;
this - > UpdatePassProgress ( ) ;
2021-12-03 02:41:52 -05:00
} ) ;
2021-12-03 16:04:00 -05:00
ResourcesDumpPasses + + ;
}
}
// Look whether the pass matches matches r.DumpGPU.Root
bool IsDumpingPass ( const FRDGPass * Pass )
{
FString RootWildcardString = GDumpGPURootCVar . GetValueOnRenderThread ( ) ;
FWildcardString WildcardFilter ( RootWildcardString ) ;
bool bDumpPass = ( RootWildcardString = = TEXT ( " * " ) ) ;
if ( ! bDumpPass )
{
bDumpPass = WildcardFilter . IsMatch ( Pass - > GetEventName ( ) . GetTCHAR ( ) ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
# if RDG_GPU_SCOPES
if ( ! bDumpPass )
{
const FRDGEventScope * ParentScope = Pass - > GetGPUScopes ( ) . Event ;
while ( ParentScope )
{
bDumpPass = bDumpPass | | WildcardFilter . IsMatch ( ParentScope - > Name . GetTCHAR ( ) ) ;
ParentScope = ParentScope - > ParentScope ;
}
}
# endif
return bDumpPass ;
2021-12-03 02:41:52 -05:00
}
} ;
2022-02-04 12:01:46 -05:00
// 0 = not dumping, MAX_uint64 dump request for next frame, otherwise dump frame counter
2022-01-11 09:24:45 -05:00
static uint64 DumpingFrameCounter_GameThread = 0 ;
2021-12-03 02:41:52 -05:00
FRDGResourceDumpContext GRDGResourceDumpContext ;
}
2021-12-03 16:04:00 -05:00
bool IsDumpingRDGResources ( )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
return GRDGResourceDumpContext . IsDumpingFrame ( ) ;
}
2022-02-04 12:01:46 -05:00
void FRDGBuilder : : InitResourceDump ( )
{
if ( DumpingFrameCounter_GameThread = = MAX_uint64 )
{
DumpingFrameCounter_GameThread = GFrameCounter ;
}
}
2021-12-03 16:04:00 -05:00
FString FRDGBuilder : : BeginResourceDump ( const TArray < FString > & Args )
{
check ( IsInGameThread ( ) ) ;
2022-01-11 09:24:45 -05:00
if ( DumpingFrameCounter_GameThread ! = 0 )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
return FString ( ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
FRDGResourceDumpContext NewResourceDumpContext ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
NewResourceDumpContext . Time = FDateTime : : Now ( ) ;
{
2022-02-14 09:56:14 -05:00
FString CVarDirectoryPath = GDumpGPUDirectoryCVar . GetValueOnGameThread ( ) ;
FString EnvDirectoryPath = FPlatformMisc : : GetEnvironmentVariable ( TEXT ( " UE-DumpGPUPath " ) ) ;
FString DirectoryPath ;
if ( ! CVarDirectoryPath . IsEmpty ( ) )
2021-12-03 16:04:00 -05:00
{
2022-02-14 09:56:14 -05:00
DirectoryPath = CVarDirectoryPath ;
2021-12-03 16:04:00 -05:00
}
2022-02-14 09:56:14 -05:00
else if ( ! EnvDirectoryPath . IsEmpty ( ) )
{
DirectoryPath = EnvDirectoryPath ;
}
else
{
DirectoryPath = FPaths : : ProjectSavedDir ( ) / TEXT ( " GPUDumps/ " ) ;
}
NewResourceDumpContext . DumpingDirectoryPath = DirectoryPath / FApp : : GetProjectName ( ) + TEXT ( " - " ) + FPlatformProperties : : PlatformName ( ) + TEXT ( " - " ) + NewResourceDumpContext . Time . ToString ( ) + TEXT ( " / " ) ;
2021-12-03 16:04:00 -05:00
}
NewResourceDumpContext . bEnableDiskWrite = GDumpTestEnableDiskWrite . GetValueOnGameThread ( ) ! = 0 ;
2022-03-15 15:53:25 -04:00
{
if ( GDumpGPUCompressResources . GetValueOnGameThread ( ) = = 1 )
{
NewResourceDumpContext . ResourceCompressionName = NAME_Zlib ;
}
else if ( GDumpGPUCompressResources . GetValueOnGameThread ( ) = = 2 )
{
NewResourceDumpContext . ResourceCompressionName = NAME_Gzip ;
}
}
2021-12-03 16:04:00 -05:00
NewResourceDumpContext . bShowInExplore = NewResourceDumpContext . bEnableDiskWrite & & GDumpExploreCVar . GetValueOnGameThread ( ) ! = 0 ;
NewResourceDumpContext . MemoryConstants = FPlatformMemory : : GetConstants ( ) ;
NewResourceDumpContext . MemoryStats = FPlatformMemory : : GetStats ( ) ;
2021-12-03 02:41:52 -05:00
IPlatformFile & PlatformFile = FPlatformFileManager : : Get ( ) . GetPlatformFile ( ) ;
2021-12-03 16:04:00 -05:00
if ( NewResourceDumpContext . bEnableDiskWrite )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
if ( ! PlatformFile . DirectoryExists ( * NewResourceDumpContext . DumpingDirectoryPath ) )
{
PlatformFile . CreateDirectoryTree ( * NewResourceDumpContext . DumpingDirectoryPath ) ;
}
PlatformFile . CreateDirectoryTree ( * ( NewResourceDumpContext . DumpingDirectoryPath / FRDGResourceDumpContext : : kBaseDir ) ) ;
PlatformFile . CreateDirectoryTree ( * ( NewResourceDumpContext . DumpingDirectoryPath / FRDGResourceDumpContext : : kResourcesDir ) ) ;
2022-01-31 12:53:35 -05:00
NewResourceDumpContext . DumpStringToFile ( TEXT ( " " ) , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " Passes.json " ) ) ;
NewResourceDumpContext . DumpStringToFile ( TEXT ( " " ) , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " ResourceDescs.json " ) ) ;
2021-12-03 16:04:00 -05:00
NewResourceDumpContext . DumpStringToFile ( TEXT ( " " ) , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " PassDrawCounts.json " ) ) ;
2021-12-03 02:41:52 -05:00
}
// Output informations
{
2021-12-03 16:04:00 -05:00
const TCHAR * BranchName = BuildSettings : : GetBranchName ( ) ;
const TCHAR * BuildDate = BuildSettings : : GetBuildDate ( ) ;
const TCHAR * BuildVersion = BuildSettings : : GetBuildVersion ( ) ;
FString BuildConfiguration = LexToString ( FApp : : GetBuildConfiguration ( ) ) ;
FString BuildTarget = LexToString ( FApp : : GetBuildTargetType ( ) ) ;
FGPUDriverInfo GPUDriverInfo = FPlatformMisc : : GetGPUDriverInfo ( GRHIAdapterName ) ;
2021-12-03 02:41:52 -05:00
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " Project " ) , FApp : : GetProjectName ( ) ) ;
2021-12-03 16:04:00 -05:00
JsonObject - > SetNumberField ( TEXT ( " EngineMajorVersion " ) , ENGINE_MAJOR_VERSION ) ;
JsonObject - > SetNumberField ( TEXT ( " EngineMinorVersion " ) , ENGINE_MINOR_VERSION ) ;
JsonObject - > SetNumberField ( TEXT ( " EnginePatchVersion " ) , ENGINE_PATCH_VERSION ) ;
JsonObject - > SetStringField ( TEXT ( " BuildBranch " ) , BranchName ? BranchName : TEXT ( " " ) ) ;
JsonObject - > SetStringField ( TEXT ( " BuildDate " ) , BuildDate ? BuildDate : TEXT ( " " ) ) ;
JsonObject - > SetStringField ( TEXT ( " BuildVersion " ) , BuildVersion ? BuildVersion : TEXT ( " " ) ) ;
JsonObject - > SetStringField ( TEXT ( " BuildTarget " ) , BuildTarget ) ;
JsonObject - > SetStringField ( TEXT ( " BuildConfiguration " ) , BuildConfiguration ) ;
JsonObject - > SetNumberField ( TEXT ( " Build64Bits " ) , ( PLATFORM_64BITS ? 1 : 0 ) ) ;
2021-12-03 02:41:52 -05:00
JsonObject - > SetStringField ( TEXT ( " Platform " ) , FPlatformProperties : : IniPlatformName ( ) ) ;
2021-12-03 16:04:00 -05:00
JsonObject - > SetStringField ( TEXT ( " DeviceName " ) , FPlatformProcess : : ComputerName ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " CPUVendor " ) , FPlatformMisc : : GetCPUVendor ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " CPUBrand " ) , FPlatformMisc : : GetCPUBrand ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " CPUNumberOfCores " ) , FPlatformMisc : : NumberOfCores ( ) ) ;
JsonObject - > SetNumberField ( TEXT ( " CPUNumberOfCoresIncludingHyperthreads " ) , FPlatformMisc : : NumberOfCoresIncludingHyperthreads ( ) ) ;
2021-12-03 02:41:52 -05:00
JsonObject - > SetStringField ( TEXT ( " GPUVendor " ) , RHIVendorIdToString ( ) ) ;
2021-12-03 16:04:00 -05:00
JsonObject - > SetStringField ( TEXT ( " GPUDeviceDescription " ) , GPUDriverInfo . DeviceDescription ) ;
JsonObject - > SetStringField ( TEXT ( " GPUDriverUserVersion " ) , GPUDriverInfo . UserDriverVersion ) ;
JsonObject - > SetStringField ( TEXT ( " GPUDriverInternalVersion " ) , GPUDriverInfo . GetUnifiedDriverVersion ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " GPUDriverDate " ) , GPUDriverInfo . DriverDate ) ;
JsonObject - > SetNumberField ( TEXT ( " MemoryTotalPhysical " ) , NewResourceDumpContext . MemoryConstants . TotalPhysical ) ;
JsonObject - > SetNumberField ( TEXT ( " MemoryPageSize " ) , NewResourceDumpContext . MemoryConstants . PageSize ) ;
JsonObject - > SetStringField ( TEXT ( " RHI " ) , GDynamicRHI - > GetName ( ) ) ;
JsonObject - > SetStringField ( TEXT ( " RHIMaxFeatureLevel " ) , LexToString ( GMaxRHIFeatureLevel ) ) ;
JsonObject - > SetStringField ( TEXT ( " DumpTime " ) , NewResourceDumpContext . Time . ToString ( ) ) ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
NewResourceDumpContext . DumpJsonToFile ( JsonObject , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " Infos.json " ) ) ;
}
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
// Dump the rendering cvars
if ( NewResourceDumpContext . bEnableDiskWrite )
{
NewResourceDumpContext . DumpRenderingCVarsToCSV ( ) ;
2021-12-03 02:41:52 -05:00
}
// Copy the viewer
2021-12-03 16:04:00 -05:00
if ( NewResourceDumpContext . bEnableDiskWrite )
2021-12-03 02:41:52 -05:00
{
2022-01-18 12:29:27 -05:00
const TCHAR * OpenGPUDumpViewerBatName = TEXT ( " OpenGPUDumpViewer.bat " ) ;
const TCHAR * OpenGPUDumpViewerShName = TEXT ( " OpenGPUDumpViewer.sh " ) ;
2021-12-03 02:41:52 -05:00
const TCHAR * ViewerHTML = TEXT ( " GPUDumpViewer.html " ) ;
FString DumpGPUViewerSourcePath = FPaths : : EngineDir ( ) + FString ( TEXT ( " Extras " ) ) / TEXT ( " GPUDumpViewer " ) ;
2021-12-03 16:04:00 -05:00
PlatformFile . CopyFile ( * ( NewResourceDumpContext . DumpingDirectoryPath / ViewerHTML ) , * ( DumpGPUViewerSourcePath / ViewerHTML ) ) ;
2022-01-18 12:29:27 -05:00
PlatformFile . CopyFile ( * ( NewResourceDumpContext . DumpingDirectoryPath / OpenGPUDumpViewerBatName ) , * ( DumpGPUViewerSourcePath / OpenGPUDumpViewerBatName ) ) ;
PlatformFile . CopyFile ( * ( NewResourceDumpContext . DumpingDirectoryPath / OpenGPUDumpViewerShName ) , * ( DumpGPUViewerSourcePath / OpenGPUDumpViewerShName ) ) ;
2021-12-03 02:41:52 -05:00
}
2021-12-03 16:04:00 -05:00
ENQUEUE_RENDER_COMMAND ( FStartGPUDump ) (
[ NewResourceDumpContext ] ( FRHICommandListImmediate & ImmediateRHICmdList )
{
check ( IsInRenderingThread ( ) ) ;
GRDGResourceDumpContext = NewResourceDumpContext ;
2021-12-03 16:04:39 -05:00
ImmediateRHICmdList . SubmitCommandsAndFlushGPU ( ) ;
// Disable the validation for BUF_SourceCopy so that all buffers can be copied into staging buffer for CPU readback.
# if ENABLE_RHI_VALIDATION
GRHIValidateBufferSourceCopy = false ;
# endif
2021-12-03 16:04:00 -05:00
} ) ;
2022-02-04 12:01:46 -05:00
// Mark ready for dump on next available frame
DumpingFrameCounter_GameThread = MAX_uint64 ;
2021-12-03 16:04:00 -05:00
if ( NewResourceDumpContext . bEnableDiskWrite )
{
return NewResourceDumpContext . DumpingDirectoryPath ;
}
return FString ( ) ;
2021-12-03 02:41:52 -05:00
}
void FRDGBuilder : : EndResourceDump ( )
{
2021-12-03 16:04:00 -05:00
check ( IsInGameThread ( ) ) ;
2022-02-04 12:01:46 -05:00
// make sure at least one frame has passed since we start a resource dump and we are not waiting on the dump to begin
2022-01-11 09:24:45 -05:00
if ( DumpingFrameCounter_GameThread = = 0 | |
2022-02-04 12:01:46 -05:00
DumpingFrameCounter_GameThread = = MAX_uint64 | |
DumpingFrameCounter_GameThread > = GFrameCounter )
2021-12-03 02:41:52 -05:00
{
return ;
}
2021-12-03 16:04:00 -05:00
// Wait all rendering commands are completed to finish with GRDGResourceDumpContext.
{
UE_LOG ( LogRendererCore , Display , TEXT ( " Stalling game thread until render thread finishes to dump resources " ) ) ;
2021-12-03 16:04:39 -05:00
ENQUEUE_RENDER_COMMAND ( FEndGPUDump ) (
[ ] ( FRHICommandListImmediate & ImmediateRHICmdList )
{
ImmediateRHICmdList . SubmitCommandsAndFlushGPU ( ) ;
# if ENABLE_RHI_VALIDATION
GRHIValidateBufferSourceCopy = true ;
# endif
} ) ;
2021-12-03 16:04:00 -05:00
FlushRenderingCommands ( ) ;
}
// Log information about the dump.
FString AbsDumpingDirectoryPath = IFileManager : : Get ( ) . ConvertToAbsolutePathForExternalAppForRead ( * GRDGResourceDumpContext . DumpingDirectoryPath ) ;
{
2022-03-15 13:52:36 -04:00
FDateTime Now = FDateTime : : Now ( ) ;
double TotalDumpSeconds = ( Now - GRDGResourceDumpContext . Time ) . GetTotalSeconds ( ) ;
double RHIReadbackCommandsSeconds = GRDGResourceDumpContext . TimingBucket [ int32 ( FRDGResourceDumpContext : : ETimingBucket : : RHIReadbackCommands ) ] ;
double GPUWaitSeconds = GRDGResourceDumpContext . TimingBucket [ int32 ( FRDGResourceDumpContext : : ETimingBucket : : GPUWait ) ] ;
2022-03-15 15:53:25 -04:00
double CPUPostProcessingSeconds = GRDGResourceDumpContext . TimingBucket [ int32 ( FRDGResourceDumpContext : : ETimingBucket : : CPUPostProcessing ) ] ;
2022-03-15 13:52:36 -04:00
double MetadataFileWriteSeconds = GRDGResourceDumpContext . TimingBucket [ int32 ( FRDGResourceDumpContext : : ETimingBucket : : MetadataFileWrite ) ] ;
double ResourceBinaryFileWriteSeconds = GRDGResourceDumpContext . TimingBucket [ int32 ( FRDGResourceDumpContext : : ETimingBucket : : ResourceBinaryFileWrite ) ] ;
double RHIReleaseResourcesTimeSeconds = GRDGResourceDumpContext . TimingBucket [ int32 ( FRDGResourceDumpContext : : ETimingBucket : : RHIReleaseResources ) ] ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped %d resources in %.3f s to %s " ) , GRDGResourceDumpContext . ResourcesDumpPasses , float ( TotalDumpSeconds ) , * AbsDumpingDirectoryPath ) ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped GPU readback commands: %.3f s " ) , float ( RHIReadbackCommandsSeconds ) ) ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped GPU wait: %.3f s " ) , float ( GPUWaitSeconds ) ) ;
2022-03-15 15:53:25 -04:00
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped CPU resource binary post processing: %.3f s " ) , float ( CPUPostProcessingSeconds ) ) ;
if ( ! GRDGResourceDumpContext . ResourceCompressionName . IsNone ( ) )
{
double CPUCompressionSeconds = GRDGResourceDumpContext . TimingBucket [ int32 ( FRDGResourceDumpContext : : ETimingBucket : : CPUCompression ) ] ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped CPU resource binary compression from %.3f MB to %.3f MB (%.1f %%) using %s " ) ,
float ( GRDGResourceDumpContext . ResourceBinaryUncompressedBytes ) / float ( 1024 * 1024 ) ,
float ( GRDGResourceDumpContext . ResourceBinaryWriteBytes ) / float ( 1024 * 1024 ) ,
100.0f * float ( GRDGResourceDumpContext . ResourceBinaryWriteBytes ) / float ( GRDGResourceDumpContext . ResourceBinaryUncompressedBytes ) ,
* GRDGResourceDumpContext . ResourceCompressionName . ToString ( ) ) ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped CPU resource binary compression took %.3f s at %.3f MB/s " ) ,
float ( CPUCompressionSeconds ) ,
float ( GRDGResourceDumpContext . ResourceBinaryUncompressedBytes ) / ( float ( 1024 * 1024 ) * float ( CPUCompressionSeconds ) ) ) ;
}
2022-03-15 13:52:36 -04:00
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped metadata: %.3f MB in %d files under %.3f s at %.3f MB/s " ) ,
float ( GRDGResourceDumpContext . MetadataFilesWriteBytes ) / float ( 1024 * 1024 ) ,
GRDGResourceDumpContext . MetadataFilesOpened ,
float ( MetadataFileWriteSeconds ) ,
float ( GRDGResourceDumpContext . MetadataFilesWriteBytes ) / ( float ( 1024 * 1024 ) * float ( MetadataFileWriteSeconds ) ) ) ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped resource binary: %.3f MB in %d files under %.3f s at %.3f MB/s " ) ,
float ( GRDGResourceDumpContext . ResourceBinaryWriteBytes ) / float ( 1024 * 1024 ) ,
GRDGResourceDumpContext . ResourceBinaryFilesOpened ,
float ( ResourceBinaryFileWriteSeconds ) ,
float ( GRDGResourceDumpContext . ResourceBinaryWriteBytes ) / ( float ( 1024 * 1024 ) * float ( ResourceBinaryFileWriteSeconds ) ) ) ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped GPU readback resource release: %.3f s " ) , float ( RHIReleaseResourcesTimeSeconds ) ) ;
2021-12-03 16:04:00 -05:00
}
// Dump the log into the dump directory.
if ( GRDGResourceDumpContext . bEnableDiskWrite )
{
if ( GLog )
{
GLog - > FlushThreadedLogs ( ) ;
GLog - > Flush ( ) ;
}
FGenericCrashContext : : DumpLog ( GRDGResourceDumpContext . DumpingDirectoryPath / FRDGResourceDumpContext : : kBaseDir ) ;
}
2021-12-03 02:41:52 -05:00
# if PLATFORM_DESKTOP
2021-12-03 16:04:00 -05:00
if ( GRDGResourceDumpContext . bShowInExplore )
2021-12-03 02:41:52 -05:00
{
FPlatformProcess : : ExploreFolder ( * AbsDumpingDirectoryPath ) ;
}
# endif
GRDGResourceDumpContext = FRDGResourceDumpContext ( ) ;
2022-01-11 09:24:45 -05:00
DumpingFrameCounter_GameThread = 0 ;
2021-12-03 02:41:52 -05:00
}
2022-01-07 08:18:56 -05:00
static const TCHAR * GetPassEventNameWithGPUMask ( const FRDGPass * Pass , FString & OutNameStorage )
{
# if WITH_MGPU
if ( ( GNumExplicitGPUsForRendering > 1 ) & & GDumpGPUMask . GetValueOnRenderThread ( ) )
{
// Prepend GPU mask on the event name of each pass, so you can see which GPUs the pass ran on. Putting the mask at the
// front rather than the back makes all the masks line up, and easier to read (or ignore if you don't care about them).
// Also, it's easy to globally search for passes with a particular GPU mask using name search in the dump browser.
OutNameStorage = FString : : Printf ( TEXT ( " [%x] %s " ) , Pass - > GetGPUMask ( ) . GetNative ( ) , Pass - > GetEventName ( ) . GetTCHAR ( ) ) ;
return * OutNameStorage ;
}
else
# endif // WITH_MGPU
{
return Pass - > GetEventName ( ) . GetTCHAR ( ) ;
}
}
2021-12-03 02:41:52 -05:00
void FRDGBuilder : : DumpResourcePassOutputs ( const FRDGPass * Pass )
{
if ( bInDebugPassScope )
{
return ;
}
if ( ! GRDGResourceDumpContext . IsDumpingFrame ( ) )
{
return ;
}
2021-12-03 16:04:00 -05:00
check ( IsInRenderingThread ( ) ) ;
if ( ! GRDGResourceDumpContext . IsDumpingPass ( Pass ) )
2021-12-03 02:41:52 -05:00
{
2021-12-03 16:04:00 -05:00
return ;
2021-12-03 02:41:52 -05:00
}
bInDebugPassScope = true ;
TArray < TSharedPtr < FJsonValue > > InputResourceNames ;
TArray < TSharedPtr < FJsonValue > > OutputResourceNames ;
Pass - > GetParameters ( ) . Enumerate ( [ & ] ( FRDGParameter Parameter )
{
switch ( Parameter . GetType ( ) )
{
case UBMT_RDG_TEXTURE :
{
if ( FRDGTextureRef Texture = Parameter . GetAsTexture ( ) )
{
2021-12-03 16:04:00 -05:00
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : Create ( Texture ) ;
GRDGResourceDumpContext . AddDumpTexturePasses ( * this , InputResourceNames , OutputResourceNames , Pass , TextureSubResource , /* bIsOutputResource = */ false ) ;
2021-12-03 02:41:52 -05:00
}
}
break ;
case UBMT_RDG_TEXTURE_SRV :
{
if ( FRDGTextureSRVRef SRV = Parameter . GetAsTextureSRV ( ) )
{
2021-12-03 16:04:00 -05:00
if ( SRV - > Desc . MetaData = = ERHITextureMetaDataAccess : : None )
{
GRDGResourceDumpContext . AddDumpTexturePasses ( * this , InputResourceNames , OutputResourceNames , Pass , SRV - > Desc , /* bIsOutputResource = */ false ) ;
}
else
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Dumping texture %s's meta data unsupported " ) , SRV - > Desc . Texture - > Name ) ;
}
2021-12-03 02:41:52 -05:00
}
}
break ;
case UBMT_RDG_TEXTURE_UAV :
{
if ( FRDGTextureUAVRef UAV = Parameter . GetAsTextureUAV ( ) )
{
2021-12-03 16:04:00 -05:00
if ( UAV - > Desc . MetaData = = ERHITextureMetaDataAccess : : None )
{
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : CreateForMipLevel ( UAV - > Desc . Texture , UAV - > Desc . MipLevel ) ;
GRDGResourceDumpContext . AddDumpTextureSubResourcePass ( * this , InputResourceNames , OutputResourceNames , Pass , TextureSubResource , /* bIsOutputResource = */ true ) ;
}
else
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Dumping texture %s's meta data unsupported " ) , UAV - > Desc . Texture - > Name ) ;
}
2021-12-03 02:41:52 -05:00
}
}
break ;
case UBMT_RDG_TEXTURE_ACCESS :
{
if ( FRDGTextureAccess TextureAccess = Parameter . GetAsTextureAccess ( ) )
{
bool bIsOutputResource = (
TextureAccess . GetAccess ( ) = = ERHIAccess : : UAVCompute | |
TextureAccess . GetAccess ( ) = = ERHIAccess : : UAVGraphics | |
TextureAccess . GetAccess ( ) = = ERHIAccess : : RTV ) ;
2021-12-03 16:04:00 -05:00
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : Create ( TextureAccess ) ;
GRDGResourceDumpContext . AddDumpTexturePasses ( * this , InputResourceNames , OutputResourceNames , Pass , TextureSubResource , bIsOutputResource ) ;
2021-12-03 02:41:52 -05:00
}
}
break ;
case UBMT_RDG_TEXTURE_ACCESS_ARRAY :
{
const FRDGTextureAccessArray & TextureAccessArray = Parameter . GetAsTextureAccessArray ( ) ;
for ( FRDGTextureAccess TextureAccess : TextureAccessArray )
{
bool bIsOutputResource = (
TextureAccess . GetAccess ( ) = = ERHIAccess : : UAVCompute | |
TextureAccess . GetAccess ( ) = = ERHIAccess : : UAVGraphics | |
TextureAccess . GetAccess ( ) = = ERHIAccess : : RTV ) ;
2021-12-03 16:04:00 -05:00
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : Create ( TextureAccess ) ;
GRDGResourceDumpContext . AddDumpTexturePasses ( * this , InputResourceNames , OutputResourceNames , Pass , TextureSubResource , bIsOutputResource ) ;
2021-12-03 02:41:52 -05:00
}
}
break ;
case UBMT_RDG_BUFFER_SRV :
{
if ( FRDGBufferSRVRef SRV = Parameter . GetAsBufferSRV ( ) )
{
FRDGBufferRef Buffer = SRV - > Desc . Buffer ;
GRDGResourceDumpContext . AddDumpBufferPass ( * this , InputResourceNames , OutputResourceNames , Pass , Buffer , /* bIsOutputResource = */ false ) ;
}
}
break ;
case UBMT_RDG_BUFFER_UAV :
{
if ( FRDGBufferUAVRef UAV = Parameter . GetAsBufferUAV ( ) )
{
FRDGBufferRef Buffer = UAV - > Desc . Buffer ;
GRDGResourceDumpContext . AddDumpBufferPass ( * this , InputResourceNames , OutputResourceNames , Pass , Buffer , /* bIsOutputResource = */ true ) ;
}
}
break ;
case UBMT_RDG_BUFFER_ACCESS :
{
if ( FRDGBufferAccess BufferAccess = Parameter . GetAsBufferAccess ( ) )
{
bool bIsOutputResource = (
BufferAccess . GetAccess ( ) = = ERHIAccess : : UAVCompute | |
BufferAccess . GetAccess ( ) = = ERHIAccess : : UAVGraphics ) ;
GRDGResourceDumpContext . AddDumpBufferPass ( * this , InputResourceNames , OutputResourceNames , Pass , BufferAccess , bIsOutputResource ) ;
}
}
break ;
case UBMT_RDG_BUFFER_ACCESS_ARRAY :
{
const FRDGBufferAccessArray & BufferAccessArray = Parameter . GetAsBufferAccessArray ( ) ;
for ( FRDGBufferAccess BufferAccess : BufferAccessArray )
{
bool bIsOutputResource = (
BufferAccess . GetAccess ( ) = = ERHIAccess : : UAVCompute | |
BufferAccess . GetAccess ( ) = = ERHIAccess : : UAVGraphics ) ;
GRDGResourceDumpContext . AddDumpBufferPass ( * this , InputResourceNames , OutputResourceNames , Pass , BufferAccess , bIsOutputResource ) ;
}
}
break ;
case UBMT_RENDER_TARGET_BINDING_SLOTS :
{
const FRenderTargetBindingSlots & RenderTargets = Parameter . GetAsRenderTargetBindingSlots ( ) ;
RenderTargets . Enumerate ( [ & ] ( FRenderTargetBinding RenderTarget )
{
FRDGTextureRef Texture = RenderTarget . GetTexture ( ) ;
2021-12-03 16:04:00 -05:00
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : CreateForMipLevel ( Texture , RenderTarget . GetMipIndex ( ) ) ;
GRDGResourceDumpContext . AddDumpTextureSubResourcePass ( * this , InputResourceNames , OutputResourceNames , Pass , TextureSubResource , /* bIsOutputResource = */ true ) ;
2021-12-03 02:41:52 -05:00
} ) ;
const FDepthStencilBinding & DepthStencil = RenderTargets . DepthStencil ;
if ( FRDGTextureRef Texture = DepthStencil . GetTexture ( ) )
{
2021-12-03 16:04:00 -05:00
FExclusiveDepthStencil DepthStencilAccess = DepthStencil . GetDepthStencilAccess ( ) ;
if ( DepthStencilAccess . IsUsingDepth ( ) )
{
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : CreateForMipLevel ( Texture , 0 ) ;
GRDGResourceDumpContext . AddDumpTextureSubResourcePass (
* this , InputResourceNames , OutputResourceNames , Pass , TextureSubResource ,
/* bIsOutputResource = */ DepthStencilAccess . IsDepthWrite ( ) ) ;
}
if ( DepthStencilAccess . IsUsingStencil ( ) )
{
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : CreateWithPixelFormat ( Texture , PF_X24_G8 ) ;
GRDGResourceDumpContext . AddDumpTextureSubResourcePass (
* this , InputResourceNames , OutputResourceNames , Pass , TextureSubResource ,
/* bIsOutputResource = */ DepthStencilAccess . IsStencilWrite ( ) ) ;
}
2021-12-03 02:41:52 -05:00
}
}
break ;
}
} ) ;
2021-12-03 16:04:00 -05:00
// Dump the pass informations
2021-12-03 02:41:52 -05:00
{
TArray < TSharedPtr < FJsonValue > > ParentEventScopeNames ;
# if RDG_GPU_SCOPES
{
const FRDGEventScope * ParentScope = Pass - > GetGPUScopes ( ) . Event ;
while ( ParentScope )
{
ParentEventScopeNames . Add ( MakeShareable ( new FJsonValueString ( ParentScope - > Name . GetTCHAR ( ) ) ) ) ;
ParentScope = ParentScope - > ParentScope ;
}
}
# endif
2021-12-03 16:04:00 -05:00
{
ParentEventScopeNames . Add ( MakeShareable ( new FJsonValueString ( FString : : Printf ( TEXT ( " Frame %llu " ) , GFrameCounterRenderThread ) ) ) ) ;
}
2021-12-03 02:41:52 -05:00
2022-01-07 08:18:56 -05:00
FString EventNameStorage ;
2021-12-03 02:41:52 -05:00
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
2022-01-07 08:18:56 -05:00
JsonObject - > SetStringField ( TEXT ( " EventName " ) , GetPassEventNameWithGPUMask ( Pass , EventNameStorage ) ) ;
2021-12-03 02:41:52 -05:00
JsonObject - > SetStringField ( TEXT ( " ParametersName " ) , Pass - > GetParameters ( ) . GetLayout ( ) . GetDebugName ( ) ) ;
2021-12-03 16:04:00 -05:00
JsonObject - > SetStringField ( TEXT ( " Parameters " ) , FRDGResourceDumpContext : : PtrToString ( Pass - > GetParameters ( ) . GetContents ( ) ) ) ;
JsonObject - > SetStringField ( TEXT ( " ParametersMetadata " ) , FRDGResourceDumpContext : : PtrToString ( Pass - > GetParameters ( ) . GetMetadata ( ) ) ) ;
JsonObject - > SetStringField ( TEXT ( " Pointer " ) , FString : : Printf ( TEXT ( " %016x " ) , FRDGResourceDumpContext : : PtrToUint ( Pass ) ) ) ;
2021-12-03 02:41:52 -05:00
JsonObject - > SetNumberField ( TEXT ( " Id " ) , GRDGResourceDumpContext . PassesCount ) ;
JsonObject - > SetArrayField ( TEXT ( " ParentEventScopes " ) , ParentEventScopeNames ) ;
JsonObject - > SetArrayField ( TEXT ( " InputResources " ) , InputResourceNames ) ;
JsonObject - > SetArrayField ( TEXT ( " OutputResources " ) , OutputResourceNames ) ;
2021-12-03 16:04:00 -05:00
GRDGResourceDumpContext . DumpJsonToFile ( JsonObject , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " Passes.json " ) , FILEWRITE_Append ) ;
}
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
// Dump the pass' parameters
if ( GDumpGPUPassParameters . GetValueOnRenderThread ( ) ! = 0 )
{
int32 PassParametersByteSize = 0 ;
{
const FShaderParametersMetadata * Metadata = Pass - > GetParameters ( ) . GetMetadata ( ) ;
if ( Metadata )
{
GRDGResourceDumpContext . Dump ( Metadata ) ;
PassParametersByteSize = Metadata - > GetSize ( ) ;
}
}
if ( PassParametersByteSize = = 0 & & Pass - > GetParameters ( ) . GetLayoutPtr ( ) )
{
PassParametersByteSize = Pass - > GetParameters ( ) . GetLayout ( ) . ConstantBufferSize ;
}
const uint8 * PassParametersContent = Pass - > GetParameters ( ) . GetContents ( ) ;
if ( PassParametersContent & & ! GRDGResourceDumpContext . IsDumped ( PassParametersContent ) )
{
TArrayView < const uint8 > ArrayView ( PassParametersContent , PassParametersByteSize ) ;
FString DumpFilePath = FRDGResourceDumpContext : : kStructuresDir / FRDGResourceDumpContext : : PtrToString ( PassParametersContent ) + TEXT ( " .bin " ) ;
GRDGResourceDumpContext . DumpBinaryToFile ( ArrayView , DumpFilePath ) ;
GRDGResourceDumpContext . SetDumped ( PassParametersContent ) ;
}
2021-12-03 02:41:52 -05:00
}
GRDGResourceDumpContext . PassesCount + + ;
bInDebugPassScope = false ;
}
2021-12-03 16:04:00 -05:00
# if RDG_DUMP_RESOURCES_AT_EACH_DRAW
void FRDGBuilder : : BeginPassDump ( const FRDGPass * Pass )
{
if ( ! GRDGResourceDumpContext . IsDumpingFrame ( ) )
{
return ;
}
if ( ! GDumpGPUDraws . GetValueOnRenderThread ( ) )
{
return ;
}
if ( ! EnumHasAnyFlags ( Pass - > GetFlags ( ) , ERDGPassFlags : : Raster ) )
{
return ;
}
if ( ! IsInRenderingThread ( ) )
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Couldn't start dumping draw's resources for pass %s because not in the rendering thread " ) , Pass - > GetEventName ( ) . GetTCHAR ( ) ) ;
return ;
}
check ( GRDGResourceDumpContext . DrawDumpingPass = = nullptr ) ;
if ( GRDGResourceDumpContext . IsDumpingPass ( Pass ) )
{
GRDGResourceDumpContext . DrawDumpingPass = Pass ;
GRDGResourceDumpContext . DrawDumpCount = 0 ;
}
}
// static
void FRDGBuilder : : DumpDraw ( const FRDGEventName & DrawEventName )
{
if ( ! GRDGResourceDumpContext . IsDumpingFrame ( ) )
{
return ;
}
if ( ! IsInRenderingThread ( ) )
{
UE_LOG ( LogRendererCore , Warning , TEXT ( " Couldn't dump draw because not in the rendering thread " ) ) ;
return ;
}
if ( ! GRDGResourceDumpContext . DrawDumpingPass )
{
return ;
}
const FRDGPass * Pass = GRDGResourceDumpContext . DrawDumpingPass ;
FRHICommandListImmediate & RHICmdList = FRHICommandListExecutor : : GetImmediateCommandList ( ) ;
if ( EnumHasAnyFlags ( Pass - > GetFlags ( ) , ERDGPassFlags : : Raster ) )
{
RHICmdList . EndRenderPass ( ) ;
}
Pass - > GetParameters ( ) . Enumerate ( [ & ] ( FRDGParameter Parameter )
{
switch ( Parameter . GetType ( ) )
{
case UBMT_RENDER_TARGET_BINDING_SLOTS :
{
const FRenderTargetBindingSlots & RenderTargets = Parameter . GetAsRenderTargetBindingSlots ( ) ;
RenderTargets . Enumerate ( [ & ] ( FRenderTargetBinding RenderTarget )
{
FRDGTextureRef Texture = RenderTarget . GetTexture ( ) ;
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : CreateForMipLevel ( Texture , RenderTarget . GetMipIndex ( ) ) ;
GRDGResourceDumpContext . DumpDrawTextureSubResource (
RHICmdList ,
TextureSubResource ,
ERHIAccess : : RTV ) ;
} ) ;
const FDepthStencilBinding & DepthStencil = RenderTargets . DepthStencil ;
if ( FRDGTextureRef Texture = DepthStencil . GetTexture ( ) )
{
FExclusiveDepthStencil DepthStencilAccess = DepthStencil . GetDepthStencilAccess ( ) ;
if ( DepthStencilAccess . IsDepthWrite ( ) )
{
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : CreateForMipLevel ( Texture , 0 ) ;
GRDGResourceDumpContext . DumpDrawTextureSubResource (
RHICmdList ,
TextureSubResource ,
ERHIAccess : : RTV ) ;
}
if ( DepthStencilAccess . IsStencilWrite ( ) )
{
FRDGTextureSRVDesc TextureSubResource = FRDGTextureSRVDesc : : CreateWithPixelFormat ( Texture , PF_X24_G8 ) ;
GRDGResourceDumpContext . DumpDrawTextureSubResource (
RHICmdList ,
TextureSubResource ,
ERHIAccess : : RTV ) ;
}
}
}
break ;
}
} ) ;
if ( EnumHasAnyFlags ( Pass - > GetFlags ( ) , ERDGPassFlags : : Raster ) )
{
RHICmdList . BeginRenderPass ( Pass - > GetParameters ( ) . GetRenderPassInfo ( ) , Pass - > GetName ( ) ) ;
}
// Dump the draw even name
{
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " DrawName " ) , DrawEventName . GetTCHAR ( ) ) ;
FString DumpFilePath = FRDGResourceDumpContext : : kPassesDir / FString : : Printf ( TEXT ( " Pass.%016x.Draws.json " ) , FRDGResourceDumpContext : : PtrToUint ( Pass ) ) ;
GRDGResourceDumpContext . DumpJsonToFile ( JsonObject , DumpFilePath , FILEWRITE_Append ) ;
}
GRDGResourceDumpContext . DrawDumpCount + + ;
if ( GRDGResourceDumpContext . DrawDumpCount % 10 = = 0 )
{
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped %d draws' resources " ) , GRDGResourceDumpContext . DrawDumpCount ) ;
return ;
}
}
void FRDGBuilder : : EndPassDump ( const FRDGPass * Pass )
{
if ( ! GRDGResourceDumpContext . IsDumpingFrame ( ) )
{
return ;
}
if ( ! IsInRenderingThread ( ) )
{
return ;
}
if ( ! GRDGResourceDumpContext . DrawDumpingPass )
{
return ;
}
check ( Pass = = GRDGResourceDumpContext . DrawDumpingPass ) ;
// Output how many draw has been dump for this pass.
if ( GRDGResourceDumpContext . DrawDumpCount > 0 )
{
2022-01-07 08:18:56 -05:00
FString EventNameStorage ;
2021-12-03 16:04:00 -05:00
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
2022-01-07 08:18:56 -05:00
JsonObject - > SetStringField ( TEXT ( " EventName " ) , GetPassEventNameWithGPUMask ( Pass , EventNameStorage ) ) ;
2021-12-03 16:04:00 -05:00
JsonObject - > SetStringField ( TEXT ( " Pointer " ) , FString : : Printf ( TEXT ( " %016x " ) , FRDGResourceDumpContext : : PtrToUint ( Pass ) ) ) ;
JsonObject - > SetNumberField ( TEXT ( " DrawCount " ) , GRDGResourceDumpContext . DrawDumpCount ) ;
GRDGResourceDumpContext . DumpJsonToFile ( JsonObject , FString ( FRDGResourceDumpContext : : kBaseDir ) / TEXT ( " PassDrawCounts.json " ) , FILEWRITE_Append ) ;
UE_LOG ( LogRendererCore , Display , TEXT ( " Completed dump of %d draws for pass: %s " ) , GRDGResourceDumpContext . DrawDumpCount , Pass - > GetEventName ( ) . GetTCHAR ( ) ) ;
}
GRDGResourceDumpContext . DrawDumpingPass = nullptr ;
GRDGResourceDumpContext . DrawDumpCount = 0 ;
}
// static
2022-01-11 09:24:45 -05:00
bool FRDGBuilder : : IsDumpingFrame ( )
{
return GRDGResourceDumpContext . IsDumpingFrame ( ) ;
}
2021-12-03 16:04:00 -05:00
bool FRDGBuilder : : IsDumpingDraws ( )
{
if ( ! GRDGResourceDumpContext . IsDumpingFrame ( ) )
{
return false ;
}
return GDumpGPUDraws . GetValueOnRenderThread ( ) ! = 0 ;
}
# endif // RDG_DUMP_RESOURCES_AT_EACH_DRAW
# else //! RDG_DUMP_RESOURCES
bool IsDumpingRDGResources ( )
{
return false ;
}
2022-01-07 08:18:56 -05:00
# endif //! RDG_DUMP_RESOURCES