// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Array.h" #include "Containers/StringFwd.h" #include "Containers/UnrealString.h" #include "CoreMinimal.h" #include "CrossCompilerDefinitions.h" #include "HAL/Platform.h" #include "RHIDefinitions.h" #include "ShaderCompilerCore.h" #include "ShaderCore.h" #include "Templates/Function.h" #define UE_API SHADERCOMPILERCOMMON_API class FShaderCompilerDefinitions; // Cross compiler support/common functionality namespace CrossCompiler { /** Shader model version for HLSL input language. */ struct FHlslShaderModel { /** Major shader model version (e.g. 6 in SM6.2). */ uint16 Major; /** Minor shader model version (e.g. 2 in SM6.2). */ uint16 Minor; FORCEINLINE bool operator == (const FHlslShaderModel& Rhs) const { return Major == Rhs.Major && Minor == Rhs.Minor; } FORCEINLINE bool operator != (const FHlslShaderModel& Rhs) const { return !(*this == Rhs); } FORCEINLINE bool operator < (const FHlslShaderModel& Rhs) const { return Major < Rhs.Major || (Major == Rhs.Major && Minor < Rhs.Minor); } FORCEINLINE bool operator <= (const FHlslShaderModel& Rhs) const { return *this < Rhs || *this == Rhs; } FORCEINLINE bool operator > (const FHlslShaderModel& Rhs) const { return Rhs < *this; } FORCEINLINE bool operator >= (const FHlslShaderModel& Rhs) const { return *this > Rhs || *this == Rhs; } }; /** Wrapper structure to pass options descriptor to ShaderConductor. This is mapped to . */ struct FShaderConductorOptions { /** Removes unused global variables and resources. This can only be used in the HLSL rewrite pass, i.e. 'RewriteHlslSource'. */ bool bRemoveUnusedGlobals = false; /** Experimental: Decide how a matrix get packed. Default in HLSL is row-major. This will be inverted in the SPIR-V backend to match SPIR-V's column-major default. */ bool bPackMatricesInRowMajor = true; /** Enable 16-bit types, such as half, uint16_t. Requires shader model 6.2+. */ bool bEnable16bitTypes = false; /** Embed debug info into the binary. */ bool bEnableDebugInfo = false; /** Force to turn off optimizations. Ignore optimizationLevel below. */ bool bDisableOptimizations = false; /** Enable a pass that converts floating point MUL+ADD pairs into FMAs to avoid re-association. */ bool bEnableFMAPass = false; /** Disables scalar block layout for structured buffers. True for Vulkan mobile due to low coverage of 'VK_EXT_scalar_block_layout' extension. */ bool bDisableScalarBlockLayout = true; /** Enables separate samplers in GLSL via extensions. */ bool bEnableSeparateSamplersInGlsl = false; /** Decorate SV_Position implicitly as invariant. This can drastically reduce Z-fighting but also prevent certain optimizations. */ bool bSvPositionImplicitInvariant = true; /** Decorate output semantics as precise. */ bool bSupportPreciseOutputs = false; /** Preserve storage inputs used for OpenGL */ bool bPreserveStorageInput = false; bool bForceStorageImageFormat = false; /** Treat warnigns as errors. This adds '-WX' to the DXC arguments. See CFLAG_WarningsAsErrors. */ bool bWarningsAsErrors = false; enum class ETargetEnvironment { Vulkan_1_0, Vulkan_1_1, Vulkan_1_2, Vulkan_1_3, }; ETargetEnvironment TargetEnvironment = ETargetEnvironment::Vulkan_1_1; /** Shader model version of the input language. By default SM6.2. */ FHlslShaderModel ShaderModel = { 6, 2 }; /** HLSL language input version: 2015, 2016, 2017, 2018 (Default), 2021 (Breaking changes in short-circuiting evaluation). */ uint32 HlslVersion = 2018; /** * SPIR-V specific optimization passes to override the default '-O' argument. This will be passed to DXC via the '-Oconfig=...' argument. * Use "preset(relax-nested-expr)" for pre-defined set of optimization passes to relax nested expressions. */ FString SpirvCustomOptimizationPasses; }; /** Target high level languages for ShaderConductor output. */ enum class EShaderConductorLanguage { Hlsl, Glsl, Essl, Metal_macOS, Metal_iOS, }; /** Intermediate representation languages for ShaderConductor disassembly output. */ enum class EShaderConductorIR { Spirv, Dxil, }; /** Shader conductor output target descriptor. */ struct FShaderConductorTarget { UE_API FShaderConductorTarget(); /** Target shader semantics, e.g. "macOS" or "iOS" for Metal GPU semantics. */ EShaderConductorLanguage Language = EShaderConductorLanguage::Glsl; /** Target shader version. Valid values for HLSL: 50, 60, 61, 62, 63, 64, 65, 66. Valid values for Metal family: 20300, 20200, 20100, 20000, 10200, 10100, 10000. Valid values for GLSL family: 310, 320, 330, 430. */ int32 Version = 0; /** Cross compilation flags. This is used for high-level cross compilation (such as Metal output) that is send over to SPIRV-Cross, e.g. { "invariant_float_math", "1" }. */ TPimplPtr CompileFlags; /** Optional callback to rename certain variable types. */ TFunction VariableTypeRenameCallback; }; /** Container for all special case SPIR-V identifiers generated by ShaderConductor. */ struct FShaderConductorIdentifierTable { /** Identifier for vertex input attributes: "in.var.ATTRIBUTE". */ const ANSICHAR* InputAttribute; /** Identifier for globals uniform buffers: "$Globals". */ const ANSICHAR* GlobalsUniformBuffer; /** Identifier for the intermediate output variable in a tessellation-control shader. */ const ANSICHAR* IntermediateTessControlOutput; /** Identifier for dummy samplers used for platforms where samplers are required */ const ANSICHAR* DummySampler; }; /** Wrapper class to handle interface between UE and ShaderConductor. Use to compile HLSL shaders to SPIR-V or high-level languages such as Metal. */ class FShaderConductorContext { public: /** Initializes the context with internal buffers used for the conversion of input and option descriptors between UE and ShaderConductor. */ UE_API FShaderConductorContext(); /** Release the internal buffers. */ UE_API ~FShaderConductorContext(); /** Move constructor to take ownership of internal buffers from 'Rhs'. */ UE_API FShaderConductorContext(FShaderConductorContext&& Rhs); /** Move operator to take ownership of internal buffers from 'Rhs'. */ UE_API FShaderConductorContext& operator = (FShaderConductorContext&& Rhs); FShaderConductorContext(const FShaderConductorContext&) = delete; FShaderConductorContext& operator = (const FShaderConductorContext&) = delete; /** Loads the shader source and converts the input descriptor to a format suitable for ShaderConductor. If 'Definitions' is null, the previously loaded definitions are not modified. */ UE_API bool LoadSource(FStringView ShaderSource, const FString& Filename, const FString& EntryPoint, EShaderFrequency ShaderStage, const FShaderCompilerDefinitions* Definitions = nullptr, const TArray* ExtraDxcArgs = nullptr); UE_API bool LoadSource(FAnsiStringView ShaderSource, const FString& Filename, const FString& EntryPoint, EShaderFrequency ShaderStage, const FShaderCompilerDefinitions* Definitions = nullptr, const TArray* ExtraDxcArgs = nullptr); UE_API bool LoadSource(const ANSICHAR* ShaderSource, const ANSICHAR* Filename, const ANSICHAR* EntryPoint, EShaderFrequency ShaderStage, const FShaderCompilerDefinitions* Definitions = nullptr, const TArray* ExtraDxcArgs = nullptr); /** Compiles the specified HLSL shader source code to DXIL. */ UE_API bool CompileHlslToDxil(const FShaderConductorOptions& Options, TArray& OutDxil); /** Compiles the specified HLSL shader source code to SPIR-V. */ UE_API bool CompileHlslToSpirv(const FShaderConductorOptions& Options, TArray& OutSpirv); /** Replaces #line 123 directives with //ine 123. Required to work around platform-specific shader debug data handling issues. */ UE_API void RemoveLineDirectives(); /** Performs the specified optimization passes (e.g. "-O" or "--strip-reflect") on the SPIR-V module. */ UE_API bool OptimizeSpirv(TArray& Spirv, const ANSICHAR* const * OptConfigs, int32 OptConfigCount); /** Compiles the specified SPIR-V shader binary code to high level source code (Metal or GLSL). */ UE_API bool CompileSpirvToSource(const FShaderConductorOptions& Options, const FShaderConductorTarget& Target, const void* InSpirv, uint32 InSpirvByteSize, FString& OutSource); /** Compiles the specified SPIR-V shader binary code to high level source code (Metal or GLSL) stored as null terminated ANSI string. */ UE_API bool CompileSpirvToSourceAnsi(const FShaderConductorOptions& Options, const FShaderConductorTarget& Target, const void* InSpirv, uint32 InSpirvByteSize, TArray& OutSource); /** Compiles the specified SPIR-V shader binary code to high level source code (Metal or GLSL) stored as byte buffer (without null terminator as it comes from ShaderConductor). */ UE_API bool CompileSpirvToSourceBuffer(const FShaderConductorOptions& Options, const FShaderConductorTarget& Target, const void* InSpirv, uint32 InSpirvByteSize, const TFunction& OutputCallback); /** Flushes the list of current compile errors and moves the ownership to the caller. */ UE_API void FlushErrors(TArray& OutErrors); /** Returns a pointer to a null terminated ANSI string of the internal loaded sources, or null if no source has been loaded yet. This is automatically updated when RewriteHlsl() is called. */ UE_API const ANSICHAR* GetSourceString() const; /** Returns a length of the internal loaded sources (excluding the null terminator). This is automatically updated when RewriteHlsl() is called. */ UE_API int32 GetSourceLength() const; /** Returns the DXC command line arguments for the specified options. This does not include an output file, i.e. "-Fo" argument is not included. */ UE_API FString GenerateDxcArguments(const FShaderConductorOptions& Options) const; /** Returns the list of current compile errors. */ inline const TArray& GetErrors() const { return Errors; } public: /** Convert array of error string lines into array of . */ static UE_API void ConvertCompileErrors(TArray&& ErrorStringLines, TArray& OutErrors); /** Returns whether the specified variable name denotes an intermediate output variable. This is only true for a special identifiers generated by DXC to communicate patch constant data in the Hull Shader. */ static UE_API bool IsIntermediateSpirvOutputVariable(const ANSICHAR* SpirvVariableName); /** Returns the table of special identifiers generated by ShaderConductor. */ static UE_API const FShaderConductorIdentifierTable& GetIdentifierTable(); /** Disassembles the specified SPIR-V or DXIL module and returns its assembly as text representation. */ static UE_API bool Disassemble(EShaderConductorIR Language, const void* Binary, uint32 BinaryByteSize, TArray& OutAssemblyText); /** Helper function to disassemble the specified SPIR-V or DXIL module and add it to the output code reflection array. */ static UE_API bool Disassemble(EShaderConductorIR Language, const void* Binary, uint32 BinaryByteSize, FGenericShaderStat& OutShaderStat); /** Returns a filename extension for the specified shading language and shader stage, e.g. "frag" for a GLSL pixel shader. */ static UE_API const TCHAR* GetShaderFileExt(EShaderConductorLanguage Language, EShaderFrequency ShaderStage = SF_NumFrequencies); /** Explicitly shut down ShaderConductor and DXC shared libraries. Only used for Linux to prevent dangling mutex on exit. */ static UE_API void Shutdown(); public: struct FShaderConductorIntermediates; // Pimpl idiom private: TArray Errors; FShaderConductorIntermediates* Intermediates; // Pimpl idiom }; } #undef UE_API