2020-02-07 11:01:05 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2020-02-06 13:13:41 -05:00
/*=============================================================================
ShaderResource . cpp : ShaderResource implementation .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "Shader.h"
# include "Misc/CoreMisc.h"
2020-04-21 13:46:17 -04:00
# include "Misc/StringBuilder.h"
2020-02-06 13:13:41 -05:00
# include "Stats/StatsMisc.h"
# include "Serialization/MemoryWriter.h"
# include "VertexFactory.h"
# include "ProfilingDebugging/DiagnosticTable.h"
# include "Interfaces/ITargetPlatform.h"
# include "Interfaces/ITargetPlatformManagerModule.h"
# include "Interfaces/IShaderFormat.h"
# include "ShaderCodeLibrary.h"
# include "ShaderCore.h"
# include "RenderUtils.h"
# include "Misc/ConfigCacheIni.h"
# include "Misc/ScopeLock.h"
# include "UObject/RenderingObjectVersion.h"
# include "UObject/FortniteMainBranchObjectVersion.h"
# include "ProfilingDebugging/LoadTimeTracker.h"
# include "Misc/MemStack.h"
2020-06-23 18:40:00 -04:00
# include "ShaderCompilerCore.h"
2020-02-06 13:13:41 -05:00
# if WITH_EDITORONLY_DATA
# include "Interfaces/IShaderFormat.h"
# endif
2021-03-11 21:19:35 -04:00
int32 GShaderCompressionFormatChoice = 1 ;
static FAutoConsoleVariableRef CVarShaderCompressionFormatChoice (
TEXT ( " r.Shaders.CompressionFormat " ) ,
GShaderCompressionFormatChoice ,
TEXT ( " Select the compression methods for the shader code. \n " )
TEXT ( " 0: None (uncompressed) \n " )
TEXT ( " 1: LZ4 (default) " ) ,
ECVF_ReadOnly ) ;
FName GetShaderCompressionFormat ( const FName & ShaderFormat )
{
// support an older developer-only CVar for compatibility and make it preempt
# if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
static const IConsoleVariable * CVarSkipCompression = IConsoleManager : : Get ( ) . FindConsoleVariable ( TEXT ( " r.Shaders.SkipCompression " ) ) ;
static bool bSkipCompression = ( CVarSkipCompression & & CVarSkipCompression - > GetInt ( ) ! = 0 ) ;
if ( UNLIKELY ( bSkipCompression ) )
{
return NAME_None ;
}
# endif
return ( GShaderCompressionFormatChoice = = 1 ) ? NAME_LZ4 : NAME_None ;
}
2020-02-06 13:13:41 -05:00
bool FShaderMapResource : : ArePlatformsCompatible ( EShaderPlatform CurrentPlatform , EShaderPlatform TargetPlatform )
{
bool bFeatureLevelCompatible = CurrentPlatform = = TargetPlatform ;
if ( ! bFeatureLevelCompatible & & IsPCPlatform ( CurrentPlatform ) & & IsPCPlatform ( TargetPlatform ) )
{
bFeatureLevelCompatible = GetMaxSupportedFeatureLevel ( CurrentPlatform ) > = GetMaxSupportedFeatureLevel ( TargetPlatform ) ;
2021-03-11 19:26:01 -04:00
bool const bIsTargetD3D = IsD3DPlatform ( TargetPlatform ) ;
2020-02-06 13:13:41 -05:00
2021-03-11 19:26:01 -04:00
bool const bIsCurrentPlatformD3D = IsD3DPlatform ( CurrentPlatform ) ;
2020-02-06 13:13:41 -05:00
// For Metal in Editor we can switch feature-levels, but not in cooked projects when using Metal shader librariss.
bool const bIsCurrentMetal = IsMetalPlatform ( CurrentPlatform ) ;
bool const bIsTargetMetal = IsMetalPlatform ( TargetPlatform ) ;
bool const bIsMetalCompatible = ( bIsCurrentMetal = = bIsTargetMetal )
# if !WITH_EDITOR // Static analysis doesn't like (|| WITH_EDITOR)
& & ( ! IsMetalPlatform ( CurrentPlatform ) | | ( CurrentPlatform = = TargetPlatform ) )
# endif
;
bool const bIsCurrentOpenGL = IsOpenGLPlatform ( CurrentPlatform ) ;
bool const bIsTargetOpenGL = IsOpenGLPlatform ( TargetPlatform ) ;
bFeatureLevelCompatible = bFeatureLevelCompatible & & ( bIsCurrentPlatformD3D = = bIsTargetD3D & & bIsMetalCompatible & & bIsCurrentOpenGL = = bIsTargetOpenGL ) ;
}
return bFeatureLevelCompatible ;
}
# if RHI_RAYTRACING
static TArray < uint32 > GlobalUnusedIndicies ;
static TArray < FRHIRayTracingShader * > GlobalRayTracingMaterialLibrary ;
static FCriticalSection GlobalRayTracingMaterialLibraryCS ;
void FShaderMapResource : : GetRayTracingMaterialLibrary ( TArray < FRHIRayTracingShader * > & RayTracingMaterials , FRHIRayTracingShader * DefaultShader )
{
FScopeLock Lock ( & GlobalRayTracingMaterialLibraryCS ) ;
RayTracingMaterials = GlobalRayTracingMaterialLibrary ;
for ( uint32 Index : GlobalUnusedIndicies )
{
RayTracingMaterials [ Index ] = DefaultShader ;
}
}
static uint32 AddToRayTracingLibrary ( FRHIRayTracingShader * Shader )
{
FScopeLock Lock ( & GlobalRayTracingMaterialLibraryCS ) ;
if ( GlobalUnusedIndicies . Num ( ) ! = 0 )
{
uint32 Index = GlobalUnusedIndicies . Pop ( false ) ;
checkSlow ( GlobalRayTracingMaterialLibrary [ Index ] = = nullptr ) ;
GlobalRayTracingMaterialLibrary [ Index ] = Shader ;
return Index ;
}
else
{
GlobalRayTracingMaterialLibrary . Add ( Shader ) ;
return GlobalRayTracingMaterialLibrary . Num ( ) - 1 ;
}
}
static void RemoveFromRayTracingLibrary ( uint32 Index )
{
if ( Index ! = ~ 0u )
{
FScopeLock Lock ( & GlobalRayTracingMaterialLibraryCS ) ;
GlobalUnusedIndicies . Push ( Index ) ;
GlobalRayTracingMaterialLibrary [ Index ] = nullptr ;
}
}
# endif // RHI_RAYTRACING
static void ApplyResourceStats ( FShaderMapResourceCode & Resource )
{
# if STATS
INC_DWORD_STAT_BY ( STAT_Shaders_ShaderResourceMemory , Resource . GetSizeBytes ( ) ) ;
2020-04-21 13:46:17 -04:00
for ( const FShaderMapResourceCode : : FShaderEntry & Shader : Resource . ShaderEntries )
2020-02-06 13:13:41 -05:00
{
2020-04-21 13:46:17 -04:00
INC_DWORD_STAT_BY_FName ( GetMemoryStatType ( Shader . Frequency ) . GetName ( ) , Shader . Code . Num ( ) ) ;
2020-02-06 13:13:41 -05:00
}
# endif // STATS
}
static void RemoveResourceStats ( FShaderMapResourceCode & Resource )
{
# if STATS
DEC_DWORD_STAT_BY ( STAT_Shaders_ShaderResourceMemory , Resource . GetSizeBytes ( ) ) ;
2020-04-21 13:46:17 -04:00
for ( const FShaderMapResourceCode : : FShaderEntry & Shader : Resource . ShaderEntries )
2020-02-06 13:13:41 -05:00
{
2020-04-21 13:46:17 -04:00
DEC_DWORD_STAT_BY_FName ( GetMemoryStatType ( Shader . Frequency ) . GetName ( ) , Shader . Code . Num ( ) ) ;
2020-02-06 13:13:41 -05:00
}
# endif // STATS
}
Fix memory leak of shader code during shader compilation (during cooks, deployments, or editor). Depending on settings and project this can save a considerable amount. For example, this saved ~39GB on the test project I was cooking.
- FShaderMapResourceCode derives from FThreadSafeRefCountedObject which has a member that keeps track of the number of references to the object. We were doing deep copies using the Copy Constructor of FShaderMapResourceCode which resulted in a member wise copy of the num refs. This is not the behavior you'd expect.
- The offending line of code that caused the memory leak was in FShaderMapBase::AssignCopy:
- Code = new FShaderMapResourceCode(*Source.Code);
- Like FRefCountBase I've deleted the copy constructor and assignment operator for FRefCountedObject and FThreadSafeRefCountedObject to catch issues at compile time where we are copying across NumRefs incorrectly.
- To fix the issue I've manually created copy constructors for FGPUProfilerEventNodeStats and FShaderMapResourceCode.
#rb Devin.Doucette, Dave.Jones, Arciel.Rekman, Ben.Ingram
#review-16115628 @Devin.Doucette, @Dave.Jones, @Arciel.Rekman, @Ben.Ingram
#jira UE-108598, UE-113736
[CL 16116577 by Jason Nadro in ue5-main branch]
2021-04-26 14:03:07 -04:00
FShaderMapResourceCode : : FShaderMapResourceCode ( const FShaderMapResourceCode & Other )
{
ResourceHash = Other . ResourceHash ;
ShaderHashes = Other . ShaderHashes ;
ShaderEntries = Other . ShaderEntries ;
# if WITH_EDITORONLY_DATA
PlatformDebugData = Other . PlatformDebugData ;
PlatformDebugDataHashes = Other . PlatformDebugDataHashes ;
# endif // WITH_EDITORONLY_DATA
}
2020-02-06 13:13:41 -05:00
FShaderMapResourceCode : : ~ FShaderMapResourceCode ( )
{
RemoveResourceStats ( * this ) ;
}
void FShaderMapResourceCode : : Finalize ( )
{
FSHA1 Hasher ;
Hasher . Update ( ( uint8 * ) ShaderHashes . GetData ( ) , ShaderHashes . Num ( ) * sizeof ( FSHAHash ) ) ;
Hasher . Final ( ) ;
Hasher . GetHash ( ResourceHash . Hash ) ;
ApplyResourceStats ( * this ) ;
}
2020-04-21 13:46:17 -04:00
uint32 FShaderMapResourceCode : : GetSizeBytes ( ) const
{
uint32 Size = sizeof ( * this ) + ShaderHashes . GetAllocatedSize ( ) + ShaderEntries . GetAllocatedSize ( ) ;
for ( const FShaderEntry & Entry : ShaderEntries )
{
Size + = Entry . Code . GetAllocatedSize ( ) ;
}
return Size ;
}
int32 FShaderMapResourceCode : : FindShaderIndex ( const FSHAHash & InHash ) const
{
return Algo : : BinarySearch ( ShaderHashes , InHash ) ;
}
2020-06-23 18:40:00 -04:00
void FShaderMapResourceCode : : AddShaderCompilerOutput ( const FShaderCompilerOutput & Output )
{
# if WITH_EDITORONLY_DATA
AddPlatformDebugData ( Output . PlatformDebugData ) ;
# endif
2021-03-11 21:19:35 -04:00
AddShaderCode ( Output . Target . GetFrequency ( ) , Output . OutputHash , Output . ShaderCode ) ;
2020-06-23 18:40:00 -04:00
}
2021-03-11 21:19:35 -04:00
void FShaderMapResourceCode : : AddShaderCode ( EShaderFrequency InFrequency , const FSHAHash & InHash , const FShaderCode & InCode )
2020-04-21 13:46:17 -04:00
{
2021-01-25 12:06:02 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FShaderMapResourceCode : : AddShaderCode ) ;
2020-04-21 13:46:17 -04:00
const int32 Index = Algo : : LowerBound ( ShaderHashes , InHash ) ;
if ( Index > = ShaderHashes . Num ( ) | | ShaderHashes [ Index ] ! = InHash )
{
ShaderHashes . Insert ( InHash , Index ) ;
FShaderEntry & Entry = ShaderEntries . InsertDefaulted_GetRef ( Index ) ;
Entry . Frequency = InFrequency ;
2021-03-11 21:19:35 -04:00
const TArray < uint8 > & ShaderCode = InCode . GetReadAccess ( ) ;
2020-04-21 13:46:17 -04:00
2021-03-11 21:19:35 -04:00
FName ShaderCompressionFormat = GetShaderCompressionFormat ( ) ;
if ( ShaderCompressionFormat ! = NAME_None )
2020-04-21 13:46:17 -04:00
{
2021-03-11 21:19:35 -04:00
Entry . UncompressedSize = InCode . GetUncompressedSize ( ) ;
// we trust that SCWs also obeyed by the same CVar, so we expect a compressed shader code at this point
// However, if we see an uncompressed shader, it perhaps means that SCW tried to compress it, but the result was worse than uncompressed.
// Because of that we special-case NAME_None here
if ( ShaderCompressionFormat ! = InCode . GetCompressionFormat ( ) )
{
if ( InCode . GetCompressionFormat ( ) ! = NAME_None )
{
UE_LOG ( LogShaders , Fatal , TEXT ( " Shader %s is expected to be compressed with %s, but it is compressed with %s instead. " ) ,
* InHash . ToString ( ) ,
* ShaderCompressionFormat . ToString ( ) ,
* InCode . GetCompressionFormat ( ) . ToString ( )
) ;
// unreachable
return ;
}
// assume uncompressed due to worse ratio than the compression
Entry . UncompressedSize = ShaderCode . Num ( ) ;
UE_LOG ( LogShaders , Warning , TEXT ( " Shader %s is expected to be compressed with %s, but it arrived uncompressed. Assuming compressing made it longer and storing uncompressed. " ) ,
* InHash . ToString ( ) ,
2021-05-13 13:22:48 -04:00
* ShaderCompressionFormat . ToString ( )
2021-03-11 21:19:35 -04:00
) ;
}
2020-04-21 13:46:17 -04:00
}
else
{
2021-03-11 21:19:35 -04:00
Entry . UncompressedSize = ShaderCode . Num ( ) ;
2020-04-21 13:46:17 -04:00
}
2021-03-11 21:19:35 -04:00
Entry . Code = ShaderCode ;
2020-04-21 13:46:17 -04:00
}
}
# if WITH_EDITORONLY_DATA
void FShaderMapResourceCode : : AddPlatformDebugData ( TConstArrayView < uint8 > InPlatformDebugData )
{
if ( InPlatformDebugData . Num ( ) = = 0 )
{
return ;
}
FSHAHash Hash ;
{
FSHA1 Hasher ;
Hasher . Update ( InPlatformDebugData . GetData ( ) , InPlatformDebugData . Num ( ) ) ;
Hasher . Final ( ) ;
Hasher . GetHash ( Hash . Hash ) ;
}
const int32 Index = Algo : : LowerBound ( PlatformDebugDataHashes , Hash ) ;
if ( Index > = PlatformDebugDataHashes . Num ( ) | | PlatformDebugDataHashes [ Index ] ! = Hash )
{
PlatformDebugDataHashes . Insert ( Hash , Index ) ;
PlatformDebugData . EmplaceAt ( Index , InPlatformDebugData . GetData ( ) , InPlatformDebugData . Num ( ) ) ;
}
}
# endif // WITH_EDITORONLY_DATA
void FShaderMapResourceCode : : ToString ( FStringBuilderBase & OutString ) const
{
OutString . Appendf ( TEXT ( " Shaders: Num=%d \n " ) , ShaderHashes . Num ( ) ) ;
for ( int32 i = 0 ; i < ShaderHashes . Num ( ) ; + + i )
{
const FShaderEntry & Entry = ShaderEntries [ i ] ;
OutString . Appendf ( TEXT ( " [%d]: { Hash: %s, Freq: %s, Size: %d, UncompressedSize: %d } \n " ) ,
i , * ShaderHashes [ i ] . ToString ( ) , GetShaderFrequencyString ( Entry . Frequency ) , Entry . Code . Num ( ) , Entry . UncompressedSize ) ;
}
}
2020-03-02 21:52:09 -05:00
void FShaderMapResourceCode : : Serialize ( FArchive & Ar , bool bLoadedByCookedMaterial )
2020-02-06 13:13:41 -05:00
{
Ar < < ResourceHash ;
Ar < < ShaderHashes ;
Ar < < ShaderEntries ;
check ( ShaderEntries . Num ( ) = = ShaderHashes . Num ( ) ) ;
2020-03-02 21:52:09 -05:00
# if WITH_EDITORONLY_DATA
const bool bSerializePlatformData = ! bLoadedByCookedMaterial & & ( ! Ar . IsCooking ( ) | | Ar . CookingTarget ( ) - > HasEditorOnlyData ( ) ) ;
if ( bSerializePlatformData )
{
2020-04-21 13:46:17 -04:00
Ar < < PlatformDebugDataHashes ;
2020-03-02 21:52:09 -05:00
Ar < < PlatformDebugData ;
}
# endif // WITH_EDITORONLY_DATA
2020-02-06 13:13:41 -05:00
ApplyResourceStats ( * this ) ;
}
2020-03-02 21:52:09 -05:00
# if WITH_EDITORONLY_DATA
2021-08-16 23:18:23 -04:00
void FShaderMapResourceCode : : NotifyShadersCompiled ( FName FormatName )
2020-03-02 21:52:09 -05:00
{
# if WITH_ENGINE
// Notify the platform shader format that this particular shader is being used in the cook.
// We discard this data in cooked builds unless Ar.CookingTarget()->HasEditorOnlyData() is true.
2020-04-21 13:46:17 -04:00
if ( PlatformDebugData . Num ( ) )
2020-03-02 21:52:09 -05:00
{
2021-08-16 23:18:23 -04:00
if ( const IShaderFormat * ShaderFormat = GetTargetPlatformManagerRef ( ) . FindShaderFormat ( FormatName ) )
2020-03-02 21:52:09 -05:00
{
2021-08-16 23:18:23 -04:00
for ( const TArray < uint8 > & Entry : PlatformDebugData )
2020-03-02 21:52:09 -05:00
{
2021-08-16 23:18:23 -04:00
ShaderFormat - > NotifyShaderCompiled ( Entry , FormatName ) ;
2020-03-02 21:52:09 -05:00
}
}
}
# endif // WITH_ENGINE
}
2021-08-16 23:18:23 -04:00
void FShaderMapResourceCode : : NotifyShadersCooked ( const ITargetPlatform * TargetPlatform )
{
# if WITH_ENGINE
TArray < FName > ShaderFormatNames ;
TargetPlatform - > GetAllTargetedShaderFormats ( ShaderFormatNames ) ;
for ( FName FormatName : ShaderFormatNames )
{
NotifyShadersCompiled ( FormatName ) ;
}
# endif
}
2020-03-02 21:52:09 -05:00
# endif // WITH_EDITORONLY_DATA
2020-02-06 13:13:41 -05:00
FShaderMapResource : : FShaderMapResource ( EShaderPlatform InPlatform , int32 NumShaders )
2020-06-23 18:40:00 -04:00
: NumRHIShaders ( NumShaders )
, Platform ( InPlatform )
2020-02-06 13:13:41 -05:00
, NumRefs ( 0 )
{
2020-06-23 18:40:00 -04:00
RHIShaders = MakeUnique < std : : atomic < FRHIShader * > [ ] > ( NumRHIShaders ) ; // this MakeUnique() zero-initializes the array
2020-02-06 13:13:41 -05:00
# if RHI_RAYTRACING
2021-11-23 10:25:31 -05:00
if ( GRHISupportsRayTracing & & GRHISupportsRayTracingShaders )
2020-06-23 18:40:00 -04:00
{
RayTracingMaterialLibraryIndices . AddUninitialized ( NumShaders ) ;
FMemory : : Memset ( RayTracingMaterialLibraryIndices . GetData ( ) , 0xff , NumShaders * RayTracingMaterialLibraryIndices . GetTypeSize ( ) ) ;
}
2020-02-06 13:13:41 -05:00
# endif // RHI_RAYTRACING
}
FShaderMapResource : : ~ FShaderMapResource ( )
{
2020-06-23 18:40:00 -04:00
ReleaseShaders ( ) ;
2020-02-06 13:13:41 -05:00
check ( NumRefs = = 0 ) ;
}
void FShaderMapResource : : AddRef ( )
{
FPlatformAtomics : : InterlockedIncrement ( ( volatile int32 * ) & NumRefs ) ;
}
void FShaderMapResource : : Release ( )
{
check ( NumRefs > 0 ) ;
if ( FPlatformAtomics : : InterlockedDecrement ( ( volatile int32 * ) & NumRefs ) = = 0 & & TryRelease ( ) )
{
// Send a release message to the rendering thread when the shader loses its last reference.
BeginReleaseResource ( this ) ;
BeginCleanup ( this ) ;
DEC_DWORD_STAT_BY ( STAT_Shaders_ShaderResourceMemory , GetSizeBytes ( ) ) ;
}
}
2020-06-23 18:40:00 -04:00
void FShaderMapResource : : ReleaseShaders ( )
{
if ( RHIShaders )
{
for ( int32 Idx = 0 ; Idx < NumRHIShaders ; + + Idx )
{
if ( FRHIShader * Shader = RHIShaders [ Idx ] . load ( std : : memory_order_acquire ) )
{
Shader - > Release ( ) ;
2021-09-27 19:54:25 -04:00
DEC_DWORD_STAT ( STAT_Shaders_NumShadersUsedForRendering ) ;
2020-06-23 18:40:00 -04:00
}
}
RHIShaders = nullptr ;
NumRHIShaders = 0 ;
}
}
2020-02-06 13:13:41 -05:00
void FShaderMapResource : : ReleaseRHI ( )
{
# if RHI_RAYTRACING
for ( int32 Index : RayTracingMaterialLibraryIndices )
{
RemoveFromRayTracingLibrary ( Index ) ;
}
RayTracingMaterialLibraryIndices . Empty ( ) ;
# endif // RHI_RAYTRACING
2020-06-23 18:40:00 -04:00
ReleaseShaders ( ) ;
2020-02-06 13:13:41 -05:00
}
void FShaderMapResource : : BeginCreateAllShaders ( )
{
FShaderMapResource * Resource = this ;
ENQUEUE_RENDER_COMMAND ( InitCommand ) (
[ Resource ] ( FRHICommandListImmediate & RHICmdList )
{
for ( int32 ShaderIndex = 0 ; ShaderIndex < Resource - > GetNumShaders ( ) ; + + ShaderIndex )
{
Resource - > GetShader ( ShaderIndex ) ;
}
} ) ;
}
2020-06-23 18:40:00 -04:00
FRHIShader * FShaderMapResource : : CreateShader ( int32 ShaderIndex )
2020-02-06 13:13:41 -05:00
{
check ( IsInParallelRenderingThread ( ) ) ;
2020-06-23 18:40:00 -04:00
check ( ! RHIShaders [ ShaderIndex ] . load ( std : : memory_order_acquire ) ) ;
2020-02-06 13:13:41 -05:00
TRefCountPtr < FRHIShader > RHIShader = CreateRHIShader ( ShaderIndex ) ;
# if RHI_RAYTRACING
2021-11-23 10:25:31 -05:00
if ( GRHISupportsRayTracing & & GRHISupportsRayTracingShaders & & RHIShader . IsValid ( ) & & RHIShader - > GetFrequency ( ) = = SF_RayHitGroup )
2020-02-06 13:13:41 -05:00
{
RayTracingMaterialLibraryIndices [ ShaderIndex ] = AddToRayTracingLibrary ( static_cast < FRHIRayTracingShader * > ( RHIShader . GetReference ( ) ) ) ;
}
# endif // RHI_RAYTRACING
2020-06-23 18:40:00 -04:00
// keep the reference alive (the caller will release)
if ( RHIShader . IsValid ( ) )
{
RHIShader - > AddRef ( ) ;
}
return RHIShader . GetReference ( ) ;
2020-02-06 13:13:41 -05:00
}
TRefCountPtr < FRHIShader > FShaderMapResource_InlineCode : : CreateRHIShader ( int32 ShaderIndex )
{
Fix memory leak of shader code during shader compilation (during cooks, deployments, or editor). Depending on settings and project this can save a considerable amount. For example, this saved ~39GB on the test project I was cooking.
- FShaderMapResourceCode derives from FThreadSafeRefCountedObject which has a member that keeps track of the number of references to the object. We were doing deep copies using the Copy Constructor of FShaderMapResourceCode which resulted in a member wise copy of the num refs. This is not the behavior you'd expect.
- The offending line of code that caused the memory leak was in FShaderMapBase::AssignCopy:
- Code = new FShaderMapResourceCode(*Source.Code);
- Like FRefCountBase I've deleted the copy constructor and assignment operator for FRefCountedObject and FThreadSafeRefCountedObject to catch issues at compile time where we are copying across NumRefs incorrectly.
- To fix the issue I've manually created copy constructors for FGPUProfilerEventNodeStats and FShaderMapResourceCode.
#rb Devin.Doucette, Dave.Jones, Arciel.Rekman, Ben.Ingram
#review-16115628 @Devin.Doucette, @Dave.Jones, @Arciel.Rekman, @Ben.Ingram
#jira UE-108598, UE-113736
[CL 16116577 by Jason Nadro in ue5-main branch]
2021-04-26 14:03:07 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( FShaderMapResource_InlineCode : : CreateRHIShader ) ;
2021-09-27 19:54:25 -04:00
# if STATS
double TimeFunctionEntered = FPlatformTime : : Seconds ( ) ;
ON_SCOPE_EXIT
{
if ( IsInRenderingThread ( ) )
{
double ShaderCreationTime = FPlatformTime : : Seconds ( ) - TimeFunctionEntered ;
INC_FLOAT_STAT_BY ( STAT_Shaders_TotalRTShaderInitForRenderingTime , ShaderCreationTime ) ;
}
} ;
# endif
Fix memory leak of shader code during shader compilation (during cooks, deployments, or editor). Depending on settings and project this can save a considerable amount. For example, this saved ~39GB on the test project I was cooking.
- FShaderMapResourceCode derives from FThreadSafeRefCountedObject which has a member that keeps track of the number of references to the object. We were doing deep copies using the Copy Constructor of FShaderMapResourceCode which resulted in a member wise copy of the num refs. This is not the behavior you'd expect.
- The offending line of code that caused the memory leak was in FShaderMapBase::AssignCopy:
- Code = new FShaderMapResourceCode(*Source.Code);
- Like FRefCountBase I've deleted the copy constructor and assignment operator for FRefCountedObject and FThreadSafeRefCountedObject to catch issues at compile time where we are copying across NumRefs incorrectly.
- To fix the issue I've manually created copy constructors for FGPUProfilerEventNodeStats and FShaderMapResourceCode.
#rb Devin.Doucette, Dave.Jones, Arciel.Rekman, Ben.Ingram
#review-16115628 @Devin.Doucette, @Dave.Jones, @Arciel.Rekman, @Ben.Ingram
#jira UE-108598, UE-113736
[CL 16116577 by Jason Nadro in ue5-main branch]
2021-04-26 14:03:07 -04:00
2020-02-06 13:13:41 -05:00
// we can't have this called on the wrong platform's shaders
if ( ! ArePlatformsCompatible ( GMaxRHIShaderPlatform , GetPlatform ( ) ) )
{
if ( FPlatformProperties : : RequiresCookedData ( ) )
{
UE_LOG ( LogShaders , Fatal , TEXT ( " FShaderMapResource_InlineCode::InitRHI got platform %s but it is not compatible with %s " ) ,
* LegacyShaderPlatformToShaderFormat ( GetPlatform ( ) ) . ToString ( ) , * LegacyShaderPlatformToShaderFormat ( GMaxRHIShaderPlatform ) . ToString ( ) ) ;
}
return TRefCountPtr < FRHIShader > ( ) ;
}
FMemStackBase & MemStack = FMemStack : : Get ( ) ;
const FShaderMapResourceCode : : FShaderEntry & ShaderEntry = Code - > ShaderEntries [ ShaderIndex ] ;
2020-04-21 13:46:17 -04:00
const uint8 * ShaderCode = ShaderEntry . Code . GetData ( ) ;
2020-02-06 13:13:41 -05:00
FMemMark Mark ( MemStack ) ;
2020-04-21 13:46:17 -04:00
if ( ShaderEntry . Code . Num ( ) ! = ShaderEntry . UncompressedSize )
2020-02-06 13:13:41 -05:00
{
void * UncompressedCode = MemStack . Alloc ( ShaderEntry . UncompressedSize , 16 ) ;
2021-03-11 21:19:35 -04:00
auto bSucceed = FCompression : : UncompressMemory ( GetShaderCompressionFormat ( ) , UncompressedCode , ShaderEntry . UncompressedSize , ShaderCode , ShaderEntry . Code . Num ( ) ) ;
2020-02-06 13:13:41 -05:00
check ( bSucceed ) ;
ShaderCode = ( uint8 * ) UncompressedCode ;
}
const auto ShaderCodeView = MakeArrayView ( ShaderCode , ShaderEntry . UncompressedSize ) ;
const FSHAHash & ShaderHash = Code - > ShaderHashes [ ShaderIndex ] ;
const EShaderFrequency Frequency = ShaderEntry . Frequency ;
TRefCountPtr < FRHIShader > RHIShader ;
switch ( Frequency )
{
case SF_Vertex : RHIShader = RHICreateVertexShader ( ShaderCodeView , ShaderHash ) ; break ;
2021-03-18 18:42:49 -04:00
case SF_Mesh : RHIShader = RHICreateMeshShader ( ShaderCodeView , ShaderHash ) ; break ;
case SF_Amplification : RHIShader = RHICreateAmplificationShader ( ShaderCodeView , ShaderHash ) ; break ;
2020-02-06 13:13:41 -05:00
case SF_Pixel : RHIShader = RHICreatePixelShader ( ShaderCodeView , ShaderHash ) ; break ;
case SF_Geometry : RHIShader = RHICreateGeometryShader ( ShaderCodeView , ShaderHash ) ; break ;
case SF_Compute : RHIShader = RHICreateComputeShader ( ShaderCodeView , ShaderHash ) ; break ;
case SF_RayGen : case SF_RayMiss : case SF_RayHitGroup : case SF_RayCallable :
2020-09-01 14:07:48 -04:00
# if RHI_RAYTRACING
2021-11-23 10:25:31 -05:00
if ( GRHISupportsRayTracing & & GRHISupportsRayTracingShaders )
2020-02-06 13:13:41 -05:00
{
RHIShader = RHICreateRayTracingShader ( ShaderCodeView , ShaderHash , Frequency ) ;
}
# endif // RHI_RAYTRACING
2020-09-01 14:07:48 -04:00
break ;
2020-02-06 13:13:41 -05:00
default :
checkNoEntry ( ) ;
break ;
}
if ( RHIShader )
{
2021-09-27 19:54:25 -04:00
INC_DWORD_STAT ( STAT_Shaders_NumShadersUsedForRendering ) ;
2020-02-06 13:13:41 -05:00
RHIShader - > SetHash ( ShaderHash ) ;
}
return RHIShader ;
}