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 ) ;
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 ) ;
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 ;
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
bool bShowInExplore = false ;
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 ) ;
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 ) ;
return FFileHelper : : SaveArrayToFile ( ArrayView , * FullPath ) ;
}
2021-12-03 17:00:32 -05:00
bool DumpBinaryToFile ( const TArray64 < uint8 > & Array , const FString & FileName )
{
// Make it has if the write happened and was successful.
if ( ! bEnableDiskWrite )
{
return true ;
}
FString FullPath = GetDumpFullPath ( FileName ) ;
return FFileHelper : : SaveArrayToFile ( Array , * FullPath ) ;
}
bool IsUnsafeToDumpResource ( SIZE_T ResourceByteSize , float DumpMemoryMultiplier ) const
2021-12-03 16:04:00 -05:00
{
uint64 AproximatedStagingMemoryRequired = uint64 ( double ( ResourceByteSize ) * DumpMemoryMultiplier ) ;
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 )
{
// 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 )
{
IConsoleManager : : Get ( ) . ForEachConsoleObjectThatStartsWith ( FConsoleObjectVisitor : : CreateLambda ( OnConsoleVariable ) , TEXT ( " r. " ) ) ;
IConsoleManager : : Get ( ) . ForEachConsoleObjectThatStartsWith ( FConsoleObjectVisitor : : CreateLambda ( OnConsoleVariable ) , TEXT ( " sg. " ) ) ;
IConsoleManager : : Get ( ) . ForEachConsoleObjectThatContains ( FConsoleObjectVisitor : : CreateLambda ( OnConsoleVariable ) , TEXT ( " rhi " ) ) ;
}
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
2021-12-03 16:04:00 -05:00
TSharedPtr < FJsonObject > ToJson ( const TCHAR * Name , const FRDGTextureDesc & Desc )
{
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 ) ;
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 ;
}
TSharedPtr < FJsonObject > ToJson ( const TCHAR * Name , const FRDGBufferDesc & Desc , int32 ResourceByteSize )
{
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " Name " ) , Name ) ;
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.
SubresourceDumpDesc . bPreprocessForStaging = SubresourceDumpDesc . PreprocessedPixelFormat ! = Desc . Format | | SubresourceDesc . MipLevel ! = 0 ;
return SubresourceDumpDesc ;
}
void DumpTextureSubResource (
FRHICommandListImmediate & RHICmdList ,
const TCHAR * TextureDebugName ,
FRHITexture * Texture ,
FRHIShaderResourceView * SubResourceSRV ,
const FTextureSubresourceDumpDesc & SubresourceDumpDesc ,
const FString & DumpFilePath )
{
check ( IsInRenderingThread ( ) ) ;
// Preprocess
FTextureRHIRef StagingSrcTexture ;
if ( SubresourceDumpDesc . bPreprocessForStaging )
{
{
FRHIResourceCreateInfo CreateInfo ( TEXT ( " DumpGPU.PreprocessTexture " ) ) ;
StagingSrcTexture = RHICreateTexture2D (
SubresourceDumpDesc . SubResourceExtent . X ,
SubresourceDumpDesc . SubResourceExtent . Y ,
SubresourceDumpDesc . 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
{
StagingSrcTexture = Texture ;
}
// Copy the texture for CPU readback
FTextureRHIRef StagingTexture ;
{
FRHIResourceCreateInfo CreateInfo ( TEXT ( " DumpGPU.StagingTexture " ) ) ;
StagingTexture = RHICreateTexture2D (
SubresourceDumpDesc . SubResourceExtent . X ,
SubresourceDumpDesc . SubResourceExtent . Y ,
SubresourceDumpDesc . PreprocessedPixelFormat ,
/* NumMips = */ 1 ,
/* NumSamples = */ 1 ,
TexCreate_CPUReadback | TexCreate_HideInVisualizeTexture ,
CreateInfo ) ;
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 ) ) ;
}
// Submit to GPU and wait for completion.
static const FName FenceName ( TEXT ( " DumpGPU.TextureFence " ) ) ;
FGPUFenceRHIRef Fence = RHICreateGPUFence ( FenceName ) ;
{
Fence - > Clear ( ) ;
RHICmdList . WriteGPUFence ( Fence ) ;
RHICmdList . SubmitCommandsAndFlushGPU ( ) ;
RHICmdList . BlockUntilGPUIdle ( ) ;
}
void * Content = nullptr ;
int32 RowPitchInPixels = 0 ;
int32 ColumnPitchInPixels = 0 ;
RHICmdList . MapStagingSurface ( StagingTexture , Fence . GetReference ( ) , Content , RowPitchInPixels , ColumnPitchInPixels ) ;
if ( Content )
{
2021-12-03 17:00:32 -05:00
TArray64 < uint8 > Array ;
2021-12-03 16:04:00 -05:00
Array . SetNumUninitialized ( SubresourceDumpDesc . ByteSize ) ;
2021-12-03 17:00:32 -05:00
SIZE_T BytePerPixel = SIZE_T ( GPixelFormats [ SubresourceDumpDesc . PreprocessedPixelFormat ] . BlockBytes ) ;
2021-12-03 16:04:00 -05:00
2021-12-03 17:00:32 -05:00
const uint8 * SrcData = static_cast < const uint8 * > ( Content ) ;
2021-12-03 16:04:00 -05:00
for ( int32 y = 0 ; y < SubresourceDumpDesc . SubResourceExtent . Y ; y + + )
{
// Flip the data to be bottom left corner for the WebGL viewer.
2021-12-03 17:00:32 -05:00
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 ;
2021-12-03 16:04:00 -05:00
2021-12-03 17:00:32 -05:00
FPlatformMemory : : Memmove ( DstPos , SrcPos , SIZE_T ( SubresourceDumpDesc . SubResourceExtent . X ) * BytePerPixel ) ;
2021-12-03 16:04:00 -05:00
}
RHICmdList . UnmapStagingSurface ( StagingTexture ) ;
2021-12-03 17:00:32 -05:00
DumpBinaryToFile ( Array , 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 )
{
TSharedPtr < FJsonObject > JsonObject = ToJson ( SubresourceDesc . Texture - > Name , Desc ) ;
FString ResourceInfoJsonPath = kResourcesDir / UniqueResourceName + TEXT ( " .json " ) ;
DumpJsonToFile ( JsonObject , ResourceInfoJsonPath ) ;
}
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
{
2021-12-03 16:04:00 -05:00
TSharedPtr < FJsonObject > JsonObject = ToJson ( Buffer - > Name , Desc , ByteSize ) ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05:00
FString ResourceInfoJsonPath = kResourcesDir / UniqueResourceName + TEXT ( " .json " ) ;
DumpJsonToFile ( JsonObject , ResourceInfoJsonPath ) ;
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 ( ) ) ;
2021-12-03 16:04:00 -05:00
FStagingBufferRHIRef StagingBuffer = RHICreateStagingBuffer ( ) ;
2021-12-03 02:41:52 -05:00
2021-12-03 16:04:00 -05: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 ) ;
{
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 )
{
TArrayView < const uint8 > ArrayView ( reinterpret_cast < const uint8 * > ( Content ) , ByteSize ) ;
2021-12-03 16:04:00 -05:00
DumpBinaryToFile ( ArrayView , 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
}
} ;
2021-12-03 16:04:00 -05:00
bool bIsDumpingFrame_GameThread = false ;
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 ( ) ;
}
FString FRDGBuilder : : BeginResourceDump ( const TArray < FString > & Args )
{
check ( IsInGameThread ( ) ) ;
if ( bIsDumpingFrame_GameThread )
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 ( ) ;
{
FString MasterDirectoryPath = GDumpGPUDirectoryCVar . GetValueOnGameThread ( ) ;
if ( MasterDirectoryPath . IsEmpty ( ) )
{
MasterDirectoryPath = FPaths : : ProjectSavedDir ( ) / TEXT ( " GPUDumps/ " ) ;
}
NewResourceDumpContext . DumpingDirectoryPath = MasterDirectoryPath / FApp : : GetProjectName ( ) + TEXT ( " - " ) + FPlatformProperties : : PlatformName ( ) + TEXT ( " - " ) + NewResourceDumpContext . Time . ToString ( ) + TEXT ( " / " ) ;
}
NewResourceDumpContext . bEnableDiskWrite = GDumpTestEnableDiskWrite . GetValueOnGameThread ( ) ! = 0 ;
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 ) ) ;
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
{
const TCHAR * OpenGPUDumpViewerWindowsName = TEXT ( " OpenGPUDumpViewer.bat " ) ;
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 ) ) ;
PlatformFile . CopyFile ( * ( NewResourceDumpContext . DumpingDirectoryPath / OpenGPUDumpViewerWindowsName ) , * ( DumpGPUViewerSourcePath / OpenGPUDumpViewerWindowsName ) ) ;
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
} ) ;
bIsDumpingFrame_GameThread = true ;
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 ( ) ) ;
if ( ! bIsDumpingFrame_GameThread )
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 ) ;
{
UE_LOG ( LogRendererCore , Display , TEXT ( " Dumped %d resources to %s " ) , GRDGResourceDumpContext . ResourcesDumpPasses , * AbsDumpingDirectoryPath ) ;
}
// 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 ( ) ;
2021-12-03 16:04:00 -05:00
bIsDumpingFrame_GameThread = false ;
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
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " EventName " ) , Pass - > GetEventName ( ) . GetTCHAR ( ) ) ;
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 )
{
TSharedPtr < FJsonObject > JsonObject = MakeShareable ( new FJsonObject ) ;
JsonObject - > SetStringField ( TEXT ( " EventName " ) , Pass - > GetEventName ( ) . GetTCHAR ( ) ) ;
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
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 ;
}
2021-12-03 02:41:52 -05:00
# endif //! RDG_DUMP_RESOURCES