2022-06-21 10:49:56 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ShaderParameterParser.h"
2023-04-19 10:32:12 -04:00
# include "Containers/UnrealString.h"
# include "ShaderCompilerCore.h"
# include "String/RemoveFrom.h"
2022-07-15 12:25:44 -04:00
# include "Misc/StringBuilder.h"
2023-10-26 23:07:19 -04:00
inline FStringView StripTemplateFromType ( const FStringView & Input )
{
FStringView UntemplatedType = FStringView ( Input ) ;
if ( int32 Index = Input . Find ( TEXT ( " < " ) ) ; Index ! = INDEX_NONE )
{
// Remove the template argument but don't forget to clean up the type name
const int32 NumChars = Input . Len ( ) - Index ;
UntemplatedType = Input . LeftChop ( NumChars ) . TrimEnd ( ) ;
}
return UntemplatedType ;
}
2022-07-15 12:25:44 -04:00
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
{
2024-04-05 13:22:22 -04:00
const bool bParametersAreExpanded = NumElements > 0 & & ( IsShaderParameterTypeRHIResource ( BaseType ) | | IsRDGResourceReferenceShaderParameterType ( BaseType ) ) ;
2022-07-15 12:25:44 -04:00
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 ;
2023-11-07 16:26:20 -05:00
FShaderParameterParser : : FShaderParameterParser ( const FPlatformConfiguration & InPlatformConfiguration )
: PlatformConfiguration ( InPlatformConfiguration )
2023-04-19 10:32:12 -04:00
{
2023-11-07 16:26:20 -05:00
}
2023-04-19 10:32:12 -04:00
2023-11-07 16:26:20 -05:00
static const FStringView s_AllSRVTypes [ ] =
{
TEXTVIEW ( " Texture1D " ) ,
TEXTVIEW ( " Texture1DArray " ) ,
TEXTVIEW ( " Texture2D " ) ,
TEXTVIEW ( " Texture2DArray " ) ,
TEXTVIEW ( " Texture2DMS " ) ,
TEXTVIEW ( " Texture2DMSArray " ) ,
TEXTVIEW ( " Texture3D " ) ,
TEXTVIEW ( " TextureCube " ) ,
TEXTVIEW ( " TextureCubeArray " ) ,
TEXTVIEW ( " Buffer " ) ,
TEXTVIEW ( " ByteAddressBuffer " ) ,
TEXTVIEW ( " StructuredBuffer " ) ,
TEXTVIEW ( " RaytracingAccelerationStructure " ) ,
2023-04-19 10:32:12 -04:00
} ;
2023-11-07 16:26:20 -05:00
static const FStringView s_AllUAVTypes [ ] =
2023-04-19 10:32:12 -04:00
{
2023-11-07 16:26:20 -05:00
TEXTVIEW ( " AppendStructuredBuffer " ) ,
TEXTVIEW ( " RWBuffer " ) ,
TEXTVIEW ( " RWByteAddressBuffer " ) ,
TEXTVIEW ( " RWStructuredBuffer " ) ,
TEXTVIEW ( " RWTexture1D " ) ,
TEXTVIEW ( " RWTexture1DArray " ) ,
TEXTVIEW ( " RWTexture2D " ) ,
TEXTVIEW ( " RWTexture2DArray " ) ,
TEXTVIEW ( " RWTexture3D " ) ,
TEXTVIEW ( " RasterizerOrderedTexture2D " ) ,
2023-04-19 10:32:12 -04:00
} ;
2023-11-07 16:26:20 -05:00
static const FStringView s_AllSamplerTypes [ ] =
2023-04-19 10:32:12 -04:00
{
2023-11-07 16:26:20 -05:00
TEXTVIEW ( " SamplerState " ) ,
TEXTVIEW ( " SamplerComparisonState " ) ,
2023-04-19 10:32:12 -04:00
} ;
2023-11-07 16:26:20 -05:00
EShaderParameterType FShaderParameterParser : : ParseParameterType ( FStringView InType )
2023-04-19 10:32:12 -04:00
{
2023-11-07 16:26:20 -05:00
TConstArrayView < FStringView > AllSamplerTypes ( s_AllSamplerTypes ) ;
TConstArrayView < FStringView > AllSRVTypes ( s_AllSRVTypes ) ;
TConstArrayView < FStringView > AllUAVTypes ( s_AllUAVTypes ) ;
2023-04-19 10:32:12 -04:00
if ( AllSamplerTypes . Contains ( InType ) )
{
return EShaderParameterType : : Sampler ;
}
2023-08-25 22:19:10 -04:00
FStringView UntemplatedType = StripTemplateFromType ( InType ) ;
2023-04-19 10:32:12 -04:00
2023-11-07 16:26:20 -05:00
if ( AllSRVTypes . Contains ( UntemplatedType ) | | PlatformConfiguration . ExtraSRVTypes . Contains ( UntemplatedType ) )
2023-04-19 10:32:12 -04:00
{
return EShaderParameterType : : SRV ;
}
2023-11-07 16:26:20 -05:00
if ( AllUAVTypes . Contains ( UntemplatedType ) | | PlatformConfiguration . ExtraUAVTypes . Contains ( UntemplatedType ) )
2023-04-19 10:32:12 -04:00
{
return EShaderParameterType : : UAV ;
}
return EShaderParameterType : : LooseData ;
}
EShaderParameterType FShaderParameterParser : : ParseAndRemoveBindlessParameterPrefix ( FStringView & InName )
{
const FStringView OriginalName = InName ;
2023-10-20 23:12:44 -04:00
if ( InName = UE : : String : : RemoveFromStart ( InName , FStringView ( kBindlessSRVPrefix ) ) ; InName ! = OriginalName )
2023-04-19 10:32:12 -04:00
{
2023-10-20 23:12:44 -04:00
return EShaderParameterType : : BindlessSRV ;
}
if ( InName = UE : : String : : RemoveFromStart ( InName , FStringView ( kBindlessUAVPrefix ) ) ; InName ! = OriginalName )
{
return EShaderParameterType : : BindlessUAV ;
2023-04-19 10:32:12 -04:00
}
if ( InName = UE : : String : : RemoveFromStart ( InName , FStringView ( kBindlessSamplerPrefix ) ) ; InName ! = OriginalName )
{
2023-10-20 23:12:44 -04:00
return EShaderParameterType : : BindlessSampler ;
2023-04-19 10:32:12 -04:00
}
return EShaderParameterType : : LooseData ;
}
EShaderParameterType FShaderParameterParser : : ParseAndRemoveBindlessParameterPrefix ( FString & InName )
{
FStringView Name ( InName ) ;
const EShaderParameterType ParameterType = ParseAndRemoveBindlessParameterPrefix ( Name ) ;
InName = FString ( Name ) ;
return ParameterType ;
}
bool FShaderParameterParser : : RemoveBindlessParameterPrefix ( FString & InName )
{
2023-10-20 23:12:44 -04:00
return InName . RemoveFromStart ( kBindlessSRVPrefix )
| | InName . RemoveFromStart ( kBindlessUAVPrefix )
2023-04-19 10:32:12 -04:00
| | InName . RemoveFromStart ( kBindlessSamplerPrefix ) ;
}
2023-10-20 23:12:44 -04:00
FStringView FShaderParameterParser : : GetBindlessParameterPrefix ( EShaderParameterType InShaderParameterType )
{
switch ( InShaderParameterType )
{
case EShaderParameterType : : BindlessSampler : return kBindlessSamplerPrefix ;
case EShaderParameterType : : BindlessSRV : return kBindlessSRVPrefix ;
case EShaderParameterType : : BindlessUAV : return kBindlessUAVPrefix ;
}
return FStringView ( ) ;
}
2023-04-19 10:32:12 -04:00
2022-07-15 12:25:44 -04:00
bool FShaderParameterParser : : ParseParameters (
2023-04-21 13:45:43 -04:00
const FShaderParametersMetadata * RootParametersStructure ,
TArray < FShaderCompilerError > & OutErrors )
2022-06-21 10:49:56 -04:00
{
2022-07-15 12:25:44 -04:00
const FStringView ShaderSource ( OriginalParsedShader ) ;
2023-04-21 13:45:43 -04:00
if ( RootParametersStructure )
2022-06-21 10:49:56 -04:00
{
2022-07-15 12:25:44 -04:00
// Reserves the number of parameters up front.
2023-04-21 13:45:43 -04:00
ParsedParameters . Reserve ( RootParametersStructure - > GetSize ( ) / sizeof ( int32 ) ) ;
2022-07-15 12:25:44 -04:00
IterateShaderParameterMembers (
2023-04-21 13:45:43 -04:00
* RootParametersStructure ,
2022-07-15 12:25:44 -04:00
[ & ] ( const FShaderParametersMetadata & ParametersMetadata ,
const FShaderParametersMetadata : : FMember & Member ,
const TCHAR * ShaderBindingName ,
uint16 ByteOffset )
{
FParsedShaderParameter ParsedParameter ;
2023-04-18 10:06:12 -04:00
ParsedParameter . bIsBindable = true ;
2022-07-15 12:25:44 -04:00
ParsedParameter . ConstantBufferOffset = ByteOffset ;
2023-04-18 10:06:12 -04:00
ParsedParameter . BaseType = Member . GetBaseType ( ) ;
ParsedParameter . PrecisionModifier = Member . GetPrecision ( ) ;
ParsedParameter . NumRows = Member . GetNumRows ( ) ;
ParsedParameter . NumColumns = Member . GetNumColumns ( ) ;
ParsedParameter . MemberSize = Member . IsVariableNativeType ( ) ? Member . GetMemberSize ( ) : 0u ;
2022-07-15 12:25:44 -04:00
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
2023-04-21 13:45:43 -04:00
int32 CurrentPragmaLineOffset = - 1 ;
int32 CurrentLineOffset = 0 ;
2022-06-21 10:49:56 -04:00
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 ;
2023-10-03 19:32:18 -04:00
bool bGoToCommentClose = false ;
2022-06-21 10:49:56 -04:00
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 ;
2023-04-21 13:45:43 -04:00
ExtractFileAndLine ( CurrentPragmaLineOffset , CurrentLineOffset , Error . ErrorVirtualFilePath , Error . ErrorLineString ) ;
OutErrors . Add ( Error ) ;
2022-06-21 10:49:56 -04:00
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 ) ;
2023-04-19 10:32:12 -04:00
EShaderParameterType ParsedParameterType = ParseAndRemoveBindlessParameterPrefix ( Name ) ;
2023-10-20 23:12:44 -04:00
const bool bBindlessIndex = ( ParsedParameterType ! = EShaderParameterType : : LooseData ) ;
2022-07-15 12:25:44 -04:00
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 )
{
2023-11-07 16:26:20 -05:00
ParsedParameterType = ParseParameterType ( Type ) ;
2022-07-15 12:25:44 -04:00
2023-10-20 23:12:44 -04:00
if ( bBindlessResources & & ParsedParameterType = = EShaderParameterType : : SRV )
2022-07-15 12:25:44 -04:00
{
2023-10-20 23:12:44 -04:00
BindlessConversionType = EBindlessConversionType : : SRV ;
ParsedConstantBufferType = EShaderParameterType : : BindlessSRV ;
}
else if ( bBindlessResources & & ParsedParameterType = = EShaderParameterType : : UAV )
{
BindlessConversionType = EBindlessConversionType : : UAV ;
ParsedConstantBufferType = EShaderParameterType : : BindlessUAV ;
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 ;
2023-10-20 23:12:44 -04:00
ParsedConstantBufferType = EShaderParameterType : : BindlessSampler ;
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
2023-08-25 22:19:10 -04:00
FStringView StrippedTypeStringView = StripTemplateFromType ( Type ) ;
FString StrippedTypeString ( StrippedTypeStringView ) ;
EShaderCodeResourceBindingType TypeDecl = ParseShaderResourceBindingType ( * StrippedTypeString ) ;
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.
2023-04-18 10:06:12 -04:00
if ( bNeedToMoveToRootConstantBuffer & & ParsedParameter . bIsBindable )
2022-06-21 10:49:56 -04:00
{
2023-04-18 10:06:12 -04:00
const EUniformBufferBaseType BaseType = ParsedParameter . BaseType ;
2022-07-15 12:25:44 -04:00
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 ;
2023-04-25 11:39:20 -04:00
ParsedParameter . ParsedPragmaLineOffset = CurrentPragmaLineOffset ;
2023-04-21 13:45:43 -04:00
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 ;
2023-08-25 22:19:10 -04:00
ParsedParameter . ParsedTypeDecl = TypeDecl ;
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 ;
2023-04-21 13:45:43 -04:00
CurrentLineOffset + = Char = = ' \n ' ;
2022-06-21 10:49:56 -04:00
// Go to the next line if this is a preprocessor macro.
if ( bGoToNextLine )
{
if ( Char = = ' \n ' )
{
bGoToNextLine = false ;
}
continue ;
}
2023-10-03 19:32:18 -04:00
else if ( bGoToCommentClose )
{
if ( Char = = ' * ' & & UpComing [ 1 ] = = ' / ' )
{
Cursor + + ;
bGoToCommentClose = false ;
}
continue ;
}
2022-06-21 10:49:56 -04:00
else if ( Char = = ' # ' )
{
if ( RemainingSize > 6 & & FCString : : Strncmp ( UpComing , TEXT ( " #line " ) , 6 ) = = 0 )
{
2023-04-21 13:45:43 -04:00
CurrentPragmaLineOffset = Cursor ;
CurrentLineOffset = - 1 ; // that will be incremented to 0 when reaching the \n at the end of the #line
2022-06-21 10:49:56 -04:00
}
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 " ) ,
2023-06-14 17:00:19 -04:00
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.
}
2023-10-03 19:32:18 -04:00
else if ( Char = = ' / ' )
{
if ( UpComing [ 1 ] = = ' / ' )
{
bGoToNextLine = true ;
continue ;
}
if ( UpComing [ 1 ] = = ' * ' )
{
bGoToCommentClose = true ;
continue ;
}
}
2022-06-21 10:49:56 -04:00
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.
}
2023-09-06 13:10:25 -04:00
else if ( Char = = ' : ' )
{
// Handle :: in type names
if ( UpComing [ 1 ] = = ' : ' )
{
// next loop iteration takes us to the next ':', so go past that
Cursor + + ;
}
}
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.
}
2023-09-06 13:10:25 -04:00
else if ( Char = = ' : ' )
{
// Handle :: in type names
if ( UpComing [ 1 ] = = ' : ' )
{
// next loop iteration takes us to the next ':', so go past that
Cursor + + ;
}
}
2022-07-15 12:25:44 -04:00
else if ( bIsWhiteSpace | | Char = = ' , ' )
{
2023-09-06 13:10:25 -04:00
// Spaces and comas are legal within argument of the template arguments.
2022-07-15 12:25:44 -04:00
}
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-10-20 23:12:44 -04:00
static FStringView GetBindlessParameterPrefix ( EBindlessConversionType InConversionType )
{
switch ( InConversionType )
{
case EBindlessConversionType : : SRV : return FShaderParameterParser : : kBindlessSRVPrefix ;
case EBindlessConversionType : : UAV : return FShaderParameterParser : : kBindlessUAVPrefix ;
case EBindlessConversionType : : Sampler : return FShaderParameterParser : : kBindlessSamplerPrefix ;
}
return FStringView ( ) ;
}
static FStringView GetBindlessArrayHeapPrefix ( EBindlessConversionType InConversionType )
{
switch ( InConversionType )
{
case EBindlessConversionType : : SRV : return FShaderParameterParser : : kBindlessSRVArrayPrefix ;
case EBindlessConversionType : : UAV : return FShaderParameterParser : : kBindlessUAVArrayPrefix ;
case EBindlessConversionType : : Sampler : return FShaderParameterParser : : kBindlessSamplerArrayPrefix ;
}
return FStringView ( ) ;
}
2023-01-11 13:03:47 -05:00
FString FShaderParameterParser : : GenerateBindlessParameterDeclaration ( const FParsedShaderParameter & ParsedParameter ) const
{
const bool bIsSampler = ( ParsedParameter . BindlessConversionType = = EBindlessConversionType : : Sampler ) ;
const FStringView Name = ParsedParameter . ParsedName ;
const FStringView Type = ParsedParameter . ParsedType ;
2023-07-28 11:11:42 -04:00
const TCHAR * StorageClass = ParsedParameter . bGloballyCoherent ? TEXT ( " globallycoherent " ) : TEXT ( " " ) ;
2023-10-20 23:12:44 -04:00
const FStringView IndexPrefix = : : GetBindlessParameterPrefix ( ParsedParameter . BindlessConversionType ) ;
2023-01-11 13:03:47 -05:00
2023-11-07 16:26:20 -05:00
TStringBuilder < 64 > IndexString ;
IndexString < < IndexPrefix < < Name ;
2023-01-11 13:03:47 -05:00
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;`
2023-11-07 16:26:20 -05:00
Result < < TEXT ( " uint " ) < < IndexString < < TEXT ( " ; " ) ;
2023-07-28 11:11:42 -04:00
}
2023-11-07 16:26:20 -05:00
// Add the typedef to keep return types shortened
// `typedef Type SafeType##Name;`
2023-07-28 11:11:42 -04:00
2023-11-07 16:26:20 -05:00
TStringBuilder < 64 > TypedefName ;
TypedefName < < TEXT ( " SafeType " ) < < Name ;
2023-01-11 13:03:47 -05:00
2023-11-07 16:26:20 -05:00
Result < < TEXT ( " typedef " ) < < Type < < TEXT ( " " ) < < TypedefName < < TEXT ( " ; " ) ;
2023-01-11 13:03:47 -05:00
2023-11-07 16:26:20 -05:00
// Full type to use for return types. Makes sure globallycoherent is used where needed. Should be using the typedef name.
TStringBuilder < 64 > FullType ;
FullType < < StorageClass < < TypedefName ;
2024-03-20 14:41:02 -04:00
TStringBuilder < 64 > HeapName ;
2023-11-07 16:26:20 -05:00
if ( EnumHasAnyFlags ( PlatformConfiguration . Flags , EShaderParameterParserConfigurationFlags : : BindlessUsesArrays ) )
2023-07-28 11:11:42 -04:00
{
2023-10-20 23:12:44 -04:00
const FStringView HeapPrefix = GetBindlessArrayHeapPrefix ( ParsedParameter . BindlessConversionType ) ;
2023-07-28 11:11:42 -04:00
2024-03-20 14:41:02 -04:00
HeapName < < HeapPrefix < < TypedefName ;
2023-07-28 11:11:42 -04:00
// Declare a heap for the RewriteType
// e.g. `SafeType##Name ResourceDescriptorHeap_SafeType##Name[];`
2024-03-20 14:41:02 -04:00
Result < < FullType < < TEXT ( " " ) < < HeapName < < TEXT ( " []; " ) ;
2023-07-28 11:11:42 -04:00
// :todo-jn: specify the descriptor set and binding directly in source instead of patching SPIRV
}
2023-11-07 16:26:20 -05:00
2024-03-20 14:41:02 -04:00
const FString BindlessAccess = PlatformConfiguration . GenerateBindlessAccess ( ParsedParameter . BindlessConversionType , FullType , HeapName , IndexString ) ;
const TCHAR * FunctionPrefix = bIsSampler ? TEXT ( " GetBindlessSampler " ) : TEXT ( " GetBindlessResource " ) ;
2023-07-28 11:11:42 -04:00
2024-03-20 14:41:02 -04:00
// 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 < < FullType < < TEXT ( " " ) < < FunctionPrefix < < Name < < TEXT ( " () { return " ) < < BindlessAccess < < TEXT ( " ; } " ) ;
Result < < TEXT ( " static const " ) < < FullType < < TEXT ( " " ) < < Name < < TEXT ( " = " ) < < FunctionPrefix < < Name < < TEXT ( " (); " ) ;
2023-01-11 13:03:47 -05:00
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 ( ) ) ;
2024-06-25 13:22:29 -04:00
const bool bReplaceGlobals = EnumHasAnyFlags ( PlatformConfiguration . Flags , EShaderParameterParserConfigurationFlags : : ReplaceGlobals ) ;
2022-07-15 12:25:44 -04:00
for ( TPair < FString , FParsedShaderParameter > & Itr : ParsedParameters )
{
2024-06-25 13:22:29 -04:00
const FParsedShaderParameter & ParsedParameter = Itr . Value ;
2022-07-15 12:25:44 -04:00
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 ) ;
}
2024-06-25 13:22:29 -04:00
else if ( bReplaceGlobals )
{
const bool IsGlobalParam =
( ParsedParameter . BaseType = = UBMT_INVALID ) & &
! ParsedParameter . ParsedName . StartsWith ( FShaderParameterParser : : kBindlessSamplerArrayPrefix ) & &
! ParsedParameter . ParsedName . StartsWith ( FShaderParameterParser : : kBindlessSRVArrayPrefix ) & &
! ParsedParameter . ParsedName . StartsWith ( FShaderParameterParser : : kBindlessUAVArrayPrefix ) ;
if ( IsGlobalParam )
{
FShaderCodeModifications Modif ;
Modif . CharOffsetStart = ParsedParameter . ParsedCharOffsetStart ;
Modif . CharOffsetEnd = ParsedParameter . ParsedCharOffsetEnd + 1 ;
const int32 NumChars = Modif . CharOffsetEnd - Modif . CharOffsetStart ;
Modif . Replace = PlatformConfiguration . ReplaceGlobal ( FStringView ( & OriginalParsedShader [ Modif . CharOffsetStart ] , NumChars ) , ParsedParameter . ParsedName ) ;
Modifications . Add ( Modif ) ;
}
}
2022-07-15 12:25:44 -04:00
}
// 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 ;
2023-10-07 11:24:14 -04:00
bModifiedShader = true ;
2022-07-15 12:25:44 -04:00
}
}
}
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 (
2023-04-21 13:45:43 -04:00
const FShaderParametersMetadata * RootParametersStructure ,
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.
2023-04-21 13:45:43 -04:00
if ( RootParametersStructure & & bNeedToMoveToRootConstantBuffer )
2022-06-21 10:49:56 -04:00
{
2023-11-07 16:26:20 -05:00
FStringBuilderBase ConstantBufferCode ;
ConstantBufferCode < < PlatformConfiguration . ConstantBufferType < < TEXT ( " " ) < < FShaderParametersMetadata : : kRootUniformBufferBindingName < < TEXT ( " \n { \n " ) ;
2022-06-21 10:49:56 -04:00
2022-07-15 12:25:44 -04:00
IterateShaderParameterMembers (
2023-04-21 13:45:43 -04:00
* 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()
2023-10-20 23:12:44 -04:00
if ( IsParameterBindless ( ParsedParameter - > ConstantBufferParameterType ) )
2022-07-15 12:25:44 -04:00
{
2023-10-20 23:12:44 -04:00
const FStringView Prefix = GetBindlessParameterPrefix ( ParsedParameter - > ConstantBufferParameterType ) ;
2023-11-07 16:26:20 -05:00
ConstantBufferCode . Appendf (
2023-10-20 23:12:44 -04:00
TEXT ( " uint %.*s%.*s : packoffset(c%d%s); \n " ) ,
SVARG ( Prefix ) ,
2022-10-07 13:35:29 -04:00
SVARG ( ParsedParameter - > ParsedName ) ,
2022-07-15 12:25:44 -04:00
ConstantRegister ,
2023-11-07 16:26:20 -05:00
ConstantSwizzle
) ;
2022-07-15 12:25:44 -04:00
}
else if ( ParsedParameter - > ConstantBufferParameterType = = EShaderParameterType : : LooseData )
{
2022-06-21 10:49:56 -04:00
if ( ! ParsedParameter - > ParsedArraySize . IsEmpty ( ) )
{
2023-11-07 16:26:20 -05:00
ConstantBufferCode . Appendf (
2023-10-13 08:53:55 -04:00
TEXT ( " %.*s %s[%.*s] : packoffset(c%d%s); \n " ) ,
2022-07-15 12:25:44 -04:00
SVARG ( ParsedParameter - > ParsedType ) ,
ShaderBindingName ,
SVARG ( ParsedParameter - > ParsedArraySize ) ,
2022-06-21 10:49:56 -04:00
ConstantRegister ,
2023-11-07 16:26:20 -05:00
ConstantSwizzle
) ;
2022-06-21 10:49:56 -04:00
}
else
{
2023-11-07 16:26:20 -05:00
ConstantBufferCode . Appendf (
2023-10-13 08:53:55 -04:00
TEXT ( " %.*s %s : packoffset(c%d%s); \n " ) ,
2022-07-15 12:25:44 -04:00
SVARG ( ParsedParameter - > ParsedType ) ,
ShaderBindingName ,
2022-06-21 10:49:56 -04:00
ConstantRegister ,
2023-11-07 16:26:20 -05:00
ConstantSwizzle
) ;
2022-06-21 10:49:56 -04:00
}
}
2022-07-15 12:25:44 -04:00
# undef SVARG
2022-06-21 10:49:56 -04:00
}
} ) ;
2023-11-07 16:26:20 -05:00
ConstantBufferCode < < TEXT ( " } \n \n " ) ;
2022-06-21 10:49:56 -04:00
FString NewShaderCode = (
2023-11-07 16:26:20 -05:00
MakeInjectedShaderCodeBlock ( TEXT ( " MoveShaderParametersToRootConstantBuffer " ) , * ConstantBufferCode ) +
2022-06-21 10:49:56 -04:00
PreprocessedShaderSource ) ;
PreprocessedShaderSource = MoveTemp ( NewShaderCode ) ;
bMovedLoosedParametersToRootConstantBuffer = true ;
2023-10-07 11:24:14 -04:00
bModifiedShader = true ;
2022-07-15 12:25:44 -04:00
} // if (CompilerInput.RootParametersStructure && bNeedToMoveToRootConstantBuffer)
2022-06-21 10:49:56 -04:00
return bSuccess ;
}
2023-11-07 16:26:20 -05:00
bool FShaderParameterParser : : ParseAndModify ( const FShaderCompilerInput & CompilerInput , TArray < FShaderCompilerError > & OutErrors , FString & PreprocessedShaderSource )
2022-07-15 12:25:44 -04:00
{
bBindlessResources = CompilerInput . Environment . CompilerFlags . Contains ( CFLAG_BindlessResources ) ;
bBindlessSamplers = CompilerInput . Environment . CompilerFlags . Contains ( CFLAG_BindlessSamplers ) ;
2023-04-21 13:45:43 -04:00
2023-11-07 16:26:20 -05:00
const bool bUseStableConstantBuffer = EnumHasAnyFlags ( PlatformConfiguration . Flags , EShaderParameterParserConfigurationFlags : : UseStableConstantBuffer ) ;
const bool bSupportsBindless = EnumHasAnyFlags ( PlatformConfiguration . Flags , EShaderParameterParserConfigurationFlags : : SupportsBindless ) ;
2024-05-22 04:15:09 -04:00
const bool bAlwaysParseParams = EnumHasAnyFlags ( PlatformConfiguration . Flags , EShaderParameterParserConfigurationFlags : : AlwaysParseParams ) ;
2022-10-18 15:25:05 -04:00
const bool bHasRootParameters = ( CompilerInput . RootParametersStructure ! = nullptr ) ;
2023-11-07 16:26:20 -05:00
const bool bRootParametersModification = bUseStableConstantBuffer & & ( CompilerInput . IsRayTracingShader ( ) | | CompilerInput . ShouldUseStableConstantBuffer ( ) ) ;
const bool bBindlessModifications = bSupportsBindless & & ( bBindlessResources | | bBindlessSamplers ) ;
const bool bShouldModify = bRootParametersModification | | bBindlessModifications ;
// Always parse if we have root parameters since we need that data during reflection validation
2024-05-22 04:15:09 -04:00
const bool bShouldParse = bHasRootParameters | | bShouldModify | | bAlwaysParseParams ;
2022-07-15 12:25:44 -04:00
// The shader doesn't have any parameter binding through shader structure, therefore don't do anything.
2023-11-07 16:26:20 -05:00
if ( ! bShouldParse )
2022-07-15 12:25:44 -04:00
{
return true ;
}
2023-11-07 16:26:20 -05:00
bNeedToMoveToRootConstantBuffer = bRootParametersModification ;
2022-07-15 12:25:44 -04:00
OriginalParsedShader = PreprocessedShaderSource ;
2023-04-21 13:45:43 -04:00
if ( ! ParseParameters ( CompilerInput . RootParametersStructure , OutErrors ) )
2022-07-15 12:25:44 -04:00
{
return false ;
}
2023-10-07 11:24:14 -04:00
bool bResult = true ;
2023-11-07 16:26:20 -05:00
if ( bShouldModify )
2022-07-15 12:25:44 -04:00
{
2023-11-07 16:26:20 -05:00
RemoveMovingParametersFromSource ( PreprocessedShaderSource ) ;
if ( bSupportsBindless )
{
ApplyBindlessModifications ( PreprocessedShaderSource ) ;
}
if ( bNeedToMoveToRootConstantBuffer )
{
bResult = MoveShaderParametersToRootConstantBuffer ( CompilerInput . RootParametersStructure , PreprocessedShaderSource ) ;
}
2023-10-07 11:24:14 -04:00
# if DO_GUARD_SLOW
2023-11-07 16:26:20 -05:00
if ( bResult )
2023-10-07 11:24:14 -04:00
{
2023-11-07 16:26:20 -05:00
if ( DidModifyShader ( ) )
{
checkSlow ( PreprocessedShaderSource ! = OriginalParsedShader ) ;
}
else
{
checkSlow ( PreprocessedShaderSource = = OriginalParsedShader ) ;
}
2023-10-07 11:24:14 -04:00
}
# endif
2023-11-07 16:26:20 -05:00
}
return bResult ;
2022-07-15 12:25:44 -04:00
}
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 ) ;
2023-04-19 10:32:12 -04:00
const bool bBindlessHack = RemoveBindlessParameterPrefix ( BindingName ) ;
2022-06-21 10:49:56 -04:00
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 ;
2023-04-18 10:06:12 -04:00
FShaderParametersMetadata : : FMember : : GenerateShaderParameterType (
ExpectedShaderType ,
bPlatformSupportsPrecisionModifier ,
ParsedParameter . BaseType ,
ParsedParameter . PrecisionModifier ,
ParsedParameter . NumRows ,
ParsedParameter . NumColumns ) ;
2022-06-21 10:49:56 -04:00
2023-04-18 10:06:12 -04:00
const bool bShouldBeInt = ParsedParameter . BaseType = = UBMT_INT32 ;
const bool bShouldBeUint = ParsedParameter . BaseType = = UBMT_UINT32 ;
2022-06-21 10:49:56 -04:00
// 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
2023-04-18 10:06:12 -04:00
if ( ParsedParameter . ParsedType . StartsWith ( TEXT ( " half " ) ) & & ParsedParameter . BaseType = = UBMT_FLOAT32 )
2022-06-21 10:49:56 -04:00
{
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.
2023-04-18 10:06:12 -04:00
if ( ! bBindlessHack & & ReflectionSize > int32 ( ParsedParameter . MemberSize ) )
2022-06-21 10:49:56 -04:00
{
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 ,
2023-04-18 10:06:12 -04:00
ParsedParameter . MemberSize ) ;
2022-06-21 10:49:56 -04:00
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 ) ;
} ) ;
}
2023-04-25 11:39:20 -04:00
void FShaderParameterParser : : ExtractFileAndLine ( int32 PragmaLineOffset , int32 LineOffset , FString & OutFile , FString & OutLine ) const
2022-06-21 10:49:56 -04:00
{
2023-04-25 11:39:20 -04:00
if ( PragmaLineOffset = = - 1 )
2022-06-21 10:49:56 -04:00
{
return ;
}
2023-04-25 11:39:20 -04:00
check ( FCString : : Strncmp ( ( * OriginalParsedShader ) + PragmaLineOffset , TEXT ( " #line " ) , 5 ) = = 0 ) ;
2022-06-21 10:49:56 -04:00
const int32 ShaderSourceLen = OriginalParsedShader . Len ( ) ;
int32 StartFilePos = - 1 ;
int32 EndFilePos = - 1 ;
2023-04-25 11:39:20 -04:00
int32 StartLinePos = - 1 ;
2022-06-21 10:49:56 -04:00
int32 EndLinePos = - 1 ;
2023-04-25 11:39:20 -04:00
for ( int32 Cursor = PragmaLineOffset + 5 ; Cursor < ShaderSourceLen ; Cursor + + )
2022-06-21 10:49:56 -04:00
{
const TCHAR Char = OriginalParsedShader [ Cursor ] ;
if ( Char = = ' \n ' )
{
break ;
}
2023-04-25 11:39:20 -04:00
else if ( StartLinePos = = - 1 & & FChar : : IsDigit ( Char ) )
2022-06-21 10:49:56 -04:00
{
2023-04-25 11:39:20 -04:00
StartLinePos = Cursor ;
2022-06-21 10:49:56 -04:00
}
2023-04-25 11:39:20 -04:00
else if ( StartLinePos ! = - 1 & & EndLinePos = = - 1 & & ! FChar : : IsDigit ( Char ) )
2022-06-21 10:49:56 -04:00
{
2023-04-25 11:39:20 -04:00
EndLinePos = Cursor - 1 ;
2022-06-21 10:49:56 -04:00
}
2023-04-25 11:39:20 -04:00
else if ( StartFilePos = = - 1 & & Char = = TEXT ( ' " ' ) )
2022-06-21 10:49:56 -04:00
{
2023-04-25 11:39:20 -04:00
StartFilePos = Cursor + 1 ;
}
else if ( StartFilePos ! = - 1 & & EndFilePos = = - 1 & & Char = = TEXT ( ' " ' ) )
{
EndFilePos = Cursor - 1 ;
break ;
2022-06-21 10:49:56 -04:00
}
}
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 ) ;
}