Files
UnrealEngineUWP/Engine/Source/Developer/ShaderCompilerCommon/Public/ShaderCompilerCommon.h
christopher waters 1e01768e33 Bindless shader parameters
- Adding support for bindless resources/samplers. The loose resources have to be wrapped in a macro that generates an index and proxy resource when bindless is enabled. Reflection has to parse these differently from other resources since they're parsed as uint parameters. The runtime also has to bind these differently since they end up needing to find their real resource in the binding data and then update a constant buffer.
- To assist in conversions, the shader compiler will detect if a shader is compiled with both bindless and non-bindless resources/samplers and will emit errors if that is the case.
- Adding support for bindless Uniform Buffer resources/samplers. Because Uniform Buffers aren't per-shader, they'll always update their constants with the bindless indices of their resources.
- Adding more flags to FShaderCodePackedResourceCounts required changing all the array initializations to individual parameters since not all the new flags are used everywhere.
- No shaders have been configured to support bindless resources/samplers. Yet.

#jira UE-139616
#rb zach.bethel, arciel.rekman
#preflight 6282b8ec44349a6581a21a39

[CL 20250348 by christopher waters in ue5-main branch]
2022-05-17 16:30:48 -04:00

586 lines
19 KiB
C++

// 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<FString,FResourceTableEntry>& ResourceTableMap,
const TMap<FString,FUniformBufferEntry>& UniformBufferMap,
TBitArray<>& UsedUniformBufferSlots,
FShaderParameterMap& ParameterMap,
FShaderCompilerResourceTable& OutSRT
);
/** Culls global uniform buffer entries from the parameter map. */
extern SHADERCOMPILERCOMMON_API void CullGlobalUniformBuffers(const TMap<FString, FUniformBufferEntry>& 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<uint32>& InResourceMap,
int32 MaxBoundResourceTable,
TArray<uint32>& 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<FString, FParsedShaderParameter> 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<FString>& InUsedOutputs, const TArray<FString>& InExceptions, FString& InOutEntryPoint, TArray<FString>& OutErrors);
extern SHADERCOMPILERCOMMON_API bool RemoveUnusedInputs(FString& InOutSourceCode, const TArray<FString>& InUsedInputs, FString& InOutEntryPoint, TArray<FString>& OutErrors);
extern SHADERCOMPILERCOMMON_API bool ConvertFromFP32ToFP16(FString& InOutSourceCode, TArray<FString>& 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<FShaderCompilerError>& 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<FMember> Members;
};
struct FPackedUBCopy
{
int32 SourceUB;
int32 SourceOffset;
int32 DestUB;
ANSICHAR DestPackedType;
int32 DestOffset;
int32 Count;
};
struct FSampler
{
FString Name;
int32 Offset;
int32 Count;
TArray<FString> SamplerStates;
};
struct FUAV
{
FString Name;
int32 Offset;
int32 Count;
};
struct FAccelerationStructure
{
FString Name;
int32 Offset = 0;
};
FString Name;
TArray<FInOut> Inputs;
TArray<FInOut> Outputs;
TArray<FAttribute> UniformBlocks;
//TArray<FUniform> Uniforms;
TArray<FPackedGlobal> PackedGlobals;
TArray<FPackedUB> PackedUBs;
TArray<FPackedUBCopy> PackedUBCopies;
TArray<FPackedUBCopy> PackedUBGlobalCopies;
TArray<FSampler> Samplers;
TArray<FUAV> UAVs;
TArray<FAttribute> SamplerStates;
TArray<FAccelerationStructure> AccelerationStructures;
uint32 NumThreads[3];
static bool ReadInOut(const ANSICHAR*& ShaderSource, TArray<FInOut>& OutAttributes);
static bool ReadCopies(const ANSICHAR*& ShaderSource, bool bGlobals, TArray<FPackedUBCopy>& 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 <typename T>
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 <typename T>
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;
}
}