2019-01-17 18:54:05 -05:00
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
2018-12-04 09:59:43 -05:00
/*=============================================================================
ShaderParameterMetadata . cpp : Shader parameter metadata implementations .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "ShaderParameterMetadata.h"
# include "RenderCore.h"
# include "ShaderCore.h"
static TLinkedList < FShaderParametersMetadata * > * GUniformStructList = nullptr ;
TLinkedList < FShaderParametersMetadata * > * & FShaderParametersMetadata : : GetStructList ( )
{
return GUniformStructList ;
}
TMap < FName , FShaderParametersMetadata * > & FShaderParametersMetadata : : GetNameStructMap ( )
{
static TMap < FName , FShaderParametersMetadata * > GlobalNameStructMap ;
return GlobalNameStructMap ;
}
FShaderParametersMetadata * FindUniformBufferStructByName ( const TCHAR * StructName )
{
FName FindByName ( StructName , FNAME_Find ) ;
FShaderParametersMetadata * FoundStruct = FShaderParametersMetadata : : GetNameStructMap ( ) . FindRef ( FindByName ) ;
return FoundStruct ;
}
FShaderParametersMetadata * FindUniformBufferStructByFName ( FName StructName )
{
return FShaderParametersMetadata : : GetNameStructMap ( ) . FindRef ( StructName ) ;
}
class FUniformBufferMemberAndOffset
{
public :
FUniformBufferMemberAndOffset ( const FShaderParametersMetadata & InContainingStruct , const FShaderParametersMetadata : : FMember & InMember , int32 InStructOffset ) :
ContainingStruct ( InContainingStruct ) ,
Member ( InMember ) ,
StructOffset ( InStructOffset )
{ }
const FShaderParametersMetadata & ContainingStruct ;
const FShaderParametersMetadata : : FMember & Member ;
int32 StructOffset ;
} ;
FShaderParametersMetadata : : FShaderParametersMetadata (
EUseCase InUseCase ,
const FName & InLayoutName ,
const TCHAR * InStructTypeName ,
const TCHAR * InShaderVariableName ,
uint32 InSize ,
const TArray < FMember > & InMembers )
: StructTypeName ( InStructTypeName )
, ShaderVariableName ( InShaderVariableName )
, Size ( InSize )
, UseCase ( InUseCase )
, Layout ( InLayoutName )
, Members ( InMembers )
, GlobalListLink ( this )
, bLayoutInitialized ( false )
{
check ( StructTypeName ) ;
if ( UseCase = = EUseCase : : ShaderParameterStruct )
{
check ( ShaderVariableName = = nullptr ) ;
}
else
{
check ( ShaderVariableName ) ;
}
if ( UseCase = = EUseCase : : GlobalShaderParameterStruct )
{
// Register this uniform buffer struct in global list.
GlobalListLink . LinkHead ( GetStructList ( ) ) ;
FName StructTypeFName ( StructTypeName ) ;
// Verify that during FName creation there's no case conversion
checkSlow ( FCString : : Strcmp ( StructTypeName , * StructTypeFName . GetPlainNameString ( ) ) = = 0 ) ;
GetNameStructMap ( ) . Add ( FName ( StructTypeFName ) , this ) ;
}
else
{
// We cannot initialize the layout during global initialization, since we have to walk nested struct members.
// Structs created during global initialization will have bRegisterForAutoBinding==false, and are initialized during startup.
// Structs created at runtime with bRegisterForAutoBinding==true can be initialized now.
InitializeLayout ( ) ;
}
}
void FShaderParametersMetadata : : InitializeAllGlobalStructs ( )
{
for ( TLinkedList < FShaderParametersMetadata * > : : TIterator StructIt ( FShaderParametersMetadata : : GetStructList ( ) ) ; StructIt ; StructIt . Next ( ) )
{
StructIt - > InitializeLayout ( ) ;
}
}
void FShaderParametersMetadata : : InitializeLayout ( )
{
check ( ! bLayoutInitialized ) ;
Layout . ConstantBufferSize = Size ;
TArray < FUniformBufferMemberAndOffset > MemberStack ;
MemberStack . Reserve ( Members . Num ( ) ) ;
for ( int32 MemberIndex = 0 ; MemberIndex < Members . Num ( ) ; MemberIndex + + )
{
MemberStack . Push ( FUniformBufferMemberAndOffset ( * this , Members [ MemberIndex ] , 0 ) ) ;
}
2019-01-30 21:24:04 -05:00
/** The point of RDG is to track resources that have deferred allocation. Could deffer the creation of uniform buffer,
* but there is a risk where it create more resource dependency than necessary on passes that reference this deferred
* uniform buffers . Therefore only allow graph resources in shader parameter structures .
*/
2018-12-04 09:59:43 -05:00
bool bAllowGraphResources = UseCase = = EUseCase : : ShaderParameterStruct ;
2019-01-30 21:24:04 -05:00
/** Uniform buffer references are only allowed in shader parameter structures that may be used as a root shader parameter
* structure .
*/
bool bAllowUniformBufferReferences = UseCase = = EUseCase : : ShaderParameterStruct ;
2019-03-13 14:52:34 -04:00
/** Resource array are currently only supported for shader parameter structures. */
bool bAllowResourceArrays = UseCase = = EUseCase : : ShaderParameterStruct ;
2019-01-30 21:24:04 -05:00
/** White list all use cases that inline a structure within another. Data driven are not known to inline structures. */
bool bAllowStructureInlining = UseCase = = EUseCase : : ShaderParameterStruct | | UseCase = = EUseCase : : GlobalShaderParameterStruct ;
2018-12-04 09:59:43 -05:00
for ( int32 i = 0 ; i < MemberStack . Num ( ) ; + + i )
{
const FShaderParametersMetadata & CurrentStruct = MemberStack [ i ] . ContainingStruct ;
const FMember & CurrentMember = MemberStack [ i ] . Member ;
2019-03-13 14:52:34 -04:00
EUniformBufferBaseType BaseType = CurrentMember . GetBaseType ( ) ;
const uint32 ArraySize = CurrentMember . GetNumElements ( ) ;
2019-03-13 14:40:42 -04:00
const FShaderParametersMetadata * ChildStruct = CurrentMember . GetStructMetadata ( ) ;
2019-03-13 14:40:25 -04:00
2019-03-13 14:52:34 -04:00
const bool bIsArray = ArraySize > 0 ;
const bool bIsRHIResource = (
BaseType = = UBMT_TEXTURE | |
BaseType = = UBMT_SRV | |
BaseType = = UBMT_SAMPLER ) ;
const bool bIsRDGResource = IsRDGResourceReferenceShaderParameterType ( BaseType ) ;
const bool bIsVariableNativeType = (
BaseType = = UBMT_BOOL | |
BaseType = = UBMT_INT32 | |
BaseType = = UBMT_UINT32 | |
BaseType = = UBMT_FLOAT32 ) ;
2018-12-04 09:59:43 -05:00
if ( DO_CHECK )
{
const FString CppName = FString : : Printf ( TEXT ( " %s::%s " ) , CurrentStruct . GetStructTypeName ( ) , CurrentMember . GetName ( ) ) ;
2019-01-30 21:24:04 -05:00
if ( IsRDGResourceReferenceShaderParameterType ( BaseType ) | | BaseType = = UBMT_RENDER_TARGET_BINDING_SLOTS )
2018-12-04 09:59:43 -05:00
{
2019-01-30 21:24:04 -05:00
if ( ! bAllowGraphResources )
{
UE_LOG ( LogRendererCore , Fatal , TEXT ( " Shader parameter %s error: Graph resources are only allowed in shader parameter structs. " ) , * CppName ) ;
}
2018-12-04 09:59:43 -05:00
}
2019-01-30 21:24:04 -05:00
else if ( BaseType = = UBMT_REFERENCED_STRUCT )
2018-12-04 09:59:43 -05:00
{
2019-01-30 21:24:04 -05:00
if ( ! bAllowUniformBufferReferences )
2018-12-04 09:59:43 -05:00
{
UE_LOG ( LogRendererCore , Fatal , TEXT ( " Shader parameter %s error: Shader parameter struct reference can only be done in shader parameter structs. " ) , * CppName ) ;
}
}
2019-01-30 21:24:04 -05:00
else if ( BaseType = = UBMT_NESTED_STRUCT | | BaseType = = UBMT_INCLUDED_STRUCT )
2018-12-04 09:59:43 -05:00
{
check ( ChildStruct ) ;
2019-01-30 21:24:04 -05:00
if ( ! bAllowStructureInlining )
{
UE_LOG ( LogRendererCore , Fatal , TEXT ( " Shader parameter %s error: Shader parameter struct is not known inline other structures. " ) , * CppName ) ;
}
else if ( ChildStruct - > GetUseCase ( ) ! = EUseCase : : ShaderParameterStruct & & UseCase = = EUseCase : : ShaderParameterStruct )
{
UE_LOG ( LogRendererCore , Fatal , TEXT ( " Shader parameter %s error: can only nests or include shader parameter struct define with BEGIN_SHADER_PARAMETER_STRUCT(), but %s is not. " ) , * CppName , ChildStruct - > GetStructTypeName ( ) ) ;
}
2018-12-04 09:59:43 -05:00
}
2019-03-13 14:52:34 -04:00
const bool bTypeCanBeArray = ( bAllowResourceArrays & & ( bIsRHIResource | | bIsRDGResource ) ) | | bIsVariableNativeType ;
if ( bIsArray & & ! bTypeCanBeArray )
{
UE_LOG ( LogRendererCore , Fatal , TEXT ( " Shader parameter %s error: Not allowed to be an array. " ) , * CppName ) ;
}
2018-12-04 09:59:43 -05:00
}
2019-01-30 21:24:04 -05:00
if ( IsShaderParameterTypeForUniformBufferLayout ( BaseType ) )
2018-12-04 09:59:43 -05:00
{
2019-03-13 14:52:34 -04:00
for ( uint32 ArrayElementId = 0 ; ArrayElementId < ( bIsArray ? ArraySize : 1u ) ; ArrayElementId + + )
{
const uint32 AbsoluteMemberOffset = CurrentMember . GetOffset ( ) + MemberStack [ i ] . StructOffset + ArrayElementId * SHADER_PARAMETER_POINTER_ALIGNMENT ;
check ( AbsoluteMemberOffset < ( 1u < < ( sizeof ( FRHIUniformBufferLayout : : FResourceParameter : : MemberOffset ) * 8 ) ) ) ;
Layout . Resources . Add ( FRHIUniformBufferLayout : : FResourceParameter { uint16 ( AbsoluteMemberOffset ) , BaseType } ) ;
}
2018-12-04 09:59:43 -05:00
}
if ( ChildStruct & & BaseType ! = UBMT_REFERENCED_STRUCT )
{
int32 AbsoluteStructOffset = CurrentMember . GetOffset ( ) + MemberStack [ i ] . StructOffset ;
for ( int32 StructMemberIndex = 0 ; StructMemberIndex < ChildStruct - > Members . Num ( ) ; StructMemberIndex + + )
{
const FMember & StructMember = ChildStruct - > Members [ StructMemberIndex ] ;
MemberStack . Insert ( FUniformBufferMemberAndOffset ( * ChildStruct , StructMember , AbsoluteStructOffset ) , i + 1 + StructMemberIndex ) ;
}
}
2019-03-13 14:52:34 -04:00
} // for (int32 i = 0; i < MemberStack.Num(); ++i)
2018-12-04 09:59:43 -05:00
2019-01-30 21:24:04 -05:00
#if 0
/** Sort the resource on MemberType first to avoid CPU miss predictions when iterating over the resources. Then based on ascending offset
* to still allow O ( N ) complexity on offset cross referencing such as done in ClearUnusedGraphResourcesImpl ( ) .
*/
Layout . Resources . Sort ( [ ] (
const FRHIUniformBufferLayout : : FResourceParameter & A ,
const FRHIUniformBufferLayout : : FResourceParameter & B )
{
if ( A . MemberType = = B . MemberType )
{
return A . MemberOffset < B . MemberOffset ;
}
return A . MemberType < B . MemberType ;
} ) ;
# endif
2018-12-04 09:59:43 -05:00
Layout . ComputeHash ( ) ;
bLayoutInitialized = true ;
}
void FShaderParametersMetadata : : GetNestedStructs ( TArray < const FShaderParametersMetadata * > & OutNestedStructs ) const
{
for ( int32 i = 0 ; i < Members . Num ( ) ; + + i )
{
const FMember & CurrentMember = Members [ i ] ;
const FShaderParametersMetadata * MemberStruct = CurrentMember . GetStructMetadata ( ) ;
if ( MemberStruct )
{
OutNestedStructs . Add ( MemberStruct ) ;
MemberStruct - > GetNestedStructs ( OutNestedStructs ) ;
}
}
}
void FShaderParametersMetadata : : AddResourceTableEntries ( TMap < FString , FResourceTableEntry > & ResourceTableMap , TMap < FString , uint32 > & ResourceTableLayoutHashes ) const
{
uint16 ResourceIndex = 0 ;
FString Prefix = FString : : Printf ( TEXT ( " %s_ " ) , ShaderVariableName ) ;
AddResourceTableEntriesRecursive ( ShaderVariableName , * Prefix , ResourceIndex , ResourceTableMap ) ;
ResourceTableLayoutHashes . Add ( ShaderVariableName , GetLayout ( ) . GetHash ( ) ) ;
}
void FShaderParametersMetadata : : AddResourceTableEntriesRecursive ( const TCHAR * UniformBufferName , const TCHAR * Prefix , uint16 & ResourceIndex , TMap < FString , FResourceTableEntry > & ResourceTableMap ) const
{
for ( int32 MemberIndex = 0 ; MemberIndex < Members . Num ( ) ; + + MemberIndex )
{
const FMember & Member = Members [ MemberIndex ] ;
2019-01-30 21:24:04 -05:00
if ( IsShaderParameterTypeForUniformBufferLayout ( Member . GetBaseType ( ) ) )
2018-12-04 09:59:43 -05:00
{
FResourceTableEntry & Entry = ResourceTableMap . FindOrAdd ( FString : : Printf ( TEXT ( " %s%s " ) , Prefix , Member . GetName ( ) ) ) ;
if ( Entry . UniformBufferName . IsEmpty ( ) )
{
Entry . UniformBufferName = UniformBufferName ;
Entry . Type = Member . GetBaseType ( ) ;
Entry . ResourceIndex = ResourceIndex + + ;
}
}
else if ( Member . GetBaseType ( ) = = UBMT_NESTED_STRUCT )
{
check ( Member . GetStructMetadata ( ) ) ;
FString MemberPrefix = FString : : Printf ( TEXT ( " %s%s_ " ) , Prefix , Member . GetName ( ) ) ;
Member . GetStructMetadata ( ) - > AddResourceTableEntriesRecursive ( UniformBufferName , * MemberPrefix , ResourceIndex , ResourceTableMap ) ;
}
else if ( Member . GetBaseType ( ) = = UBMT_INCLUDED_STRUCT )
{
check ( Member . GetStructMetadata ( ) ) ;
Member . GetStructMetadata ( ) - > AddResourceTableEntriesRecursive ( UniformBufferName , Prefix , ResourceIndex , ResourceTableMap ) ;
}
}
}
2019-03-13 14:52:34 -04:00
void FShaderParametersMetadata : : FindMemberFromOffset ( uint16 MemberOffset , const FShaderParametersMetadata * * OutContainingStruct , const FShaderParametersMetadata : : FMember * * OutMember , int32 * ArrayElementId , FString * NamePrefix ) const
2018-12-04 09:59:43 -05:00
{
check ( MemberOffset < GetSize ( ) ) ;
for ( const FMember & Member : Members )
{
EUniformBufferBaseType BaseType = Member . GetBaseType ( ) ;
if ( BaseType = = UBMT_NESTED_STRUCT | | BaseType = = UBMT_INCLUDED_STRUCT )
{
const FShaderParametersMetadata * SubStruct = Member . GetStructMetadata ( ) ;
if ( MemberOffset < Member . GetOffset ( ) + SubStruct - > GetSize ( ) )
{
2019-03-13 14:52:34 -04:00
if ( NamePrefix )
{
* NamePrefix = FString : : Printf ( TEXT ( " %s%s:: " ) , * * NamePrefix , Member . GetName ( ) ) ;
}
return SubStruct - > FindMemberFromOffset ( MemberOffset - Member . GetOffset ( ) , OutContainingStruct , OutMember , ArrayElementId , NamePrefix ) ;
}
}
else if ( Member . GetNumElements ( ) > 0 & & (
BaseType = = UBMT_TEXTURE | |
BaseType = = UBMT_SRV | |
BaseType = = UBMT_SAMPLER | |
IsRDGResourceReferenceShaderParameterType ( BaseType ) ) )
{
uint16 ArrayStartOffset = Member . GetOffset ( ) ;
uint16 ArrayEndOffset = ArrayStartOffset + SHADER_PARAMETER_POINTER_ALIGNMENT * Member . GetNumElements ( ) ;
if ( MemberOffset > = ArrayStartOffset & & MemberOffset < ArrayEndOffset )
{
check ( ( MemberOffset % SHADER_PARAMETER_POINTER_ALIGNMENT ) = = 0 ) ;
* OutContainingStruct = this ;
* OutMember = & Member ;
* ArrayElementId = ( MemberOffset - ArrayStartOffset ) / SHADER_PARAMETER_POINTER_ALIGNMENT ;
return ;
2018-12-04 09:59:43 -05:00
}
}
else if ( Member . GetOffset ( ) = = MemberOffset )
{
* OutContainingStruct = this ;
* OutMember = & Member ;
2019-03-13 14:52:34 -04:00
* ArrayElementId = 0 ;
2018-12-04 09:59:43 -05:00
return ;
}
}
checkf ( 0 , TEXT ( " Looks like this offset is invalid. " ) ) ;
}