// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= Shader.h: Shader definitions. =============================================================================*/ #pragma once #include "Algo/BinarySearch.h" #include "Containers/Array.h" #include "Containers/ArrayView.h" #include "Containers/ContainersFwd.h" #include "Containers/EnumAsByte.h" #include "Containers/HashTable.h" #include "Containers/List.h" #include "Containers/Map.h" #include "Containers/Set.h" #include "Containers/StringFwd.h" #include "Containers/UnrealString.h" #include "CoreMinimal.h" #include "CoreTypes.h" #include "HAL/CriticalSection.h" #include "HAL/PlatformCrt.h" #include "HAL/ThreadSafeBool.h" #include "HAL/UnrealMemory.h" #include "Math/NumericLimits.h" #include "Misc/AssertionMacros.h" #include "Misc/CString.h" #include "Misc/EnumClassFlags.h" #include "Misc/ScopeLock.h" #include "Misc/SecureHash.h" #include "RHI.h" #include "RHIDefinitions.h" #include "RHIMemoryLayout.h" #include "RenderResource.h" #include "RenderDeferredCleanup.h" #include "Serialization/Archive.h" #include "Serialization/ArchiveProxy.h" #include "Serialization/MemoryImage.h" #include "Serialization/MemoryLayout.h" #include "ShaderCore.h" #include "ShaderParameterMetadata.h" #include "ShaderParameters.h" #include "ShaderPermutation.h" #include "ShaderPermutationUtils.h" #include "Templates/RefCounting.h" #include "Templates/TypeHash.h" #include "Templates/UniquePtr.h" #include "Templates/UnrealTemplate.h" #include "Templates/UnrealTypeTraits.h" #include "UObject/NameTypes.h" #include "UObject/RenderingObjectVersion.h" #include // For FShaderUniformBufferParameter #if WITH_EDITOR #include "UObject/DebugSerializationFlags.h" #endif class ITargetPlatform; class FCbFieldView; class FCbWriter; class FComputeKernelShaderType; class FNNERuntimeIREEShaderType; class FGlobalShaderType; class FMaterialShaderType; class FMemoryImageWriter; class FMemoryUnfreezeContent; class FMeshMaterialShaderType; class FNiagaraShaderType; class FOpenColorIOShaderType; class FRHIComputeCommandList; class FShader; class FShaderKeyGenerator; class FShaderMapBase; class FShaderMapPointerTable; class FShaderParametersMetadata; class FShaderPipelineType; class FShaderType; class FVertexFactoryType; class FRHIShaderBindingLayout; struct FShaderCompiledShaderInitializerType; struct FShaderCompileJobKey; struct FShaderCompilerOutput; struct FShaderSerializeContext; using FShaderMapAssetPaths = TSet; // Copied from ShaderCodeLibrary.h /** Define a shader permutation uniquely according to its type, and permutation id.*/ template struct TShaderTypePermutation { MetaShaderType* const Type; const int32 PermutationId; TShaderTypePermutation(MetaShaderType* InType, int32 InPermutationId) : Type(InType) , PermutationId(InPermutationId) { } inline bool operator==(const TShaderTypePermutation& Other)const { return Type == Other.Type && PermutationId == Other.PermutationId; } inline bool operator!=(const TShaderTypePermutation& Other)const { return !(*this == Other); } friend inline uint32 GetTypeHash(const TShaderTypePermutation& Var) { return HashCombine(GetTypeHash(Var.Type), (uint32)Var.PermutationId); } }; using FShaderPermutation = TShaderTypePermutation; inline const int32 kUniqueShaderPermutationId = 0; /** Used to compare order shader types permutation deterministically. */ template class TCompareShaderTypePermutation { public: inline bool operator()(const TShaderTypePermutation& A, const TShaderTypePermutation& B) const { int32 AL = FCString::Strlen(A.Type->GetName()); int32 BL = FCString::Strlen(B.Type->GetName()); if (AL == BL) { int32 StrCmp = FCString::Strncmp(A.Type->GetName(), B.Type->GetName(), AL); if (StrCmp != 0) { return StrCmp > 0; } return A.PermutationId > B.PermutationId; } return AL > BL; } }; class FShaderUniformBufferParameterInfo { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderUniformBufferParameterInfo, RENDERCORE_API, NonVirtual); public: LAYOUT_FIELD(uint16, BaseIndex); FShaderUniformBufferParameterInfo() = default; FShaderUniformBufferParameterInfo(uint16 InBaseIndex) { BaseIndex = InBaseIndex; checkf(BaseIndex == InBaseIndex, TEXT("Tweak FShaderUniformBufferParameterInfo type sizes")); } friend FArchive& operator<<(FArchive& Ar, FShaderUniformBufferParameterInfo& Info) { Ar << Info.BaseIndex; return Ar; } inline bool operator==(const FShaderUniformBufferParameterInfo& Rhs) const { return BaseIndex == Rhs.BaseIndex; } inline bool operator<(const FShaderUniformBufferParameterInfo& Rhs) const { return BaseIndex < Rhs.BaseIndex; } }; class FShaderResourceParameterInfo { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderResourceParameterInfo, RENDERCORE_API, NonVirtual); public: LAYOUT_FIELD(uint16, BaseIndex); LAYOUT_FIELD(uint8, BufferIndex); LAYOUT_FIELD(EShaderParameterType, Type); FShaderResourceParameterInfo() = default; FShaderResourceParameterInfo(uint16 InBaseIndex, uint8 InBufferIndex, EShaderParameterType InType) { BaseIndex = InBaseIndex; BufferIndex = InBufferIndex; Type = InType; checkf(BaseIndex == InBaseIndex && BufferIndex == InBufferIndex && Type == InType, TEXT("Tweak FShaderResourceParameterInfo type sizes")); } friend FArchive& operator<<(FArchive& Ar, FShaderResourceParameterInfo& Info) { Ar << Info.BaseIndex; Ar << Info.BufferIndex; Ar << Info.Type; return Ar; } inline bool operator==(const FShaderResourceParameterInfo& Rhs) const { return BaseIndex == Rhs.BaseIndex && BufferIndex == Rhs.BufferIndex && Type == Rhs.Type; } inline bool operator<(const FShaderResourceParameterInfo& Rhs) const { return BaseIndex < Rhs.BaseIndex; } }; class FShaderLooseParameterInfo { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderLooseParameterInfo, RENDERCORE_API, NonVirtual); public: LAYOUT_FIELD(uint16, BaseIndex); LAYOUT_FIELD(uint16, Size); FShaderLooseParameterInfo() = default; FShaderLooseParameterInfo(uint16 InBaseIndex, uint16 InSize) { BaseIndex = InBaseIndex; Size = InSize; checkf(BaseIndex == InBaseIndex && Size == InSize, TEXT("Tweak FShaderLooseParameterInfo type sizes")); } friend FArchive& operator<<(FArchive& Ar, FShaderLooseParameterInfo& Info) { Ar << Info.BaseIndex; Ar << Info.Size; return Ar; } inline bool operator==(const FShaderLooseParameterInfo& Rhs) const { return BaseIndex == Rhs.BaseIndex && Size == Rhs.Size; } inline bool operator<(const FShaderLooseParameterInfo& Rhs) const { return BaseIndex < Rhs.BaseIndex; } }; class FShaderLooseParameterBufferInfo { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderLooseParameterBufferInfo, RENDERCORE_API, NonVirtual); public: LAYOUT_FIELD(uint16, BaseIndex); LAYOUT_FIELD(uint16, Size); LAYOUT_FIELD(TMemoryImageArray, Parameters); FShaderLooseParameterBufferInfo() {} FShaderLooseParameterBufferInfo(uint16 InBufferIndex, uint16 InBufferSize) { BaseIndex = InBufferIndex; Size = InBufferSize; checkf(BaseIndex == InBufferIndex, TEXT("Tweak FShaderLooseParameterBufferInfo type sizes")); } friend FArchive& operator<<(FArchive& Ar,FShaderLooseParameterBufferInfo& Info) { Ar << Info.BaseIndex; Ar << Info.Size; Ar << Info.Parameters; return Ar; } inline bool operator==(const FShaderLooseParameterBufferInfo& Rhs) const { return BaseIndex == Rhs.BaseIndex && Size == Rhs.Size && Parameters == Rhs.Parameters; } inline bool operator<(const FShaderLooseParameterBufferInfo& Rhs) const { return BaseIndex < Rhs.BaseIndex; } }; class FShaderParameterMapInfo { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderParameterMapInfo, RENDERCORE_API, NonVirtual); public: LAYOUT_FIELD(TMemoryImageArray, UniformBuffers); LAYOUT_FIELD(TMemoryImageArray, TextureSamplers); LAYOUT_FIELD(TMemoryImageArray, SRVs); LAYOUT_FIELD(TMemoryImageArray, LooseParameterBuffers); LAYOUT_FIELD(uint64, Hash); friend FArchive& operator<<(FArchive& Ar,FShaderParameterMapInfo& Info) { Ar << Info.UniformBuffers; Ar << Info.TextureSamplers; Ar << Info.SRVs; Ar << Info.LooseParameterBuffers; Ar << Info.Hash; return Ar; } inline bool operator==(const FShaderParameterMapInfo& Rhs) const { return Hash == Rhs.Hash; } }; enum class ERayTracingPayloadType : uint32; // actual enum is defined in /Shaders/Shared/RayTracingPayloadType.h typedef uint32(*TRaytracingPayloadSizeFunction)(); ENUM_CLASS_FLAGS(ERayTracingPayloadType); RENDERCORE_API uint32 GetRayTracingPayloadTypeMaxSize(ERayTracingPayloadType PayloadType); RENDERCORE_API void RegisterRayTracingPayloadType(ERayTracingPayloadType PayloadType, uint32 PayloadSize, TRaytracingPayloadSizeFunction PayloadSizeFunction); struct FRegisterRayTracingPayloadTypeHelper { FRegisterRayTracingPayloadTypeHelper(ERayTracingPayloadType PayloadType, uint32 PayloadSize, TRaytracingPayloadSizeFunction PayloadSizeFunction) { RegisterRayTracingPayloadType(PayloadType, PayloadSize, PayloadSizeFunction); } }; #define IMPLEMENT_RT_PAYLOAD_TYPE_CONCAT2( x, y ) x##y #define IMPLEMENT_RT_PAYLOAD_TYPE_CONCAT( x, y ) IMPLEMENT_RT_PAYLOAD_TYPE_CONCAT2( x, y ) #define IMPLEMENT_RT_PAYLOAD_TYPE(PayloadType, PayloadSize) static FRegisterRayTracingPayloadTypeHelper IMPLEMENT_RT_PAYLOAD_TYPE_CONCAT(PayloadHelper, __COUNTER__) = FRegisterRayTracingPayloadTypeHelper(PayloadType, PayloadSize, nullptr); #define IMPLEMENT_RT_PAYLOAD_TYPE_FUNCTION(PayloadType, PayloadSizeFunction) static FRegisterRayTracingPayloadTypeHelper IMPLEMENT_RT_PAYLOAD_TYPE_CONCAT(PayloadHelper, __COUNTER__) = FRegisterRayTracingPayloadTypeHelper(PayloadType, 0u, PayloadSizeFunction); class FShaderMapResource : public FRenderResource, public FDeferredCleanupInterface { public: static RENDERCORE_API bool ArePlatformsCompatible(EShaderPlatform CurrentPlatform, EShaderPlatform TargetPlatform); EShaderPlatform GetPlatform() const { return Platform; } RENDERCORE_API void AddRef(); RENDERCORE_API void Release(); inline int32 GetNumRefs() const { return NumRefs.load(std::memory_order_relaxed); } // FRenderResource interface. RENDERCORE_API virtual void ReleaseRHI() override; inline int32 GetNumShaders() const { return NumRHIShaders; } inline bool IsValidShaderIndex(int32 ShaderIndex) const { return static_cast(ShaderIndex) < NumRHIShaders; } inline bool HasShader(int32 ShaderIndex) const { return RHIShaders[ShaderIndex].load(std::memory_order_acquire) != nullptr; } virtual uint32 GetShaderSizeBytes(int32 ShaderIndex) const; virtual void PreloadShader(int32 ShaderIndex, FGraphEventArray& OutCompletionEvents) { /* no-op when not using shader library */ }; virtual void PreloadShaderMap(FGraphEventArray& OutCompletionEvents) { /* no-op when not using shader library */ }; virtual int32 GetGroupIndexForShader(int32 ShaderIndex) const { /* return invalid index when not using shader library */ return INDEX_NONE; }; virtual int32 GetLibraryId() const { /* return invalid index when not using shader library */ return INDEX_NONE; } virtual int32 GetLibraryShaderIndex(int32 ShaderIndex) const { /* return invalid index when not using shader library */ return INDEX_NONE; } inline bool ContainsAtLeastOneRHIShaderCreated() const { return bAtLeastOneRHIShaderCreated; } inline FRHIShader* GetShader(int32 ShaderIndex, bool bRequired = true) { // This is a double checked locking. This trickery arises from the fact that we're // synchronizing two threads: one that takes a lock and another that doesn't. // Without fences, there is a race between storing the shader pointer and accessing it // on the other (lockless) thread. FRHIShader* Shader = RHIShaders[ShaderIndex].load(std::memory_order_acquire); if (UNLIKELY(Shader == nullptr)) { Shader = CreateShaderOrCrash(ShaderIndex, bRequired); } return Shader; } /** Return shader hash for a particular shader without creating it. */ virtual FSHAHash GetShaderHash(int32 ShaderIndex) = 0; virtual FSHAHash GetShaderMapHash() const { /* return invalid hash when not using shader library */ return FSHAHash(); } RENDERCORE_API void BeginCreateAllShaders(); #if RHI_RAYTRACING UE_DEPRECATED(5.6, "GetRayTracingHitGroupLibrary needs current EShaderPlatform") static void GetRayTracingHitGroupLibrary(TArray& RayTracingHitGroupShaders, FRHIRayTracingShader* DefaultShader) { GetRayTracingHitGroupLibrary(GMaxRHIShaderPlatform, RayTracingHitGroupShaders, DefaultShader); } UE_DEPRECATED(5.6, "GetRayTracingCallableShaderLibrary needs current EShaderPlatform") static void GetRayTracingCallableShaderLibrary(TArray& RayTracingCallableShaders, FRHIRayTracingShader* DefaultShader) { GetRayTracingCallableShaderLibrary(GMaxRHIShaderPlatform, RayTracingCallableShaders, DefaultShader); } UE_DEPRECATED(5.6, "GetRayTracingMissShaderLibrary needs current EShaderPlatform") static void GetRayTracingMissShaderLibrary(TArray& RayTracingMissShaders, FRHIRayTracingShader* DefaultShader) { GetRayTracingMissShaderLibrary(GMaxRHIShaderPlatform, RayTracingMissShaders, DefaultShader); } static RENDERCORE_API void GetRayTracingHitGroupLibrary(EShaderPlatform ShaderPlatform, TArray& RayTracingHitGroupShaders, FRHIRayTracingShader* DefaultShader); static RENDERCORE_API void GetRayTracingCallableShaderLibrary(EShaderPlatform ShaderPlatform, TArray& RayTracingCallableShaders, FRHIRayTracingShader* DefaultShader); static RENDERCORE_API void GetRayTracingMissShaderLibrary(EShaderPlatform ShaderPlatform, TArray& RayTracingMissShaders, FRHIRayTracingShader* DefaultShader); inline uint32 GetRayTracingHitGroupLibraryIndex(int32 ShaderIndex) { FRHIShader* Shader = GetShader(ShaderIndex); // make sure the shader is created checkSlow(Shader->GetFrequency() == SF_RayHitGroup); return RayTracingLibraryIndices[ShaderIndex]; } inline uint32 GetRayTracingCallableShaderLibraryIndex(int32 ShaderIndex) { FRHIShader* Shader = GetShader(ShaderIndex); // make sure the shader is created checkSlow(Shader->GetFrequency() == SF_RayCallable); return RayTracingLibraryIndices[ShaderIndex]; } inline uint32 GetRayTracingMissShaderLibraryIndex(int32 ShaderIndex) { FRHIShader* Shader = GetShader(ShaderIndex); // make sure the shader is created checkSlow(Shader->GetFrequency() == SF_RayMiss); return RayTracingLibraryIndices[ShaderIndex]; } #endif // RHI_RAYTRACING virtual uint32 GetSizeBytes() const = 0; protected: RENDERCORE_API explicit FShaderMapResource(EShaderPlatform InPlatform, int32 NumShaders); RENDERCORE_API virtual ~FShaderMapResource(); SIZE_T GetAllocatedSize() const { SIZE_T Size = NumRHIShaders * sizeof(std::atomic); #if RHI_RAYTRACING Size += RayTracingLibraryIndices.GetAllocatedSize(); #endif return Size; } /** Creates RHI shader, with a reference (so the caller can release). Never returns nullptr (inability to create is Fatal) */ virtual FRHIShader* CreateRHIShaderOrCrash(int32 ShaderIndex, bool bRequired) = 0; /** Signal the shader library that it can release compressed shader code for a shader that it keeps preloaded in memory. */ virtual void ReleasePreloadedShaderCode(int32 ShaderIndex) { /* no-op when not using shader library */ }; virtual bool TryRelease() { return true; } RENDERCORE_API void ReleaseShaders(); private: /** Creates an entry in RHIShaders array and registers it among the raytracing libs if needed. Created shader is returned. */ RENDERCORE_API FRHIShader* CreateShaderOrCrash(int32 ShaderIndex, bool bRequired); /** This lock is to prevent two threads creating the same RHIShaders element. It is only taken if the element is to be created. */ FCriticalSection RHIShadersCreationGuard; #if RHI_RAYTRACING TArray RayTracingLibraryIndices; #endif // RHI_RAYTRACING /** An array of shader pointers (refcount is managed manually). */ TUniquePtr[]> RHIShaders; /** Since the shaders are no longer a TArray, this is their count (the size of the RHIShaders array). This does not count actually created RHI shaders, just the size of the above array of pointers to them. */ uint32 NumRHIShaders : 31; /** Whether we have at least one RHI shader created. */ uint32 bAtLeastOneRHIShaderCreated : 1; EShaderPlatform Platform; /** The number of references to this shader map. */ std::atomic NumRefs; }; class FShaderMapResourceCode : public FThreadSafeRefCountedObject { public: #if WITH_EDITORONLY_DATA struct FShaderEditorOnlyDataEntry { UE_DEPRECATED(5.6, "Use SymbolBuffer") TArray PlatformDebugData; // Sets the symbols for this entry to the given buffer if either unset or the hash of the new buffer is "less" than the hash of the stored buffer. // This is done purely for determinism's sake (we always want to store the same copy of symbols in the editoronly data for any given shader bytecode). void ConditionalSetSymbolBuffer(FCompressedBuffer InSymbols); uint64 SymbolHash = 0u; // Used only to ensure the copy of symbols saved is stable for determinism's sake; does not need to be serialized. FCompressedBuffer SymbolBuffer; /** This field contains a debug string stored in a ShaderSymbols.info file when using r.Shaders.SymbolInfo=1 * Used to facilitate reverse lookup of a shader from a platform shader hash when no full shader symbol * information is available */ FString DebugInfo; /** A (deduplicated/sorted) array of all the compiler warnings that were emitted when all shaders resulting * in the associated bytecode were compiled (i.e. if multiple shader sources have warnings but compile to * the same code, all warnings for each unique source will be reported). * Does not contain errors since if there were any errors, this object wouldn't exist. */ TArray CompilerWarnings; /** An array of the hash of all FShaderType names which generate this particular shader (i.e. the return value * of FShaderType::GetHashedName().GetHash()). */ TArray ShaderTypeHashes; /** Generic, data-driven key/value pairs of statistics. */ TArray ShaderStatistics; friend FArchive& operator<<(FArchive& Ar, FShaderEditorOnlyDataEntry& Entry) { return Ar << Entry.DebugInfo << Entry.CompilerWarnings << Entry.ShaderTypeHashes << Entry.ShaderStatistics; } PRAGMA_DISABLE_DEPRECATION_WARNINGS // Explicitly-defaulted copy/move ctors & assignment operators are needed temporarily due to deprecation // of PlatformDebugData field. These can be removed once the deprecation window for said field ends. FShaderEditorOnlyDataEntry() = default; FShaderEditorOnlyDataEntry(FShaderEditorOnlyDataEntry&&) = default; FShaderEditorOnlyDataEntry(const FShaderEditorOnlyDataEntry&) = default; FShaderEditorOnlyDataEntry& operator=(FShaderEditorOnlyDataEntry&&) = default; FShaderEditorOnlyDataEntry& operator=(const FShaderEditorOnlyDataEntry&) = default; PRAGMA_ENABLE_DEPRECATION_WARNINGS }; #endif // WITH_EDITORONLY_DATA FShaderMapResourceCode() {} RENDERCORE_API FShaderMapResourceCode(const FShaderMapResourceCode& Other); RENDERCORE_API ~FShaderMapResourceCode(); RENDERCORE_API void Finalize(); RENDERCORE_API void Serialize(FShaderSerializeContext& Ctx); #if WITH_EDITORONLY_DATA RENDERCORE_API void NotifyShadersCompiled(FName FormatName); #endif // WITH_EDITORONLY_DATA RENDERCORE_API uint32 GetSizeBytes() const; UE_DEPRECATED(5.6, "AddShaderCompilerOutput now accepts an FShaderCompileJobKey instead of DebugName") inline void AddShaderCompilerOutput(const FShaderCompilerOutput& Output, const FString& DebugName = FString(), FString DebugInfo = FString()) {} RENDERCORE_API void AddShaderCompilerOutput(const FShaderCompilerOutput& Output, const FShaderCompileJobKey& Key, FString DebugInfo = FString()); int32 FindShaderIndex(const FSHAHash& InHash) const; #if WITH_EDITORONLY_DATA void AddEditorOnlyData(int32 Index, const FString& DebugName, uint64 ShaderTypeHash, FCompressedBuffer InSymbols, TConstArrayView InCompilerWarnings, TArray&& ShaderStatistics, const FString& DebugInfo = FString()); void UpdateEditorOnlyData(int32 Index, const FString& DebugName, uint64 ShaderTypeHash, FCompressedBuffer InSymbols, TConstArrayView InCompilerWarnings, const FString& DebugInfo); RENDERCORE_API void LogShaderCompilerWarnings(); #endif RENDERCORE_API void ToString(FStringBuilderBase& OutString) const; /** A hash describing the total contents of *this. Constructed from the contents of ShaderHashes during Finalize. */ FSHAHash ResourceHash; TArray ShaderHashes; TArray ShaderCodeResources; #if WITH_EDITORONLY_DATA // Optional array of editor-only data indexed in the same order as ShaderEntries (sorted by the shader hash) // Empty in the cases where the editor-only data is not serialized. TArray ShaderEditorOnlyDataEntries; private: // Given an existing shader entry and a new set of shader statistics determine if the shader stats match. // This is to prevent non-determinism issues. void ValidateShaderStatisticsEditorOnlyData(int32 Index, const TArray& ShaderStatistics); #endif // WITH_EDITORONLY_DATA }; class FShaderMapResource_InlineCode : public FShaderMapResource { public: FShaderMapResource_InlineCode(EShaderPlatform InPlatform, FShaderMapResourceCode* InCode) : FShaderMapResource(InPlatform, InCode->ShaderCodeResources.Num()) , Code(InCode) {} // FShaderMapResource interface RENDERCORE_API virtual FSHAHash GetShaderHash(int32 ShaderIndex) override; RENDERCORE_API virtual FRHIShader* CreateRHIShaderOrCrash(int32 ShaderIndex, bool bRequired) override; virtual FString GetFriendlyName() const override { return TEXT("InlineCode"); } virtual uint32 GetSizeBytes() const override; virtual uint32 GetShaderSizeBytes(int32 ShaderIndex) const override; TRefCountPtr Code; }; class FShaderMapPointerTable : public FPointerTableBase { public: RENDERCORE_API virtual int32 AddIndexedPointer(const FTypeLayoutDesc& TypeDesc, void* Ptr) override; RENDERCORE_API virtual void* GetIndexedPointer(const FTypeLayoutDesc& TypeDesc, uint32 i) const override; virtual FShaderMapPointerTable* Clone() const { return new FShaderMapPointerTable(*this); } RENDERCORE_API virtual void SaveToArchive(FArchive& Ar, const FPlatformTypeLayoutParameters& LayoutParams, const void* FrozenObject) const override; RENDERCORE_API virtual bool LoadFromArchive(FArchive& Ar, const FPlatformTypeLayoutParameters& LayoutParams, void* FrozenObject) override; TPtrTable ShaderTypes; TPtrTable VFTypes; }; /** Encapsulates information about a shader's serialization behavior, used to detect when C++ serialization changes to auto-recompile. */ class FShaderKey { public: inline FShaderKey(const FSHAHash& InMaterialShaderMapHash, const FShaderPipelineType* InShaderPipeline, FVertexFactoryType* InVertexFactoryType, int32 PermutationId, EShaderPlatform InPlatform) : VertexFactoryType(InVertexFactoryType) , ShaderPipeline(InShaderPipeline) , MaterialShaderMapHash(InMaterialShaderMapHash) , PermutationId(PermutationId) , Platform(InPlatform) {} friend inline uint32 GetTypeHash(const FShaderKey& Id) { return HashCombine( HashCombine(*(uint32*)&Id.MaterialShaderMapHash, GetTypeHash(Id.Platform)), HashCombine(GetTypeHash(Id.VertexFactoryType), uint32(Id.PermutationId))); } friend bool operator==(const FShaderKey& X, const FShaderKey& Y) { return X.MaterialShaderMapHash == Y.MaterialShaderMapHash && X.ShaderPipeline == Y.ShaderPipeline && X.VertexFactoryType == Y.VertexFactoryType && X.PermutationId == Y.PermutationId && X.Platform == Y.Platform; } RENDERCORE_API friend FArchive& operator<<(FArchive& Ar, FShaderKey& Ref); FVertexFactoryType* VertexFactoryType; const FShaderPipelineType* ShaderPipeline; FSHAHash MaterialShaderMapHash; int32 PermutationId; uint32 Platform : SP_NumBits; }; /** * Uniquely identifies an FShader instance. * Used to link FMaterialShaderMaps and FShaders on load. */ class FShaderId { public: inline FShaderId() {} inline FShaderId(const FShaderType* InType, const FSHAHash& InMaterialShaderMapHash, const FHashedName& InShaderPipeline, const FVertexFactoryType* InVertexFactoryType, int32 InPermutationId, EShaderPlatform InPlatform) : Type(InType) , VFType(InVertexFactoryType) , ShaderPipelineName(InShaderPipeline) , MaterialShaderMapHash(InMaterialShaderMapHash) , PermutationId(InPermutationId) , Platform(InPlatform) {} const FShaderType* Type; const FVertexFactoryType* VFType; FHashedName ShaderPipelineName; FSHAHash MaterialShaderMapHash; int32 PermutationId; uint32 Platform : SP_NumBits; friend inline uint32 GetTypeHash( const FShaderId& Id ) { return HashCombine( GetTypeHash(Id.Type), HashCombine(GetTypeHash(Id.VFType), HashCombine(GetTypeHash(Id.ShaderPipelineName), HashCombine(GetTypeHash(Id.MaterialShaderMapHash), HashCombine(GetTypeHash(Id.PermutationId), GetTypeHash(Id.Platform)))))); } friend bool operator==(const FShaderId& X, const FShaderId& Y) { return X.Type == Y.Type && X.MaterialShaderMapHash == Y.MaterialShaderMapHash && X.ShaderPipelineName == Y.ShaderPipelineName && X.VFType == Y.VFType && X.PermutationId == Y.PermutationId && X.Platform == Y.Platform; } friend bool operator!=(const FShaderId& X, const FShaderId& Y) { return !(X == Y); } }; class FMaterial; /** * Stores all shader parameter bindings and their corresponding offset and size in the shader's parameters struct. */ class FShaderParameterBindings { public: struct FParameter { DECLARE_INLINE_TYPE_LAYOUT(FParameter, NonVirtual); LAYOUT_FIELD(uint16, BufferIndex); LAYOUT_FIELD(uint16, BaseIndex); LAYOUT_FIELD(uint16, ByteOffset); LAYOUT_FIELD(uint16, ByteSize); }; struct FResourceParameter { DECLARE_INLINE_TYPE_LAYOUT(FResourceParameter, NonVirtual); LAYOUT_FIELD(uint16, ByteOffset); LAYOUT_FIELD(uint8, BaseIndex); LAYOUT_FIELD(EUniformBufferBaseType, BaseType); }; struct FBindlessResourceParameter { DECLARE_INLINE_TYPE_LAYOUT(FBindlessResourceParameter, NonVirtual); LAYOUT_FIELD(uint16, ByteOffset); LAYOUT_FIELD(uint16, GlobalConstantOffset); LAYOUT_FIELD(EUniformBufferBaseType, BaseType); }; struct FParameterStructReference { DECLARE_INLINE_TYPE_LAYOUT(FParameterStructReference, NonVirtual); LAYOUT_FIELD(uint16, BufferIndex); LAYOUT_FIELD(uint16, ByteOffset); }; DECLARE_EXPORTED_TYPE_LAYOUT(FShaderParameterBindings, RENDERCORE_API, NonVirtual); public: static constexpr uint16 kInvalidBufferIndex = 0xFFFF; RENDERCORE_API void BindForLegacyShaderParameters(const FShader* Shader, int32 PermutationId, const FShaderParameterMap& ParameterMaps, const FShaderParametersMetadata& StructMetaData, bool bShouldBindEverything = false); RENDERCORE_API void BindForRootShaderParameters(const FShader* Shader, int32 PermutationId, const FShaderParameterMap& ParameterMaps); LAYOUT_FIELD(TMemoryImageArray, Parameters); LAYOUT_FIELD(TMemoryImageArray, ResourceParameters); LAYOUT_FIELD(TMemoryImageArray, BindlessResourceParameters); LAYOUT_FIELD(TMemoryImageArray, GraphUniformBuffers); LAYOUT_FIELD(TMemoryImageArray, ParameterReferences); // Hash of the shader parameter structure when doing the binding. LAYOUT_FIELD_INITIALIZED(uint32, StructureLayoutHash, 0); // Buffer index of FShaderParametersMetadata::kRootUniformBufferBindingName LAYOUT_FIELD_INITIALIZED(uint16, RootParameterBufferIndex, FShaderParameterBindings::kInvalidBufferIndex); }; // FShaderParameterBindings inline int16 GetParameterIndex(const FShaderParameterBindings::FResourceParameter& Parameter) { return Parameter.BaseIndex; } inline int16 GetParameterIndex(const FShaderParameterBindings::FBindlessResourceParameter& Parameter) { return Parameter.GlobalConstantOffset; } inline int16 GetParameterIndex(const FShaderParameterBindings::FParameterStructReference& Parameter) { return Parameter.BufferIndex; } enum class EShaderPermutationPrecacheRequest : uint8 { Precached, //< Permutation should be precached because it can be used at runtime NotPrecached, //< Permutation doesn't have to be precached (debug feature only for example) NotUsed, //< Permutation is not used with current cvar/configuration/feature level }; /** Get the shader permutation flags associated with a platform layout. */ RENDERCORE_API EShaderPermutationFlags GetShaderPermutationFlags(const FPlatformTypeLayoutParameters& LayoutParams); /** Set global additional flags that are always added in GetShaderPermutationFlags(). Expected to be called once at startup. */ RENDERCORE_API void SetAdditionalShaderPermutationFlags(EShaderPermutationFlags AdditionalFlags); namespace Freeze { RENDERCORE_API void IntrinsicToString(const TIndexedPtr& Object, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FMemoryToStringContext& OutContext); RENDERCORE_API void IntrinsicToString(const TIndexedPtr& Object, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FMemoryToStringContext& OutContext); } DECLARE_EXPORTED_TEMPLATE_INTRINSIC_TYPE_LAYOUT(template<>, TIndexedPtr, RENDERCORE_API); DECLARE_EXPORTED_TEMPLATE_INTRINSIC_TYPE_LAYOUT(template<>, TIndexedPtr, RENDERCORE_API); /** A compiled shader and its parameter bindings. */ class FShader { friend class FShaderType; DECLARE_EXPORTED_TYPE_LAYOUT(FShader, RENDERCORE_API, NonVirtual); public: using FPermutationDomain = FShaderPermutationNone; using FPermutationParameters = FShaderPermutationParameters; using CompiledShaderInitializerType = FShaderCompiledShaderInitializerType; using ShaderMetaType = FShaderType; using ShaderStatKeyType = FMemoryImageName; using FShaderStatisticMap = TMemoryImageMap; /** * Used to construct a shader for deserialization. * This still needs to initialize members to safe values since FShaderType::GenerateSerializationHistory uses this constructor. */ RENDERCORE_API FShader(); /** * Construct a shader from shader compiler output. */ RENDERCORE_API FShader(const CompiledShaderInitializerType& Initializer); RENDERCORE_API ~FShader(); /** Can be overridden by FShader subclasses to modify their compile environment just before compilation occurs. */ static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment&) {} /** Get shader binding layout used by the shader */ static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters&) { return nullptr; } /** Can be overridden by FShader subclasses to determine whether a specific permutation should be compiled. */ static bool ShouldCompilePermutation(const FShaderPermutationParameters&) { return true; } /** Can be overridden by FShader subclasses to determine whether a specific permutation should be precached. */ static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::Precached; } /** Can be overridden by FShader subclasses to determine whether compilation is valid. */ static bool ValidateCompiledResult(EShaderPlatform InPlatform, const FShaderParameterMap& InParameterMap, TArray& OutError) { return true; } /** Can be overridden by FShader subclasses to set a custom job priority (EShaderCompileJobPriority) or EShaderCompileJobPriority::None to ignore custom priority. This can only increase priority. */ static EShaderCompileJobPriority GetOverrideJobPriority() { return EShaderCompileJobPriority::None; } /** Can be overriden by FShader subclasses to specify which raytracing payload should be used. This method is only called for raytracing shaders. */ static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId) { return static_cast(0); } /** Returns the hash of the shader file that this shader was compiled with. */ RENDERCORE_API const FSHAHash& GetHash() const; RENDERCORE_API const FSHAHash& GetVertexFactoryHash() const; RENDERCORE_API const FSHAHash& GetOutputHash() const; /** Returns an identifier suitable for deterministic sorting of shaders between sessions. */ uint32 GetSortKey() const { return SortKey; } RENDERCORE_API void Finalize(const FShaderMapResourceCode* Code); // Accessors. inline FShaderType* GetType(const FShaderMapPointerTable& InPointerTable) const { return Type.Get(InPointerTable.ShaderTypes); } inline FShaderType* GetType(const FPointerTableBase* InPointerTable) const { return Type.Get(InPointerTable); } inline FVertexFactoryType* GetVertexFactoryType(const FShaderMapPointerTable& InPointerTable) const { return VFType.Get(InPointerTable.VFTypes); } inline FVertexFactoryType* GetVertexFactoryType(const FPointerTableBase* InPointerTable) const { return VFType.Get(InPointerTable); } inline FShaderType* GetTypeUnfrozen() const { return Type.GetUnfrozen(); } inline int32 GetResourceIndex() const { checkSlow(ResourceIndex != INDEX_NONE); return ResourceIndex; } inline EShaderPlatform GetShaderPlatform() const { return Target.GetPlatform(); } inline EShaderFrequency GetFrequency() const { return Target.GetFrequency(); } inline const FShaderTarget GetTarget() const { return Target; } inline bool IsFrozen() const { return Type.IsFrozen(); } #if WITH_EDITORONLY_DATA inline uint32 GetNumInstructions() const { return NumInstructions; } inline uint32 GetNumTextureSamplers() const { return NumTextureSamplers; } inline uint32 GetCodeSize() const { return CodeSize; } inline void SetNumInstructions(uint32 Value) { NumInstructions = Value; } #else inline uint32 GetNumInstructions() const { return 0u; } inline uint32 GetNumTextureSamplers() const { return 0u; } inline uint32 GetCodeSize() const { return 0u; } inline void SetNumInstructions(uint32 Value) { } #endif /** Finds an automatically bound uniform buffer matching the given uniform buffer type if one exists, or returns an unbound parameter. */ template inline const TShaderUniformBufferParameter& GetUniformBufferParameter() const { const FShaderUniformBufferParameter& FoundParameter = GetUniformBufferParameter(UniformBufferStructType::FTypeInfo::GetStructMetadata()); return static_cast&>(FoundParameter); } /** Finds an automatically bound uniform buffer matching the given uniform buffer struct if one exists, or returns an unbound parameter. */ inline const FShaderUniformBufferParameter& GetUniformBufferParameter(const FShaderParametersMetadata* SearchStruct) const { const FHashedName SearchName = SearchStruct->GetShaderVariableHashedName(); return GetUniformBufferParameter(SearchName); } /** Finds an automatically bound uniform buffer matching the HashedName if one exists, or returns an unbound parameter. */ inline const FShaderUniformBufferParameter& GetUniformBufferParameter(const FHashedName SearchName) const { int32 FoundIndex = INDEX_NONE; TArrayView UniformBufferParameterStructsView(UniformBufferParameterStructs); for (int32 StructIndex = 0, Count = UniformBufferParameterStructsView.Num(); StructIndex < Count; StructIndex++) { if (UniformBufferParameterStructsView[StructIndex] == SearchName) { FoundIndex = StructIndex; break; } } if (FoundIndex != INDEX_NONE) { const FShaderUniformBufferParameter& FoundParameter = UniformBufferParameters[FoundIndex]; return FoundParameter; } else { // This can happen if the uniform buffer was not bound // There's no good way to distinguish not being bound due to temporary debugging / compiler optimizations or an actual code bug, // Hence failing silently instead of an error message static FShaderUniformBufferParameter UnboundParameter; return UnboundParameter; } } RENDERCORE_API const FShaderParametersMetadata* FindAutomaticallyBoundUniformBufferStruct(int32 BaseIndex) const; RENDERCORE_API void DumpDebugInfo(const FShaderMapPointerTable& InPtrTable); #if WITH_EDITOR RENDERCORE_API void SaveShaderStableKeys(const FShaderMapPointerTable& InPtrTable, EShaderPlatform TargetShaderPlatform, int32 PermutationId, const struct FStableShaderKeyAndValue& SaveKeyVal); #endif // WITH_EDITOR /** Returns the meta data for the root shader parameter struct. */ static inline const FShaderParametersMetadata* GetRootParametersMetadata() { return nullptr; } private: RENDERCORE_API void BuildParameterMapInfo(const TMap& ParameterMap); public: LAYOUT_FIELD(FShaderParameterBindings, Bindings); LAYOUT_FIELD(FShaderParameterMapInfo, ParameterMapInfo); protected: LAYOUT_FIELD(TMemoryImageArray, UniformBufferParameterStructs); LAYOUT_FIELD(TMemoryImageArray, UniformBufferParameters); /** * Hash of the compiled output from this shader and the resulting parameter map. * This is used to find a matching resource. */ LAYOUT_FIELD_EDITORONLY(FSHAHash, OutputHash); /** Vertex factory source hash, stored so that an FShaderId can be constructed from this shader. */ LAYOUT_FIELD_EDITORONLY(FSHAHash, VFSourceHash); /** Hash of this shader's source files generated at compile time, and stored to allow creating an FShaderId. */ LAYOUT_FIELD_EDITORONLY(FSHAHash, SourceHash); private: LAYOUT_FIELD(TIndexedPtr, Type); LAYOUT_FIELD(TIndexedPtr, VFType); /** Target platform and frequency. */ LAYOUT_FIELD(FShaderTarget, Target); /** Index of this shader within the FShaderMapResource */ LAYOUT_FIELD(int32, ResourceIndex); /** The number of instructions the shader takes to execute. */ LAYOUT_FIELD_EDITORONLY(uint32, NumInstructions); /** Truncated version of OutputHash, intended for sorting. Not suitable for unique shader identification. */ LAYOUT_FIELD(uint32, SortKey); /** Number of texture samplers the shader uses. */ LAYOUT_FIELD_EDITORONLY(uint32, NumTextureSamplers); /** Size of shader's compiled code */ LAYOUT_FIELD_EDITORONLY(uint32, CodeSize); }; RENDERCORE_API const FTypeLayoutDesc& GetTypeLayoutDesc(const FPointerTableBase* PtrTable, const FShader& Shader); template class TShaderRefBase { public: TShaderRefBase() : ShaderContent(nullptr), ShaderMap(nullptr) {} TShaderRefBase(ShaderType* InShader, const FShaderMapBase& InShaderMap) : ShaderContent(InShader), ShaderMap(&InShaderMap) { checkSlow(InShader); } TShaderRefBase(const TShaderRefBase&) = default; template TShaderRefBase(const TShaderRefBase& Rhs) : ShaderContent(Rhs.GetShader()), ShaderMap(Rhs.GetShaderMap()) {} TShaderRefBase& operator=(const TShaderRefBase&) = default; template TShaderRefBase& operator=(const TShaderRefBase& Rhs) { ShaderContent = Rhs.GetShader(); ShaderMap = Rhs.GetShaderMap(); return *this; } template static TShaderRefBase Cast(const TShaderRefBase& Rhs) { return TShaderRefBase(static_cast(Rhs.GetShader()), Rhs.GetShaderMap()); } template static TShaderRefBase ReinterpretCast(const TShaderRefBase& Rhs) { return TShaderRefBase(reinterpret_cast(Rhs.GetShader()), Rhs.GetShaderMap()); } inline bool IsValid() const { return ShaderContent != nullptr; } inline bool IsNull() const { return ShaderContent == nullptr; } inline void Reset() { ShaderContent = nullptr; ShaderMap = nullptr; } inline ShaderType* GetShader() const { return ShaderContent; } inline const FShaderMapBase* GetShaderMap() const { return ShaderMap; } inline const FShaderMapBase& GetShaderMapChecked() const { check(ShaderMap); return *ShaderMap; } inline FShaderType* GetType() const { return ShaderContent->GetType(GetPointerTable()); } inline FVertexFactoryType* GetVertexFactoryType() const { return ShaderContent->GetVertexFactoryType(GetPointerTable()); } inline FShaderMapResource& GetResourceChecked() const { FShaderMapResource* Resource = GetResource(); check(Resource); return *Resource; } const PointerTableType& GetPointerTable() const; FShaderMapResource* GetResource() const; inline ShaderType* operator->() const { return ShaderContent; } inline FRHIShader* GetRHIShaderBase(EShaderFrequency Frequency, bool bRequired = true) const { FRHIShader* RHIShader = nullptr; if(ShaderContent) { checkSlow(ShaderContent->GetFrequency() == Frequency); RHIShader = GetResourceChecked().GetShader(ShaderContent->GetResourceIndex(), bRequired); if (RHIShader == nullptr) { UE_LOG(LogShaders, Log, TEXT("Failed to create shader for type %s with resource index %d."), GetType()->GetName(), ShaderContent->GetResourceIndex()); return nullptr; } checkSlow(RHIShader->GetFrequency() == Frequency); } return RHIShader; } inline FRHIGraphicsShader* GetGraphicsShader(bool bRequired = true) const { FRHIGraphicsShader* RHIShader = nullptr; if(ShaderContent) { checkSlow(IsValidGraphicsFrequency(ShaderContent->GetFrequency())); RHIShader = static_cast(GetResourceChecked().GetShader(ShaderContent->GetResourceIndex(), bRequired)); if (RHIShader == nullptr) { UE_LOG(LogShaders, Log, TEXT("Failed to create shader for type %s with resource index %d."), GetType()->GetName(), ShaderContent->GetResourceIndex()); return nullptr; } checkSlow(IsValidGraphicsFrequency(RHIShader->GetFrequency())); } return RHIShader; } /** @return the shader's vertex shader */ inline FRHIVertexShader* GetVertexShader(bool bRequired = true) const { return static_cast(GetRHIShaderBase(SF_Vertex, bRequired)); } /** @return the shader's mesh shader */ inline FRHIMeshShader* GetMeshShader() const { return static_cast(GetRHIShaderBase(SF_Mesh)); } /** @return the shader's aplification shader */ inline FRHIAmplificationShader* GetAmplificationShader() const { return static_cast(GetRHIShaderBase(SF_Amplification)); } /** @return the shader's pixel shader */ inline FRHIPixelShader* GetPixelShader(bool bRequired = true) const { return static_cast(GetRHIShaderBase(SF_Pixel, bRequired)); } /** @return the shader's geometry shader */ inline FRHIGeometryShader* GetGeometryShader() const { return static_cast(GetRHIShaderBase(SF_Geometry)); } /** @return the shader's compute shader */ inline FRHIComputeShader* GetComputeShader() const { return static_cast(GetRHIShaderBase(SF_Compute)); } /** @return the shader's work graph shader */ inline FRHIWorkGraphShader* GetWorkGraphShader() const { FRHIWorkGraphShader* RHIShader = nullptr; if (ShaderContent) { const EShaderFrequency Frequency = ShaderContent->GetFrequency(); checkSlow(IsWorkGraphShaderFrequency(Frequency)); RHIShader = static_cast(GetRHIShaderBase(Frequency)); } return RHIShader; } #if RHI_RAYTRACING inline FRHIRayTracingShader* GetRayTracingShader() const { FRHIRayTracingShader* RHIShader = nullptr; if(ShaderContent) { const EShaderFrequency Frequency = ShaderContent->GetFrequency(); checkSlow(Frequency == SF_RayGen || Frequency == SF_RayMiss || Frequency == SF_RayHitGroup || Frequency == SF_RayCallable); RHIShader = static_cast(GetResourceChecked().GetShader(ShaderContent->GetResourceIndex())); checkSlow(RHIShader->GetFrequency() == Frequency); } return RHIShader; } UE_DEPRECATED(5.1, "GetRayTracingMaterialLibraryIndex is deprecated. Use GetRayTracingHitGroupLibraryIndex instead.") inline uint32 GetRayTracingMaterialLibraryIndex() const { return GetRayTracingHitGroupLibraryIndex(); } inline uint32 GetRayTracingHitGroupLibraryIndex() const { checkSlow(ShaderContent); checkSlow(ShaderContent->GetFrequency() == SF_RayHitGroup); return GetResourceChecked().GetRayTracingHitGroupLibraryIndex(ShaderContent->GetResourceIndex()); } inline uint32 GetRayTracingCallableShaderLibraryIndex() const { checkSlow(ShaderContent); checkSlow(ShaderContent->GetFrequency() == SF_RayCallable); return GetResourceChecked().GetRayTracingCallableShaderLibraryIndex(ShaderContent->GetResourceIndex()); } inline uint32 GetRayTracingMissShaderLibraryIndex() const { checkSlow(ShaderContent); checkSlow(ShaderContent->GetFrequency() == SF_RayMiss); return GetResourceChecked().GetRayTracingMissShaderLibraryIndex(ShaderContent->GetResourceIndex()); } #endif // RHI_RAYTRACING private: TShaderRefBase(ShaderType* InShader, const FShaderMapBase* InShaderMap) : ShaderContent(InShader), ShaderMap(InShaderMap) { checkSlow((!InShader && !InShaderMap) || (InShader && InShaderMap)); } ShaderType* ShaderContent; const FShaderMapBase* ShaderMap; template friend inline bool operator==(const TShaderRefBase& Lhs, const TShaderRefBase& Rhs) { if (Lhs.GetShader() == Rhs.GetShader()) { check(Lhs.GetShaderMap() == Rhs.GetShaderMap()); return true; } return false; } template friend inline bool operator!=(const TShaderRefBase& Lhs, const TShaderRefBase& Rhs) { return !operator==(Lhs, Rhs); } }; template using TShaderRef = TShaderRefBase; #define SHADER_TYPE_LIST \ SHADER_TYPE_ENTRY(Global), \ SHADER_TYPE_ENTRY(Material), \ SHADER_TYPE_ENTRY(MeshMaterial), \ SHADER_TYPE_ENTRY(Niagara), \ SHADER_TYPE_ENTRY(OCIO), \ SHADER_TYPE_ENTRY(ComputeKernel), \ SHADER_TYPE_ENTRY(NNERuntimeIREE), /** * An object which is used to serialize/deserialize, compile, and cache a particular shader class. * * A shader type can manage multiple instance of FShader across mutiple dimensions such as EShaderPlatform, or permutation id. * The number of permutation of a shader type is simply given by GetPermutationCount(). */ class FShaderType { public: #define SHADER_TYPE_ENTRY(Name) Name enum class EShaderTypeForDynamicCast : uint32 { SHADER_TYPE_LIST NumShaderTypes, }; #undef SHADER_TYPE_ENTRY /** * Derived FShaderTypes should derive from this class to pass params to FShader constructor */ class FParameters { public: virtual ~FParameters(){} }; typedef class FShader* (*ConstructSerializedType)(); typedef FShader* (*ConstructCompiledType)(const FShader::CompiledShaderInitializerType& Initializer); typedef bool (*ShouldCompilePermutationType)(const FShaderPermutationParameters&); typedef EShaderPermutationPrecacheRequest (*ShouldPrecachePermutationType)(const FShaderPermutationParameters&); typedef ERayTracingPayloadType(*GetRayTracingPayloadTypeType)(const int32 PermutationId); typedef const FShaderBindingLayout*(*GetShaderBindingLayoutType)(const FShaderPermutationParameters&); #if WITH_EDITOR typedef void (*ModifyCompilationEnvironmentType)(const FShaderPermutationParameters&, FShaderCompilerEnvironment&); typedef bool (*ValidateCompiledResultType)(EShaderPlatform, const FShaderParameterMap&, TArray&); typedef EShaderCompileJobPriority(*GetOverrideJobPriorityType)(); typedef void (*GetPermutationIdStringType)(int32 PermutationId, FString&, bool); #endif // WITH_EDITOR /** @return The global shader factory list. */ static RENDERCORE_API TLinkedList*& GetTypeList(); static RENDERCORE_API FShaderType* GetShaderTypeByName(const TCHAR* Name); static RENDERCORE_API TArray GetShaderTypesByFilename(const TCHAR* Filename, bool bSearchAsRegexFilter = false); static RENDERCORE_API TArray GetShaderTypesByFilenameFilter(const TFunction& InFilenameFilter); /** @return The global shader name to type map */ static RENDERCORE_API TMap& GetNameToTypeMap(); static RENDERCORE_API const TArray& GetSortedTypes(EShaderTypeForDynamicCast Type); /** Initialize FShaderType static members, this must be called before any shader types are created. */ static RENDERCORE_API void Initialize(const TMap >& ShaderFileToUniformBufferVariables); /** Uninitializes FShaderType cached data. */ static RENDERCORE_API void Uninitialize(); /** Minimal initialization constructor. */ RENDERCORE_API FShaderType( EShaderTypeForDynamicCast InShaderTypeForDynamicCast, FTypeLayoutDesc& InTypeLayout, const TCHAR* InName, const TCHAR* InSourceFilename, const TCHAR* InFunctionName, uint32 InFrequency, int32 TotalPermutationCount, ConstructSerializedType InConstructSerializedRef, ConstructCompiledType InConstructCompiledRef, ShouldCompilePermutationType InShouldCompilePermutationRef, ShouldPrecachePermutationType InShouldPrecachePermutationRef, GetRayTracingPayloadTypeType InGetRayTracingPayloadTypeRef, GetShaderBindingLayoutType InGetShaderBindingLayoutTypeRef, #if WITH_EDITOR ModifyCompilationEnvironmentType InModifyCompilationEnvironmentRef, ValidateCompiledResultType InValidateCompiledResultRef, GetOverrideJobPriorityType InGetOverrideJobPriorityRef, #endif // WITH_EDITOR uint32 InTypeSize, const FShaderParametersMetadata* InRootParametersMetadata #if WITH_EDITOR , GetPermutationIdStringType InGetPermutationIdStringRef = nullptr #endif ); RENDERCORE_API virtual ~FShaderType(); /** Constructs a new instance of the shader type for deserialization. */ RENDERCORE_API FShader* ConstructForDeserialization() const; RENDERCORE_API FShader* ConstructCompiled(const FShader::CompiledShaderInitializerType& Initializer) const; RENDERCORE_API bool ShouldCompilePermutation(const FShaderPermutationParameters& Parameters) const; RENDERCORE_API EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FShaderPermutationParameters& Parameters) const; RENDERCORE_API const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters) const; #if WITH_EDITOR RENDERCORE_API void ModifyCompilationEnvironment(const FShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) const; /** * Checks if the shader type should pass compilation for a particular set of parameters. * @param Platform - Platform to validate. * @param ParameterMap - Shader parameters to validate. * @param OutError - List for appending validation errors. */ RENDERCORE_API bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray& OutError) const; /** Returns a custom compile job priority or EShaderCompileJobPriority::None to ignore the customization. */ RENDERCORE_API EShaderCompileJobPriority GetOverrideJobPriority() const; /** * Returns a string representation for the specified permutation Id. * @param PermutationId Zero based index in the half-open range [0, GetPermutationCount()). * @param bFullNames Specifies whether the full shader defines are meant to be included. If false, a short form is returned as comma-separated integers. */ RENDERCORE_API FString GetPermutationIdString(int32 PermutationId, bool bFullNames) const; #endif // WITH_EDITOR RENDERCORE_API ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId) const; /** Calculates a Hash based on this shader type's source code and includes */ RENDERCORE_API const FSHAHash& GetSourceHash(EShaderPlatform ShaderPlatform) const; /** Serializes a shader type reference by name. */ RENDERCORE_API friend FArchive& operator<<(FArchive& Ar,FShaderType*& Ref); /** Hashes a pointer to a shader type. */ friend uint32 GetTypeHash(FShaderType* Ref) { return Ref ? GetTypeHash(Ref->HashedName) : 0u; } // Dynamic casts. inline FGlobalShaderType* GetGlobalShaderType() { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Global) ? reinterpret_cast(this) : nullptr; } inline const FGlobalShaderType* GetGlobalShaderType() const { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Global) ? reinterpret_cast(this) : nullptr; } inline FMaterialShaderType* GetMaterialShaderType() { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Material) ? reinterpret_cast(this) : nullptr; } inline const FMaterialShaderType* GetMaterialShaderType() const { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Material) ? reinterpret_cast(this) : nullptr; } inline FMeshMaterialShaderType* GetMeshMaterialShaderType() { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::MeshMaterial) ? reinterpret_cast(this) : nullptr; } inline const FMeshMaterialShaderType* GetMeshMaterialShaderType() const { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::MeshMaterial) ? reinterpret_cast(this) : nullptr; } inline const FNiagaraShaderType* GetNiagaraShaderType() const { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Niagara) ? reinterpret_cast(this) : nullptr; } inline FNiagaraShaderType* GetNiagaraShaderType() { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Niagara) ? reinterpret_cast(this) : nullptr; } inline const FOpenColorIOShaderType* GetOpenColorIOShaderType() const { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::OCIO) ? reinterpret_cast(this) : nullptr; } inline FOpenColorIOShaderType* GetOpenColorIOShaderType() { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::OCIO) ? reinterpret_cast(this) : nullptr; } inline const FComputeKernelShaderType* GetComputeKernelShaderType() const { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::ComputeKernel) ? reinterpret_cast(this) : nullptr; } inline FComputeKernelShaderType* GetComputeKernelShaderType() { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::ComputeKernel) ? reinterpret_cast(this) : nullptr; } inline const FNNERuntimeIREEShaderType* GetNNERuntimeIREEShaderType() const { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::NNERuntimeIREE) ? reinterpret_cast(this) : nullptr; } inline FNNERuntimeIREEShaderType* GetNNERuntimeIREEShaderType() { return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::NNERuntimeIREE) ? reinterpret_cast(this) : nullptr; } inline const FGlobalShaderType* AsGlobalShaderType() const { checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Global, TEXT("ShaderType %s is not Global"), GetName()); return reinterpret_cast(this); } inline const FMaterialShaderType* AsMaterialShaderType() const { checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Material, TEXT("ShaderType %s is not Material"), GetName()); return reinterpret_cast(this); } inline const FMeshMaterialShaderType* AsMeshMaterialShaderType() const { checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::MeshMaterial, TEXT("ShaderType %s is not MeshMaterial"), GetName()); return reinterpret_cast(this); } inline const FNiagaraShaderType* AsNiagaraShaderType() const { checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Niagara, TEXT("ShaderType %s is not Niagara"), GetName()); return reinterpret_cast(this); } inline const FOpenColorIOShaderType* AsOpenColorIOShaderType() const { checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::OCIO, TEXT("ShaderType %s is not OCIO"), GetName()); return reinterpret_cast(this); } inline EShaderTypeForDynamicCast GetTypeForDynamicCast() const { return ShaderTypeForDynamicCast; } // Accessors. inline const FTypeLayoutDesc& GetLayout() const { return *TypeLayout; } inline EShaderFrequency GetFrequency() const { return (EShaderFrequency)Frequency; } inline const TCHAR* GetName() const { return Name; } inline const FName& GetFName() const { return TypeName; } inline const FHashedName& GetHashedName() const { return HashedName; } inline const TCHAR* GetShaderFilename() const { return SourceFilename; } inline const FHashedName& GetHashedShaderFilename() const { return HashedSourceFilename; } inline const TCHAR* GetFunctionName() const { return FunctionName; } inline uint32 GetTypeSize() const { return TypeSize; } inline int32 GetNumShaders() const { // TODO count this return 0; } inline int32 GetPermutationCount() const { return TotalPermutationCount; } /** Returns the meta data for the root shader parameter struct. */ inline const FShaderParametersMetadata* GetRootParametersMetadata() const { return RootParametersMetadata; } #if WITH_EDITOR const TSet& GetReferencedUniformBuffers() const { return ReferencedUniformBuffers; } /** Adds include statements for uniform buffers that this shader type references. */ RENDERCORE_API void AddUniformBufferIncludesToEnvironment(FShaderCompilerEnvironment& OutEnvironment, EShaderPlatform Platform) const; RENDERCORE_API void UpdateReferencedUniformBufferNames(const TMap>& ShaderFileToUniformBufferVariables); RENDERCORE_API void GetShaderStableKeyParts(struct FStableShaderKeyAndValue& SaveKeyVal); #endif // WITH_EDITOR RENDERCORE_API void DumpDebugInfo(); private: EShaderTypeForDynamicCast ShaderTypeForDynamicCast; const FTypeLayoutDesc* TypeLayout; const TCHAR* Name; FName TypeName; FHashedName HashedName; FHashedName HashedSourceFilename; const TCHAR* SourceFilename; const TCHAR* FunctionName; uint32 Frequency; uint32 TypeSize; int32 TotalPermutationCount; ConstructSerializedType ConstructSerializedRef; ConstructCompiledType ConstructCompiledRef; ShouldCompilePermutationType ShouldCompilePermutationRef; ShouldPrecachePermutationType ShouldPrecachePermutationRef; GetRayTracingPayloadTypeType GetRayTracingPayloadTypeRef; GetShaderBindingLayoutType GetShaderBindingLayoutTypeRef; #if WITH_EDITOR ModifyCompilationEnvironmentType ModifyCompilationEnvironmentRef; ValidateCompiledResultType ValidateCompiledResultRef; GetOverrideJobPriorityType GetOverrideJobPriorityRef; GetPermutationIdStringType GetPermutationIdStringRef; #endif const FShaderParametersMetadata* const RootParametersMetadata; TLinkedList GlobalListLink; friend void RENDERCORE_API DumpShaderStats( EShaderPlatform Platform, EShaderFrequency Frequency ); #if WITH_EDITOR protected: /** * Cache of referenced uniform buffer structs. * These are derived from source files so they need to be flushed when editing and recompiling shaders on the fly. * FShaderType::Initialize will add the referenced uniform buffers, but this set may be updated by FlushShaderFileCache. */ TSet ReferencedUniformBuffers; #endif // WITH_EDITOR }; inline FString LexToString(FShaderType::EShaderTypeForDynamicCast ShaderType) { #define SHADER_TYPE_ENTRY(Name) TEXT(#Name) static const TCHAR* TypeStrings[] = { SHADER_TYPE_LIST }; #undef SHADER_TYPE_ENTRY checkf(ShaderType < FShaderType::EShaderTypeForDynamicCast::NumShaderTypes, TEXT("Invalid shader type")); static_assert((sizeof(TypeStrings) / sizeof(TCHAR*)) == (int)FShaderType::EShaderTypeForDynamicCast::NumShaderTypes); return TypeStrings[static_cast(ShaderType)]; } /** * Registers a shader type in various systems. Should be created as a static field/global. * * Each shader type is collected here, not as an instance but as an accessor, so the actual construction can be deferred. * The collection happens during static init, the actual construction happens later during launch. * The purpose of collecting the types is the CommitAll function, called in LaunchEngineLoop, to ensure all type instances are constructed and registered before other systems start iterating them. */ class FShaderTypeRegistration { public: FShaderTypeRegistration(TFunctionRef LazyShaderTypeAccessor) : LazyShaderTypeAccessor(LazyShaderTypeAccessor) { checkf(!AreShaderTypesInitialized(), TEXT("Shader type was loaded too late, use ELoadingPhase::PostConfigInit on your module to cause it to load earlier. This shader will not be compiled or function. The module and shader will appear in the callstack of this assert.")); GetInstances().Add(this); } static RENDERCORE_API TArray& GetInstances(); // Actually register all the types and clear the array static RENDERCORE_API void CommitAll(); static RENDERCORE_API bool AreShaderTypesInitialized(); private: static bool bShaderTypesInitialized; TFunctionRef LazyShaderTypeAccessor; }; struct FShaderCompiledShaderInitializerType { const FShaderType* Type; const FShaderType::FParameters* Parameters; FShaderTarget Target; TConstArrayView Code; const FShaderParameterMap& ParameterMap; const FSHAHash& OutputHash; FSHAHash MaterialShaderMapHash; const FShaderPipelineType* ShaderPipeline; const FVertexFactoryType* VertexFactoryType; uint32 NumInstructions; uint32 NumTextureSamplers; uint32 CodeSize; int32 PermutationId; TArray ShaderStatistics; RENDERCORE_API FShaderCompiledShaderInitializerType( const FShaderType* InType, const FShaderType::FParameters* InParameters, int32 InPermutationId, const FShaderCompilerOutput& CompilerOutput, const FSHAHash& InMaterialShaderMapHash, const FShaderPipelineType* InShaderPipeline, const FVertexFactoryType* InVertexFactoryType ); }; #if WITH_EDITOR #define SHADER_DECLARE_EDITOR_VTABLE(ShaderClass) \ static void ModifyCompilationEnvironmentImpl(const FShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) \ { \ const typename ShaderClass::FPermutationDomain PermutationVector(Parameters.PermutationId); \ PermutationVector.ModifyCompilationEnvironment(OutEnvironment); \ ShaderClass::ModifyCompilationEnvironment(static_cast(Parameters), OutEnvironment); \ } \ static void GetPermutationIdStringImpl(int32 PermutationId, FString& OutString, bool bFullNames) \ { \ const typename ShaderClass::FPermutationDomain PermutationVector(PermutationId); \ UE::ShaderPermutationUtils::FormatPermutationDomain(PermutationVector, OutString, bFullNames, nullptr); \ } #else #define SHADER_DECLARE_EDITOR_VTABLE(ShaderClass) #endif // WITH_EDITOR #define SHADER_DECLARE_VTABLE(ShaderClass) \ static FShader* ConstructSerializedInstance() { return new ShaderClass(); } \ static FShader* ConstructCompiledInstance(const typename FShader::CompiledShaderInitializerType& Initializer) \ { return new ShaderClass(static_cast(Initializer)); }\ static bool ShouldCompilePermutationImpl(const FShaderPermutationParameters& Parameters) \ { return ShaderClass::ShouldCompilePermutation(static_cast(Parameters)); } \ static EShaderPermutationPrecacheRequest ShouldPrecachePermutationImpl(const FShaderPermutationParameters& Parameters) \ { return ShaderClass::ShouldPrecachePermutation(static_cast(Parameters)); } \ SHADER_DECLARE_EDITOR_VTABLE(ShaderClass) #define INTERNAL_DECLARE_SHADER_TYPE_COMMON(ShaderClass,ShaderMetaTypeShortcut,RequiredAPI) \ public: \ using ShaderMetaType = F##ShaderMetaTypeShortcut##ShaderType; \ using ShaderMapType = F##ShaderMetaTypeShortcut##ShaderMap; \ \ static RequiredAPI ShaderMetaType& GetStaticType(); \ private: \ static FShaderTypeRegistration ShaderTypeRegistration; \ public: \ \ SHADER_DECLARE_VTABLE(ShaderClass) /** * A macro to declare a new shader type. This should be called in the class body of the new shader type. * @param ShaderClass - The name of the class representing an instance of the shader type. * @param ShaderMetaTypeShortcut - The shortcut for the shader meta type: simple, material, meshmaterial, etc. The shader meta type * controls */ #define DECLARE_EXPORTED_SHADER_TYPE(ShaderClass,ShaderMetaTypeShortcut,RequiredAPI, ...) \ INTERNAL_DECLARE_SHADER_TYPE_COMMON(ShaderClass, ShaderMetaTypeShortcut, RequiredAPI); \ DECLARE_EXPORTED_TYPE_LAYOUT(ShaderClass, RequiredAPI, NonVirtual); \ public: #define DECLARE_SHADER_TYPE(ShaderClass,ShaderMetaTypeShortcut,...) \ DECLARE_EXPORTED_SHADER_TYPE(ShaderClass,ShaderMetaTypeShortcut,, ##__VA_ARGS__) #define DECLARE_SHADER_TYPE_EXPLICIT_BASES(ShaderClass,ShaderMetaTypeShortcut,...) \ INTERNAL_DECLARE_SHADER_TYPE_COMMON(ShaderClass, ShaderMetaTypeShortcut,); \ DECLARE_EXPORTED_TYPE_LAYOUT_EXPLICIT_BASES(ShaderClass,, NonVirtual, __VA_ARGS__); \ public: #if WITH_EDITOR #define SHADER_TYPE_EDITOR_VTABLE(ShaderClass) \ , ShaderClass::ModifyCompilationEnvironmentImpl \ , ShaderClass::ValidateCompiledResult \ , ShaderClass::GetOverrideJobPriority #define SHADER_TYPE_EDITOR_PERMUTATION_METADATA(ShaderClass) \ , ShaderClass::GetPermutationIdStringImpl #else #define SHADER_TYPE_EDITOR_VTABLE(ShaderClass) #define SHADER_TYPE_EDITOR_PERMUTATION_METADATA(ShaderClass) #endif #define SHADER_TYPE_VTABLE(ShaderClass) \ ShaderClass::ConstructSerializedInstance, \ ShaderClass::ConstructCompiledInstance, \ ShaderClass::ShouldCompilePermutationImpl, \ ShaderClass::ShouldPrecachePermutationImpl, \ ShaderClass::GetRayTracingPayloadType, \ ShaderClass::GetShaderBindingLayout \ SHADER_TYPE_EDITOR_VTABLE(ShaderClass) #if !UE_BUILD_DOCS /** A macro to implement a shader type. */ #define IMPLEMENT_SHADER_TYPE(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency) \ IMPLEMENT_UNREGISTERED_TEMPLATE_TYPE_LAYOUT(TemplatePrefix, ShaderClass); \ TemplatePrefix \ ShaderClass::ShaderMetaType& ShaderClass::GetStaticType() \ { \ static ShaderClass::ShaderMetaType StaticType( \ ShaderClass::StaticGetTypeLayout(), \ TEXT(#ShaderClass), \ SourceFilename, \ FunctionName, \ Frequency, \ ShaderClass::FPermutationDomain::PermutationCount, \ SHADER_TYPE_VTABLE(ShaderClass), \ sizeof(ShaderClass), \ ShaderClass::GetRootParametersMetadata() \ SHADER_TYPE_EDITOR_PERMUTATION_METADATA(ShaderClass) \ ); \ return StaticType; \ } \ TemplatePrefix FShaderTypeRegistration ShaderClass::ShaderTypeRegistration{TFunctionRef<::FShaderType&()>{ShaderClass::GetStaticType}}; /** A macro to implement a shader type. Shader name is got from GetDebugName(), which is helpful for templated shaders. */ #define IMPLEMENT_SHADER_TYPE_WITH_DEBUG_NAME(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency) \ IMPLEMENT_UNREGISTERED_TEMPLATE_TYPE_LAYOUT(TemplatePrefix, ShaderClass); \ TemplatePrefix \ typename ShaderClass::ShaderMetaType& ShaderClass::GetStaticType() \ { \ static typename ShaderClass::ShaderMetaType StaticType( \ ShaderClass::StaticGetTypeLayout(), \ ShaderClass::GetDebugName(), \ SourceFilename, \ FunctionName, \ Frequency, \ ShaderClass::FPermutationDomain::PermutationCount, \ SHADER_TYPE_VTABLE(ShaderClass), \ sizeof(ShaderClass), \ ShaderClass::GetRootParametersMetadata() \ SHADER_TYPE_EDITOR_PERMUTATION_METADATA(ShaderClass) \ ); \ return StaticType; \ } \ TemplatePrefix FShaderTypeRegistration ShaderClass::ShaderTypeRegistration{TFunctionRef<::FShaderType&()>{ShaderClass::GetStaticType}}; /** A macro to implement a templated shader type, the function name and the source filename comes from the class. */ #define IMPLEMENT_SHADER_TYPE2_WITH_TEMPLATE_PREFIX(TemplatePrefix,ShaderClass,Frequency) \ IMPLEMENT_UNREGISTERED_TEMPLATE_TYPE_LAYOUT(TemplatePrefix, ShaderClass); \ TemplatePrefix \ ShaderClass::ShaderMetaType& ShaderClass::GetStaticType() \ { \ static ShaderClass::ShaderMetaType StaticType( \ ShaderClass::StaticGetTypeLayout(), \ TEXT(#ShaderClass), \ ShaderClass::GetSourceFilename(), \ ShaderClass::GetFunctionName(), \ Frequency, \ ShaderClass::FPermutationDomain::PermutationCount, \ SHADER_TYPE_VTABLE(ShaderClass), \ sizeof(ShaderClass), \ ShaderClass::GetRootParametersMetadata() \ SHADER_TYPE_EDITOR_PERMUTATION_METADATA(ShaderClass) \ ); \ return StaticType; \ } \ TemplatePrefix FShaderTypeRegistration ShaderClass::ShaderTypeRegistration{TFunctionRef<::FShaderType&()>{ShaderClass::GetStaticType}}; #define IMPLEMENT_SHADER_TYPE2(ShaderClass,Frequency) \ IMPLEMENT_SHADER_TYPE2_WITH_TEMPLATE_PREFIX(template<>, ShaderClass, Frequency) /** todo: this should replace IMPLEMENT_SHADER_TYPE */ #define IMPLEMENT_SHADER_TYPE3(ShaderClass,Frequency) \ IMPLEMENT_UNREGISTERED_TEMPLATE_TYPE_LAYOUT(,ShaderClass); \ ShaderClass::ShaderMetaType& ShaderClass::GetStaticType() \ { \ static ShaderClass::ShaderMetaType StaticType( \ ShaderClass::StaticGetTypeLayout(), \ TEXT(#ShaderClass), \ ShaderClass::GetSourceFilename(), \ ShaderClass::GetFunctionName(), \ Frequency, \ ShaderClass::FPermutationDomain::PermutationCount, \ SHADER_TYPE_VTABLE(ShaderClass), \ sizeof(ShaderClass), \ ShaderClass::GetRootParametersMetadata() \ SHADER_TYPE_EDITOR_PERMUTATION_METADATA(ShaderClass) \ ); \ return StaticType; \ } \ FShaderTypeRegistration ShaderClass::ShaderTypeRegistration{TFunctionRef<::FShaderType&()>{ShaderClass::GetStaticType}}; #endif // !UE_BUILD_DOCS #define IMPLEMENT_SHADER_TYPE4_WITH_TEMPLATE_PREFIX(TemplatePrefix,RequiredAPI,ShaderClass,Frequency) \ IMPLEMENT_UNREGISTERED_TEMPLATE_TYPE_LAYOUT(TemplatePrefix, ShaderClass); \ TemplatePrefix RequiredAPI \ ShaderClass::ShaderMetaType& ShaderClass::GetStaticType() \ { \ static ShaderClass::ShaderMetaType StaticType( \ ShaderClass::StaticGetTypeLayout(), \ TEXT(#ShaderClass), \ ShaderClass::GetSourceFilename(), \ ShaderClass::GetFunctionName(), \ Frequency, \ ShaderClass::FPermutationDomain::PermutationCount, \ SHADER_TYPE_VTABLE(ShaderClass), \ sizeof(ShaderClass), \ ShaderClass::GetRootParametersMetadata() \ SHADER_TYPE_EDITOR_PERMUTATION_METADATA(ShaderClass) \ ); \ return StaticType; \ } \ TemplatePrefix FShaderTypeRegistration ShaderClass::ShaderTypeRegistration{TFunctionRef<::FShaderType&()>{ShaderClass::GetStaticType}}; #if WITH_EDITOR #define IMPLEMENT_SHADER_TYPE_CONSTRUCTOR(ShaderClass, InShaderTypeForDynamicCast, InShaderClassUserFriendlyName) \ ShaderClass( \ FTypeLayoutDesc& InTypeLayout, \ const TCHAR* InName, \ const TCHAR* InSourceFilename, \ const TCHAR* InFunctionName, \ uint32 InFrequency, \ int32 InTotalPermutationCount, \ ConstructSerializedType InConstructSerializedRef, \ ConstructCompiledType InConstructCompiledRef, \ ShouldCompilePermutationType InShouldCompilePermutationRef, \ ShouldPrecachePermutationType InShouldPrecachePermutationRef, \ GetRayTracingPayloadTypeType InGetRayTracingPayloadTypeRef, \ GetShaderBindingLayoutType InGetShaderBindingLayoutTypeRef, \ ModifyCompilationEnvironmentType InModifyCompilationEnvironmentRef, \ ValidateCompiledResultType InValidateCompiledResultRef, \ GetOverrideJobPriorityType InGetOverrideJobPriorityRef, \ uint32 InTypeSize, \ const FShaderParametersMetadata* InRootParametersMetadata = nullptr, \ GetPermutationIdStringType InGetPermutationIdStringRef = nullptr \ ): \ FShaderType( \ InShaderTypeForDynamicCast, \ InTypeLayout, \ InName, \ InSourceFilename, \ InFunctionName, \ InFrequency, \ InTotalPermutationCount, \ InConstructSerializedRef, \ InConstructCompiledRef, \ InShouldCompilePermutationRef, \ InShouldPrecachePermutationRef, \ InGetRayTracingPayloadTypeRef, \ InGetShaderBindingLayoutTypeRef, \ InModifyCompilationEnvironmentRef, \ InValidateCompiledResultRef, \ InGetOverrideJobPriorityRef, \ InTypeSize, \ InRootParametersMetadata, \ InGetPermutationIdStringRef \ ) \ { \ checkf(FPaths::GetExtension(InSourceFilename) == TEXT("usf"), \ TEXT("Incorrect virtual shader path extension for %s '%s': Only .usf files should be compiled."), \ InShaderClassUserFriendlyName, InSourceFilename); \ } #else #define IMPLEMENT_SHADER_TYPE_CONSTRUCTOR(ShaderClass, InShaderTypeForDynamicCast, InShaderClassUserFriendlyName) \ ShaderClass( \ FTypeLayoutDesc& InTypeLayout, \ const TCHAR* InName, \ const TCHAR* InSourceFilename, \ const TCHAR* InFunctionName, \ uint32 InFrequency, \ int32 InTotalPermutationCount, \ ConstructSerializedType InConstructSerializedRef, \ ConstructCompiledType InConstructCompiledRef, \ ShouldCompilePermutationType InShouldCompilePermutationRef, \ ShouldPrecachePermutationType InShouldPrecachePermutationRef, \ GetRayTracingPayloadTypeType InGetRayTracingPayloadTypeRef, \ GetShaderBindingLayoutType InGetShaderBindingLayoutTypeRef, \ uint32 InTypeSize, \ const FShaderParametersMetadata* InRootParametersMetadata = nullptr \ ): \ FShaderType( \ InShaderTypeForDynamicCast, \ InTypeLayout, \ InName, \ InSourceFilename, \ InFunctionName, \ InFrequency, \ InTotalPermutationCount, \ InConstructSerializedRef, \ InConstructCompiledRef, \ InShouldCompilePermutationRef, \ InShouldPrecachePermutationRef, \ InGetRayTracingPayloadTypeRef, \ InGetShaderBindingLayoutTypeRef, \ InTypeSize, \ InRootParametersMetadata \ ) \ { \ checkf(FPaths::GetExtension(InSourceFilename) == TEXT("usf"), \ TEXT("Incorrect virtual shader path extension for %s '%s': Only .usf files should be compiled."), \ InShaderClassUserFriendlyName, InSourceFilename); \ } #endif // WITH_EDITOR // Binding of a set of shader stages in a single pipeline class FShaderPipelineType { public: // Set bShouldOptimizeUnusedOutputs to true if we want unique FShaders for each shader pipeline // Set bShouldOptimizeUnusedOutputs to false if the FShaders will point to the individual shaders in the map RENDERCORE_API FShaderPipelineType( const TCHAR* InName, const FShaderType* InVertexOrMeshShader, const FShaderType* InGeometryOrAmplificationShader, const FShaderType* InPixelShader, bool bInIsMeshPipeline, bool bInShouldOptimizeUnusedOutputs); RENDERCORE_API ~FShaderPipelineType(); inline bool HasMeshShader() const { return AllStages[SF_Mesh] != nullptr; } inline bool HasGeometry() const { return AllStages[SF_Geometry] != nullptr; } inline bool HasPixelShader() const { return AllStages[SF_Pixel] != nullptr; } inline const FShaderType* GetShader(EShaderFrequency Frequency) const { check(Frequency < SF_NumFrequencies); return AllStages[Frequency]; } inline FName GetFName() const { return TypeName; } inline TCHAR const* GetName() const { return Name; } inline const FHashedName& GetHashedName() const { return HashedName; } inline const FHashedName& GetHashedPrimaryShaderFilename() const { return HashedPrimaryShaderFilename; } // Returns an array of valid stages, sorted from PS->GS->DS->HS->VS, no gaps if missing stages inline const TArray& GetStages() const { return Stages; } static RENDERCORE_API TLinkedList*& GetTypeList(); static RENDERCORE_API const TArray& GetSortedTypes(FShaderType::EShaderTypeForDynamicCast Type); /** @return The global shader pipeline name to type map */ static RENDERCORE_API TMap& GetNameToTypeMap(); static RENDERCORE_API const FShaderPipelineType* GetShaderPipelineTypeByName(const FHashedName& Name); /** Initialize static members, this must be called before any shader types are created. */ static RENDERCORE_API void Initialize(); static RENDERCORE_API TArray GetShaderPipelineTypesByFilename(const TCHAR* Filename, bool bSearchAsRegexFilter = false); static RENDERCORE_API TArray GetShaderPipelineTypesByFilenameFilter(const TFunction& InFilenameFilter); /** Serializes a shader type reference by name. */ RENDERCORE_API friend FArchive& operator<<(FArchive& Ar, const FShaderPipelineType*& Ref); /** Hashes a pointer to a shader type. */ friend uint32 GetTypeHash(FShaderPipelineType* Ref) { return Ref ? Ref->HashIndex : 0; } friend uint32 GetTypeHash(const FShaderPipelineType* Ref) { return Ref ? Ref->HashIndex : 0; } // Check if this pipeline is built of specific types bool IsGlobalTypePipeline() const { return Stages[0]->GetGlobalShaderType() != nullptr; } bool IsMaterialTypePipeline() const { return Stages[0]->GetMaterialShaderType() != nullptr; } bool IsMeshMaterialTypePipeline() const { return Stages[0]->GetMeshMaterialShaderType() != nullptr; } RENDERCORE_API bool ShouldOptimizeUnusedOutputs(EShaderPlatform Platform) const; /** Calculates a Hash based on this shader pipeline type stages' source code and includes */ RENDERCORE_API const FSHAHash& GetSourceHash(EShaderPlatform ShaderPlatform) const; RENDERCORE_API bool ShouldCompilePermutation(const FShaderPermutationParameters& Parameters) const; RENDERCORE_API EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FShaderPermutationParameters& Parameters) const; protected: const TCHAR* const Name; FName TypeName; FHashedName HashedName; FHashedName HashedPrimaryShaderFilename; // Pipeline Stages, ordered from lowest (usually PS) to highest (VS). Guaranteed at least one stage (for VS). TArray Stages; const FShaderType* AllStages[SF_NumFrequencies]; TLinkedList GlobalListLink; uint32 HashIndex; bool bShouldOptimizeUnusedOutputs; static RENDERCORE_API bool bInitialized; }; #if !UE_BUILD_DOCS // Vertex+Pixel #define IMPLEMENT_SHADERPIPELINE_TYPE_VSPS(PipelineName, VertexShaderType, PixelShaderType, bRemoveUnused) \ static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &VertexShaderType::GetStaticType(), nullptr, &PixelShaderType::GetStaticType(), false, bRemoveUnused); // Only VS #define IMPLEMENT_SHADERPIPELINE_TYPE_VS(PipelineName, VertexShaderType, bRemoveUnused) \ static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &VertexShaderType::GetStaticType(), nullptr, nullptr, false, bRemoveUnused); // Vertex+Geometry+Pixel #define IMPLEMENT_SHADERPIPELINE_TYPE_VSGSPS(PipelineName, VertexShaderType, GeometryShaderType, PixelShaderType, bRemoveUnused) \ static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &VertexShaderType::GetStaticType(), &GeometryShaderType::GetStaticType(), &PixelShaderType::GetStaticType(), false, bRemoveUnused); // Vertex+Geometry #define IMPLEMENT_SHADERPIPELINE_TYPE_VSGS(PipelineName, VertexShaderType, GeometryShaderType, bRemoveUnused) \ static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &VertexShaderType::GetStaticType(), &GeometryShaderType::GetStaticType(), nullptr, false, bRemoveUnused); // Mesh+Pixel #define IMPLEMENT_SHADERPIPELINE_TYPE_MSPS(PipelineName, MeshShaderType, PixelShaderType, bRemoveUnused) \ static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &MeshShaderType::GetStaticType(), nullptr, &PixelShaderType::GetStaticType(), true, bRemoveUnused); // Mesh+Amplification+Pixel #define IMPLEMENT_SHADERPIPELINE_TYPE_MSASPS(PipelineName, MeshShaderType, AmplificationShaderType, PixelShaderType, bRemoveUnused) \ static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &MeshShaderType::GetStaticType(), &AmplificationShaderType::GetStaticType(), &PixelShaderType::GetStaticType(), true, bRemoveUnused); #endif /** Encapsulates a dependency on a shader type and saved state from that shader type. */ class FShaderTypeDependency { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderTypeDependency, RENDERCORE_API, NonVirtual); public: FShaderTypeDependency() : PermutationId(0) {} FShaderTypeDependency(FShaderType* InShaderType, EShaderPlatform ShaderPlatform) : ShaderTypeName(InShaderType->GetHashedName()) , PermutationId(0) { if (InShaderType) { SourceHash = InShaderType->GetSourceHash(ShaderPlatform); } } friend FArchive& operator<<(FArchive& Ar,class FShaderTypeDependency& Ref) { Ar.UsingCustomVersion(FRenderingObjectVersion::GUID); Ar << Ref.ShaderTypeName; Ar << Ref.SourceHash; if (Ar.CustomVer(FRenderingObjectVersion::GUID) >= FRenderingObjectVersion::ShaderPermutationId) { Ar << Ref.PermutationId; } return Ar; } bool operator==(const FShaderTypeDependency& Reference) const { return ShaderTypeName == Reference.ShaderTypeName && PermutationId == Reference.PermutationId && SourceHash == Reference.SourceHash; } bool operator!=(const FShaderTypeDependency& Reference) const { return !(*this == Reference); } /** Call GetShaderFileHash to get the cached value for the filename's hash in the current process. */ RENDERCORE_API void RefreshCachedSourceHash(EShaderPlatform ShaderPlatform); /** Shader type */ LAYOUT_FIELD(FHashedName, ShaderTypeName); /** Unique permutation identifier of the global shader type. */ LAYOUT_FIELD(int32, PermutationId); /** Used to detect changes to the shader source files. This is always present, as this type is sometimes frozen. */ LAYOUT_FIELD(FSHAHash, SourceHash); private: #if WITH_EDITOR // Compact binary API with hidden friend operator<< RENDERCORE_API void Save(FCbWriter& Writer) const; bool TryLoad(FCbFieldView Field); friend inline FCbWriter& operator<<(FCbWriter& Writer, const FShaderTypeDependency& Value) { Value.Save(Writer); return Writer; } friend RENDERCORE_API bool LoadFromCompactBinary(FCbFieldView Field, FShaderTypeDependency& OutValue); #endif }; class FShaderPipelineTypeDependency { public: FShaderPipelineTypeDependency() {} FShaderPipelineTypeDependency(const FShaderPipelineType* InShaderPipelineType, EShaderPlatform ShaderPlatform) : ShaderPipelineTypeName(InShaderPipelineType->GetHashedName()) { if (InShaderPipelineType) { StagesSourceHash = InShaderPipelineType->GetSourceHash(ShaderPlatform); } } /** Shader Pipeline type */ FHashedName ShaderPipelineTypeName; /** Used to detect changes to the shader source files. */ FSHAHash StagesSourceHash; friend FArchive& operator<<(FArchive& Ar, class FShaderPipelineTypeDependency& Ref) { Ar << Ref.ShaderPipelineTypeName; Ar << Ref.StagesSourceHash; return Ar; } bool operator==(const FShaderPipelineTypeDependency& Reference) const { return ShaderPipelineTypeName == Reference.ShaderPipelineTypeName && StagesSourceHash == Reference.StagesSourceHash; } bool operator!=(const FShaderPipelineTypeDependency& Reference) const { return !(*this == Reference); } /** Call GetShaderFileHash to get the cached value for the filename's hash in the current process. */ RENDERCORE_API void RefreshCachedSourceHash(EShaderPlatform ShaderPlatform); private: #if WITH_EDITOR // Compact binary API with hidden friend operator<< RENDERCORE_API void Save(FCbWriter& Writer) const; bool TryLoad(FCbFieldView Field); friend inline FCbWriter& operator<<(FCbWriter& Writer, const FShaderPipelineTypeDependency& Value) { Value.Save(Writer); return Writer; } friend RENDERCORE_API bool LoadFromCompactBinary(FCbFieldView Field, FShaderPipelineTypeDependency& OutValue); #endif }; /** Used to compare two shader types by name. */ class FCompareShaderTypes { public: inline bool operator()(const FShaderType& A, const FShaderType& B ) const { int32 AL = FCString::Strlen(A.GetName()); int32 BL = FCString::Strlen(B.GetName()); if ( AL == BL ) { return FCString::Strncmp(A.GetName(), B.GetName(), AL) > 0; } return AL > BL; } }; // A Shader Pipeline instance with compiled stages class FShaderPipeline { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderPipeline, RENDERCORE_API, NonVirtual); public: explicit FShaderPipeline(const FShaderPipelineType* InType) : TypeName(InType->GetHashedName()) { FMemory::Memzero(&PermutationIds, sizeof(PermutationIds)); } RENDERCORE_API ~FShaderPipeline(); RENDERCORE_API void AddShader(FShader* Shader, int32 PermutationId); RENDERCORE_API FShader* FindOrAddShader(FShader* Shader, int32 PermutationId); inline uint32 GetNumShaders() const { uint32 NumShaders = 0u; for (uint32 i = 0u; i < SF_NumGraphicsFrequencies; ++i) { if (Shaders[i].IsValid()) { ++NumShaders; } } return NumShaders; } // Find a shader inside the pipeline template ShaderType* GetShader(const FShaderMapPointerTable& InPtrTable) { const FShaderType& Type = ShaderType::GetStaticType(); const EShaderFrequency Frequency = Type.GetFrequency(); if (Frequency < SF_NumGraphicsFrequencies && Shaders[Frequency].IsValid()) { FShader* Shader = Shaders[Frequency].GetChecked(); if (Shader->GetType(InPtrTable) == &Type) { return static_cast(Shader); } } return nullptr; } FShader* GetShader(EShaderFrequency Frequency) { check(Frequency < SF_NumGraphicsFrequencies); return Shaders[Frequency]; } const FShader* GetShader(EShaderFrequency Frequency) const { check(Frequency < SF_NumGraphicsFrequencies); return Shaders[Frequency]; } inline TArray> GetShaders(const FShaderMapBase& InShaderMap) const { TArray> Result; for (uint32 i = 0u; i < SF_NumGraphicsFrequencies; ++i) { if (Shaders[i].IsValid()) { Result.Add(TShaderRef(Shaders[i].GetChecked(), InShaderMap)); } } return Result; } RENDERCORE_API void Validate(const FShaderPipelineType* InPipelineType) const; RENDERCORE_API void Finalize(const FShaderMapResourceCode* Code); enum EFilter { EAll, // All pipelines EOnlyShared, // Only pipelines with shared shaders EOnlyUnique, // Only pipelines with unique shaders }; /** Saves stable keys for the shaders in the pipeline */ #if WITH_EDITOR RENDERCORE_API void SaveShaderStableKeys(const FShaderMapPointerTable& InPtrTable, EShaderPlatform TargetShaderPlatform, const struct FStableShaderKeyAndValue& SaveKeyVal) const; #endif // WITH_EDITOR LAYOUT_FIELD(FHashedName, TypeName); LAYOUT_ARRAY(TMemoryImagePtr, Shaders, SF_NumGraphicsFrequencies); LAYOUT_ARRAY(int32, PermutationIds, SF_NumGraphicsFrequencies); }; inline bool operator<(const FShaderPipeline& Lhs, const FShaderPipeline& Rhs) { return Lhs.TypeName.GetHash() < Rhs.TypeName.GetHash(); } class FShaderPipelineRef { public: FShaderPipelineRef() : ShaderPipeline(nullptr), ShaderMap(nullptr) {} FShaderPipelineRef(FShaderPipeline* InPipeline, const FShaderMapBase& InShaderMap) : ShaderPipeline(InPipeline), ShaderMap(&InShaderMap) { checkSlow(InPipeline); } inline bool IsValid() const { return ShaderPipeline != nullptr; } inline bool IsNull() const { return ShaderPipeline == nullptr; } template TShaderRef GetShader() const { return TShaderRef(ShaderPipeline->GetShader(GetPointerTable()), *ShaderMap); } TShaderRef GetShader(EShaderFrequency Frequency) const { return TShaderRef(ShaderPipeline->GetShader(Frequency), *ShaderMap); } inline TArray> GetShaders() const { return ShaderPipeline->GetShaders(*ShaderMap); } inline FShaderPipeline* GetPipeline() const { return ShaderPipeline; } FShaderMapResource* GetResource() const; const FShaderMapPointerTable& GetPointerTable() const; inline FShaderPipeline* operator->() const { check(ShaderPipeline); return ShaderPipeline; } private: FShaderPipeline* ShaderPipeline; const FShaderMapBase* ShaderMap; }; DECLARE_DELEGATE_EightParams(FShaderListReport, const TCHAR*, const TCHAR*, const TCHAR*, const TCHAR*, uint32, uint32, const TCHAR*, void*); /** A collection of shaders of different types */ class FShaderMapContent { DECLARE_EXPORTED_TYPE_LAYOUT(FShaderMapContent, RENDERCORE_API, NonVirtual); public: struct FProjectShaderPipelineToKey { inline FHashedName operator()(const FShaderPipeline* InShaderPipeline) { return InShaderPipeline->TypeName; } }; /** Default constructor. */ RENDERCORE_API explicit FShaderMapContent(EShaderPlatform InPlatform); /** Destructor ensures pipelines cleared up. */ RENDERCORE_API ~FShaderMapContent(); RENDERCORE_API EShaderPlatform GetShaderPlatform() const; RENDERCORE_API void Validate(const FShaderMapBase& InShaderMap) const; /** Finds the shader with the given type. Asserts on failure. */ template ShaderType* GetShader(int32 PermutationId = 0) const { FShader* Shader = GetShader(&ShaderType::GetStaticType(), PermutationId); checkf(Shader != nullptr, TEXT("Failed to find shader type %s in Platform %s"), ShaderType::GetStaticType().GetName(), *LegacyShaderPlatformToShaderFormat(GetShaderPlatform()).ToString()); return static_cast(Shader); } /** Finds the shader with the given type. Asserts on failure. */ template ShaderType* GetShader( const typename ShaderType::FPermutationDomain& PermutationVector ) const { return GetShader( PermutationVector.ToDimensionValueId() ); } /** Finds the shader with the given type. May return NULL. */ FShader* GetShader(const FShaderType* ShaderType, int32 PermutationId = 0) const { return GetShader(ShaderType->GetHashedName(), PermutationId); } /** Finds the shader with the given type name. May return NULL. */ RENDERCORE_API FShader* GetShader(const FHashedName& TypeName, int32 PermutationId = 0) const; /** Finds the shader with the given type. */ bool HasShader(const FHashedName& TypeName, int32 PermutationId) const { const FShader* Shader = GetShader(TypeName, PermutationId); return Shader != nullptr; } bool HasShader(const FShaderType* Type, int32 PermutationId) const { return HasShader(Type->GetHashedName(), PermutationId); } inline TArrayView> GetShaders() const { return Shaders; } inline TArrayView> GetShaderPipelines() const { return ShaderPipelines; } RENDERCORE_API void AddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader); RENDERCORE_API FShader* FindOrAddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader); RENDERCORE_API void AddShaderPipeline(FShaderPipeline* Pipeline); RENDERCORE_API FShaderPipeline* FindOrAddShaderPipeline(FShaderPipeline* Pipeline); /** * Removes the shader of the given type from the shader map * @param Type Shader type to remove the entry for */ RENDERCORE_API void RemoveShaderTypePermutaion(const FHashedName& TypeName, int32 PermutationId); inline void RemoveShaderTypePermutaion(const FShaderType* Type, int32 PermutationId) { RemoveShaderTypePermutaion(Type->GetHashedName(), PermutationId); } RENDERCORE_API void RemoveShaderPipelineType(const FShaderPipelineType* ShaderPipelineType); #if !UE_BUILD_SHIPPING RENDERCORE_API void DumpShaderList(const FShaderMapBase& InShaderMap, FShaderListReport& Out) const; #endif // !UE_BUILD_SHIPPING /** Builds a list of the shaders in a shader map. */ RENDERCORE_API void GetShaderList(const FShaderMapBase& InShaderMap, const FSHAHash& InMaterialShaderMapHash, TMap>& OutShaders) const; /** Builds a list of the shaders in a shader map. Key is FShaderType::TypeName */ RENDERCORE_API void GetShaderList(const FShaderMapBase& InShaderMap, TMap>& OutShaders) const; /** Builds a list of the shader pipelines in a shader map. */ RENDERCORE_API void GetShaderPipelineList(const FShaderMapBase& InShaderMap, TArray& OutShaderPipelines, FShaderPipeline::EFilter Filter) const; #if WITH_EDITOR RENDERCORE_API uint32 GetMaxTextureSamplersShaderMap(const FShaderMapBase& InShaderMap) const; RENDERCORE_API void GetOutdatedTypes(const FShaderMapBase& InShaderMap, TArray& OutdatedShaderTypes, TArray& OutdatedShaderPipelineTypes, TArray& OutdatedFactoryTypes) const; RENDERCORE_API void SaveShaderStableKeys(const FShaderMapBase& InShaderMap, EShaderPlatform TargetShaderPlatform, const struct FStableShaderKeyAndValue& SaveKeyVal); #endif // WITH_EDITOR #if WITH_EDITORONLY_DATA RENDERCORE_API TArray GetShaderStatistics(const FShaderMapBase& InShaderMap, FShaderType* ShaderType) const; #endif // WITH_EDITORONLY_DATA /** @return true if the map is empty */ inline bool IsEmpty() const { return Shaders.Num() == 0; } /** @return The number of shaders in the map. */ RENDERCORE_API uint32 GetNumShaders() const; /** @return The number of shader pipelines in the map. */ inline uint32 GetNumShaderPipelines() const { return ShaderPipelines.Num(); } /** clears out all shaders and deletes shader pipelines held in the map */ RENDERCORE_API void Empty(); inline FShaderPipeline* GetShaderPipeline(const FHashedName& PipelineTypeName) const { const int32 Index = Algo::BinarySearchBy(ShaderPipelines, PipelineTypeName, FProjectShaderPipelineToKey()); return (Index != INDEX_NONE) ? ShaderPipelines[Index].Get() : nullptr; } inline FShaderPipeline* GetShaderPipeline(const FShaderPipelineType* PipelineType) const { return GetShaderPipeline(PipelineType->GetHashedName()); } inline bool HasShaderPipeline(const FHashedName& PipelineTypeName) const { return GetShaderPipeline(PipelineTypeName) != nullptr; } inline bool HasShaderPipeline(const FShaderPipelineType* PipelineType) const { return (GetShaderPipeline(PipelineType) != nullptr); } RENDERCORE_API uint32 GetMaxNumInstructionsForShader(const FShaderMapBase& InShaderMap, FShaderType* ShaderType) const; RENDERCORE_API void Finalize(const FShaderMapResourceCode* Code); RENDERCORE_API void UpdateHash(FSHA1& Hasher) const; protected: RENDERCORE_API void EmptyShaderPipelines(); using FMemoryImageHashTable = THashTable; LAYOUT_FIELD(FMemoryImageHashTable, ShaderHash); LAYOUT_FIELD(TMemoryImageArray, ShaderTypes); LAYOUT_FIELD(TMemoryImageArray, ShaderPermutations); LAYOUT_FIELD(TMemoryImageArray>, Shaders); LAYOUT_FIELD(TMemoryImageArray>, ShaderPipelines); /** The ShaderPlatform Name this shader map was compiled with */ LAYOUT_FIELD(FMemoryImageName, ShaderPlatformName); }; class FShaderMapBase { public: RENDERCORE_API virtual ~FShaderMapBase(); RENDERCORE_API FShaderMapResourceCode* GetResourceCode(); inline FShaderMapResource* GetResource() const { return Resource; } inline FShaderMapResource* GetResourceChecked() const { check(Resource); return Resource; } inline const FShaderMapPointerTable& GetPointerTable() const { check(PointerTable); return *PointerTable; } inline const FShaderMapContent* GetContent() const { return Content.Object; } inline FShaderMapContent* GetMutableContent() { UnfreezeContent(); return Content.Object; } /** Builds a list of the shaders in a shader map. Key is FShaderType::TypeName */ virtual void GetShaderList(TMap>& OutShaders) const = 0; virtual void GetShaderPipelineList(TArray& OutShaderPipelines) const = 0; #if !UE_BUILD_SHIPPING virtual void DumpShaderList(FShaderListReport& Out) const { GetContent()->DumpShaderList(*this, Out); } #endif // !UE_BUILD_SHIPPING inline EShaderPlatform GetShaderPlatform() const { return Content.Object ? Content.Object->GetShaderPlatform() : SP_NumPlatforms; } inline uint32 GetFrozenContentSize() const { return Content.FrozenSize; } RENDERCORE_API void AssignContent(TMemoryImageObject InContent); RENDERCORE_API void FinalizeContent(); RENDERCORE_API void UnfreezeContent(); RENDERCORE_API bool Serialize(FShaderSerializeContext& Ctx); EShaderPermutationFlags GetPermutationFlags() const { return PermutationFlags; } RENDERCORE_API FString ToString() const; #if WITH_EDITORONLY_DATA RENDERCORE_API TArray GetShaderStatistics(FShaderType* ShaderType) const; RENDERCORE_API TArray GetShaderStatistics(FShader& Shader) const; #endif #if WITH_EDITOR inline void GetOutdatedTypes(TArray& OutdatedShaderTypes, TArray& OutdatedShaderPipelineTypes, TArray& OutdatedFactoryTypes) const { Content.Object->GetOutdatedTypes(*this, OutdatedShaderTypes, OutdatedShaderPipelineTypes, OutdatedFactoryTypes); } void SaveShaderStableKeys(EShaderPlatform TargetShaderPlatform, const struct FStableShaderKeyAndValue& SaveKeyVal) { Content.Object->SaveShaderStableKeys(*this, TargetShaderPlatform, SaveKeyVal); } /** Associates a shadermap with an asset (note: one shadermap can be used by several assets, e.g. MIs). * This helps cooker lay out the shadermaps (and shaders) in the file open order, if provided. Maps not associated with any assets * may be placed after all maps associated with known assets. Global shadermaps need to be associated with a "Global" asset */ void AssociateWithAsset(const FName& AssetPath) { AssociatedAssets.Add(AssetPath); } void AssociateWithAssets(const FShaderMapAssetPaths& AssetPaths) { AssociatedAssets.Append(AssetPaths); } const FShaderMapAssetPaths& GetAssociatedAssets() const { return AssociatedAssets; } #endif // WITH_EDITOR protected: RENDERCORE_API FShaderMapBase(); RENDERCORE_API void AssignCopy(const FShaderMapBase& Source); RENDERCORE_API void InitResource(); RENDERCORE_API void DestroyContent(); protected: virtual const FTypeLayoutDesc& GetContentTypeDesc() const = 0; virtual FShaderMapPointerTable* CreatePointerTable() const = 0; virtual void PostFinalizeContent() { } private: #if WITH_EDITOR /** List of the assets that are using this shadermap. This is only available in the editor (cooker) to influence ordering of shader libraries. */ FShaderMapAssetPaths AssociatedAssets; #endif TRefCountPtr Resource; TRefCountPtr Code; FShaderMapPointerTable* PointerTable; TMemoryImageObject Content; uint32 NumFrozenShaders; EShaderPermutationFlags PermutationFlags; }; template class TShaderMap : public FShaderMapBase { public: inline const PointerTableType& GetPointerTable() const { return static_cast(FShaderMapBase::GetPointerTable()); } inline const ContentType* GetContent() const { return static_cast(FShaderMapBase::GetContent()); } inline ContentType* GetMutableContent() { return static_cast(FShaderMapBase::GetMutableContent()); } void FinalizeContent() { ContentType* LocalContent = this->GetMutableContent(); check(LocalContent); LocalContent->Finalize(this->GetResourceCode()); LocalContent->Validate(*this); FShaderMapBase::FinalizeContent(); } protected: virtual const FTypeLayoutDesc& GetContentTypeDesc() const final override { return StaticGetTypeLayoutDesc(); } virtual FShaderMapPointerTable* CreatePointerTable() const final override { return new PointerTableType(); } }; template inline const PointerTableType& TShaderRefBase::GetPointerTable() const { checkSlow(ShaderMap); return static_cast(ShaderMap->GetPointerTable()); } template inline FShaderMapResource* TShaderRefBase::GetResource() const { checkSlow(ShaderMap); return ShaderMap->GetResource(); } inline const FShaderMapPointerTable& FShaderPipelineRef::GetPointerTable() const { checkSlow(ShaderMap); return ShaderMap->GetPointerTable(); } inline FShaderMapResource* FShaderPipelineRef::GetResource() const { checkSlow(ShaderMap); return ShaderMap->GetResource(); } /** A reference which is initialized with the requested shader type from a shader map. */ template class TShaderMapRef : public TShaderRef { public: TShaderMapRef(const typename ShaderType::ShaderMapType* ShaderIndex) : TShaderRef(ShaderIndex->template GetShader(/* PermutationId = */ 0)) // gcc3 needs the template quantifier so it knows the < is not a less-than { static_assert( std::is_same_v, "Missing permutation vector argument for shader that have a permutation domain."); } TShaderMapRef( const typename ShaderType::ShaderMapType* ShaderIndex, const typename ShaderType::FPermutationDomain& PermutationVector) : TShaderRef(ShaderIndex->template GetShader(PermutationVector.ToDimensionValueId())) // gcc3 needs the template quantifier so it knows the < is not a less-than { } }; /** A reference to an optional shader, initialized with a shader type from a shader map if it is available or nullptr if it is not. */ template class TOptionalShaderMapRef : public TShaderRef { public: TOptionalShaderMapRef(const typename ShaderType::ShaderMapType* ShaderIndex): TShaderRef(TShaderRef::Cast(ShaderIndex->GetShader(&ShaderType::GetStaticType()))) // gcc3 needs the template quantifier so it knows the < is not a less-than {} }; /** * Dumps shader stats to the log. Will also print some shader pipeline information. * @param Platform - Platform to dump shader info for, use SP_NumPlatforms for all * @param Frequency - Whether to dump PS or VS info, use SF_NumFrequencies to dump both */ extern RENDERCORE_API void DumpShaderStats( EShaderPlatform Platform, EShaderFrequency Frequency ); /** * Dumps shader pipeline stats to the log. Does not include material (eg shader pipeline instance) information. * @param Platform - Platform to dump shader info for, use SP_NumPlatforms for all */ extern RENDERCORE_API void DumpShaderPipelineStats(EShaderPlatform Platform); /** * Finds the shader type with a given name. * @param ShaderTypeName - The name of the shader type to find. * @return The shader type, or NULL if none matched. */ extern RENDERCORE_API FShaderType* FindShaderTypeByName(const FHashedName& ShaderTypeName); /** Helper function to dispatch a compute shader while checking that parameters have been set correctly. */ extern RENDERCORE_API void DispatchComputeShader( FRHIComputeCommandList& RHICmdList, FShader* Shader, uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ); /** Helper function to dispatch a compute shader indirectly while checking that parameters have been set correctly. */ extern RENDERCORE_API void DispatchIndirectComputeShader( FRHIComputeCommandList& RHICmdList, FShader* Shader, FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset); inline void DispatchComputeShader( FRHIComputeCommandList& RHICmdList, const TShaderRef& Shader, uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) { DispatchComputeShader(RHICmdList, Shader.GetShader(), ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ); } /** Returns whether the platform is using emulated uniform buffers */ extern RENDERCORE_API bool IsUsingEmulatedUniformBuffers(EShaderPlatform Platform); /** Returns whether DirectXShaderCompiler (DXC) is enabled for the specified shader platform. See console variables "r.OpenGL.ForceDXC", "r.D3D.ForceDXC". */ extern RENDERCORE_API bool IsDxcEnabledForPlatform(EShaderPlatform Platform, bool bHlslVersion2021 = false); /** Appends to KeyString for all shaders. */ extern RENDERCORE_API void ShaderMapAppendKeyString(EShaderPlatform Platform, FString& KeyString); extern RENDERCORE_API void ShaderMapAppendKey(EShaderPlatform Platform, FShaderKeyGenerator& KeyGen);