2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Shader . cpp : Shader implementation .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "ShaderCore.h"
# include "Shader.h"
# include "VertexFactory.h"
# include "DiagnosticTable.h"
# include "DerivedDataCacheInterface.h"
# include "ModuleManager.h"
# include "TargetPlatform.h"
DEFINE_LOG_CATEGORY ( LogShaders ) ;
void FShaderParameterMap : : VerifyBindingsAreComplete ( const TCHAR * ShaderTypeName , EShaderFrequency Frequency , FVertexFactoryType * InVertexFactoryType ) const
{
# if WITH_EDITORONLY_DATA
// Only people working on shaders (and therefore have LogShaders unsuppressed) will want to see these errors
if ( UE_LOG_ACTIVE ( LogShaders , Warning ) )
{
const TCHAR * VertexFactoryName = InVertexFactoryType ? InVertexFactoryType - > GetName ( ) : TEXT ( " ? " ) ;
bool bBindingsComplete = true ;
FString UnBoundParameters = TEXT ( " " ) ;
for ( TMap < FString , FParameterAllocation > : : TConstIterator ParameterIt ( ParameterMap ) ; ParameterIt ; + + ParameterIt )
{
const FString & ParamName = ParameterIt . Key ( ) ;
const FParameterAllocation & ParamValue = ParameterIt . Value ( ) ;
if ( ! ParamValue . bBound )
{
// Only valid parameters should be in the shader map
checkSlow ( ParamValue . Size > 0 ) ;
bBindingsComplete = bBindingsComplete & & ParamValue . bBound ;
UnBoundParameters + = FString ( TEXT ( " Parameter " ) ) + ParamName + TEXT ( " not bound! \n " ) ;
}
}
if ( ! bBindingsComplete )
{
FString ErrorMessage = FString ( TEXT ( " Found unbound parameters being used in shadertype " ) ) + ShaderTypeName + TEXT ( " (VertexFactory: " ) + VertexFactoryName + TEXT ( " ) \n " ) + UnBoundParameters ;
// An unbound parameter means the engine is not going to set its value (because it was never bound)
// but it will be used in rendering, which will most likely cause artifacts
// We use a non-Slate message box to avoid problem where we haven't compiled the shaders for Slate.
FPlatformMisc : : MessageBoxExt ( EAppMsgType : : Ok , * ErrorMessage , TEXT ( " Error " ) ) ;
}
}
# endif // WITH_EDITORONLY_DATA
}
void FShaderParameterMap : : UpdateHash ( FSHA1 & HashState ) const
{
for ( TMap < FString , FParameterAllocation > : : TConstIterator ParameterIt ( ParameterMap ) ; ParameterIt ; + + ParameterIt )
{
const FString & ParamName = ParameterIt . Key ( ) ;
const FParameterAllocation & ParamValue = ParameterIt . Value ( ) ;
HashState . Update ( ( const uint8 * ) * ParamName , ParamName . Len ( ) * sizeof ( TCHAR ) ) ;
HashState . Update ( ( const uint8 * ) & ParamValue . BufferIndex , sizeof ( ParamValue . BufferIndex ) ) ;
HashState . Update ( ( const uint8 * ) & ParamValue . BaseIndex , sizeof ( ParamValue . BaseIndex ) ) ;
HashState . Update ( ( const uint8 * ) & ParamValue . Size , sizeof ( ParamValue . Size ) ) ;
}
}
bool FShaderType : : bInitializedSerializationHistory = false ;
FShaderType : : FShaderType (
const TCHAR * InName ,
const TCHAR * InSourceFilename ,
const TCHAR * InFunctionName ,
uint32 InFrequency ,
ConstructSerializedType InConstructSerializedRef
) :
Name ( InName ) ,
SourceFilename ( InSourceFilename ) ,
FunctionName ( InFunctionName ) ,
Frequency ( InFrequency ) ,
ConstructSerializedRef ( InConstructSerializedRef ) ,
GlobalListLink ( this )
{
for ( int32 Platform = 0 ; Platform < SP_NumPlatforms ; Platform + + )
{
bCachedUniformBufferStructDeclarations [ Platform ] = false ;
}
// This will trigger if an IMPLEMENT_SHADER_TYPE was in a module not loaded before InitializeShaderTypes
// Shader types need to be implemented in modules that are loaded before that
check ( ! bInitializedSerializationHistory ) ;
//make sure the name is shorter than the maximum serializable length
check ( FCString : : Strlen ( InName ) < NAME_SIZE ) ;
// register this shader type
GlobalListLink . Link ( GetTypeList ( ) ) ;
GetNameToTypeMap ( ) . Add ( FName ( InName ) , this ) ;
// Assign the shader type the next unassigned hash index.
static uint32 NextHashIndex = 0 ;
HashIndex = NextHashIndex + + ;
}
FShaderType : : ~ FShaderType ( )
{
GlobalListLink . Unlink ( ) ;
GetNameToTypeMap ( ) . Remove ( FName ( Name ) ) ;
}
TLinkedList < FShaderType * > * & FShaderType : : GetTypeList ( )
{
static TLinkedList < FShaderType * > * GShaderTypeList = NULL ;
return GShaderTypeList ;
}
FShaderType * FShaderType : : GetShaderTypeByName ( const TCHAR * Name )
{
for ( TLinkedList < FShaderType * > : : TIterator It ( GetTypeList ( ) ) ; It ; It . Next ( ) )
{
FShaderType * Type = * It ;
if ( FPlatformString : : Strcmp ( Name , Type - > GetName ( ) ) = = 0 )
{
return Type ;
}
}
return NULL ;
}
TArray < FShaderType * > FShaderType : : GetShaderTypesByFilename ( const TCHAR * Filename )
{
TArray < FShaderType * > OutShaders ;
for ( TLinkedList < FShaderType * > : : TIterator It ( GetTypeList ( ) ) ; It ; It . Next ( ) )
{
FShaderType * Type = * It ;
if ( FPlatformString : : Strcmp ( Filename , Type - > GetShaderFilename ( ) ) = = 0 )
{
OutShaders . Add ( Type ) ;
}
}
return OutShaders ;
}
TMap < FName , FShaderType * > & FShaderType : : GetNameToTypeMap ( )
{
static TMap < FName , FShaderType * > * GShaderNameToTypeMap = NULL ;
if ( ! GShaderNameToTypeMap )
{
GShaderNameToTypeMap = new TMap < FName , FShaderType * > ( ) ;
}
return * GShaderNameToTypeMap ;
}
void FShaderType : : GetOutdatedTypes ( TArray < FShaderType * > & OutdatedShaderTypes , TArray < const FVertexFactoryType * > & OutdatedFactoryTypes )
{
for ( TLinkedList < FShaderType * > : : TIterator It ( GetTypeList ( ) ) ; It ; It . Next ( ) )
{
FShaderType * Type = * It ;
for ( TMap < FShaderId , FShader * > : : TConstIterator ShaderIt ( Type - > ShaderIdMap ) ; ShaderIt ; + + ShaderIt )
{
FShader * Shader = ShaderIt . Value ( ) ;
const FVertexFactoryParameterRef * VFParameterRef = Shader - > GetVertexFactoryParameterRef ( ) ;
const FSHAHash & SavedHash = Shader - > GetHash ( ) ;
const FSHAHash & CurrentHash = Type - > GetSourceHash ( ) ;
const bool bOutdatedShader = SavedHash ! = CurrentHash ;
const bool bOutdatedVertexFactory =
VFParameterRef & & VFParameterRef - > GetVertexFactoryType ( ) & & VFParameterRef - > GetVertexFactoryType ( ) - > GetSourceHash ( ) ! = VFParameterRef - > GetHash ( ) ;
if ( bOutdatedShader )
{
OutdatedShaderTypes . AddUnique ( Shader - > Type ) ;
}
if ( bOutdatedVertexFactory )
{
OutdatedFactoryTypes . AddUnique ( VFParameterRef - > GetVertexFactoryType ( ) ) ;
}
}
}
for ( int32 TypeIndex = 0 ; TypeIndex < OutdatedShaderTypes . Num ( ) ; TypeIndex + + )
{
UE_LOG ( LogShaders , Warning , TEXT ( " Recompiling %s " ) , OutdatedShaderTypes [ TypeIndex ] - > GetName ( ) ) ;
}
for ( int32 TypeIndex = 0 ; TypeIndex < OutdatedFactoryTypes . Num ( ) ; TypeIndex + + )
{
UE_LOG ( LogShaders , Warning , TEXT ( " Recompiling %s " ) , OutdatedFactoryTypes [ TypeIndex ] - > GetName ( ) ) ;
}
}
FArchive & operator < < ( FArchive & Ar , FShaderType * & Ref )
{
if ( Ar . IsSaving ( ) )
{
FName FactoryName = Ref ? FName ( Ref - > Name ) : NAME_None ;
Ar < < FactoryName ;
}
else if ( Ar . IsLoading ( ) )
{
FName FactoryName = NAME_None ;
Ar < < FactoryName ;
Ref = NULL ;
if ( FactoryName ! = NAME_None )
{
// look for the shader type in the global name to type map
FShaderType * * ShaderType = FShaderType : : GetNameToTypeMap ( ) . Find ( FactoryName ) ;
if ( ShaderType )
{
// if we found it, use it
Ref = * ShaderType ;
}
}
}
return Ar ;
}
FShader * FShaderType : : FindShaderById ( const FShaderId & Id ) const
{
return ShaderIdMap . FindRef ( Id ) ;
}
FShader * FShaderType : : ConstructForDeserialization ( ) const
{
return ( * ConstructSerializedRef ) ( ) ;
}
const FSHAHash & FShaderType : : GetSourceHash ( ) const
{
return GetShaderFileHash ( GetShaderFilename ( ) ) ;
}
void FShaderType : : Initialize ( const TMap < FString , TArray < const TCHAR * > > & ShaderFileToUniformBufferVariables )
{
if ( ! FPlatformProperties : : RequiresCookedData ( ) )
{
for ( TLinkedList < FShaderType * > : : TIterator It ( FShaderType : : GetTypeList ( ) ) ; It ; It . Next ( ) )
{
FShaderType * Type = * It ;
GenerateReferencedUniformBuffers ( Type - > SourceFilename , Type - > Name , ShaderFileToUniformBufferVariables , Type - > ReferencedUniformBufferStructsCache ) ;
// Cache serialization history for each shader type
// This history is used to detect when shader serialization changes without a corresponding .usf change
{
// Construct a temporary shader, which is initialized to safe values for serialization
FShader * TempShader = Type - > ConstructForDeserialization ( ) ;
check ( TempShader ! = NULL ) ;
TempShader - > Type = Type ;
// Serialize the temp shader to memory and record the number and sizes of serializations
TArray < uint8 > TempData ;
FMemoryWriter Ar ( TempData , true ) ;
FShaderSaveArchive SaveArchive ( Ar , Type - > SerializationHistory ) ;
TempShader - > SerializeBase ( SaveArchive , false ) ;
// Destroy the temporary shader
delete TempShader ;
}
}
}
bInitializedSerializationHistory = true ;
}
void FShaderType : : Uninitialize ( )
{
for ( TLinkedList < FShaderType * > : : TIterator It ( FShaderType : : GetTypeList ( ) ) ; It ; It . Next ( ) )
{
FShaderType * Type = * It ;
Type - > SerializationHistory = FSerializationHistory ( ) ;
}
bInitializedSerializationHistory = false ;
}
TMap < FShaderResourceId , FShaderResource * > FShaderResource : : ShaderResourceIdMap ;
FShaderResource : : FShaderResource ( )
: NumInstructions ( 0 )
, NumTextureSamplers ( 0 )
, NumRefs ( 0 )
{
INC_DWORD_STAT_BY ( STAT_Shaders_NumShaderResourcesLoaded , 1 ) ;
}
FShaderResource : : FShaderResource ( const FShaderCompilerOutput & Output )
: NumInstructions ( Output . NumInstructions )
, NumTextureSamplers ( Output . NumTextureSamplers )
, NumRefs ( 0 )
{
Target = Output . Target ;
Code = Output . Code ;
check ( Code . Num ( ) > 0 ) ;
OutputHash = Output . OutputHash ;
checkSlow ( OutputHash ! = FSHAHash ( ) ) ;
ShaderResourceIdMap . Add ( GetId ( ) , this ) ;
INC_DWORD_STAT_BY_FName ( * GetMemoryStatType ( ( EShaderFrequency ) Target . Frequency ) , Code . Num ( ) ) ;
INC_DWORD_STAT_BY ( STAT_Shaders_ShaderResourceMemory , GetSizeBytes ( ) ) ;
INC_DWORD_STAT_BY ( STAT_Shaders_NumShaderResourcesLoaded , 1 ) ;
}
FShaderResource : : ~ FShaderResource ( )
{
DEC_DWORD_STAT_BY_FName ( * GetMemoryStatType ( ( EShaderFrequency ) Target . Frequency ) , Code . Num ( ) ) ;
DEC_DWORD_STAT_BY ( STAT_Shaders_ShaderResourceMemory , GetSizeBytes ( ) ) ;
DEC_DWORD_STAT_BY ( STAT_Shaders_NumShaderResourcesLoaded , 1 ) ;
}
void FShaderResource : : Register ( )
{
ShaderResourceIdMap . Add ( GetId ( ) , this ) ;
}
void FShaderResource : : Serialize ( FArchive & Ar )
{
Ar < < Target ;
Ar < < Code ;
Ar < < OutputHash ;
Ar < < NumInstructions ;
Ar < < NumTextureSamplers ;
if ( Ar . IsLoading ( ) )
{
INC_DWORD_STAT_BY_FName ( * GetMemoryStatType ( ( EShaderFrequency ) Target . Frequency ) , ( int64 ) Code . Num ( ) ) ;
INC_DWORD_STAT_BY ( STAT_Shaders_ShaderResourceMemory , GetSizeBytes ( ) ) ;
}
}
void FShaderResource : : AddRef ( )
{
+ + NumRefs ;
}
void FShaderResource : : Release ( )
{
check ( NumRefs ! = 0 ) ;
if ( - - NumRefs = = 0 )
{
ShaderResourceIdMap . Remove ( GetId ( ) ) ;
// Send a release message to the rendering thread when the shader loses its last reference.
BeginReleaseResource ( this ) ;
BeginCleanup ( this ) ;
}
}
FShaderResource * FShaderResource : : FindShaderResourceById ( const FShaderResourceId & Id )
{
return ShaderResourceIdMap . FindRef ( Id ) ;
}
FShaderResource * FShaderResource : : FindOrCreateShaderResource ( const FShaderCompilerOutput & Output )
{
const FShaderResourceId ResourceId ( Output ) ;
FShaderResource * Resource = FindShaderResourceById ( ResourceId ) ;
if ( ! Resource )
{
Resource = new FShaderResource ( Output ) ;
}
return Resource ;
}
void FShaderResource : : GetAllShaderResourceId ( TArray < FShaderResourceId > & Ids )
{
ShaderResourceIdMap . GetKeys ( Ids ) ;
}
void FShaderResource : : FinishCleanup ( )
{
delete this ;
}
void FShaderResource : : InitRHI ( )
{
checkf ( Code . Num ( ) > 0 , TEXT ( " FShaderResource::InitRHI was called with empty bytecode, which can happen if the resource is initialized multiple times on platforms with no editor data. " ) ) ;
// we can't have this called on the wrong platform's shaders
if ( Target . Platform ! = GRHIShaderPlatform )
{
if ( FPlatformProperties : : RequiresCookedData ( ) )
{
UE_LOG ( LogShaders , Fatal , TEXT ( " FShaderResource::InitRHI got platform %s but expected %s " ) ,
* LegacyShaderPlatformToShaderFormat ( ( EShaderPlatform ) Target . Platform ) . ToString ( ) , * LegacyShaderPlatformToShaderFormat ( GRHIShaderPlatform ) . ToString ( ) ) ;
}
return ;
}
INC_DWORD_STAT_BY ( STAT_Shaders_NumShadersUsedForRendering , 1 ) ;
SCOPE_CYCLE_COUNTER ( STAT_Shaders_RTShaderLoadTime ) ;
if ( Target . Frequency = = SF_Vertex )
{
VertexShader = RHICreateVertexShader ( Code ) ;
}
else if ( Target . Frequency = = SF_Pixel )
{
PixelShader = RHICreatePixelShader ( Code ) ;
}
else if ( Target . Frequency = = SF_Hull )
{
HullShader = RHICreateHullShader ( Code ) ;
}
else if ( Target . Frequency = = SF_Domain )
{
DomainShader = RHICreateDomainShader ( Code ) ;
}
else if ( Target . Frequency = = SF_Geometry )
{
GeometryShader = RHICreateGeometryShader ( Code ) ;
}
else if ( Target . Frequency = = SF_Compute )
{
ComputeShader = RHICreateComputeShader ( Code ) ;
}
if ( ! FPlatformProperties : : HasEditorOnlyData ( ) )
{
DEC_DWORD_STAT_BY_FName ( * GetMemoryStatType ( ( EShaderFrequency ) Target . Frequency ) , Code . Num ( ) ) ;
DEC_DWORD_STAT_BY ( STAT_Shaders_ShaderResourceMemory , Code . GetAllocatedSize ( ) ) ;
Code . Empty ( ) ;
}
}
void FShaderResource : : ReleaseRHI ( )
{
DEC_DWORD_STAT_BY ( STAT_Shaders_NumShadersUsedForRendering , 1 ) ;
VertexShader . SafeRelease ( ) ;
PixelShader . SafeRelease ( ) ;
HullShader . SafeRelease ( ) ;
DomainShader . SafeRelease ( ) ;
GeometryShader . SafeRelease ( ) ;
ComputeShader . SafeRelease ( ) ;
}
void FShaderResource : : InitializeVertexShaderRHI ( )
{
if ( ! IsInitialized ( ) )
{
STAT ( double ShaderInitializationTime = 0 ) ;
{
SCOPE_CYCLE_COUNTER ( STAT_Shaders_FrameRTShaderInitForRenderingTime ) ;
SCOPE_SECONDS_COUNTER ( ShaderInitializationTime ) ;
InitResource ( ) ;
}
INC_FLOAT_STAT_BY ( STAT_Shaders_TotalRTShaderInitForRenderingTime , ( float ) ShaderInitializationTime ) ;
}
checkSlow ( IsInitialized ( ) ) ;
}
void FShaderResource : : InitializePixelShaderRHI ( )
{
if ( ! IsInitialized ( ) )
{
STAT ( double ShaderInitializationTime = 0 ) ;
{
SCOPE_CYCLE_COUNTER ( STAT_Shaders_FrameRTShaderInitForRenderingTime ) ;
SCOPE_SECONDS_COUNTER ( ShaderInitializationTime ) ;
InitResource ( ) ;
}
INC_FLOAT_STAT_BY ( STAT_Shaders_TotalRTShaderInitForRenderingTime , ( float ) ShaderInitializationTime ) ;
}
checkSlow ( IsInitialized ( ) ) ;
}
const FHullShaderRHIRef & FShaderResource : : GetHullShader ( )
{
checkSlow ( Target . Frequency = = SF_Hull ) ;
if ( ! IsInitialized ( ) )
{
STAT ( double ShaderInitializationTime = 0 ) ;
{
SCOPE_CYCLE_COUNTER ( STAT_Shaders_FrameRTShaderInitForRenderingTime ) ;
SCOPE_SECONDS_COUNTER ( ShaderInitializationTime ) ;
InitResource ( ) ;
}
INC_FLOAT_STAT_BY ( STAT_Shaders_TotalRTShaderInitForRenderingTime , ( float ) ShaderInitializationTime ) ;
}
checkSlow ( IsInitialized ( ) ) ;
return HullShader ;
}
const FDomainShaderRHIRef & FShaderResource : : GetDomainShader ( )
{
checkSlow ( Target . Frequency = = SF_Domain ) ;
if ( ! IsInitialized ( ) )
{
STAT ( double ShaderInitializationTime = 0 ) ;
{
SCOPE_CYCLE_COUNTER ( STAT_Shaders_FrameRTShaderInitForRenderingTime ) ;
SCOPE_SECONDS_COUNTER ( ShaderInitializationTime ) ;
InitResource ( ) ;
}
INC_FLOAT_STAT_BY ( STAT_Shaders_TotalRTShaderInitForRenderingTime , ( float ) ShaderInitializationTime ) ;
}
checkSlow ( IsInitialized ( ) ) ;
return DomainShader ;
}
const FGeometryShaderRHIRef & FShaderResource : : GetGeometryShader ( )
{
checkSlow ( Target . Frequency = = SF_Geometry ) ;
if ( ! IsInitialized ( ) )
{
STAT ( double ShaderInitializationTime = 0 ) ;
{
SCOPE_CYCLE_COUNTER ( STAT_Shaders_FrameRTShaderInitForRenderingTime ) ;
SCOPE_SECONDS_COUNTER ( ShaderInitializationTime ) ;
InitResource ( ) ;
}
INC_FLOAT_STAT_BY ( STAT_Shaders_TotalRTShaderInitForRenderingTime , ( float ) ShaderInitializationTime ) ;
}
checkSlow ( IsInitialized ( ) ) ;
return GeometryShader ;
}
const FComputeShaderRHIRef & FShaderResource : : GetComputeShader ( )
{
checkSlow ( Target . Frequency = = SF_Compute ) ;
if ( ! IsInitialized ( ) )
{
STAT ( double ShaderInitializationTime = 0 ) ;
{
SCOPE_CYCLE_COUNTER ( STAT_Shaders_FrameRTShaderInitForRenderingTime ) ;
SCOPE_SECONDS_COUNTER ( ShaderInitializationTime ) ;
InitResource ( ) ;
}
INC_FLOAT_STAT_BY ( STAT_Shaders_TotalRTShaderInitForRenderingTime , ( float ) ShaderInitializationTime ) ;
}
checkSlow ( IsInitialized ( ) ) ;
return ComputeShader ;
}
FShaderId : : FShaderId ( const FSHAHash & InMaterialShaderMapHash , FVertexFactoryType * InVertexFactoryType , FShaderType * InShaderType , FShaderTarget InTarget )
: MaterialShaderMapHash ( InMaterialShaderMapHash )
, ShaderType ( InShaderType )
, SourceHash ( InShaderType - > GetSourceHash ( ) )
, SerializationHistory ( InShaderType - > GetSerializationHistory ( ) )
, Target ( InTarget )
{
if ( InVertexFactoryType )
{
VFSerializationHistory = InVertexFactoryType - > GetSerializationHistory ( ( EShaderFrequency ) InTarget . Frequency ) ;
VertexFactoryType = InVertexFactoryType ;
VFSourceHash = InVertexFactoryType - > GetSourceHash ( ) ;
}
else
{
VFSerializationHistory = NULL ;
VertexFactoryType = NULL ;
}
}
FSelfContainedShaderId : : FSelfContainedShaderId ( ) :
Target ( FShaderTarget ( SF_NumFrequencies , SP_NumPlatforms ) )
{ }
FSelfContainedShaderId : : FSelfContainedShaderId ( const FShaderId & InShaderId )
{
MaterialShaderMapHash = InShaderId . MaterialShaderMapHash ;
VertexFactoryTypeName = InShaderId . VertexFactoryType ? InShaderId . VertexFactoryType - > GetName ( ) : TEXT ( " " ) ;
VFSourceHash = InShaderId . VFSourceHash ;
VFSerializationHistory = InShaderId . VFSerializationHistory ? * InShaderId . VFSerializationHistory : FSerializationHistory ( ) ;
ShaderTypeName = InShaderId . ShaderType - > GetName ( ) ;
SourceHash = InShaderId . SourceHash ;
SerializationHistory = InShaderId . SerializationHistory ;
Target = InShaderId . Target ;
}
bool FSelfContainedShaderId : : IsValid ( )
{
FShaderType * * TypePtr = FShaderType : : GetNameToTypeMap ( ) . Find ( FName ( * ShaderTypeName ) ) ;
if ( TypePtr & & SourceHash = = ( * TypePtr ) - > GetSourceHash ( ) & & SerializationHistory = = ( * TypePtr ) - > GetSerializationHistory ( ) )
{
FVertexFactoryType * VFTypePtr = FVertexFactoryType : : GetVFByName ( VertexFactoryTypeName ) ;
if ( VertexFactoryTypeName = = TEXT ( " " )
| | ( VFTypePtr & & VFSourceHash = = VFTypePtr - > GetSourceHash ( ) & & VFSerializationHistory = = * VFTypePtr - > GetSerializationHistory ( ( EShaderFrequency ) Target . Frequency ) ) )
{
return true ;
}
}
return false ;
}
FArchive & operator < < ( FArchive & Ar , class FSelfContainedShaderId & Ref )
{
Ar < < Ref . MaterialShaderMapHash
< < Ref . VertexFactoryTypeName
< < Ref . VFSourceHash
< < Ref . VFSerializationHistory
< < Ref . ShaderTypeName
< < Ref . SourceHash
< < Ref . SerializationHistory
< < Ref . Target ;
return Ar ;
}
/**
* Used to construct a shader for deserialization .
* This still needs to initialize members to safe values since FShaderType : : GenerateSerializationHistory uses this constructor .
*/
FShader : : FShader ( ) :
VFType ( NULL ) ,
Type ( NULL ) ,
NumRefs ( 0 ) ,
SetParametersId ( 0 ) ,
Canary ( ShaderMagic_Uninitialized )
{
// set to undefined (currently shared with SF_Vertex)
Target . Frequency = 0 ;
Target . Platform = GRHIShaderPlatform ;
}
/**
* Construct a shader from shader compiler output .
*/
FShader : : FShader ( const CompiledShaderInitializerType & Initializer ) :
MaterialShaderMapHash ( Initializer . MaterialShaderMapHash ) ,
VFType ( Initializer . VertexFactoryType ) ,
Type ( Initializer . Type ) ,
Target ( Initializer . Target ) ,
NumRefs ( 0 ) ,
SetParametersId ( 0 ) ,
Canary ( ShaderMagic_Initialized )
{
OutputHash = Initializer . OutputHash ;
checkSlow ( OutputHash ! = FSHAHash ( ) ) ;
if ( Type )
{
// Store off the source hash that this shader was compiled with
// This will be used as part of the shader key in order to identify when shader files have been changed and a recompile is needed
SourceHash = Type - > GetSourceHash ( ) ;
}
if ( VFType )
{
// Store off the VF source hash that this shader was compiled with
VFSourceHash = VFType - > GetSourceHash ( ) ;
}
// Bind uniform buffer parameters automatically
for ( TLinkedList < FUniformBufferStruct * > : : TIterator StructIt ( FUniformBufferStruct : : GetStructList ( ) ) ; StructIt ; StructIt . Next ( ) )
{
if ( Initializer . ParameterMap . ContainsParameterAllocation ( StructIt - > GetShaderVariableName ( ) ) )
{
UniformBufferParameterStructs . Add ( * StructIt ) ;
UniformBufferParameters . Add ( StructIt - > ConstructTypedParameter ( ) ) ;
FShaderUniformBufferParameter * Parameter = UniformBufferParameters . Last ( ) ;
Parameter - > Bind ( Initializer . ParameterMap , StructIt - > GetShaderVariableName ( ) , SPF_Mandatory ) ;
}
}
SetResource ( Initializer . Resource ) ;
// Register the shader now that it is valid, so that it can be reused
Register ( ) ;
}
FShader : : ~ FShader ( )
{
check ( Canary = = ShaderMagic_Uninitialized | | Canary = = ShaderMagic_Initialized ) ;
check ( NumRefs = = 0 ) ;
Canary = 0 ;
for ( int32 StructIndex = 0 ; StructIndex < UniformBufferParameters . Num ( ) ; StructIndex + + )
{
delete UniformBufferParameters [ StructIndex ] ;
}
}
const FSHAHash & FShader : : GetHash ( ) const
{
return SourceHash ;
}
bool FShader : : SerializeBase ( FArchive & Ar , bool bShadersInline )
{
Serialize ( Ar ) ;
Ar < < OutputHash ;
Ar < < MaterialShaderMapHash ;
Ar < < VFType ;
Ar < < VFSourceHash ;
Ar < < Type ;
Ar < < SourceHash ;
Ar < < Target ;
if ( Ar . IsLoading ( ) )
{
int32 NumUniformParameters ;
Ar < < NumUniformParameters ;
for ( int32 ParameterIndex = 0 ; ParameterIndex < NumUniformParameters ; ParameterIndex + + )
{
FString StructName ;
Ar < < StructName ;
FUniformBufferStruct * Struct = FindUniformBufferStructByName ( * StructName ) ;
FShaderUniformBufferParameter * Parameter = Struct ? Struct - > ConstructTypedParameter ( ) : new FShaderUniformBufferParameter ( ) ;
Ar < < * Parameter ;
UniformBufferParameterStructs . Add ( Struct ) ;
UniformBufferParameters . Add ( Parameter ) ;
}
// The shader has been serialized in, so this shader is now initialized.
Canary = ShaderMagic_Initialized ;
}
else
{
int32 NumUniformParameters = UniformBufferParameters . Num ( ) ;
Ar < < NumUniformParameters ;
for ( int32 StructIndex = 0 ; StructIndex < UniformBufferParameters . Num ( ) ; StructIndex + + )
{
FString StructName ( UniformBufferParameterStructs [ StructIndex ] - > GetStructTypeName ( ) ) ;
Ar < < StructName ;
Ar < < * UniformBufferParameters [ StructIndex ] ;
}
}
if ( bShadersInline )
{
// Save the shader resource if we are inlining shaders
if ( Ar . IsSaving ( ) )
{
Resource - > Serialize ( Ar ) ;
}
if ( Ar . IsLoading ( ) )
{
// Load the inlined shader resource
FShaderResource * ShaderResource = new FShaderResource ( ) ;
ShaderResource - > Serialize ( Ar ) ;
FShaderResource * ExistingResource = FShaderResource : : FindShaderResourceById ( ShaderResource - > GetId ( ) ) ;
// Reuse an existing shader resource if a matching one already exists in memory
if ( ExistingResource )
{
delete ShaderResource ;
ShaderResource = ExistingResource ;
}
else
{
// Register the newly loaded shader resource so it can be reused by other shaders
ShaderResource - > Register ( ) ;
}
SetResource ( ShaderResource ) ;
}
}
else
{
// if saving, there's nothing to, the required data is already saved above to look it up at load time
if ( Ar . IsLoading ( ) )
{
// generate a resource id
FShaderResourceId ResourceId ;
ResourceId . Target = Target ;
ResourceId . OutputHash = OutputHash ;
// use it to look up in the registered resource map
FShaderResource * ExistingResource = FShaderResource : : FindShaderResourceById ( ResourceId ) ;
SetResource ( ExistingResource ) ;
}
}
return false ;
}
void FShader : : AddRef ( )
{
+ + NumRefs ;
if ( NumRefs = = 1 )
{
INC_DWORD_STAT_BY ( STAT_Shaders_ShaderMemory , GetSizeBytes ( ) ) ;
INC_DWORD_STAT_BY ( STAT_Shaders_NumShadersLoaded , 1 ) ;
}
}
void FShader : : Release ( )
{
check ( NumRefs ! = 0 ) ;
if ( - - NumRefs = = 0 )
{
DEC_DWORD_STAT_BY ( STAT_Shaders_ShaderMemory , GetSizeBytes ( ) ) ;
DEC_DWORD_STAT_BY ( STAT_Shaders_NumShadersLoaded , 1 ) ;
// Deregister the shader now to eliminate references to it by the type's ShaderIdMap
Deregister ( ) ;
BeginCleanup ( this ) ;
}
}
void FShader : : Register ( )
{
FShaderId ShaderId = GetId ( ) ;
check ( ShaderId . MaterialShaderMapHash ! = FSHAHash ( ) ) ;
check ( ShaderId . SourceHash ! = FSHAHash ( ) ) ;
check ( Resource ) ;
Type - > GetShaderIdMap ( ) . Add ( ShaderId , this ) ;
}
void FShader : : Deregister ( )
{
Type - > GetShaderIdMap ( ) . Remove ( GetId ( ) ) ;
}
FShaderId FShader : : GetId ( ) const
{
FShaderId ShaderId ( Type - > GetSerializationHistory ( ) ) ;
ShaderId . MaterialShaderMapHash = MaterialShaderMapHash ;
ShaderId . VertexFactoryType = VFType ;
ShaderId . VFSourceHash = VFSourceHash ;
ShaderId . VFSerializationHistory = VFType ? VFType - > GetSerializationHistory ( ( EShaderFrequency ) GetTarget ( ) . Frequency ) : NULL ;
ShaderId . ShaderType = Type ;
ShaderId . SourceHash = SourceHash ;
ShaderId . Target = Target ;
return ShaderId ;
}
void FShader : : SetResource ( FShaderResource * InResource )
{
check ( InResource & & InResource - > Target = = Target ) ;
Resource = InResource ;
}
void FShader : : FinishCleanup ( )
{
delete this ;
}
void FShader : : VerifyBoundUniformBufferParameters ( )
{
// Support being called on a NULL pointer
2014-06-05 16:38:54 -04:00
// TODO: doesn't work with uniform buffer parameters on helper structs like FDeferredPixelShaderParameters
if ( 0 & & this )
2014-03-14 14:13:41 -04:00
{
for ( int32 StructIndex = 0 ; StructIndex < UniformBufferParameters . Num ( ) ; StructIndex + + )
{
const FShaderUniformBufferParameter & UniformParameter = * UniformBufferParameters [ StructIndex ] ;
if ( UniformParameter . SetParametersId ! = SetParametersId )
{
// Log an error when a shader was used for rendering but did not have all of its uniform buffers set
// This can have false positives, for example when sharing state between draw calls with the same shader, the SetParametersId logic will break down
// Also if the uniform buffer is compiled into the shader but not actually used due to control flow, failing to set that parameter will cause this error
UE_LOG ( LogShaders , Error , TEXT ( " Automatically bound uniform buffer parameter %s %s was not set before used for rendering in shader %s! " ) ,
UniformBufferParameterStructs [ StructIndex ] - > GetStructTypeName ( ) ,
UniformBufferParameterStructs [ StructIndex ] - > GetShaderVariableName ( ) ,
GetType ( ) - > GetName ( ) ) ;
}
}
SetParametersId + + ;
}
}
void DumpShaderStats ( EShaderPlatform Platform , EShaderFrequency Frequency )
{
# if ALLOW_DEBUG_FILES
FDiagnosticTableViewer ShaderTypeViewer ( * FDiagnosticTableViewer : : GetUniqueTemporaryFilePath ( TEXT ( " ShaderStats " ) ) ) ;
// Iterate over all shader types and log stats.
int32 TotalShaderCount = 0 ;
int32 TotalTypeCount = 0 ;
int32 TotalInstructionCount = 0 ;
int32 TotalSize = 0 ;
float TotalSizePerType = 0 ;
// Write a row of headings for the table's columns.
ShaderTypeViewer . AddColumn ( TEXT ( " Type " ) ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " Instances " ) ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " Average instructions " ) ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " Size " ) ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " AvgSizePerInstance " ) ) ;
ShaderTypeViewer . CycleRow ( ) ;
for ( TLinkedList < FShaderType * > : : TIterator It ( FShaderType : : GetTypeList ( ) ) ; It ; It . Next ( ) )
{
const FShaderType * Type = * It ;
if ( Type - > GetNumShaders ( ) )
{
// Calculate the average instruction count and total size of instances of this shader type.
float AverageNumInstructions = 0.0f ;
int32 NumInitializedInstructions = 0 ;
int32 Size = 0 ;
int32 NumShaders = 0 ;
for ( TMap < FShaderId , FShader * > : : TConstIterator ShaderIt ( Type - > ShaderIdMap ) ; ShaderIt ; + + ShaderIt )
{
const FShader * Shader = ShaderIt . Value ( ) ;
// Skip shaders that don't match frequency.
if ( Shader - > GetTarget ( ) . Frequency ! = Frequency & & Frequency ! = SF_NumFrequencies )
{
continue ;
}
// Skip shaders that don't match platform.
if ( Shader - > GetTarget ( ) . Platform ! = Platform & & Platform ! = SP_NumPlatforms )
{
continue ;
}
NumInitializedInstructions + = Shader - > GetNumInstructions ( ) ;
Size + = Shader - > GetCode ( ) . Num ( ) ;
NumShaders + + ;
}
AverageNumInstructions = ( float ) NumInitializedInstructions / ( float ) Type - > GetNumShaders ( ) ;
// Only add rows if there is a matching shader.
if ( NumShaders )
{
// Write a row for the shader type.
ShaderTypeViewer . AddColumn ( Type - > GetName ( ) ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %u " ) , NumShaders ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %.1f " ) , AverageNumInstructions ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %u " ) , Size ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %.1f " ) , Size / ( float ) NumShaders ) ;
ShaderTypeViewer . CycleRow ( ) ;
TotalShaderCount + = NumShaders ;
TotalInstructionCount + = NumInitializedInstructions ;
TotalTypeCount + + ;
TotalSize + = Size ;
TotalSizePerType + = Size / ( float ) NumShaders ;
}
}
}
// Write a total row.
ShaderTypeViewer . AddColumn ( TEXT ( " Total " ) ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %u " ) , TotalShaderCount ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %u " ) , TotalInstructionCount ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %u " ) , TotalSize ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " 0 " ) ) ;
ShaderTypeViewer . CycleRow ( ) ;
// Write an average row.
ShaderTypeViewer . AddColumn ( TEXT ( " Average " ) ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %.1f " ) , TotalShaderCount / ( float ) TotalTypeCount ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %.1f " ) , ( float ) TotalInstructionCount / TotalShaderCount ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %.1f " ) , TotalSize / ( float ) TotalShaderCount ) ;
ShaderTypeViewer . AddColumn ( TEXT ( " %.1f " ) , TotalSizePerType / TotalTypeCount ) ;
ShaderTypeViewer . CycleRow ( ) ;
# endif
}
FShaderType * FindShaderTypeByName ( const TCHAR * ShaderTypeName )
{
for ( TLinkedList < FShaderType * > : : TIterator ShaderTypeIt ( FShaderType : : GetTypeList ( ) ) ; ShaderTypeIt ; ShaderTypeIt . Next ( ) )
{
if ( ! FCString : : Stricmp ( ShaderTypeIt - > GetName ( ) , ShaderTypeName ) )
{
return * ShaderTypeIt ;
}
}
return NULL ;
}
void DispatchComputeShader (
2014-06-05 16:38:54 -04:00
FRHICommandList * RHICmdList ,
2014-03-14 14:13:41 -04:00
FShader * Shader ,
uint32 ThreadGroupCountX ,
uint32 ThreadGroupCountY ,
uint32 ThreadGroupCountZ )
2014-06-05 16:38:54 -04:00
{
check ( ! RHICmdList ) ;
2014-03-14 14:13:41 -04:00
Shader - > VerifyBoundUniformBufferParameters ( ) ;
RHIDispatchComputeShader ( ThreadGroupCountX , ThreadGroupCountY , ThreadGroupCountZ ) ;
}
void DispatchIndirectComputeShader (
2014-06-05 16:38:54 -04:00
FRHICommandList * RHICmdList ,
2014-03-14 14:13:41 -04:00
FShader * Shader ,
FVertexBufferRHIParamRef ArgumentBuffer ,
uint32 ArgumentOffset )
2014-06-05 16:38:54 -04:00
{
check ( ! RHICmdList ) ;
2014-03-14 14:13:41 -04:00
Shader - > VerifyBoundUniformBufferParameters ( ) ;
RHIDispatchIndirectComputeShader ( ArgumentBuffer , ArgumentOffset ) ;
}
const TArray < FName > & GetTargetShaderFormats ( )
{
static bool bInit = false ;
static TArray < FName > Results ;
# if WITH_ENGINE
if ( ! bInit )
{
bInit = true ;
ITargetPlatformManagerModule * TPM = GetTargetPlatformManager ( ) ;
if ( ! TPM | | TPM - > RestrictFormatsToRuntimeOnly ( ) )
{
// for now a runtime format and a cook format are very different, we don't put any formats here
}
else
{
const TArray < ITargetPlatform * > & Platforms = TPM - > GetActiveTargetPlatforms ( ) ;
for ( int32 Index = 0 ; Index < Platforms . Num ( ) ; Index + + )
{
2014-05-09 08:51:44 -04:00
Platforms [ Index ] - > GetAllTargetedShaderFormats ( Results ) ;
2014-03-14 14:13:41 -04:00
}
}
}
# endif // WITH_ENGINE
return Results ;
}
FName GetRuntimeShaderFormat ( )
{
static FName RuntimeFormat = LegacyShaderPlatformToShaderFormat ( GRHIShaderPlatform ) ;
return RuntimeFormat ;
}
2014-05-21 05:34:23 -04:00
void ShaderMapAppendKeyString ( EShaderPlatform Platform , FString & KeyString )
2014-03-14 14:13:41 -04:00
{
// Globals that should cause all shaders to recompile when changed must be appended to the key here
// Key should be kept as short as possible while being somewhat human readable for debugging
{
static const auto CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " Compat.UseDXT5NormalMaps " ) ) ;
KeyString + = ( CVar & & CVar - > GetValueOnGameThread ( ) ! = 0 ) ? TEXT ( " _DXTN " ) : TEXT ( " _BC5N " ) ;
}
{
static const auto CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.UseDiffuseSpecularMaterialInputs " ) ) ;
KeyString + = ( CVar & & CVar - > GetValueOnGameThread ( ) ! = 0 ) ? TEXT ( " _DSIN " ) : TEXT ( " _BMSIN " ) ;
}
{
static const auto CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.CompileShadersForDevelopment " ) ) ;
KeyString + = ( CVar & & CVar - > GetValueOnGameThread ( ) ! = 0 ) ? TEXT ( " _DEV " ) : TEXT ( " _NoDEV " ) ;
}
{
static const auto CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.AllowStaticLighting " ) ) ;
const bool bValue = CVar ? CVar - > GetValueOnGameThread ( ) ! = 0 : true ;
KeyString + = bValue ? TEXT ( " _SL " ) : TEXT ( " _NoSL " ) ;
}
{
static IConsoleVariable * CVar = IConsoleManager : : Get ( ) . FindConsoleVariable ( TEXT ( " r.DBuffer " ) ) ;
KeyString + = ( CVar & & CVar - > GetInt ( ) ! = 0 ) ? TEXT ( " _DBuf " ) : TEXT ( " _NoDBuf " ) ;
}
2014-05-21 05:34:23 -04:00
if ( Platform = = SP_PS4 )
{
{
static const auto CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.PS4MixedModeShaderDebugInfo " ) ) ;
if ( CVar & & CVar - > GetValueOnGameThread ( ) ! = 0 )
{
KeyString + = TEXT ( " _MMDBG " ) ;
}
}
{
static const auto CVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.PS4DumpShaderSDB " ) ) ;
if ( CVar & & CVar - > GetValueOnGameThread ( ) ! = 0 )
{
KeyString + = TEXT ( " _SDB " ) ;
}
}
}
2014-03-14 14:13:41 -04:00
}