// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "ShaderCore.h" #include "ShaderCompilerCore.h" #include "CrossCompilerDefinitions.h" #include "ShaderConductorContext.h" namespace UE::ShaderCompilerCommon { static constexpr const TCHAR* kUniformBufferConstantBufferPrefix = TEXT("UniformBufferConstants_"); static constexpr const TCHAR* kBindlessResourcePrefix = TEXT("BindlessResource_"); static constexpr const TCHAR* kBindlessSamplerPrefix = TEXT("BindlessSampler_"); } /** * This function looks for resources specified in ResourceTableMap in the * parameter map, adds them to the resource table, and removes them from the * parameter map. If a resource is used from a currently unmapped uniform * buffer we allocate a slot for it from UsedUniformBufferSlots. * Returns false if there's any internal error. */ extern SHADERCOMPILERCOMMON_API bool BuildResourceTableMapping( const TMap& ResourceTableMap, const TMap& UniformBufferMap, TBitArray<>& UsedUniformBufferSlots, FShaderParameterMap& ParameterMap, FShaderCompilerResourceTable& OutSRT ); /** Culls global uniform buffer entries from the parameter map. */ extern SHADERCOMPILERCOMMON_API void CullGlobalUniformBuffers(const TMap& UniformBufferMap, FShaderParameterMap& ParameterMap); /** * Builds a token stream out of the resource map. The resource map is one * of the arrays generated by BuildResourceTableMapping. The token stream * is used at runtime to gather resources from tables and bind them to the * appropriate slots. */ extern SHADERCOMPILERCOMMON_API void BuildResourceTableTokenStream( const TArray& InResourceMap, int32 MaxBoundResourceTable, TArray& OutTokenStream, bool bGenerateEmptyTokenStreamIfNoResources = false ); // Finds the number of used uniform buffers in a resource map extern SHADERCOMPILERCOMMON_API int16 GetNumUniformBuffersUsed(const FShaderCompilerResourceTable& InSRT); /** Validates and moves all the shader loose data parameter defined in the root scope of the shader into the root uniform buffer. */ class SHADERCOMPILERCOMMON_API FShaderParameterParser { public: struct FParsedShaderParameter { public: /** Original information about the member. */ const FShaderParametersMetadata::FMember* Member = nullptr; /** Information found about the member when parsing the preprocessed code. */ FString ParsedName; FString ParsedType; FString ParsedArraySize; /** Offset the member should be in the constant buffer. */ int32 ConstantBufferOffset = 0; /** Records if the parameter was fully moved. */ bool bOriginalParameterErased{}; /* Returns whether the shader parameter has been found when parsing. */ bool IsFound() const { return !ParsedType.IsEmpty(); } /** Returns whether the shader parameter is bindable to the shader parameter structure. */ bool IsBindable() const { return Member != nullptr; } private: int32 ParsedPragmaLineoffset = 0; int32 ParsedLineOffset = 0; friend class FShaderParameterParser; }; /** Parses the preprocessed shader code and move the parameters into root constant buffer */ bool ParseAndMoveShaderParametersToRootConstantBuffer( const FShaderCompilerInput& CompilerInput, FShaderCompilerOutput& CompilerOutput, FString& PreprocessedShaderSource, const TCHAR* ConstantBufferType); /** Gets parsing information from a parameter binding name. */ const FParsedShaderParameter& FindParameterInfos(const FString& ParameterName) const { return ParsedParameters.FindChecked(ParameterName); } /** Validates the shader parameter in code is compatible with the shader parameter structure. */ void ValidateShaderParameterType( const FShaderCompilerInput& CompilerInput, const FString& ShaderBindingName, int32 ReflectionOffset, int32 ReflectionSize, bool bPlatformSupportsPrecisionModifier, FShaderCompilerOutput& CompilerOutput) const; void ValidateShaderParameterType( const FShaderCompilerInput& CompilerInput, const FString& ShaderBindingName, int32 ReflectionOffset, int32 ReflectionSize, FShaderCompilerOutput& CompilerOutput) const { ValidateShaderParameterType(CompilerInput, ShaderBindingName, ReflectionOffset, ReflectionSize, false, CompilerOutput); } /** Validates shader parameter map is compatible with the shader parameter structure. */ void ValidateShaderParameterTypes( const FShaderCompilerInput& CompilerInput, bool bPlatformSupportsPrecisionModifier, FShaderCompilerOutput& CompilerOutput) const; void ValidateShaderParameterTypes( const FShaderCompilerInput& CompilerInput, FShaderCompilerOutput& CompilerOutput) const { ValidateShaderParameterTypes(CompilerInput, false, CompilerOutput); } /** Gets file and line of the parameter in the shader source code. */ void GetParameterFileAndLine(const FParsedShaderParameter& ParsedParameter, FString& OutFile, FString& OutLine) const { return ExtractFileAndLine(ParsedParameter.ParsedPragmaLineoffset, ParsedParameter.ParsedLineOffset, OutFile, OutLine); } private: void ExtractFileAndLine(int32 PragamLineoffset, int32 LineOffset, FString& OutFile, FString& OutLine) const; FString OriginalParsedShader; TMap ParsedParameters; bool bMovedLoosedParametersToRootConstantBuffer = false; }; namespace UE::ShaderCompilerCommon { extern SHADERCOMPILERCOMMON_API FString RemoveConstantBufferPrefix(const FString& InName); extern SHADERCOMPILERCOMMON_API EShaderParameterType ParseAndRemoveBindlessParameterPrefix(FString& InName); extern SHADERCOMPILERCOMMON_API bool RemoveBindlessParameterPrefix(FString& InName); } extern SHADERCOMPILERCOMMON_API void HandleReflectedGlobalConstantBufferMember( const FString& MemberName, uint32 ConstantBufferIndex, int32 ReflectionOffset, int32 ReflectionSize, FShaderCompilerOutput& CompilerOutput ); extern SHADERCOMPILERCOMMON_API void HandleReflectedRootConstantBufferMember( const FShaderCompilerInput& Input, const FShaderParameterParser& ShaderParameterParser, const FString& MemberName, int32 ReflectionOffset, int32 ReflectionSize, FShaderCompilerOutput& CompilerOutput ); extern SHADERCOMPILERCOMMON_API void HandleReflectedRootConstantBuffer( int32 ConstantBufferSize, FShaderCompilerOutput& CompilerOutput ); extern SHADERCOMPILERCOMMON_API void HandleReflectedUniformBuffer( const FString& UniformBufferName, int32 ReflectionSlot, int32 BaseIndex, int32 BufferSize, FShaderCompilerOutput& CompilerOutput ); inline void HandleReflectedUniformBuffer(const FString& UniformBufferName, int32 ReflectionSlot, int32 BufferSize, FShaderCompilerOutput& CompilerOutput) { HandleReflectedUniformBuffer(UniformBufferName, ReflectionSlot, 0, BufferSize, CompilerOutput); } inline void HandleReflectedUniformBuffer(const FString& UniformBufferName, int32 ReflectionSlot, FShaderCompilerOutput& CompilerOutput) { HandleReflectedUniformBuffer(UniformBufferName, ReflectionSlot, 0, CompilerOutput); } extern SHADERCOMPILERCOMMON_API void HandleReflectedShaderResource( const FString& ResourceName, int32 BindOffset, int32 ReflectionSlot, int32 BindCount, FShaderCompilerOutput& CompilerOutput ); inline void HandleReflectedShaderResource(const FString& ResourceName, int32 ReflectionSlot, int32 BindCount, FShaderCompilerOutput& CompilerOutput) { HandleReflectedShaderResource(ResourceName, 0, ReflectionSlot, BindCount, CompilerOutput); } inline void HandleReflectedShaderResource(const FString& ResourceName, int32 ReflectionSlot, FShaderCompilerOutput& CompilerOutput) { HandleReflectedShaderResource(ResourceName, ReflectionSlot, 1, CompilerOutput); } extern SHADERCOMPILERCOMMON_API void HandleReflectedShaderUAV( const FString& UAVName, int32 BindOffset, int32 ReflectionSlot, int32 BindCount, FShaderCompilerOutput& CompilerOutput ); inline void HandleReflectedShaderUAV(const FString& UAVName, int32 ReflectionSlot, int32 BindCount, FShaderCompilerOutput& CompilerOutput) { HandleReflectedShaderUAV(UAVName, 0, ReflectionSlot, BindCount, CompilerOutput); } inline void HandleReflectedShaderUAV(const FString& UAVName, int32 ReflectionSlot, FShaderCompilerOutput& CompilerOutput) { HandleReflectedShaderUAV(UAVName, ReflectionSlot, 1, CompilerOutput); } extern SHADERCOMPILERCOMMON_API void HandleReflectedShaderSampler( const FString& SamplerName, int32 BindOffset, int32 ReflectionSlot, int32 BindCount, FShaderCompilerOutput& CompilerOutput ); inline void HandleReflectedShaderSampler(const FString& SamplerName, int32 ReflectionSlot, int32 BindCount, FShaderCompilerOutput& CompilerOutput) { HandleReflectedShaderSampler(SamplerName, 0, ReflectionSlot, BindCount, CompilerOutput); } inline void HandleReflectedShaderSampler(const FString& SamplerName, int32 ReflectionSlot, FShaderCompilerOutput& CompilerOutput) { HandleReflectedShaderSampler(SamplerName, ReflectionSlot, 1, CompilerOutput); } /** Adds a note to CompilerOutput.Error about where the shader parameter structure is on C++ side. */ extern SHADERCOMPILERCOMMON_API void AddNoteToDisplayShaderParameterStructureOnCppSide( const FShaderParametersMetadata* ParametersStructure, FShaderCompilerOutput& CompilerOutput); /** Adds a note to CompilerOutput.Error about where the shader parameter is on C++ side. */ extern SHADERCOMPILERCOMMON_API void AddNoteToDisplayShaderParameterMemberOnCppSide( const FShaderCompilerInput& CompilerInput, const FShaderParameterParser::FParsedShaderParameter& ParsedParameter, FShaderCompilerOutput& CompilerOutput); /** Adds an error to CompilerOutput.Error about a shader parameters that could not be bound. */ extern SHADERCOMPILERCOMMON_API void AddUnboundShaderParameterError( const FShaderCompilerInput& CompilerInput, const FShaderParameterParser& ShaderParameterParser, const FString& ParameterBindingName, FShaderCompilerOutput& CompilerOutput); // The cross compiler doesn't yet support struct initializers needed to construct static structs for uniform buffers // Replace all uniform buffer struct member references (View.WorldToClip) with a flattened name that removes the struct dependency (View_WorldToClip) extern SHADERCOMPILERCOMMON_API void RemoveUniformBuffersFromSource(const FShaderCompilerEnvironment& Environment, FString& PreprocessedShaderSource); // Processes TEXT macros extern SHADERCOMPILERCOMMON_API void TransformStringIntoCharacterArray(FString& PreprocessedShaderSource); extern SHADERCOMPILERCOMMON_API bool RemoveUnusedOutputs(FString& InOutSourceCode, const TArray& InUsedOutputs, const TArray& InExceptions, FString& InOutEntryPoint, TArray& OutErrors); extern SHADERCOMPILERCOMMON_API bool RemoveUnusedInputs(FString& InOutSourceCode, const TArray& InUsedInputs, FString& InOutEntryPoint, TArray& OutErrors); extern SHADERCOMPILERCOMMON_API bool ConvertFromFP32ToFP16(FString& InOutSourceCode, TArray& OutErrors); extern SHADERCOMPILERCOMMON_API FString CreateShaderCompilerWorkerDirectCommandLine(const FShaderCompilerInput& Input, uint32 CCFlags = 0); enum class EShaderConductorTarget { Dxil, Spirv, }; extern SHADERCOMPILERCOMMON_API void WriteShaderConductorCommandLine(const FShaderCompilerInput& Input, const FString& SourceFilename, EShaderConductorTarget Target); // Gets the string that DumpDebugUSF writes out extern SHADERCOMPILERCOMMON_API FString GetDumpDebugUSFContents(const FShaderCompilerInput& Input, const FString& Source, uint32 HlslCCFlags); // Utility functions shared amongst all backends to write out a dumped USF extern SHADERCOMPILERCOMMON_API void DumpDebugUSF(const FShaderCompilerInput& Input, const ANSICHAR* Source, uint32 HlslCCFlags = 0, const TCHAR* OverrideBaseFilename = nullptr); extern SHADERCOMPILERCOMMON_API void DumpDebugUSF(const FShaderCompilerInput& Input, const FString& Source, uint32 HlslCCFlags = 0, const TCHAR* OverrideBaseFilename = nullptr); extern SHADERCOMPILERCOMMON_API void DumpDebugShaderText(const FShaderCompilerInput& Input, const FString& InSource, const FString& FileExtension); extern SHADERCOMPILERCOMMON_API void DumpDebugShaderText(const FShaderCompilerInput& Input, ANSICHAR* InSource, int32 InSourceLength, const FString& FileExtension); extern SHADERCOMPILERCOMMON_API void DumpDebugShaderText(const FShaderCompilerInput& Input, ANSICHAR* InSource, int32 InSourceLength, const FString& FileName, const FString& FileExtension); extern SHADERCOMPILERCOMMON_API void DumpDebugShaderBinary(const FShaderCompilerInput& Input, void* InData, int32 InDataByteSize, const FString& FileExtension); extern SHADERCOMPILERCOMMON_API void DumpDebugShaderBinary(const FShaderCompilerInput& Input, void* InData, int32 InDataByteSize, const FString& FileName, const FString& FileExtension); extern SHADERCOMPILERCOMMON_API void DumpDebugShaderDisassembledSpirv(const FShaderCompilerInput& Input, void* InData, int32 InDataByteSize, const FString& FileExtension); extern SHADERCOMPILERCOMMON_API void DumpDebugShaderDisassembledDxil(const FShaderCompilerInput& Input, void* InData, int32 InDataByteSize, const FString& FileExtension); UE_DEPRECATED(4.26, "SourceLength is no longer needed.") inline void DumpDebugUSF(const FShaderCompilerInput& Input, const ANSICHAR* Source, int32 SourceLength, uint32 HlslCCFlags = 0, const TCHAR* OverrideBaseFilename = nullptr) { DumpDebugUSF(Input, Source, HlslCCFlags, OverrideBaseFilename); } // calls 'Mali Offline Compiler' to compile the glsl source code and extract the generated instruction count extern SHADERCOMPILERCOMMON_API void CompileOfflineMali(const FShaderCompilerInput &Input, FShaderCompilerOutput& ShaderOutput, const ANSICHAR* ShaderSource, const int32 SourceSize, bool bVulkanSpirV, const ANSICHAR* VulkanSpirVEntryPoint = nullptr); // Cross compiler support/common functionality namespace CrossCompiler { extern SHADERCOMPILERCOMMON_API FString CreateResourceTableFromEnvironment(const FShaderCompilerEnvironment& Environment); extern SHADERCOMPILERCOMMON_API void CreateEnvironmentFromResourceTable(const FString& String, FShaderCompilerEnvironment& OutEnvironment); extern SHADERCOMPILERCOMMON_API void ParseHlslccError(TArray& OutErrors, const FString& InLine, bool bUseAbsolutePaths = false); struct SHADERCOMPILERCOMMON_API FHlslccHeader { FHlslccHeader(); virtual ~FHlslccHeader() { } bool Read(const ANSICHAR*& ShaderSource, int32 SourceLen); // After the standard header, different backends can output their own info virtual bool ParseCustomHeaderEntries(const ANSICHAR*& ShaderSource) { return true; } struct FInOut { FString Type; int32 Index; int32 ArrayCount; FString Name; }; struct FAttribute { int32 Index; FString Name; }; struct FPackedGlobal { ANSICHAR PackedType; FString Name; int32 Offset; int32 Count; }; //struct FUniform //{ //}; struct FPackedUB { FAttribute Attribute; struct FMember { FString Name; int32 Offset; int32 Count; }; TArray Members; }; struct FPackedUBCopy { int32 SourceUB; int32 SourceOffset; int32 DestUB; ANSICHAR DestPackedType; int32 DestOffset; int32 Count; }; struct FSampler { FString Name; int32 Offset; int32 Count; TArray SamplerStates; }; struct FUAV { FString Name; int32 Offset; int32 Count; }; struct FAccelerationStructure { FString Name; int32 Offset = 0; }; FString Name; TArray Inputs; TArray Outputs; TArray UniformBlocks; //TArray Uniforms; TArray PackedGlobals; TArray PackedUBs; TArray PackedUBCopies; TArray PackedUBGlobalCopies; TArray Samplers; TArray UAVs; TArray SamplerStates; TArray AccelerationStructures; uint32 NumThreads[3]; static bool ReadInOut(const ANSICHAR*& ShaderSource, TArray& OutAttributes); static bool ReadCopies(const ANSICHAR*& ShaderSource, bool bGlobals, TArray& OutCopies); }; extern SHADERCOMPILERCOMMON_API const TCHAR* GetFrequencyName(EShaderFrequency Frequency); inline bool ParseIdentifier(const ANSICHAR*& Str, FString& OutStr) { OutStr = TEXT(""); FString Result; while ((*Str >= 'A' && *Str <= 'Z') || (*Str >= 'a' && *Str <= 'z') || (*Str >= '0' && *Str <= '9') || *Str == '_') { OutStr += (TCHAR)*Str; ++Str; } return OutStr.Len() > 0; } inline bool ParseIdentifier(const TCHAR*& Str, FString& OutStr) { OutStr = TEXT(""); FString Result; while ((*Str >= 'A' && *Str <= 'Z') || (*Str >= 'a' && *Str <= 'z') || (*Str >= '0' && *Str <= '9') || *Str == '_') { OutStr += (TCHAR)*Str; ++Str; } return OutStr.Len() > 0; } inline bool ParseString(const ANSICHAR*& Str, FString& OutStr) { OutStr = TEXT(""); FString Result; while (*Str != ' ' && *Str != '\n') { OutStr += (TCHAR)*Str; ++Str; } return OutStr.Len() > 0; } inline bool ParseString(const TCHAR*& Str, FString& OutStr) { OutStr = TEXT(""); FString Result; while (*Str != ' ' && *Str != '\n') { OutStr += (TCHAR)*Str; ++Str; } return OutStr.Len() > 0; } FORCEINLINE bool Match(const ANSICHAR*& Str, ANSICHAR Char) { if (*Str == Char) { ++Str; return true; } return false; } FORCEINLINE bool Match(const TCHAR*& Str, ANSICHAR Char) { if (*Str == Char) { ++Str; return true; } return false; } FORCEINLINE bool Match(const ANSICHAR*& Str, const ANSICHAR* Sub) { int32 SubLen = FCStringAnsi::Strlen(Sub); if (FCStringAnsi::Strncmp(Str, Sub, SubLen) == 0) { Str += SubLen; return true; } return false; } FORCEINLINE bool Match(const TCHAR*& Str, const TCHAR* Sub) { int32 SubLen = FCString::Strlen(Sub); if (FCString::Strncmp(Str, Sub, SubLen) == 0) { Str += SubLen; return true; } return false; } template inline bool ParseIntegerNumber(const ANSICHAR*& Str, T& OutNum) { auto* OriginalStr = Str; OutNum = 0; while (*Str >= '0' && *Str <= '9') { OutNum = OutNum * 10 + *Str++ - '0'; } return Str != OriginalStr; } template inline bool ParseIntegerNumber(const TCHAR*& Str, T& OutNum) { auto* OriginalStr = Str; OutNum = 0; while (*Str >= '0' && *Str <= '9') { OutNum = OutNum * 10 + *Str++ - '0'; } return Str != OriginalStr; } inline bool ParseSignedNumber(const ANSICHAR*& Str, int32& OutNum) { int32 Sign = Match(Str, '-') ? -1 : 1; uint32 Num = 0; if (ParseIntegerNumber(Str, Num)) { OutNum = Sign * (int32)Num; return true; } return false; } inline bool ParseSignedNumber(const TCHAR*& Str, int32& OutNum) { int32 Sign = Match(Str, '-') ? -1 : 1; uint32 Num = 0; if (ParseIntegerNumber(Str, Num)) { OutNum = Sign * (int32)Num; return true; } return false; } }