2022-06-21 10:49:56 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ShaderParameterParser.h"
# include "ShaderCompilerCommon.h"
2022-07-15 12:25:44 -04:00
# include "Misc/StringBuilder.h"
template < typename TParameterFunction >
static void IterateShaderParameterMembersInternal (
const FShaderParametersMetadata & ParametersMetadata ,
uint16 ByteOffset ,
TStringBuilder < 1024 > & ShaderBindingNameBuilder ,
TParameterFunction Lambda )
{
for ( const FShaderParametersMetadata : : FMember & Member : ParametersMetadata . GetMembers ( ) )
{
EUniformBufferBaseType BaseType = Member . GetBaseType ( ) ;
const uint16 MemberOffset = ByteOffset + uint16 ( Member . GetOffset ( ) ) ;
const uint32 NumElements = Member . GetNumElements ( ) ;
int32 MemberNameLength = FCString : : Strlen ( Member . GetName ( ) ) ;
if ( BaseType = = UBMT_INCLUDED_STRUCT )
{
check ( NumElements = = 0 ) ;
const FShaderParametersMetadata & NewParametersMetadata = * Member . GetStructMetadata ( ) ;
IterateShaderParameterMembersInternal ( NewParametersMetadata , MemberOffset , ShaderBindingNameBuilder , Lambda ) ;
}
else if ( BaseType = = UBMT_NESTED_STRUCT & & NumElements = = 0 )
{
ShaderBindingNameBuilder . Append ( Member . GetName ( ) ) ;
ShaderBindingNameBuilder . Append ( TEXT ( " _ " ) ) ;
const FShaderParametersMetadata & NewParametersMetadata = * Member . GetStructMetadata ( ) ;
IterateShaderParameterMembersInternal ( NewParametersMetadata , MemberOffset , ShaderBindingNameBuilder , Lambda ) ;
ShaderBindingNameBuilder . RemoveSuffix ( MemberNameLength + 1 ) ;
}
else if ( BaseType = = UBMT_NESTED_STRUCT & & NumElements > 0 )
{
ShaderBindingNameBuilder . Append ( Member . GetName ( ) ) ;
ShaderBindingNameBuilder . Append ( TEXT ( " _ " ) ) ;
const FShaderParametersMetadata & NewParametersMetadata = * Member . GetStructMetadata ( ) ;
for ( uint32 ArrayElementId = 0 ; ArrayElementId < NumElements ; ArrayElementId + + )
{
FString ArrayElementIdString ;
ArrayElementIdString . AppendInt ( ArrayElementId ) ;
int32 ArrayElementIdLength = ArrayElementIdString . Len ( ) ;
ShaderBindingNameBuilder . Append ( ArrayElementIdString ) ;
ShaderBindingNameBuilder . Append ( TEXT ( " _ " ) ) ;
uint16 NewStructOffset = MemberOffset + ArrayElementId * NewParametersMetadata . GetSize ( ) ;
IterateShaderParameterMembersInternal ( NewParametersMetadata , NewStructOffset , ShaderBindingNameBuilder , Lambda ) ;
ShaderBindingNameBuilder . RemoveSuffix ( ArrayElementIdLength + 1 ) ;
}
ShaderBindingNameBuilder . RemoveSuffix ( MemberNameLength + 1 ) ;
}
else
{
const bool bParametersAreExpanded =
NumElements > 0 & &
( BaseType = = UBMT_TEXTURE | |
BaseType = = UBMT_SRV | |
BaseType = = UBMT_UAV | |
BaseType = = UBMT_SAMPLER | |
IsRDGResourceReferenceShaderParameterType ( BaseType ) ) ;
if ( bParametersAreExpanded )
{
const uint16 ElementSize = SHADER_PARAMETER_POINTER_ALIGNMENT ;
for ( uint32 Index = 0 ; Index < NumElements ; Index + + )
{
const FString RealBindingName = FString : : Printf ( TEXT ( " %s_%d " ) , Member . GetName ( ) , Index ) ;
ShaderBindingNameBuilder . Append ( RealBindingName ) ;
Lambda ( ParametersMetadata , Member , ShaderBindingNameBuilder . ToString ( ) , MemberOffset + Index * ElementSize ) ;
ShaderBindingNameBuilder . RemoveSuffix ( RealBindingName . Len ( ) ) ;
}
}
else
{
ShaderBindingNameBuilder . Append ( Member . GetName ( ) ) ;
Lambda ( ParametersMetadata , Member , ShaderBindingNameBuilder . ToString ( ) , MemberOffset ) ;
ShaderBindingNameBuilder . RemoveSuffix ( MemberNameLength ) ;
}
}
}
}
template < typename TParameterFunction >
static void IterateShaderParameterMembers ( const FShaderParametersMetadata & ShaderParametersMetadata , TParameterFunction Lambda )
{
FShaderParametersMetadata : : EUseCase UseCase = ShaderParametersMetadata . GetUseCase ( ) ;
TStringBuilder < 1024 > ShaderBindingNameBuilder ;
if ( UseCase = = FShaderParametersMetadata : : EUseCase : : UniformBuffer | | UseCase = = FShaderParametersMetadata : : EUseCase : : DataDrivenUniformBuffer )
{
ShaderBindingNameBuilder . Append ( ShaderParametersMetadata . GetShaderVariableName ( ) ) ;
ShaderBindingNameBuilder . Append ( TEXT ( " _ " ) ) ;
}
IterateShaderParameterMembersInternal (
ShaderParametersMetadata , /* ByteOffset = */ 0 , ShaderBindingNameBuilder , Lambda ) ;
}
2022-06-21 10:49:56 -04:00
static void AddNoteToDisplayShaderParameterMemberOnCppSide (
const FShaderCompilerInput & CompilerInput ,
const FShaderParameterParser : : FParsedShaderParameter & ParsedParameter ,
FShaderCompilerOutput & CompilerOutput )
{
const FShaderParametersMetadata * MemberContainingStruct = nullptr ;
const FShaderParametersMetadata : : FMember * Member = nullptr ;
{
int32 ArrayElementId = 0 ;
FString NamePrefix ;
CompilerInput . RootParametersStructure - > FindMemberFromOffset ( ParsedParameter . ConstantBufferOffset , & MemberContainingStruct , & Member , & ArrayElementId , & NamePrefix ) ;
}
FString CppCodeName = CompilerInput . RootParametersStructure - > GetFullMemberCodeName ( ParsedParameter . ConstantBufferOffset ) ;
FShaderCompilerError Error ;
Error . StrippedErrorMessage = FString : : Printf (
TEXT ( " Note: Definition of %s " ) ,
* CppCodeName ) ;
Error . ErrorVirtualFilePath = ANSI_TO_TCHAR ( MemberContainingStruct - > GetFileName ( ) ) ;
Error . ErrorLineString = FString : : FromInt ( Member - > GetFileLine ( ) ) ;
CompilerOutput . Errors . Add ( Error ) ;
}
2022-07-15 12:25:44 -04:00
FShaderParameterParser : : FShaderParameterParser ( ) = default ;
FShaderParameterParser : : ~ FShaderParameterParser ( ) = default ;
2022-10-18 15:25:05 -04:00
FShaderParameterParser : : FShaderParameterParser ( const TCHAR * InConstantBufferType )
: ConstantBufferType ( InConstantBufferType )
{
}
2022-07-15 12:25:44 -04:00
FShaderParameterParser : : FShaderParameterParser (
2022-10-18 15:25:05 -04:00
const TCHAR * InConstantBufferType ,
TArrayView < const TCHAR * const > InExtraSRVTypes ,
TArrayView < const TCHAR * const > InExtraUAVTypes
2022-07-15 12:25:44 -04:00
)
2022-10-18 15:25:05 -04:00
: ConstantBufferType ( InConstantBufferType )
, ExtraSRVTypes ( InExtraSRVTypes )
2022-07-15 12:25:44 -04:00
, ExtraUAVTypes ( InExtraUAVTypes )
2022-06-21 10:49:56 -04:00
{
}
2022-07-15 12:25:44 -04:00
bool FShaderParameterParser : : ParseParameters (
2022-06-21 10:49:56 -04:00
const FShaderCompilerInput & CompilerInput ,
2022-07-15 12:25:44 -04:00
FShaderCompilerOutput & CompilerOutput )
2022-06-21 10:49:56 -04:00
{
2022-07-15 12:25:44 -04:00
const FStringView ShaderSource ( OriginalParsedShader ) ;
if ( CompilerInput . RootParametersStructure )
2022-06-21 10:49:56 -04:00
{
2022-07-15 12:25:44 -04:00
// Reserves the number of parameters up front.
ParsedParameters . Reserve ( CompilerInput . RootParametersStructure - > GetSize ( ) / sizeof ( int32 ) ) ;
IterateShaderParameterMembers (
* CompilerInput . RootParametersStructure ,
[ & ] ( const FShaderParametersMetadata & ParametersMetadata ,
const FShaderParametersMetadata : : FMember & Member ,
const TCHAR * ShaderBindingName ,
uint16 ByteOffset )
{
FParsedShaderParameter ParsedParameter ;
ParsedParameter . Member = & Member ;
ParsedParameter . ConstantBufferOffset = ByteOffset ;
check ( ParsedParameter . IsBindable ( ) ) ;
ParsedParameters . Add ( ShaderBindingName , ParsedParameter ) ;
} ) ;
2022-06-21 10:49:56 -04:00
}
bool bSuccess = true ;
// Browse the code for global shader parameter, Save their type and erase them white spaces.
{
enum class EState
{
// When to look for something to scan.
Scanning ,
// When going to next ; in the global scope and reset.
GoToNextSemicolonAndReset ,
// Parsing what might be a type of the parameter.
ParsingPotentialType ,
2022-07-15 12:25:44 -04:00
ParsingPotentialTypeTemplateArguments ,
2022-06-21 10:49:56 -04:00
FinishedPotentialType ,
// Parsing what might be a name of the parameter.
ParsingPotentialName ,
FinishedPotentialName ,
// Parsing what looks like array of the parameter.
ParsingPotentialArraySize ,
FinishedArraySize ,
// Found a parameter, just finish to it's semi colon.
FoundParameter ,
} ;
2022-07-15 12:25:44 -04:00
const int32 ShaderSourceLen = ShaderSource . Len ( ) ;
2022-06-21 10:49:56 -04:00
int32 CurrentPragamLineoffset = - 1 ;
int32 CurrentLineoffset = 0 ;
int32 TypeQualifierStartPos = - 1 ;
int32 TypeStartPos = - 1 ;
int32 TypeEndPos = - 1 ;
int32 NameStartPos = - 1 ;
int32 NameEndPos = - 1 ;
int32 ArrayStartPos = - 1 ;
int32 ArrayEndPos = - 1 ;
int32 ScopeIndent = 0 ;
2022-10-06 16:27:11 -04:00
bool bGloballyCoherent = false ;
2022-06-21 10:49:56 -04:00
EState State = EState : : Scanning ;
bool bGoToNextLine = false ;
auto ResetState = [ & ] ( )
{
TypeQualifierStartPos = - 1 ;
TypeStartPos = - 1 ;
TypeEndPos = - 1 ;
NameStartPos = - 1 ;
NameEndPos = - 1 ;
ArrayStartPos = - 1 ;
ArrayEndPos = - 1 ;
2022-10-06 16:27:11 -04:00
bGloballyCoherent = false ;
2022-06-21 10:49:56 -04:00
State = EState : : Scanning ;
} ;
auto EmitError = [ & ] ( const FString & ErrorMessage )
{
FShaderCompilerError Error ;
Error . StrippedErrorMessage = ErrorMessage ;
ExtractFileAndLine ( CurrentPragamLineoffset , CurrentLineoffset , Error . ErrorVirtualFilePath , Error . ErrorLineString ) ;
CompilerOutput . Errors . Add ( Error ) ;
bSuccess = false ;
} ;
auto EmitUnpextectedHLSLSyntaxError = [ & ] ( )
{
EmitError ( TEXT ( " Unexpected syntax when parsing shader parameters from shader code. " ) ) ;
State = EState : : GoToNextSemicolonAndReset ;
} ;
for ( int32 Cursor = 0 ; Cursor < ShaderSourceLen ; Cursor + + )
{
2022-07-15 12:25:44 -04:00
const TCHAR Char = ShaderSource [ Cursor ] ;
2022-06-21 10:49:56 -04:00
auto FoundShaderParameter = [ & ] ( )
{
check ( Char = = ' ; ' ) ;
check ( TypeStartPos ! = - 1 ) ;
check ( TypeEndPos ! = - 1 ) ;
check ( NameStartPos ! = - 1 ) ;
check ( NameEndPos ! = - 1 ) ;
2022-07-15 12:25:44 -04:00
FStringView Type = ShaderSource . Mid ( TypeStartPos , TypeEndPos - TypeStartPos + 1 ) ;
FStringView Name = ShaderSource . Mid ( NameStartPos , NameEndPos - NameStartPos + 1 ) ;
2022-06-21 10:49:56 -04:00
2022-07-15 12:25:44 -04:00
FStringView Leftovers = ShaderSource . Mid ( NameEndPos + 1 , ( Cursor - 1 ) - ( NameEndPos + 1 ) + 1 ) ;
EShaderParameterType ParsedParameterType = UE : : ShaderCompilerCommon : : ParseAndRemoveBindlessParameterPrefix ( Name ) ;
const bool bBindlessIndex = ( ParsedParameterType = = EShaderParameterType : : BindlessResourceIndex | | ParsedParameterType = = EShaderParameterType : : BindlessSamplerIndex ) ;
2022-10-06 16:27:11 -04:00
EBindlessConversionType BindlessConversionType = EBindlessConversionType : : None ;
2022-10-18 15:25:05 -04:00
EShaderParameterType ParsedConstantBufferType = ParsedParameterType ;
2022-07-15 12:25:44 -04:00
if ( ( bBindlessResources | | bBindlessSamplers ) & & ParsedParameterType = = EShaderParameterType : : LooseData )
{
ParsedParameterType = UE : : ShaderCompilerCommon : : ParseParameterType ( Type , ExtraSRVTypes , ExtraUAVTypes ) ;
if ( bBindlessResources & & ( ParsedParameterType = = EShaderParameterType : : SRV | | ParsedParameterType = = EShaderParameterType : : UAV ) )
{
2022-10-06 16:27:11 -04:00
BindlessConversionType = EBindlessConversionType : : Resource ;
2022-10-18 15:25:05 -04:00
ParsedConstantBufferType = EShaderParameterType : : BindlessResourceIndex ;
2022-07-15 12:25:44 -04:00
}
else if ( bBindlessSamplers & & ParsedParameterType = = EShaderParameterType : : Sampler )
{
2022-10-06 16:27:11 -04:00
BindlessConversionType = EBindlessConversionType : : Sampler ;
2022-10-18 15:25:05 -04:00
ParsedConstantBufferType = EShaderParameterType : : BindlessSamplerIndex ;
2022-07-15 12:25:44 -04:00
}
2022-10-06 16:27:11 -04:00
if ( BindlessConversionType ! = EBindlessConversionType : : None & & Leftovers . Contains ( TEXT ( " register " ) ) )
2022-07-15 12:25:44 -04:00
{
// avoid rewriting hardcoded register assignments
2022-10-06 16:27:11 -04:00
BindlessConversionType = EBindlessConversionType : : None ;
2022-10-18 15:25:05 -04:00
ParsedConstantBufferType = ParsedParameterType ;
2022-07-15 12:25:44 -04:00
}
}
2022-06-21 10:49:56 -04:00
FParsedShaderParameter ParsedParameter ;
2022-07-15 12:25:44 -04:00
EShaderParameterType ConstantBufferParameterType = EShaderParameterType : : Num ;
bool bMoveToRootConstantBuffer = false ;
2022-06-21 10:49:56 -04:00
bool bUpdateParsedParameters = false ;
2022-07-15 12:25:44 -04:00
const FString ParsedParameterKey ( Name ) ;
if ( ParsedParameters . Contains ( ParsedParameterKey ) )
2022-06-21 10:49:56 -04:00
{
2022-07-15 12:25:44 -04:00
if ( ParsedParameters . FindChecked ( ParsedParameterKey ) . IsFound ( ) )
2022-06-21 10:49:56 -04:00
{
// If it has already been found, it means it is duplicated. Do nothing and let the shader compiler throw the error.
}
else
{
// Update the parsed parameters
bUpdateParsedParameters = true ;
2022-07-15 12:25:44 -04:00
ParsedParameter = ParsedParameters . FindChecked ( ParsedParameterKey ) ;
2022-06-21 10:49:56 -04:00
// Erase the parameter to move it into the root constant buffer.
2022-07-15 12:25:44 -04:00
if ( bNeedToMoveToRootConstantBuffer & & ParsedParameter . IsBindable ( ) )
2022-06-21 10:49:56 -04:00
{
2022-07-15 12:25:44 -04:00
const EUniformBufferBaseType BaseType = ParsedParameter . Member - > GetBaseType ( ) ;
bMoveToRootConstantBuffer =
BaseType = = UBMT_INT32 | |
BaseType = = UBMT_UINT32 | |
BaseType = = UBMT_FLOAT32 | |
bBindlessIndex | |
2022-10-06 16:27:11 -04:00
( BindlessConversionType ! = EBindlessConversionType : : None ) ;
2022-07-15 12:25:44 -04:00
if ( bMoveToRootConstantBuffer )
{
2022-10-18 15:25:05 -04:00
ConstantBufferParameterType = ParsedConstantBufferType ;
2022-07-15 12:25:44 -04:00
}
2022-06-21 10:49:56 -04:00
}
}
}
else
{
// Update the parsed parameters to still have file and line number.
bUpdateParsedParameters = true ;
}
// Update
if ( bUpdateParsedParameters )
{
2022-10-07 13:35:29 -04:00
ParsedParameter . ParsedName = Name ;
2022-06-21 10:49:56 -04:00
ParsedParameter . ParsedType = Type ;
ParsedParameter . ParsedPragmaLineoffset = CurrentPragamLineoffset ;
ParsedParameter . ParsedLineOffset = CurrentLineoffset ;
2022-07-15 12:25:44 -04:00
ParsedParameter . ParsedCharOffsetStart = TypeQualifierStartPos ! = - 1 ? TypeQualifierStartPos : TypeStartPos ;
ParsedParameter . ParsedCharOffsetEnd = Cursor ;
ParsedParameter . BindlessConversionType = BindlessConversionType ;
ParsedParameter . ConstantBufferParameterType = ConstantBufferParameterType ;
2022-10-06 16:27:11 -04:00
ParsedParameter . bGloballyCoherent = bGloballyCoherent ;
2022-06-21 10:49:56 -04:00
if ( ArrayStartPos ! = - 1 & & ArrayEndPos ! = - 1 )
{
2022-07-15 12:25:44 -04:00
ParsedParameter . ParsedArraySize = ShaderSource . Mid ( ArrayStartPos + 1 , ArrayEndPos - ArrayStartPos - 1 ) ;
2022-06-21 10:49:56 -04:00
}
2022-07-15 12:25:44 -04:00
ParsedParameters . Add ( ParsedParameterKey , ParsedParameter ) ;
2022-06-21 10:49:56 -04:00
}
ResetState ( ) ;
} ;
const bool bIsWhiteSpace = Char = = ' ' | | Char = = ' \t ' | | Char = = ' \r ' | | Char = = ' \n ' ;
const bool bIsLetter = ( Char > = ' a ' & & Char < = ' z ' ) | | ( Char > = ' A ' & & Char < = ' Z ' ) ;
const bool bIsNumber = Char > = ' 0 ' & & Char < = ' 9 ' ;
2022-07-15 12:25:44 -04:00
const TCHAR * UpComing = ShaderSource . GetData ( ) + Cursor ;
2022-06-21 10:49:56 -04:00
const int32 RemainingSize = ShaderSourceLen - Cursor ;
CurrentLineoffset + = Char = = ' \n ' ;
// Go to the next line if this is a preprocessor macro.
if ( bGoToNextLine )
{
if ( Char = = ' \n ' )
{
bGoToNextLine = false ;
}
continue ;
}
else if ( Char = = ' # ' )
{
if ( RemainingSize > 6 & & FCString : : Strncmp ( UpComing , TEXT ( " #line " ) , 6 ) = = 0 )
{
CurrentPragamLineoffset = Cursor ;
CurrentLineoffset = - 1 ; // that will be incremented to 0 when reaching the \n at the end of the #line
}
bGoToNextLine = true ;
continue ;
}
// If within a scope, just carry on until outside the scope.
if ( ScopeIndent > 0 | | Char = = ' { ' )
{
if ( Char = = ' { ' )
{
ScopeIndent + + ;
}
else if ( Char = = ' } ' )
{
ScopeIndent - - ;
if ( ScopeIndent = = 0 )
{
ResetState ( ) ;
}
}
continue ;
}
if ( State = = EState : : Scanning )
{
if ( bIsLetter )
{
2022-07-15 12:25:44 -04:00
static const TCHAR * KeywordTable [ ] =
{
2022-10-06 16:27:11 -04:00
TEXT ( " const " ) ,
TEXT ( " globallycoherent " )
2022-06-21 10:49:56 -04:00
TEXT ( " enum " ) ,
TEXT ( " class " ) ,
TEXT ( " struct " ) ,
TEXT ( " static " ) ,
} ;
2022-10-06 16:27:11 -04:00
static int32 KeywordTableSize [ ] = { 5 , 16 , 4 , 5 , 6 , 6 } ;
2022-06-21 10:49:56 -04:00
int32 RecognisedKeywordId = - 1 ;
for ( int32 KeywordId = 0 ; KeywordId < UE_ARRAY_COUNT ( KeywordTable ) ; KeywordId + + )
{
const TCHAR * Keyword = KeywordTable [ KeywordId ] ;
const int32 KeywordSize = KeywordTableSize [ KeywordId ] ;
if ( RemainingSize > KeywordSize )
{
TCHAR KeywordEndTestChar = UpComing [ KeywordSize ] ;
if ( ( KeywordEndTestChar = = ' ' | | KeywordEndTestChar = = ' \r ' | | KeywordEndTestChar = = ' \n ' | | KeywordEndTestChar = = ' \t ' ) & &
FCString : : Strncmp ( UpComing , Keyword , KeywordSize ) = = 0 )
{
RecognisedKeywordId = KeywordId ;
break ;
}
}
}
if ( RecognisedKeywordId = = - 1 )
{
// Might have found beginning of the type of a parameter.
State = EState : : ParsingPotentialType ;
TypeStartPos = Cursor ;
}
2022-10-06 16:27:11 -04:00
else if ( RecognisedKeywordId = = 0 )
2022-06-21 10:49:56 -04:00
{
// Ignore the const keywords, but still parse given it might still be a shader parameter.
if ( TypeQualifierStartPos = = - 1 )
{
// If the parameter is erased, we also have to erase *all* 'const'-qualifiers, e.g. "const int Foo" or "const const int Foo".
TypeQualifierStartPos = Cursor ;
}
Cursor + = KeywordTableSize [ RecognisedKeywordId ] ;
}
2022-10-06 16:27:11 -04:00
else if ( RecognisedKeywordId = = 1 )
{
// Mark that we got the globallycoherent keyword and keep moving to the next set of qualifiers
bGloballyCoherent = true ;
if ( TypeQualifierStartPos = = - 1 )
{
// If the parameter is erased, we also have to erase *all* qualifiers, e.g. "const int Foo" or "const const int Foo".
TypeQualifierStartPos = Cursor ;
}
Cursor + = KeywordTableSize [ RecognisedKeywordId ] ;
}
2022-06-21 10:49:56 -04:00
else
{
// Purposefully ignore enum, class, struct, static
State = EState : : GoToNextSemicolonAndReset ;
}
}
else if ( bIsWhiteSpace )
{
// Keep parsing void.
}
else if ( Char = = ' ; ' )
{
// Looks like redundant semicolon, just ignore and keep scanning.
}
else
{
// No idea what this is, just go to next semi colon.
State = EState : : GoToNextSemicolonAndReset ;
}
}
else if ( State = = EState : : GoToNextSemicolonAndReset )
{
// If need to go to next global semicolon and reach it. Resume browsing.
if ( Char = = ' ; ' )
{
ResetState ( ) ;
}
}
else if ( State = = EState : : ParsingPotentialType )
{
// Found character legal for a type...
if ( bIsLetter | |
bIsNumber | |
2022-07-15 12:25:44 -04:00
Char = = ' _ ' )
2022-06-21 10:49:56 -04:00
{
// Keep browsing what might be type of the parameter.
}
2022-07-15 12:25:44 -04:00
else if ( Char = = ' < ' )
{
2022-10-06 16:27:11 -04:00
// Found what looks like the beginning of template argument that is legal on resource types for Instance Texture2D< float >
2022-07-15 12:25:44 -04:00
State = EState : : ParsingPotentialTypeTemplateArguments ;
}
2022-06-21 10:49:56 -04:00
else if ( bIsWhiteSpace )
{
// Might have found a type.
State = EState : : FinishedPotentialType ;
TypeEndPos = Cursor - 1 ;
}
else
{
// Found unexpected character in the type.
State = EState : : GoToNextSemicolonAndReset ;
}
}
2022-07-15 12:25:44 -04:00
else if ( State = = EState : : ParsingPotentialTypeTemplateArguments )
{
// Found character legal for a template argument...
if ( bIsLetter | |
bIsNumber | |
Char = = ' _ ' )
{
// Keep browsing what might be type of the parameter.
}
else if ( bIsWhiteSpace | | Char = = ' , ' )
{
// Spaces and comas are legal within agrument of the template arguments.
}
else if ( Char = = ' > ' )
{
// Might have found a type with template argument.
State = EState : : FinishedPotentialType ;
TypeEndPos = Cursor ;
}
else
{
// Found unexpected character in the type.
State = EState : : GoToNextSemicolonAndReset ;
}
}
2022-06-21 10:49:56 -04:00
else if ( State = = EState : : FinishedPotentialType )
{
if ( bIsLetter )
{
// Might have found beginning of the name of a parameter.
State = EState : : ParsingPotentialName ;
NameStartPos = Cursor ;
}
2022-07-15 12:25:44 -04:00
else if ( Char = = ' < ' )
{
// Might have found beginning of a template argument for the type, that was separate by a whitespace from type. For instance Texture2D <float>
State = EState : : ParsingPotentialTypeTemplateArguments ;
}
2022-06-21 10:49:56 -04:00
else if ( bIsWhiteSpace )
{
// Keep parsing void.
}
else
{
// No idea what this is, just go to next semi colon.
State = EState : : GoToNextSemicolonAndReset ;
}
}
else if ( State = = EState : : ParsingPotentialName )
{
// Found character legal for a name...
if ( bIsLetter | |
bIsNumber | |
Char = = ' _ ' )
{
// Keep browsing what might be name of the parameter.
}
else if ( Char = = ' : ' | | Char = = ' = ' )
{
// Found a parameter with syntax:
// uint MyParameter : <whatever>;
// uint MyParameter = <DefaultValue>;
NameEndPos = Cursor - 1 ;
State = EState : : FoundParameter ;
}
else if ( Char = = ' ; ' )
{
// Found a parameter with syntax:
// uint MyParameter;
NameEndPos = Cursor - 1 ;
FoundShaderParameter ( ) ;
}
else if ( Char = = ' [ ' )
{
// Syntax:
// uint MyArray[
NameEndPos = Cursor - 1 ;
ArrayStartPos = Cursor ;
State = EState : : ParsingPotentialArraySize ;
}
else if ( bIsWhiteSpace )
{
// Might have found a name.
// uint MyParameter <Still need to know what is after>;
NameEndPos = Cursor - 1 ;
State = EState : : FinishedPotentialName ;
}
else
{
// Found unexpected character in the name.
// syntax:
// uint MyFunction(<Don't care what is after>
State = EState : : GoToNextSemicolonAndReset ;
}
}
2022-07-15 12:25:44 -04:00
else if ( State = = EState : : FinishedPotentialName | | State = = EState : : FinishedArraySize )
2022-06-21 10:49:56 -04:00
{
if ( Char = = ' ; ' )
{
// Found a parameter with syntax:
// uint MyParameter <a bit of OK stuf>;
FoundShaderParameter ( ) ;
}
else if ( Char = = ' : ' )
{
// Found a parameter with syntax:
// uint MyParameter <a bit of OK stuf> : <Ignore all this crap>;
State = EState : : FoundParameter ;
}
else if ( Char = = ' = ' )
{
// Found syntax that doesn't make any sens:
// uint MyParameter <a bit of OK stuf> = <Ignore all this crap>;
State = EState : : FoundParameter ;
// TDOO: should error out that this is useless.
}
else if ( Char = = ' [ ' )
{
if ( State = = EState : : FinishedPotentialName )
{
// Syntax:
// uint MyArray [
ArrayStartPos = Cursor ;
State = EState : : ParsingPotentialArraySize ;
}
else
{
EmitError ( TEXT ( " Shader parameters can only support one dimensional array " ) ) ;
}
}
else if ( bIsWhiteSpace )
{
// Keep parsing void.
}
else
{
// Found unexpected stuff.
State = EState : : GoToNextSemicolonAndReset ;
}
}
else if ( State = = EState : : ParsingPotentialArraySize )
{
if ( Char = = ' ] ' )
{
ArrayEndPos = Cursor ;
State = EState : : FinishedArraySize ;
}
else if ( Char = = ' ; ' )
{
EmitUnpextectedHLSLSyntaxError ( ) ;
}
else
{
// Keep going through the array size that might be a complex expression.
}
}
else if ( State = = EState : : FoundParameter )
{
if ( Char = = ' ; ' )
{
FoundShaderParameter ( ) ;
}
else
{
// Cary on skipping all crap we don't care about shader parameter until we find it's semi colon.
}
}
else
{
unimplemented ( ) ;
}
2022-07-15 12:25:44 -04:00
} // for (int32 Cursor = 0; Cursor < ShaderSourceLen; Cursor++)
2022-06-21 10:49:56 -04:00
}
2022-07-15 12:25:44 -04:00
return bSuccess ;
}
void FShaderParameterParser : : RemoveMovingParametersFromSource ( FString & PreprocessedShaderSource )
{
for ( TPair < FString , FParsedShaderParameter > & Itr : ParsedParameters )
{
const FParsedShaderParameter & ParsedParameter = Itr . Value ;
// If this parameter is going to be in the root constant buffer
if ( ParsedParameter . ConstantBufferParameterType ! = EShaderParameterType : : Num & &
// but it's not being converted to bindless
2022-10-06 16:27:11 -04:00
ParsedParameter . BindlessConversionType = = EBindlessConversionType : : None & &
ParsedParameter . ParsedCharOffsetStart ! = INDEX_NONE )
2022-07-15 12:25:44 -04:00
{
// then erase this shader parameter conserving the same line numbers.
for ( int32 j = ParsedParameter . ParsedCharOffsetStart ; j < = ParsedParameter . ParsedCharOffsetEnd ; j + + )
{
if ( PreprocessedShaderSource [ j ] ! = ' \r ' & & PreprocessedShaderSource [ j ] ! = ' \n ' )
{
PreprocessedShaderSource [ j ] = ' ' ;
}
}
}
}
}
2023-01-11 13:03:47 -05:00
FString FShaderParameterParser : : GenerateBindlessParameterDeclaration ( const FParsedShaderParameter & ParsedParameter ) const
{
// NOTE: Macros AUTO_BINDLESS_SAMPLER_INDEX/VARIABLE and AUTO_BINDLESS_RESOURCE_INDEX/VARIABLE in BindlessResources.ush must be kept in sync with this function
const bool bIsSampler = ( ParsedParameter . BindlessConversionType = = EBindlessConversionType : : Sampler ) ;
const TCHAR * Kind = bIsSampler ? TEXT ( " Sampler " ) : TEXT ( " Resource " ) ;
const TCHAR * StorageClass = ParsedParameter . bGloballyCoherent ? TEXT ( " globallycoherent " ) : TEXT ( " " ) ;
const FStringView Name = ParsedParameter . ParsedName ;
const FStringView Type = ParsedParameter . ParsedType ;
FString RewriteType ( Type ) ;
FString TypedefText = TEXT ( " " ) ;
if ( ParsedParameter . BindlessConversionType = = EBindlessConversionType : : Resource )
{
RewriteType = FString : : Printf ( TEXT ( " SafeType%.*s " ) , Name . Len ( ) , Name . GetData ( ) ) ;
TypedefText = FString : : Printf ( TEXT ( " typedef %.*s %s; " ) , Type . Len ( ) , Type . GetData ( ) , * RewriteType ) ;
}
TStringBuilder < 512 > Result ;
// If we weren't going to be added to a root constant buffer, that means we need to declare our index before we declare our getter.
if ( ParsedParameter . ConstantBufferParameterType = = EShaderParameterType : : Num )
{
// e.g. `uint BindlessResource_##Name;`
// or `uint BindlessSampler_##Name;`
Result < < TEXT ( " uint Bindless " ) < < Kind < < TEXT ( " _ " ) < < Name < < TEXT ( " ; " ) ;
}
Result < < TypedefText ;
// e.g. `Type GetBindlessResource##Name() { return GetResourceFromHeap(Type, BindlessResource_##Name); } static const Type Name = GetBindlessResource##Name()`
// or `Type GetBindlessSampler##Name() { return GetSamplerFromHeap(Type, BindlessSampler_##Name); } static const Type Name = GetBindlessSampler##Name()`
Result < < StorageClass < < RewriteType < < TEXT ( " GetBindless " ) < < Kind < < Name < < TEXT ( " () " ) ;
Result < < TEXT ( " { return Get " ) < < Kind < < TEXT ( " FromHeap( " ) < < StorageClass < < RewriteType < < TEXT ( " , Bindless " ) < < Kind < < TEXT ( " _ " ) < < Name < < TEXT ( " ); } " ) ;
Result < < TEXT ( " static const " ) < < StorageClass < < RewriteType < < TEXT ( " " ) < < Name < < TEXT ( " = GetBindless " ) < < Kind < < Name < < TEXT ( " (); " ) ;
return Result . ToString ( ) ;
}
2022-07-15 12:25:44 -04:00
void FShaderParameterParser : : ApplyBindlessModifications ( FString & PreprocessedShaderSource )
{
if ( bBindlessResources | | bBindlessSamplers )
{
// Array of modifications to do on PreprocessedShaderSource
struct FShaderCodeModifications
{
int32 CharOffsetStart ;
int32 CharOffsetEnd ;
FString Replace ;
} ;
TArray < FShaderCodeModifications > Modifications ;
Modifications . Reserve ( ParsedParameters . Num ( ) ) ;
for ( TPair < FString , FParsedShaderParameter > & Itr : ParsedParameters )
{
FParsedShaderParameter & ParsedParameter = Itr . Value ;
if ( ! ParsedParameter . IsFound ( ) )
{
continue ;
}
2022-10-06 16:27:11 -04:00
if ( ParsedParameter . BindlessConversionType ! = EBindlessConversionType : : None )
2022-07-15 12:25:44 -04:00
{
FShaderCodeModifications Modif ;
Modif . CharOffsetStart = ParsedParameter . ParsedCharOffsetStart ;
Modif . CharOffsetEnd = ParsedParameter . ParsedCharOffsetEnd + 1 ;
2023-01-11 13:03:47 -05:00
Modif . Replace = GenerateBindlessParameterDeclaration ( ParsedParameter ) ;
2022-07-15 12:25:44 -04:00
Modifications . Add ( Modif ) ;
}
}
// Apply all modifications
if ( Modifications . Num ( ) > 0 )
{
// Sort all the modifications in order
Modifications . Sort (
[ ] ( const FShaderCodeModifications & ModifA , const FShaderCodeModifications & ModifB )
{
return ModifA . CharOffsetStart < ModifB . CharOffsetStart ;
}
) ;
// Find out the size of the shader code after all modifications
int32 NewShaderCodeSize = PreprocessedShaderSource . Len ( ) ;
for ( const FShaderCodeModifications & Modif : Modifications )
{
// Count the number of line return \n in CharOffsetStart -> CharOffsetEnd to ensure the line number remain unchanged.
check ( ! Modif . Replace . Contains ( TEXT ( " \n " ) ) ) ;
//int32 ReplacedCarriagedReturn = 0;
for ( int32 CharPos = Modif . CharOffsetStart ; CharPos < Modif . CharOffsetEnd ; CharPos + + )
{
ensure ( PreprocessedShaderSource [ CharPos ] ! = ' \n ' ) ;
}
NewShaderCodeSize + = Modif . Replace . Len ( ) - ( Modif . CharOffsetEnd - Modif . CharOffsetStart ) ; // + ReplacedCarriagedReturn;
}
// Splice all the code and modifications together
FString NewShaderCode ;
NewShaderCode . Reserve ( NewShaderCodeSize ) ;
int32 CurrentCodePos = 0 ;
for ( const FShaderCodeModifications & Modif : Modifications )
{
check ( CurrentCodePos < = Modif . CharOffsetStart ) ;
NewShaderCode + = PreprocessedShaderSource . Mid ( CurrentCodePos , Modif . CharOffsetStart - CurrentCodePos ) ;
NewShaderCode + = Modif . Replace ;
CurrentCodePos = Modif . CharOffsetEnd ;
}
check ( CurrentCodePos < = PreprocessedShaderSource . Len ( ) ) ;
NewShaderCode + = PreprocessedShaderSource . Mid ( CurrentCodePos , PreprocessedShaderSource . Len ( ) - CurrentCodePos ) ;
// Commit all modifications to caller
PreprocessedShaderSource = NewShaderCode ;
}
}
}
static const TCHAR * GetConstantSwizzle ( uint16 ByteOffset )
{
switch ( ByteOffset % 16 )
{
default : unimplemented ( ) ;
case 0 : return TEXT ( " " ) ;
case 4 : return TEXT ( " .y " ) ;
case 8 : return TEXT ( " .z " ) ;
case 12 : return TEXT ( " .w " ) ;
}
}
bool FShaderParameterParser : : MoveShaderParametersToRootConstantBuffer (
const FShaderCompilerInput & CompilerInput ,
FShaderCompilerOutput & CompilerOutput ,
2022-10-18 15:25:05 -04:00
FString & PreprocessedShaderSource )
2022-07-15 12:25:44 -04:00
{
bool bSuccess = true ;
2022-06-21 10:49:56 -04:00
// Generate the root cbuffer content.
2022-07-15 12:25:44 -04:00
if ( CompilerInput . RootParametersStructure & & bNeedToMoveToRootConstantBuffer )
2022-06-21 10:49:56 -04:00
{
FString RootCBufferContent ;
2022-07-15 12:25:44 -04:00
IterateShaderParameterMembers (
* CompilerInput . RootParametersStructure ,
2022-06-21 10:49:56 -04:00
[ & ] ( const FShaderParametersMetadata & ParametersMetadata ,
const FShaderParametersMetadata : : FMember & Member ,
const TCHAR * ShaderBindingName ,
uint16 ByteOffset )
{
2022-07-15 12:25:44 -04:00
FParsedShaderParameter * ParsedParameter = ParsedParameters . Find ( ShaderBindingName ) ;
if ( ParsedParameter & & ParsedParameter - > IsFound ( ) & & ParsedParameter - > ConstantBufferParameterType ! = EShaderParameterType : : Num )
2022-06-21 10:49:56 -04:00
{
2022-07-15 12:25:44 -04:00
const uint32 ConstantRegister = ByteOffset / 16 ;
const TCHAR * ConstantSwizzle = GetConstantSwizzle ( ByteOffset ) ;
2022-06-21 10:49:56 -04:00
2022-07-15 12:25:44 -04:00
# define SVARG(N) N.Len(), N.GetData()
if ( ParsedParameter - > ConstantBufferParameterType = = EShaderParameterType : : BindlessResourceIndex )
{
RootCBufferContent . Append ( FString : : Printf (
2022-10-07 13:35:29 -04:00
TEXT ( " uint BindlessResource_%.*s : packoffset(c%d%s); \r \n " ) ,
SVARG ( ParsedParameter - > ParsedName ) ,
2022-07-15 12:25:44 -04:00
ConstantRegister ,
ConstantSwizzle ) ) ;
}
else if ( ParsedParameter - > ConstantBufferParameterType = = EShaderParameterType : : BindlessSamplerIndex )
{
RootCBufferContent . Append ( FString : : Printf (
2022-10-07 13:35:29 -04:00
TEXT ( " uint BindlessSampler_%.*s : packoffset(c%d%s); \r \n " ) ,
SVARG ( ParsedParameter - > ParsedName ) ,
2022-07-15 12:25:44 -04:00
ConstantRegister ,
ConstantSwizzle ) ) ;
}
else if ( ParsedParameter - > ConstantBufferParameterType = = EShaderParameterType : : LooseData )
{
2022-06-21 10:49:56 -04:00
if ( ! ParsedParameter - > ParsedArraySize . IsEmpty ( ) )
{
RootCBufferContent . Append ( FString : : Printf (
2022-07-15 12:25:44 -04:00
TEXT ( " %.*s %s[%.*s] : packoffset(c%d%s); \r \n " ) ,
SVARG ( ParsedParameter - > ParsedType ) ,
ShaderBindingName ,
SVARG ( ParsedParameter - > ParsedArraySize ) ,
2022-06-21 10:49:56 -04:00
ConstantRegister ,
ConstantSwizzle ) ) ;
}
else
{
RootCBufferContent . Append ( FString : : Printf (
2022-07-15 12:25:44 -04:00
TEXT ( " %.*s %s : packoffset(c%d%s); \r \n " ) ,
SVARG ( ParsedParameter - > ParsedType ) ,
ShaderBindingName ,
2022-06-21 10:49:56 -04:00
ConstantRegister ,
ConstantSwizzle ) ) ;
}
}
2022-07-15 12:25:44 -04:00
# undef SVARG
2022-06-21 10:49:56 -04:00
}
} ) ;
FString CBufferCodeBlock = FString : : Printf (
TEXT ( " %s %s \r \n " )
TEXT ( " { \r \n " )
TEXT ( " %s " )
TEXT ( " } \r \n \r \n " ) ,
ConstantBufferType ,
FShaderParametersMetadata : : kRootUniformBufferBindingName ,
* RootCBufferContent ) ;
FString NewShaderCode = (
2022-07-15 12:25:44 -04:00
MakeInjectedShaderCodeBlock ( TEXT ( " MoveShaderParametersToRootConstantBuffer " ) , CBufferCodeBlock ) +
2022-06-21 10:49:56 -04:00
PreprocessedShaderSource ) ;
PreprocessedShaderSource = MoveTemp ( NewShaderCode ) ;
bMovedLoosedParametersToRootConstantBuffer = true ;
2022-07-15 12:25:44 -04:00
} // if (CompilerInput.RootParametersStructure && bNeedToMoveToRootConstantBuffer)
2022-06-21 10:49:56 -04:00
return bSuccess ;
}
2022-07-15 12:25:44 -04:00
bool FShaderParameterParser : : ParseAndModify (
const FShaderCompilerInput & CompilerInput ,
FShaderCompilerOutput & CompilerOutput ,
2022-10-18 15:25:05 -04:00
FString & PreprocessedShaderSource )
2022-07-15 12:25:44 -04:00
{
bBindlessResources = CompilerInput . Environment . CompilerFlags . Contains ( CFLAG_BindlessResources ) ;
bBindlessSamplers = CompilerInput . Environment . CompilerFlags . Contains ( CFLAG_BindlessSamplers ) ;
2022-10-18 15:25:05 -04:00
const bool bHasRootParameters = ( CompilerInput . RootParametersStructure ! = nullptr ) ;
2022-07-15 12:25:44 -04:00
// The shader doesn't have any parameter binding through shader structure, therefore don't do anything.
if ( ! ( bBindlessResources | | bBindlessSamplers | | bHasRootParameters ) )
{
return true ;
}
2022-10-18 15:25:05 -04:00
bNeedToMoveToRootConstantBuffer = ConstantBufferType ! = nullptr & & ( CompilerInput . IsRayTracingShader ( ) | | UE : : ShaderCompilerCommon : : ShouldUseStableConstantBuffer ( CompilerInput ) ) ;
2022-07-15 12:25:44 -04:00
OriginalParsedShader = PreprocessedShaderSource ;
if ( ! ParseParameters ( CompilerInput , CompilerOutput ) )
{
return false ;
}
RemoveMovingParametersFromSource ( PreprocessedShaderSource ) ;
ApplyBindlessModifications ( PreprocessedShaderSource ) ;
if ( bNeedToMoveToRootConstantBuffer )
{
2022-10-18 15:25:05 -04:00
return MoveShaderParametersToRootConstantBuffer ( CompilerInput , CompilerOutput , PreprocessedShaderSource ) ;
2022-07-15 12:25:44 -04:00
}
return true ;
}
2022-06-21 10:49:56 -04:00
void FShaderParameterParser : : ValidateShaderParameterType (
const FShaderCompilerInput & CompilerInput ,
const FString & ShaderBindingName ,
int32 ReflectionOffset ,
int32 ReflectionSize ,
bool bPlatformSupportsPrecisionModifier ,
FShaderCompilerOutput & CompilerOutput ) const
{
FString BindingName ( ShaderBindingName ) ;
const bool bBindlessHack = UE : : ShaderCompilerCommon : : RemoveBindlessParameterPrefix ( BindingName ) ;
const FShaderParameterParser : : FParsedShaderParameter & ParsedParameter = FindParameterInfos ( BindingName ) ;
check ( ParsedParameter . IsFound ( ) ) ;
check ( CompilerInput . RootParametersStructure ) ;
if ( ReflectionSize > 0 & & bMovedLoosedParametersToRootConstantBuffer )
{
// Verify the offset of the parameter coming from shader reflections honor the packoffset()
check ( ReflectionOffset = = ParsedParameter . ConstantBufferOffset ) ;
}
// Validate the shader type.
if ( ! bBindlessHack )
{
FString ExpectedShaderType ;
ParsedParameter . Member - > GenerateShaderParameterType ( ExpectedShaderType , bPlatformSupportsPrecisionModifier ) ;
const bool bShouldBeInt = ParsedParameter . Member - > GetBaseType ( ) = = UBMT_INT32 ;
const bool bShouldBeUint = ParsedParameter . Member - > GetBaseType ( ) = = UBMT_UINT32 ;
// Match parsed type with expected shader type
bool bIsTypeCorrect = ParsedParameter . ParsedType = = ExpectedShaderType ;
if ( ! bIsTypeCorrect )
{
2022-07-15 12:25:44 -04:00
auto CheckTypeCorrect = [ & ParsedParameter , & ExpectedShaderType ] ( int32 ParsedOffset , int32 ExpectedOffset ) - > bool
{
const FStringView Parsed = ParsedParameter . ParsedType . RightChop ( ParsedOffset ) ;
const FStringView Expected = FStringView ( ExpectedShaderType ) . RightChop ( ExpectedOffset ) ;
return Parsed . Compare ( Expected , ESearchCase : : CaseSensitive ) = = 0 ;
} ;
2022-06-21 10:49:56 -04:00
// Accept half-precision floats when single-precision was requested
if ( ParsedParameter . ParsedType . StartsWith ( TEXT ( " half " ) ) & & ParsedParameter . Member - > GetBaseType ( ) = = UBMT_FLOAT32 )
{
2022-07-15 12:25:44 -04:00
bIsTypeCorrect = CheckTypeCorrect ( 4 , 5 ) ;
2022-06-21 10:49:56 -04:00
}
// Accept single-precision floats when half-precision was expected
else if ( ParsedParameter . ParsedType . StartsWith ( TEXT ( " float " ) ) & & ExpectedShaderType . StartsWith ( TEXT ( " half " ) ) )
{
2022-07-15 12:25:44 -04:00
bIsTypeCorrect = CheckTypeCorrect ( 5 , 4 ) ;
2022-06-21 10:49:56 -04:00
}
// support for min16float
else if ( ParsedParameter . ParsedType . StartsWith ( TEXT ( " min16float " ) ) & & ExpectedShaderType . StartsWith ( TEXT ( " float " ) ) )
{
2022-07-15 12:25:44 -04:00
bIsTypeCorrect = CheckTypeCorrect ( 10 , 5 ) ;
2022-06-21 10:49:56 -04:00
}
else if ( ParsedParameter . ParsedType . StartsWith ( TEXT ( " min16float " ) ) & & ExpectedShaderType . StartsWith ( TEXT ( " half " ) ) )
{
2022-07-15 12:25:44 -04:00
bIsTypeCorrect = CheckTypeCorrect ( 10 , 4 ) ;
2022-06-21 10:49:56 -04:00
}
}
// Allow silent casting between signed and unsigned on shader bindings.
if ( ! bIsTypeCorrect & & ( bShouldBeInt | | bShouldBeUint ) )
{
FString NewExpectedShaderType ;
if ( bShouldBeInt )
{
// tries up with an uint.
NewExpectedShaderType = TEXT ( " u " ) + ExpectedShaderType ;
}
else
{
// tries up with an int.
NewExpectedShaderType = ExpectedShaderType ;
NewExpectedShaderType . RemoveAt ( 0 ) ;
}
bIsTypeCorrect = ParsedParameter . ParsedType = = NewExpectedShaderType ;
}
if ( ! bIsTypeCorrect )
{
FString CppCodeName = CompilerInput . RootParametersStructure - > GetFullMemberCodeName ( ParsedParameter . ConstantBufferOffset ) ;
FShaderCompilerError Error ;
Error . StrippedErrorMessage = FString : : Printf (
2022-07-15 12:25:44 -04:00
TEXT ( " Error: Type %.*s of shader parameter %s in shader mismatch the shader parameter structure: %s expects a %s " ) ,
ParsedParameter . ParsedType . Len ( ) , ParsedParameter . ParsedType . GetData ( ) ,
2022-06-21 10:49:56 -04:00
* ShaderBindingName ,
* CppCodeName ,
* ExpectedShaderType ) ;
GetParameterFileAndLine ( ParsedParameter , Error . ErrorVirtualFilePath , Error . ErrorLineString ) ;
CompilerOutput . Errors . Add ( Error ) ;
CompilerOutput . bSucceeded = false ;
AddNoteToDisplayShaderParameterMemberOnCppSide ( CompilerInput , ParsedParameter , CompilerOutput ) ;
}
}
// Validate parameter size, in case this is an array.
if ( ! bBindlessHack & & ReflectionSize > int32 ( ParsedParameter . Member - > GetMemberSize ( ) ) )
{
FString CppCodeName = CompilerInput . RootParametersStructure - > GetFullMemberCodeName ( ParsedParameter . ConstantBufferOffset ) ;
FShaderCompilerError Error ;
Error . StrippedErrorMessage = FString : : Printf (
TEXT ( " Error: The size required to bind shader parameter %s is %i bytes, smaller than %s that is %i bytes in the parameter structure. " ) ,
* ShaderBindingName ,
ReflectionSize ,
* CppCodeName ,
ParsedParameter . Member - > GetMemberSize ( ) ) ;
GetParameterFileAndLine ( ParsedParameter , Error . ErrorVirtualFilePath , Error . ErrorLineString ) ;
CompilerOutput . Errors . Add ( Error ) ;
CompilerOutput . bSucceeded = false ;
AddNoteToDisplayShaderParameterMemberOnCppSide ( CompilerInput , ParsedParameter , CompilerOutput ) ;
}
}
void FShaderParameterParser : : ValidateShaderParameterTypes (
const FShaderCompilerInput & CompilerInput ,
bool bPlatformSupportsPrecisionModifier ,
FShaderCompilerOutput & CompilerOutput ) const
{
// The shader doesn't have any parameter binding through shader structure, therefore don't do anything.
if ( ! CompilerInput . RootParametersStructure )
{
return ;
}
if ( ! CompilerOutput . bSucceeded )
{
return ;
}
const TMap < FString , FParameterAllocation > & ParametersFoundByCompiler = CompilerOutput . ParameterMap . GetParameterMap ( ) ;
2022-07-15 12:25:44 -04:00
IterateShaderParameterMembers (
* CompilerInput . RootParametersStructure ,
2022-06-21 10:49:56 -04:00
[ & ] ( const FShaderParametersMetadata & ParametersMetadata ,
const FShaderParametersMetadata : : FMember & Member ,
const TCHAR * ShaderBindingName ,
uint16 ByteOffset )
{
if (
Member . GetBaseType ( ) ! = UBMT_INT32 & &
Member . GetBaseType ( ) ! = UBMT_UINT32 & &
Member . GetBaseType ( ) ! = UBMT_FLOAT32 )
{
return ;
}
const FParsedShaderParameter & ParsedParameter = ParsedParameters [ ShaderBindingName ] ;
// Did not find shader parameter in code.
if ( ! ParsedParameter . IsFound ( ) )
{
// Verify the shader compiler also did not find this parameter to make sure there is no bug in the parser.
checkf (
! ParametersFoundByCompiler . Contains ( ShaderBindingName ) ,
TEXT ( " Looks like there is a bug in FShaderParameterParser ParameterName=%s DumpDebugInfoPath=%s " ) ,
ShaderBindingName ,
* CompilerInput . DumpDebugInfoPath ) ;
return ;
}
int32 BoundOffset = 0 ;
int32 BoundSize = 0 ;
if ( const FParameterAllocation * ParameterAllocation = ParametersFoundByCompiler . Find ( ShaderBindingName ) )
{
BoundOffset = ParameterAllocation - > BaseIndex ;
BoundSize = ParameterAllocation - > Size ;
}
ValidateShaderParameterType ( CompilerInput , ShaderBindingName , BoundOffset , BoundSize , bPlatformSupportsPrecisionModifier , CompilerOutput ) ;
} ) ;
}
void FShaderParameterParser : : ExtractFileAndLine ( int32 PragamLineoffset , int32 LineOffset , FString & OutFile , FString & OutLine ) const
{
if ( PragamLineoffset = = - 1 )
{
return ;
}
check ( FCString : : Strncmp ( ( * OriginalParsedShader ) + PragamLineoffset , TEXT ( " #line " ) , 6 ) = = 0 ) ;
const int32 ShaderSourceLen = OriginalParsedShader . Len ( ) ;
int32 StartFilePos = - 1 ;
int32 EndFilePos = - 1 ;
int32 StartLinePos = PragamLineoffset + 6 ;
int32 EndLinePos = - 1 ;
for ( int32 Cursor = StartLinePos ; Cursor < ShaderSourceLen ; Cursor + + )
{
const TCHAR Char = OriginalParsedShader [ Cursor ] ;
if ( Char = = ' \n ' )
{
break ;
}
if ( EndLinePos = = - 1 )
{
if ( Char > ' 9 ' | | Char < ' 0 ' )
{
EndLinePos = Cursor - 1 ;
}
}
else if ( StartFilePos = = - 1 )
{
if ( Char = = ' " ' )
{
StartFilePos = Cursor + 1 ;
}
}
else if ( EndFilePos = = - 1 )
{
if ( Char = = ' " ' )
{
EndFilePos = Cursor - 1 ;
break ;
}
}
}
check ( StartFilePos ! = - 1 ) ;
check ( EndFilePos ! = - 1 ) ;
check ( EndLinePos ! = - 1 ) ;
OutFile = OriginalParsedShader . Mid ( StartFilePos , EndFilePos - StartFilePos + 1 ) ;
FString LineBasis = OriginalParsedShader . Mid ( StartLinePos , EndLinePos - StartLinePos + 1 ) ;
int32 FinalLine = FCString : : Atoi ( * LineBasis ) + LineOffset ;
OutLine = FString : : FromInt ( FinalLine ) ;
}