Files
UnrealEngineUWP/Engine/Source/Runtime/RenderCore/Public/Shader.h
tiago costa e98ce47a3b Keep track of all callable shaders to build RTPSO
- GetRayTracingCallableLibrary() similar to how we handle hitgroups.
FindRayTracingCallableIndex(...) to query callable shader index in RTPSO.

#rb yuriy.odonnell
#preflight 628620c99016c6dd8974b510

[CL 20278150 by tiago costa in ue5-main branch]
2022-05-19 07:03:30 -04:00

2420 lines
84 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Shader.h: Shader definitions.
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "Containers/List.h"
#include "Containers/HashTable.h"
#include "Misc/SecureHash.h"
#include "RenderResource.h"
#include "ShaderParameters.h"
#include "RenderingThread.h"
#include "ShaderCore.h"
#include "ShaderPermutation.h"
#include "ShaderCodeLibrary.h" // TODO - move to cpp
#include "Serialization/ArchiveProxy.h"
#include "UObject/RenderingObjectVersion.h"
#include "Serialization/MemoryImage.h"
#include "HAL/ThreadSafeBool.h"
#include <atomic>
// For FShaderUniformBufferParameter
#if WITH_EDITOR
#include "UObject/DebugSerializationFlags.h"
#endif
class FGlobalShaderType;
class FMaterialShaderType;
class FNiagaraShaderType;
class FOpenColorIOShaderType;
class FMeshMaterialShaderType;
class FComputeKernelShaderType;
class FShader;
class FShaderMapBase;
class FShaderPipelineType;
class FShaderType;
class FVertexFactoryType;
class FShaderParametersMetadata;
class FShaderMapPointerTable;
struct FShaderCompilerOutput;
struct FShaderCompiledShaderInitializerType;
UE_DEPRECATED(4.26, "FShadereCompiledShaderInitializerType is deprecated. Use FShaderCompiledShaderInitializerType.")
typedef FShaderCompiledShaderInitializerType FShadereCompiledShaderInitializerType;
/** Define a shader permutation uniquely according to its type, and permutation id.*/
template<typename MetaShaderType>
struct TShaderTypePermutation
{
MetaShaderType* const Type;
const int32 PermutationId;
TShaderTypePermutation(MetaShaderType* InType, int32 InPermutationId)
: Type(InType)
, PermutationId(InPermutationId)
{
}
FORCEINLINE bool operator==(const TShaderTypePermutation& Other)const
{
return Type == Other.Type && PermutationId == Other.PermutationId;
}
FORCEINLINE bool operator!=(const TShaderTypePermutation& Other)const
{
return !(*this == Other);
}
};
using FShaderPermutation = TShaderTypePermutation<FShaderType>;
const int32 kUniqueShaderPermutationId = 0;
template<typename MetaShaderType>
FORCEINLINE uint32 GetTypeHash(const TShaderTypePermutation<MetaShaderType>& Var)
{
return HashCombine(GetTypeHash(Var.Type), (uint32)Var.PermutationId);
}
/** Used to compare order shader types permutation deterministically. */
template<typename MetaShaderType>
class TCompareShaderTypePermutation
{
public:
FORCEINLINE bool operator()(const TShaderTypePermutation<MetaShaderType>& A, const TShaderTypePermutation<MetaShaderType>& 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 FShaderParameterInfo
{
DECLARE_TYPE_LAYOUT(FShaderParameterInfo, NonVirtual);
public:
LAYOUT_FIELD(uint16, BaseIndex);
LAYOUT_FIELD(uint16, Size);
FShaderParameterInfo() {}
FShaderParameterInfo(uint16 InBaseIndex, uint16 InSize)
{
BaseIndex = InBaseIndex;
Size = InSize;
checkf(BaseIndex == InBaseIndex && Size == InSize, TEXT("Tweak FShaderParameterInfo type sizes"));
}
friend FArchive& operator<<(FArchive& Ar, FShaderParameterInfo& Info)
{
Ar << Info.BaseIndex;
Ar << Info.Size;
return Ar;
}
inline bool operator==(const FShaderParameterInfo& Rhs) const
{
return BaseIndex == Rhs.BaseIndex
&& Size == Rhs.Size;
}
inline bool operator<(const FShaderParameterInfo& Rhs) const
{
return BaseIndex < Rhs.BaseIndex;
}
};
class FShaderLooseParameterBufferInfo
{
DECLARE_TYPE_LAYOUT(FShaderLooseParameterBufferInfo, NonVirtual);
public:
LAYOUT_FIELD(uint16, BaseIndex);
LAYOUT_FIELD(uint16, Size);
LAYOUT_FIELD(TMemoryImageArray<FShaderParameterInfo>, 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_TYPE_LAYOUT(FShaderParameterMapInfo, NonVirtual);
public:
LAYOUT_FIELD(TMemoryImageArray<FShaderParameterInfo>, UniformBuffers);
LAYOUT_FIELD(TMemoryImageArray<FShaderParameterInfo>, TextureSamplers);
LAYOUT_FIELD(TMemoryImageArray<FShaderParameterInfo>, SRVs);
LAYOUT_FIELD(TMemoryImageArray<FShaderLooseParameterBufferInfo>, 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;
}
};
class RENDERCORE_API FShaderMapResource : public FRenderResource, public FDeferredCleanupInterface
{
public:
static bool ArePlatformsCompatible(EShaderPlatform CurrentPlatform, EShaderPlatform TargetPlatform);
EShaderPlatform GetPlatform() const { return Platform; }
void AddRef();
void Release();
inline int32 GetNumRefs() const { return NumRefs.load(std::memory_order_relaxed); }
// FRenderResource interface.
virtual void ReleaseRHI();
inline int32 GetNumShaders() const
{
return NumRHIShaders;
}
inline bool IsValidShaderIndex(int32 ShaderIndex) const
{
return ShaderIndex >= 0 && ShaderIndex < NumRHIShaders;
}
inline bool HasShader(int32 ShaderIndex) const
{
return RHIShaders[ShaderIndex].load(std::memory_order_acquire) != nullptr;
}
inline FRHIShader* GetShader(int32 ShaderIndex)
{
// 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 (Shader == nullptr)
{
// most shadermaps have <100 shaders, and less than a half of them can be created. One lock
// for all creation seems sufficient, but if this function is often contended, per-shader
// locks are easily possible.
FScopeLock ScopeLock(&RHIShadersCreationGuard);
Shader = RHIShaders[ShaderIndex].load(std::memory_order_relaxed);
if (Shader == nullptr)
{
Shader = CreateShader(ShaderIndex);
RHIShaders[ShaderIndex].store(Shader, std::memory_order_release);
}
}
return Shader;
}
void BeginCreateAllShaders();
#if RHI_RAYTRACING
UE_DEPRECATED(5.1, "GetRayTracingMaterialLibrary is deprecated. Use GetRayTracingHitGroupLibrary instead.")
static void GetRayTracingMaterialLibrary(TArray<FRHIRayTracingShader*>& RayTracingMaterials, FRHIRayTracingShader* DefaultShader)
{
GetRayTracingHitGroupLibrary(RayTracingMaterials, DefaultShader);
}
UE_DEPRECATED(5.1, "GetRayTracingMaterialLibraryIndex is deprecated. Use GetRayTracingHitGroupLibraryIndex instead.")
inline uint32 GetRayTracingMaterialLibraryIndex(int32 ShaderIndex)
{
return GetRayTracingHitGroupLibraryIndex(ShaderIndex);
}
static void GetRayTracingHitGroupLibrary(TArray<FRHIRayTracingShader*>& RayTracingHitGroupShaders, FRHIRayTracingShader* DefaultShader);
static void GetRayTracingCallableShaderLibrary(TArray<FRHIRayTracingShader*>& RayTracingCallableShaders, FRHIRayTracingShader* DefaultShader);
static void GetRayTracingMissShaderLibrary(TArray<FRHIRayTracingShader*>& 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:
explicit FShaderMapResource(EShaderPlatform InPlatform, int32 NumShaders);
virtual ~FShaderMapResource();
SIZE_T GetAllocatedSize() const
{
SIZE_T Size = NumRHIShaders * sizeof(std::atomic<FRHIShader*>);
#if RHI_RAYTRACING
Size += RayTracingLibraryIndices.GetAllocatedSize();
#endif
return Size;
}
/** Addrefs the reference, passing the responsibility to the caller to Release() it. */
FRHIShader* CreateShader(int32 ShaderIndex);
virtual TRefCountPtr<FRHIShader> CreateRHIShader(int32 ShaderIndex) = 0;
virtual bool TryRelease() { return true; }
void ReleaseShaders();
private:
/** 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;
/** An array of shader pointers (refcount is managed manually). */
TUniquePtr<std::atomic<FRHIShader*>[]> RHIShaders;
/** Since the shaders are no longer a TArray, this is their count (the size of the RHIShadersArray). */
int32 NumRHIShaders;
#if RHI_RAYTRACING
TArray<uint32> RayTracingLibraryIndices;
#endif // RHI_RAYTRACING
EShaderPlatform Platform;
/** The number of references to this shader. */
std::atomic<int32> NumRefs;
};
class FShaderMapResourceCode : public FThreadSafeRefCountedObject
{
public:
struct FShaderEntry
{
TArray<uint8> Code;
int32 UncompressedSize;
EShaderFrequency Frequency;
friend FArchive& operator<<(FArchive& Ar, FShaderEntry& Entry)
{
uint8 Freq = Entry.Frequency;
Ar << Entry.Code << Entry.UncompressedSize << Freq;
Entry.Frequency = (EShaderFrequency)Freq;
return Ar;
}
};
#if WITH_EDITORONLY_DATA
struct FPlatformDebugEntry
{
uint32 Offset;
uint32 Size;
friend FArchive& operator<<(FArchive& Ar, FPlatformDebugEntry& Entry)
{
return Ar << Entry.Offset << Entry.Size;
}
};
#endif // WITH_EDITORONLY_DATA
RENDERCORE_API FShaderMapResourceCode() {}
RENDERCORE_API FShaderMapResourceCode(const FShaderMapResourceCode& Other);
RENDERCORE_API ~FShaderMapResourceCode();
RENDERCORE_API void Finalize();
RENDERCORE_API void Serialize(FArchive& Ar, bool bLoadedByCookedMaterial);
#if WITH_EDITORONLY_DATA
RENDERCORE_API void NotifyShadersCompiled(FName FormatName);
UE_DEPRECATED(5.0, "NotifyShadersCompiled should be called")
RENDERCORE_API void NotifyShadersCooked(const ITargetPlatform* TargetPlatform);
#endif // WITH_EDITORONLY_DATA
RENDERCORE_API uint32 GetSizeBytes() const;
RENDERCORE_API void AddShaderCompilerOutput(const FShaderCompilerOutput& Output);
int32 FindShaderIndex(const FSHAHash& InHash) const;
RENDERCORE_API void AddShaderCode(EShaderFrequency InFrequency, const FSHAHash& InHash, const FShaderCode& InCode);
#if WITH_EDITORONLY_DATA
RENDERCORE_API void AddPlatformDebugData(TConstArrayView<uint8> InPlatformDebugData);
#endif
RENDERCORE_API void ToString(FStringBuilderBase& OutString) const;
FSHAHash ResourceHash;
TArray<FSHAHash> ShaderHashes;
TArray<FShaderEntry> ShaderEntries;
#if WITH_EDITORONLY_DATA
TArray<TArray<uint8>> PlatformDebugData;
TArray<FSHAHash> PlatformDebugDataHashes;
#endif // WITH_EDITORONLY_DATA
};
class RENDERCORE_API FShaderMapResource_InlineCode : public FShaderMapResource
{
public:
FShaderMapResource_InlineCode(EShaderPlatform InPlatform, FShaderMapResourceCode* InCode)
: FShaderMapResource(InPlatform, InCode->ShaderEntries.Num())
, Code(InCode)
{}
// FShaderMapResource interface
virtual TRefCountPtr<FRHIShader> CreateRHIShader(int32 ShaderIndex) override;
virtual uint32 GetSizeBytes() const override { return sizeof(*this) + GetAllocatedSize(); }
TRefCountPtr<FShaderMapResourceCode> Code;
};
class RENDERCORE_API FShaderMapPointerTable : public FPointerTableBase
{
public:
virtual int32 AddIndexedPointer(const FTypeLayoutDesc& TypeDesc, void* Ptr) override;
virtual void* GetIndexedPointer(const FTypeLayoutDesc& TypeDesc, uint32 i) const override;
virtual FShaderMapPointerTable* Clone() const { return new FShaderMapPointerTable(*this); }
virtual void SaveToArchive(FArchive& Ar, const FPlatformTypeLayoutParameters& LayoutParams, const void* FrozenObject) const override;
virtual bool LoadFromArchive(FArchive& Ar, const FPlatformTypeLayoutParameters& LayoutParams, void* FrozenObject) override;
TPtrTable<FShaderType> ShaderTypes;
TPtrTable<FVertexFactoryType> VFTypes;
};
/** Encapsulates information about a shader's serialization behavior, used to detect when C++ serialization changes to auto-recompile. */
class FSerializationHistory
{
public:
/** Token stream stored as uint32's. Each token is 4 bits, with a 0 meaning there's an associated 32 bit value in FullLengths. */
TArray<uint32> TokenBits;
/** Number of tokens in TokenBits. */
int32 NumTokens;
/** Full size length entries. One of these are used for every token with a value of 0. */
TArray<uint32> FullLengths;
FSerializationHistory() :
NumTokens(0)
{}
FORCEINLINE void AddValue(uint32 InValue)
{
const int32 UIntIndex = NumTokens / 8;
if (UIntIndex >= TokenBits.Num())
{
// Add another uint32 if needed
TokenBits.AddZeroed();
}
uint8 Token;
// Anything that does not fit in 4 bits needs to go into FullLengths, with a special token value of 0
// InValue == 0 also should go into FullLengths, because its Token value is also 0
if (InValue > 7 || InValue == 0)
{
Token = 0;
FullLengths.Add(InValue);
}
else
{
Token = (uint8)InValue;
}
const uint32 Shift = (NumTokens % 8) * 4;
// Add the new token bits into the existing uint32
TokenBits[UIntIndex] = TokenBits[UIntIndex] | (Token << Shift);
NumTokens++;
}
FORCEINLINE uint8 GetToken(int32 Index) const
{
checkSlow(Index < NumTokens);
const int32 UIntIndex = Index / 8;
checkSlow(UIntIndex < TokenBits.Num());
const uint32 Shift = (Index % 8) * 4;
const uint8 Token = (TokenBits[UIntIndex] >> Shift) & 0xF;
return Token;
}
void AppendKeyString(FString& KeyString) const
{
KeyString += FString::FromInt(NumTokens);
KeyString += BytesToHex((uint8*)TokenBits.GetData(), TokenBits.Num() * TokenBits.GetTypeSize());
KeyString += BytesToHex((uint8*)FullLengths.GetData(), FullLengths.Num() * FullLengths.GetTypeSize());
}
inline bool operator==(const FSerializationHistory& Other) const
{
return TokenBits == Other.TokenBits && NumTokens == Other.NumTokens && FullLengths == Other.FullLengths;
}
friend FArchive& operator<<(FArchive& Ar,class FSerializationHistory& Ref)
{
Ar << Ref.TokenBits << Ref.NumTokens << Ref.FullLengths;
return Ar;
}
};
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 RENDERCORE_API 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_TYPE_LAYOUT(FShaderParameterBindings, NonVirtual);
public:
static constexpr uint16 kInvalidBufferIndex = 0xFFFF;
void BindForLegacyShaderParameters(const FShader* Shader, int32 PermutationId, const FShaderParameterMap& ParameterMaps, const FShaderParametersMetadata& StructMetaData, bool bShouldBindEverything = false);
void BindForRootShaderParameters(const FShader* Shader, int32 PermutationId, const FShaderParameterMap& ParameterMaps);
LAYOUT_FIELD(TMemoryImageArray<FParameter>, Parameters);
LAYOUT_FIELD(TMemoryImageArray<FResourceParameter>, ResourceParameters);
LAYOUT_FIELD(TMemoryImageArray<FBindlessResourceParameter>, BindlessResourceParameters);
LAYOUT_FIELD(TMemoryImageArray<FParameterStructReference>, GraphUniformBuffers);
LAYOUT_FIELD(TMemoryImageArray<FParameterStructReference>, 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
// Flags that can specialize shader permutations compiled for specific platforms
enum class EShaderPermutationFlags : uint32
{
None = 0u,
HasEditorOnlyData = (1u << 0),
};
ENUM_CLASS_FLAGS(EShaderPermutationFlags);
RENDERCORE_API EShaderPermutationFlags GetShaderPermutationFlags(const FPlatformTypeLayoutParameters& LayoutParams);
struct FShaderPermutationParameters
{
// Shader platform to compile to.
const EShaderPlatform Platform;
// Unique permutation identifier of the material shader type.
const int32 PermutationId;
// Flags that describe the permutation
const EShaderPermutationFlags Flags;
// Default to include editor-only shaders, to maintain backwards-compatibility
explicit FShaderPermutationParameters(EShaderPlatform InPlatform, int32 InPermutationId = 0, EShaderPermutationFlags InFlags = EShaderPermutationFlags::HasEditorOnlyData)
: Platform(InPlatform)
, PermutationId(InPermutationId)
, Flags(InFlags)
{}
};
namespace Freeze
{
RENDERCORE_API void IntrinsicToString(const TIndexedPtr<FShaderType>& Object, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FMemoryToStringContext& OutContext);
RENDERCORE_API void IntrinsicToString(const TIndexedPtr<FVertexFactoryType>& Object, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FMemoryToStringContext& OutContext);
}
DECLARE_EXPORTED_TEMPLATE_INTRINSIC_TYPE_LAYOUT(template<>, TIndexedPtr<FShaderType>, RENDERCORE_API);
DECLARE_EXPORTED_TEMPLATE_INTRINSIC_TYPE_LAYOUT(template<>, TIndexedPtr<FVertexFactoryType>, RENDERCORE_API);
/** A compiled shader and its parameter bindings. */
class RENDERCORE_API FShader
{
friend class FShaderType;
DECLARE_TYPE_LAYOUT(FShader, NonVirtual);
public:
using FPermutationDomain = FShaderPermutationNone;
using FPermutationParameters = FShaderPermutationParameters;
using CompiledShaderInitializerType = FShaderCompiledShaderInitializerType;
using ShaderMetaType = FShaderType;
/**
* Used to construct a shader for deserialization.
* This still needs to initialize members to safe values since FShaderType::GenerateSerializationHistory uses this constructor.
*/
FShader();
/**
* Construct a shader from shader compiler output.
*/
FShader(const CompiledShaderInitializerType& Initializer);
~FShader();
/** Can be overridden by FShader subclasses to modify their compile environment just before compilation occurs. */
static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment&) {}
/** 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 compilation is valid. */
static bool ValidateCompiledResult(EShaderPlatform InPlatform, const FShaderParameterMap& InParameterMap, TArray<FString>& OutError) { return true; }
/** Returns the hash of the shader file that this shader was compiled with. */
const FSHAHash& GetHash() const;
const FSHAHash& GetVertexFactoryHash() const;
const FSHAHash& GetOutputHash() const;
/** Returns an identifier suitable for deterministic sorting of shaders between sessions. */
uint32 GetSortKey() const { return SortKey; }
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(); }
inline uint32 GetNumInstructions() const { return NumInstructions; }
#if WITH_EDITORONLY_DATA
inline uint32 GetNumTextureSamplers() const { return NumTextureSamplers; }
inline uint32 GetCodeSize() const { return CodeSize; }
inline void SetNumInstructions(uint32 Value) { NumInstructions = Value; }
#else
inline uint32 GetNumTextureSamplers() const { return 0u; }
inline uint32 GetCodeSize() const { return 0u; }
#endif
/** Finds an automatically bound uniform buffer matching the given uniform buffer type if one exists, or returns an unbound parameter. */
template<typename UniformBufferStructType>
FORCEINLINE_DEBUGGABLE const TShaderUniformBufferParameter<UniformBufferStructType>& GetUniformBufferParameter() const
{
const FShaderUniformBufferParameter& FoundParameter = GetUniformBufferParameter(&UniformBufferStructType::StaticStructMetadata);
return static_cast<const TShaderUniformBufferParameter<UniformBufferStructType>&>(FoundParameter);
}
/** Finds an automatically bound uniform buffer matching the given uniform buffer struct if one exists, or returns an unbound parameter. */
FORCEINLINE_DEBUGGABLE 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. */
FORCEINLINE_DEBUGGABLE const FShaderUniformBufferParameter& GetUniformBufferParameter(const FHashedName SearchName) const
{
int32 FoundIndex = INDEX_NONE;
TArrayView<const FHashedName> 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;
}
}
const FShaderParametersMetadata* FindAutomaticallyBoundUniformBufferStruct(int32 BaseIndex) const;
void DumpDebugInfo(const FShaderMapPointerTable& InPtrTable);
#if WITH_EDITOR
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:
void BuildParameterMapInfo(const TMap<FString, FParameterAllocation>& ParameterMap);
public:
LAYOUT_FIELD(FShaderParameterBindings, Bindings);
LAYOUT_FIELD(FShaderParameterMapInfo, ParameterMapInfo);
protected:
LAYOUT_FIELD(TMemoryImageArray<FHashedName>, UniformBufferParameterStructs);
LAYOUT_FIELD(TMemoryImageArray<FShaderUniformBufferParameter>, 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<FShaderType>, Type);
LAYOUT_FIELD(TIndexedPtr<FVertexFactoryType>, 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(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<typename ShaderType, typename PointerTableType>
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<typename OtherShaderType, typename OtherPointerTableType>
TShaderRefBase(const TShaderRefBase<OtherShaderType, OtherPointerTableType>& Rhs) : ShaderContent(Rhs.GetShader()), ShaderMap(Rhs.GetShaderMap()) {}
TShaderRefBase& operator=(const TShaderRefBase&) = default;
template<typename OtherShaderType, typename OtherPointerTableType>
TShaderRefBase& operator=(const TShaderRefBase<OtherShaderType, OtherPointerTableType>& Rhs)
{
ShaderContent = Rhs.GetShader();
ShaderMap = Rhs.GetShaderMap();
return *this;
}
template<typename OtherShaderType, typename OtherPointerTableType>
static TShaderRefBase<ShaderType, PointerTableType> Cast(const TShaderRefBase<OtherShaderType, OtherPointerTableType>& Rhs)
{
return TShaderRefBase<ShaderType, PointerTableType>(static_cast<ShaderType*>(Rhs.GetShader()), Rhs.GetShaderMap());
}
template<typename OtherShaderType, typename OtherPointerTableType>
static TShaderRefBase<ShaderType, PointerTableType> ReinterpretCast(const TShaderRefBase<OtherShaderType, OtherPointerTableType>& Rhs)
{
return TShaderRefBase<ShaderType, PointerTableType>(reinterpret_cast<ShaderType*>(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) const
{
FRHIShader* RHIShader = nullptr;
if(ShaderContent)
{
checkSlow(ShaderContent->GetFrequency() == Frequency);
RHIShader = GetResourceChecked().GetShader(ShaderContent->GetResourceIndex());
checkSlow(RHIShader->GetFrequency() == Frequency);
}
return RHIShader;
}
/** @return the shader's vertex shader */
inline FRHIVertexShader* GetVertexShader() const
{
return static_cast<FRHIVertexShader*>(GetRHIShaderBase(SF_Vertex));
}
/** @return the shader's mesh shader */
inline FRHIMeshShader* GetMeshShader() const
{
return static_cast<FRHIMeshShader*>(GetRHIShaderBase(SF_Mesh));
}
/** @return the shader's aplification shader */
inline FRHIAmplificationShader* GetAmplificationShader() const
{
return static_cast<FRHIAmplificationShader*>(GetRHIShaderBase(SF_Amplification));
}
/** @return the shader's pixel shader */
inline FRHIPixelShader* GetPixelShader() const
{
return static_cast<FRHIPixelShader*>(GetRHIShaderBase(SF_Pixel));
}
/** @return the shader's geometry shader */
inline FRHIGeometryShader* GetGeometryShader() const
{
return static_cast<FRHIGeometryShader*>(GetRHIShaderBase(SF_Geometry));
}
/** @return the shader's compute shader */
inline FRHIComputeShader* GetComputeShader() const
{
return static_cast<FRHIComputeShader*>(GetRHIShaderBase(SF_Compute));
}
#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<FRHIRayTracingShader*>(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<typename ShaderType0, typename ShaderType1, typename PointerTableType>
inline bool operator==(const TShaderRefBase<ShaderType0, PointerTableType>& Lhs, const TShaderRefBase<ShaderType1, PointerTableType>& Rhs)
{
if (Lhs.GetShader() == Rhs.GetShader())
{
check(Lhs.GetShaderMap() == Rhs.GetShaderMap());
return true;
}
return false;
}
template<typename ShaderType0, typename ShaderType1, typename PointerTableType>
inline bool operator!=(const TShaderRefBase<ShaderType0, PointerTableType>& Lhs, const TShaderRefBase<ShaderType1, PointerTableType>& Rhs)
{
return !operator==(Lhs, Rhs);
}
template<typename ShaderType>
using TShaderRef = TShaderRefBase<ShaderType, FShaderMapPointerTable>;
/**
* 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 RENDERCORE_API FShaderType
{
public:
enum class EShaderTypeForDynamicCast : uint32
{
Global,
Material,
MeshMaterial,
Niagara,
OCIO,
ComputeKernel,
NumShaderTypes,
};
/**
* 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 void (*ModifyCompilationEnvironmentType)(const FShaderPermutationParameters&, FShaderCompilerEnvironment&);
typedef bool (*ValidateCompiledResultType)(EShaderPlatform, const FShaderParameterMap&, TArray<FString>&);
/** @return The global shader factory list. */
static TLinkedList<FShaderType*>*& GetTypeList();
static FShaderType* GetShaderTypeByName(const TCHAR* Name);
static TArray<const FShaderType*> GetShaderTypesByFilename(const TCHAR* Filename);
/** @return The global shader name to type map */
static TMap<FHashedName, FShaderType*>& GetNameToTypeMap();
static const TArray<FShaderType*>& GetSortedTypes(EShaderTypeForDynamicCast Type);
/** Initialize FShaderType static members, this must be called before any shader types are created. */
static void Initialize(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables);
/** Uninitializes FShaderType cached data. */
static void Uninitialize();
/** Minimal initialization constructor. */
FShaderType(
EShaderTypeForDynamicCast InShaderTypeForDynamicCast,
FTypeLayoutDesc& InTypeLayout,
const TCHAR* InName,
const TCHAR* InSourceFilename,
const TCHAR* InFunctionName,
uint32 InFrequency,
int32 TotalPermutationCount,
ConstructSerializedType InConstructSerializedRef,
ConstructCompiledType InConstructCompiledRef,
ModifyCompilationEnvironmentType InModifyCompilationEnvironmentRef,
ShouldCompilePermutationType InShouldCompilePermutationRef,
ValidateCompiledResultType InValidateCompiledResultRef,
uint32 InTypeSize,
const FShaderParametersMetadata* InRootParametersMetadata);
virtual ~FShaderType();
/** Constructs a new instance of the shader type for deserialization. */
FShader* ConstructForDeserialization() const;
FShader* ConstructCompiled(const FShader::CompiledShaderInitializerType& Initializer) const;
bool ShouldCompilePermutation(const FShaderPermutationParameters& Parameters) const;
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.
*/
bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutError) const;
/** Calculates a Hash based on this shader type's source code and includes */
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.
FORCEINLINE FGlobalShaderType* GetGlobalShaderType()
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Global) ? reinterpret_cast<FGlobalShaderType*>(this) : nullptr;
}
FORCEINLINE const FGlobalShaderType* GetGlobalShaderType() const
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Global) ? reinterpret_cast<const FGlobalShaderType*>(this) : nullptr;
}
FORCEINLINE FMaterialShaderType* GetMaterialShaderType()
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Material) ? reinterpret_cast<FMaterialShaderType*>(this) : nullptr;
}
FORCEINLINE const FMaterialShaderType* GetMaterialShaderType() const
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Material) ? reinterpret_cast<const FMaterialShaderType*>(this) : nullptr;
}
FORCEINLINE FMeshMaterialShaderType* GetMeshMaterialShaderType()
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::MeshMaterial) ? reinterpret_cast<FMeshMaterialShaderType*>(this) : nullptr;
}
FORCEINLINE const FMeshMaterialShaderType* GetMeshMaterialShaderType() const
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::MeshMaterial) ? reinterpret_cast<const FMeshMaterialShaderType*>(this) : nullptr;
}
FORCEINLINE const FNiagaraShaderType* GetNiagaraShaderType() const
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Niagara) ? reinterpret_cast<const FNiagaraShaderType*>(this) : nullptr;
}
FORCEINLINE FNiagaraShaderType* GetNiagaraShaderType()
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Niagara) ? reinterpret_cast<FNiagaraShaderType*>(this) : nullptr;
}
FORCEINLINE const FOpenColorIOShaderType* GetOpenColorIOShaderType() const
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::OCIO) ? reinterpret_cast<const FOpenColorIOShaderType*>(this) : nullptr;
}
FORCEINLINE FOpenColorIOShaderType* GetOpenColorIOShaderType()
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::OCIO) ? reinterpret_cast<FOpenColorIOShaderType*>(this) : nullptr;
}
FORCEINLINE const FComputeKernelShaderType* GetComputeKernelShaderType() const
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::ComputeKernel) ? reinterpret_cast<const FComputeKernelShaderType*>(this) : nullptr;
}
FORCEINLINE FComputeKernelShaderType* GetComputeKernelShaderType()
{
return (ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::ComputeKernel) ? reinterpret_cast<FComputeKernelShaderType*>(this) : nullptr;
}
FORCEINLINE const FGlobalShaderType* AsGlobalShaderType() const
{
checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Global, TEXT("ShaderType %s is not Global"), GetName());
return reinterpret_cast<const FGlobalShaderType*>(this);
}
FORCEINLINE const FMaterialShaderType* AsMaterialShaderType() const
{
checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Material, TEXT("ShaderType %s is not Material"), GetName());
return reinterpret_cast<const FMaterialShaderType*>(this);
}
FORCEINLINE const FMeshMaterialShaderType* AsMeshMaterialShaderType() const
{
checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::MeshMaterial, TEXT("ShaderType %s is not MeshMaterial"), GetName());
return reinterpret_cast<const FMeshMaterialShaderType*>(this);
}
FORCEINLINE const FNiagaraShaderType* AsNiagaraShaderType() const
{
checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::Niagara, TEXT("ShaderType %s is not Niagara"), GetName());
return reinterpret_cast<const FNiagaraShaderType*>(this);
}
FORCEINLINE const FOpenColorIOShaderType* AsOpenColorIOShaderType() const
{
checkf(ShaderTypeForDynamicCast == EShaderTypeForDynamicCast::OCIO, TEXT("ShaderType %s is not OCIO"), GetName());
return reinterpret_cast<const FOpenColorIOShaderType*>(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;
}
inline const TMap<const TCHAR*, FCachedUniformBufferDeclaration>& GetReferencedUniformBufferStructsCache() const
{
return ReferencedUniformBufferStructsCache;
}
/** Returns the meta data for the root shader parameter struct. */
inline const FShaderParametersMetadata* GetRootParametersMetadata() const
{
return RootParametersMetadata;
}
/** Adds include statements for uniform buffers that this shader type references, and builds a prefix for the shader file with the include statements. */
void AddReferencedUniformBufferIncludes(FShaderCompilerEnvironment& OutEnvironment, FString& OutSourceFilePrefix, EShaderPlatform Platform) const;
void FlushShaderFileCache(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables);
void DumpDebugInfo();
void GetShaderStableKeyParts(struct FStableShaderKeyAndValue& SaveKeyVal);
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;
ModifyCompilationEnvironmentType ModifyCompilationEnvironmentRef;
ShouldCompilePermutationType ShouldCompilePermutationRef;
ValidateCompiledResultType ValidateCompiledResultRef;
const FShaderParametersMetadata* const RootParametersMetadata;
TLinkedList<FShaderType*> GlobalListLink;
friend void RENDERCORE_API DumpShaderStats( EShaderPlatform Platform, EShaderFrequency Frequency );
/** Tracks whether serialization history for all shader types has been initialized. */
static bool bInitializedSerializationHistory;
protected:
/** Tracks what platforms ReferencedUniformBufferStructsCache has had declarations cached for. */
mutable std::atomic<EShaderPlatform> CachedUniformBufferPlatform;
/**
* Cache of referenced uniform buffer includes.
* These are derived from source files so they need to be flushed when editing and recompiling shaders on the fly.
* FShaderType::Initialize will add an entry for each referenced uniform buffer, but the declarations are added on demand as shaders are compiled.
*/
mutable TMap<const TCHAR*, FCachedUniformBufferDeclaration> ReferencedUniformBufferStructsCache;
};
struct FShaderCompiledShaderInitializerType
{
const FShaderType* Type;
const FShaderType::FParameters* Parameters;
FShaderTarget Target;
const TArray<uint8>& Code;
const FShaderParameterMap& ParameterMap;
const FSHAHash& OutputHash;
FSHAHash MaterialShaderMapHash;
const FShaderPipelineType* ShaderPipeline;
const FVertexFactoryType* VertexFactoryType;
uint32 NumInstructions;
uint32 NumTextureSamplers;
uint32 CodeSize;
int32 PermutationId;
RENDERCORE_API FShaderCompiledShaderInitializerType(
const FShaderType* InType,
const FShaderType::FParameters* InParameters,
int32 InPermutationId,
const FShaderCompilerOutput& CompilerOutput,
const FSHAHash& InMaterialShaderMapHash,
const FShaderPipelineType* InShaderPipeline,
const FVertexFactoryType* InVertexFactoryType
);
};
#define SHADER_DECLARE_VTABLE(ShaderClass) \
static FShader* ConstructSerializedInstance() { return new ShaderClass(); } \
static FShader* ConstructCompiledInstance(const typename FShader::CompiledShaderInitializerType& Initializer) \
{ return new ShaderClass(static_cast<const typename ShaderMetaType::CompiledShaderInitializerType&>(Initializer)); }\
static void ModifyCompilationEnvironmentImpl(const FShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) \
{ \
const typename ShaderClass::FPermutationDomain PermutationVector(Parameters.PermutationId); \
PermutationVector.ModifyCompilationEnvironment(OutEnvironment); \
ShaderClass::ModifyCompilationEnvironment(static_cast<const typename ShaderClass::FPermutationParameters&>(Parameters), OutEnvironment); \
} \
static bool ShouldCompilePermutationImpl(const FShaderPermutationParameters& Parameters) \
{ return ShaderClass::ShouldCompilePermutation(static_cast<const typename ShaderClass::FPermutationParameters&>(Parameters)); }
#define INTERNAL_DECLARE_SHADER_TYPE_COMMON(ShaderClass,ShaderMetaTypeShortcut,RequiredAPI) \
public: \
using ShaderMetaType = F##ShaderMetaTypeShortcut##ShaderType; \
using ShaderMapType = F##ShaderMetaTypeShortcut##ShaderMap; \
\
static RequiredAPI ShaderMetaType StaticType; \
\
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:
#define SHADER_TYPE_VTABLE(ShaderClass) \
ShaderClass::ConstructSerializedInstance, \
ShaderClass::ConstructCompiledInstance, \
ShaderClass::ModifyCompilationEnvironmentImpl, \
ShaderClass::ShouldCompilePermutationImpl, \
ShaderClass::ValidateCompiledResult
#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::StaticType( \
ShaderClass::StaticGetTypeLayout(), \
TEXT(#ShaderClass), \
SourceFilename, \
FunctionName, \
Frequency, \
ShaderClass::FPermutationDomain::PermutationCount, \
SHADER_TYPE_VTABLE(ShaderClass), \
sizeof(ShaderClass), \
ShaderClass::GetRootParametersMetadata() \
);
/** 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::StaticType( \
ShaderClass::StaticGetTypeLayout(), \
ShaderClass::GetDebugName(), \
SourceFilename, \
FunctionName, \
Frequency, \
ShaderClass::FPermutationDomain::PermutationCount, \
SHADER_TYPE_VTABLE(ShaderClass), \
sizeof(ShaderClass), \
ShaderClass::GetRootParametersMetadata() \
);
/** 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::StaticType( \
ShaderClass::StaticGetTypeLayout(), \
TEXT(#ShaderClass), \
ShaderClass::GetSourceFilename(), \
ShaderClass::GetFunctionName(), \
Frequency, \
ShaderClass::FPermutationDomain::PermutationCount, \
SHADER_TYPE_VTABLE(ShaderClass), \
sizeof(ShaderClass), \
ShaderClass::GetRootParametersMetadata() \
);
#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::StaticType( \
ShaderClass::StaticGetTypeLayout(), \
TEXT(#ShaderClass), \
ShaderClass::GetSourceFilename(), \
ShaderClass::GetFunctionName(), \
Frequency, \
ShaderClass::FPermutationDomain::PermutationCount, \
SHADER_TYPE_VTABLE(ShaderClass), \
sizeof(ShaderClass), \
ShaderClass::GetRootParametersMetadata() \
);
#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::StaticType( \
ShaderClass::StaticGetTypeLayout(), \
TEXT(#ShaderClass), \
ShaderClass::GetSourceFilename(), \
ShaderClass::GetFunctionName(), \
Frequency, \
ShaderClass::FPermutationDomain::PermutationCount, \
SHADER_TYPE_VTABLE(ShaderClass), \
sizeof(ShaderClass), \
ShaderClass::GetRootParametersMetadata() \
);
// Binding of a set of shader stages in a single pipeline
class RENDERCORE_API 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
FShaderPipelineType(
const TCHAR* InName,
const FShaderType* InVertexOrMeshShader,
const FShaderType* InGeometryOrAmplificationShader,
const FShaderType* InPixelShader,
bool bInIsMeshPipeline,
bool bInShouldOptimizeUnusedOutputs);
~FShaderPipelineType();
FORCEINLINE bool HasMeshShader() const { return AllStages[SF_Mesh] != nullptr; }
FORCEINLINE bool HasGeometry() const { return AllStages[SF_Geometry] != nullptr; }
FORCEINLINE bool HasPixelShader() const { return AllStages[SF_Pixel] != nullptr; }
FORCEINLINE const FShaderType* GetShader(EShaderFrequency Frequency) const
{
check(Frequency < SF_NumFrequencies);
return AllStages[Frequency];
}
FORCEINLINE FName GetFName() const { return TypeName; }
FORCEINLINE TCHAR const* GetName() const { return Name; }
FORCEINLINE const FHashedName& GetHashedName() const { return HashedName; }
FORCEINLINE const FHashedName& GetHashedPrimaryShaderFilename() const { return HashedPrimaryShaderFilename; }
// Returns an array of valid stages, sorted from PS->GS->DS->HS->VS, no gaps if missing stages
FORCEINLINE const TArray<const FShaderType*>& GetStages() const { return Stages; }
static TLinkedList<FShaderPipelineType*>*& GetTypeList();
static const TArray<FShaderPipelineType*>& GetSortedTypes(FShaderType::EShaderTypeForDynamicCast Type);
/** @return The global shader pipeline name to type map */
static TMap<FHashedName, FShaderPipelineType*>& GetNameToTypeMap();
static const FShaderPipelineType* GetShaderPipelineTypeByName(const FHashedName& Name);
/** Initialize static members, this must be called before any shader types are created. */
static void Initialize();
static void Uninitialize();
static TArray<const FShaderPipelineType*> GetShaderPipelineTypesByFilename(const TCHAR* Filename);
/** 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; }
FORCEINLINE bool ShouldOptimizeUnusedOutputs(EShaderPlatform Platform) const
{
return bShouldOptimizeUnusedOutputs && RHISupportsShaderPipelines(Platform);
}
/** Calculates a Hash based on this shader pipeline type stages' source code and includes */
const FSHAHash& GetSourceHash(EShaderPlatform ShaderPlatform) const;
bool ShouldCompilePermutation(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<const FShaderType*> Stages;
const FShaderType* AllStages[SF_NumFrequencies];
TLinkedList<FShaderPipelineType*> GlobalListLink;
uint32 HashIndex;
bool bShouldOptimizeUnusedOutputs;
static 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::StaticType, nullptr, &PixelShaderType::StaticType, false, bRemoveUnused);
// Only VS
#define IMPLEMENT_SHADERPIPELINE_TYPE_VS(PipelineName, VertexShaderType, bRemoveUnused) \
static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &VertexShaderType::StaticType, 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::StaticType, &GeometryShaderType::StaticType, &PixelShaderType::StaticType, false, bRemoveUnused);
// Vertex+Geometry
#define IMPLEMENT_SHADERPIPELINE_TYPE_VSGS(PipelineName, VertexShaderType, GeometryShaderType, bRemoveUnused) \
static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &VertexShaderType::StaticType, &GeometryShaderType::StaticType, nullptr, false, bRemoveUnused);
// Mesh+Pixel
#define IMPLEMENT_SHADERPIPELINE_TYPE_MSPS(PipelineName, MeshShaderType, PixelShaderType, bRemoveUnused) \
static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &MeshShaderType::StaticType, nullptr, &PixelShaderType::StaticType, true, bRemoveUnused);
// Mesh+Amplification+Pixel
#define IMPLEMENT_SHADERPIPELINE_TYPE_MSASPS(PipelineName, MeshShaderType, AmplificationShaderType, PixelShaderType, bRemoveUnused) \
static FShaderPipelineType PipelineName(TEXT(PREPROCESSOR_TO_STRING(PipelineName)), &MeshShaderType::StaticType, &AmplificationShaderType::StaticType, &PixelShaderType::StaticType, 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);
}
/** 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);
};
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);
}
};
/** Used to compare two shader types by name. */
class FCompareShaderTypes
{
public:
FORCEINLINE 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;
}
};
/** Used to compare two shader pipeline types by name. */
class FCompareShaderPipelineNameTypes
{
public:
/*FORCEINLINE*/ bool operator()(const FShaderPipelineType& A, const FShaderPipelineType& B) const
{
//#todo-rco: Avoid this by adding an FNullShaderPipelineType
bool bNullA = &A == nullptr;
bool bNullB = &B == nullptr;
if (bNullA && bNullB)
{
return false;
}
else if (bNullA)
{
return true;
}
else if (bNullB)
{
return false;
}
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 RENDERCORE_API FShaderPipeline
{
DECLARE_TYPE_LAYOUT(FShaderPipeline, NonVirtual);
public:
explicit FShaderPipeline(const FShaderPipelineType* InType) : TypeName(InType->GetHashedName()) { FMemory::Memzero(&PermutationIds, sizeof(PermutationIds)); }
~FShaderPipeline();
void AddShader(FShader* Shader, int32 PermutationId);
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<typename ShaderType>
ShaderType* GetShader(const FShaderMapPointerTable& InPtrTable)
{
const FShaderType& Type = ShaderType::StaticType;
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<ShaderType*>(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<TShaderRef<FShader>> GetShaders(const FShaderMapBase& InShaderMap) const
{
TArray<TShaderRef<FShader>> Result;
for (uint32 i = 0u; i < SF_NumGraphicsFrequencies; ++i)
{
if (Shaders[i].IsValid())
{
Result.Add(TShaderRef<FShader>(Shaders[i].GetChecked(), InShaderMap));
}
}
return Result;
}
void Validate(const FShaderPipelineType* InPipelineType) const;
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
void SaveShaderStableKeys(const FShaderMapPointerTable& InPtrTable, EShaderPlatform TargetShaderPlatform, const struct FStableShaderKeyAndValue& SaveKeyVal) const;
#endif // WITH_EDITOR
LAYOUT_FIELD(FHashedName, TypeName);
LAYOUT_ARRAY(TMemoryImagePtr<FShader>, 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<typename ShaderType>
TShaderRef<ShaderType> GetShader() const
{
return TShaderRef<ShaderType>(ShaderPipeline->GetShader<ShaderType>(GetPointerTable()), *ShaderMap);
}
TShaderRef<FShader> GetShader(EShaderFrequency Frequency) const
{
return TShaderRef<FShader>(ShaderPipeline->GetShader(Frequency), *ShaderMap);
}
inline TArray<TShaderRef<FShader>> 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;
};
/** A collection of shaders of different types */
class RENDERCORE_API FShaderMapContent
{
DECLARE_TYPE_LAYOUT(FShaderMapContent, NonVirtual);
public:
struct FProjectShaderPipelineToKey
{
inline FHashedName operator()(const FShaderPipeline* InShaderPipeline) { return InShaderPipeline->TypeName; }
};
/** Default constructor. */
explicit FShaderMapContent(EShaderPlatform InPlatform)
: ShaderHash(128u), Platform(InPlatform)
{}
/** Destructor ensures pipelines cleared up. */
~FShaderMapContent()
{
Empty();
}
EShaderPlatform GetShaderPlatform() const { return Platform; }
void Validate(const FShaderMapBase& InShaderMap) const;
/** Finds the shader with the given type. Asserts on failure. */
template<typename ShaderType>
ShaderType* GetShader(int32 PermutationId = 0) const
{
FShader* Shader = GetShader(&ShaderType::StaticType, PermutationId);
checkf(Shader != nullptr, TEXT("Failed to find shader type %s in Platform %s"), ShaderType::StaticType.GetName(), *LegacyShaderPlatformToShaderFormat(Platform).ToString());
return static_cast<ShaderType*>(Shader);
}
/** Finds the shader with the given type. Asserts on failure. */
template<typename ShaderType>
ShaderType* GetShader( const typename ShaderType::FPermutationDomain& PermutationVector ) const
{
return GetShader<ShaderType>( 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. */
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<const TMemoryImagePtr<FShader>> GetShaders() const
{
return Shaders;
}
inline TArrayView<const TMemoryImagePtr<FShaderPipeline>> GetShaderPipelines() const
{
return ShaderPipelines;
}
void AddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader);
FShader* FindOrAddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader);
void AddShaderPipeline(FShaderPipeline* Pipeline);
FShaderPipeline* FindOrAddShaderPipeline(FShaderPipeline* Pipeline);
/**
* Removes the shader of the given type from the shader map
* @param Type Shader type to remove the entry for
*/
void RemoveShaderTypePermutaion(const FHashedName& TypeName, int32 PermutationId);
inline void RemoveShaderTypePermutaion(const FShaderType* Type, int32 PermutationId)
{
RemoveShaderTypePermutaion(Type->GetHashedName(), PermutationId);
}
void RemoveShaderPipelineType(const FShaderPipelineType* ShaderPipelineType);
/** Builds a list of the shaders in a shader map. */
void GetShaderList(const FShaderMapBase& InShaderMap, const FSHAHash& InMaterialShaderMapHash, TMap<FShaderId, TShaderRef<FShader>>& OutShaders) const;
/** Builds a list of the shaders in a shader map. Key is FShaderType::TypeName */
void GetShaderList(const FShaderMapBase& InShaderMap, TMap<FHashedName, TShaderRef<FShader>>& OutShaders) const;
/** Builds a list of the shader pipelines in a shader map. */
void GetShaderPipelineList(const FShaderMapBase& InShaderMap, TArray<FShaderPipelineRef>& OutShaderPipelines, FShaderPipeline::EFilter Filter) const;
#if WITH_EDITOR
uint32 GetMaxTextureSamplersShaderMap(const FShaderMapBase& InShaderMap) const;
void GetOutdatedTypes(const FShaderMapBase& InShaderMap, TArray<const FShaderType*>& OutdatedShaderTypes, TArray<const FShaderPipelineType*>& OutdatedShaderPipelineTypes, TArray<const FVertexFactoryType*>& OutdatedFactoryTypes) const;
void SaveShaderStableKeys(const FShaderMapBase& InShaderMap, EShaderPlatform TargetShaderPlatform, const struct FStableShaderKeyAndValue& SaveKeyVal);
#endif // WITH_EDITOR
/** @return true if the map is empty */
inline bool IsEmpty() const
{
return Shaders.Num() == 0;
}
/** @return The number of shaders in the map. */
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 */
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); }
uint32 GetMaxNumInstructionsForShader(const FShaderMapBase& InShaderMap, FShaderType* ShaderType) const;
void Finalize(const FShaderMapResourceCode* Code);
void UpdateHash(FSHA1& Hasher) const;
protected:
void EmptyShaderPipelines();
using FMemoryImageHashTable = THashTable<FMemoryImageAllocator>;
LAYOUT_FIELD(FMemoryImageHashTable, ShaderHash);
LAYOUT_FIELD(TMemoryImageArray<FHashedName>, ShaderTypes);
LAYOUT_FIELD(TMemoryImageArray<int32>, ShaderPermutations);
LAYOUT_FIELD(TMemoryImageArray<TMemoryImagePtr<FShader>>, Shaders);
LAYOUT_FIELD(TMemoryImageArray<TMemoryImagePtr<FShaderPipeline>>, ShaderPipelines);
/** The platform this shader map was compiled with */
LAYOUT_FIELD(TEnumAsByte<EShaderPlatform>, Platform);
};
class RENDERCORE_API FShaderMapBase
{
public:
virtual ~FShaderMapBase();
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;
}
inline EShaderPlatform GetShaderPlatform() const { return Content.Object ? Content.Object->GetShaderPlatform() : SP_NumPlatforms; }
inline uint32 GetFrozenContentSize() const { return Content.FrozenSize; }
void AssignContent(TMemoryImageObject<FShaderMapContent> InContent);
void FinalizeContent();
void UnfreezeContent();
bool Serialize(FArchive& Ar, bool bInlineShaderResources, bool bLoadedByCookedMaterial, bool bInlineShaderCode=false);
EShaderPermutationFlags GetPermutationFlags() const
{
return PermutationFlags;
}
FString ToString() const;
#if WITH_EDITOR
inline void GetOutdatedTypes(TArray<const FShaderType*>& OutdatedShaderTypes, TArray<const FShaderPipelineType*>& OutdatedShaderPipelineTypes, TArray<const FVertexFactoryType*>& 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:
FShaderMapBase();
void AssignCopy(const FShaderMapBase& Source);
void InitResource();
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<FShaderMapResource> Resource;
TRefCountPtr<FShaderMapResourceCode> Code;
FShaderMapPointerTable* PointerTable;
TMemoryImageObject<FShaderMapContent> Content;
uint32 NumFrozenShaders;
EShaderPermutationFlags PermutationFlags;
};
template<typename ContentType, typename PointerTableType = FShaderMapPointerTable>
class TShaderMap : public FShaderMapBase
{
public:
inline const PointerTableType& GetPointerTable() const { return static_cast<const PointerTableType&>(FShaderMapBase::GetPointerTable()); }
inline const ContentType* GetContent() const { return static_cast<const ContentType*>(FShaderMapBase::GetContent()); }
inline ContentType* GetMutableContent() { return static_cast<ContentType*>(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<ContentType>(); }
virtual FShaderMapPointerTable* CreatePointerTable() const final override { return new PointerTableType(); }
};
template<typename ShaderType, typename PointerTableType>
inline const PointerTableType& TShaderRefBase<ShaderType, PointerTableType>::GetPointerTable() const
{
checkSlow(ShaderMap);
return static_cast<const PointerTableType&>(ShaderMap->GetPointerTable());
}
template<typename ShaderType, typename PointerTableType>
inline FShaderMapResource* TShaderRefBase<ShaderType, PointerTableType>::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<typename ShaderType>
class TShaderMapRef : public TShaderRef<ShaderType>
{
public:
TShaderMapRef(const typename ShaderType::ShaderMapType* ShaderIndex)
: TShaderRef<ShaderType>(ShaderIndex->template GetShader<ShaderType>(/* PermutationId = */ 0)) // gcc3 needs the template quantifier so it knows the < is not a less-than
{
static_assert(
TIsSame<typename ShaderType::FPermutationDomain, FShaderPermutationNone>::Value,
"Missing permutation vector argument for shader that have a permutation domain.");
}
TShaderMapRef(
const typename ShaderType::ShaderMapType* ShaderIndex,
const typename ShaderType::FPermutationDomain& PermutationVector)
: TShaderRef<ShaderType>(ShaderIndex->template GetShader<ShaderType>(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<typename ShaderType>
class TOptionalShaderMapRef : public TShaderRef<ShaderType>
{
public:
TOptionalShaderMapRef(const typename ShaderType::ShaderMapType* ShaderIndex):
TShaderRef<ShaderType>(TShaderRef<ShaderType>::Cast(ShaderIndex->GetShader(&ShaderType::StaticType))) // gcc3 needs the template quantifier so it knows the < is not a less-than
{}
};
/** Tracks state when traversing a FSerializationHistory. */
class FSerializationHistoryTraversalState
{
public:
const FSerializationHistory& History;
int32 NextTokenIndex;
int32 NextFullLengthIndex;
FSerializationHistoryTraversalState(const FSerializationHistory& InHistory) :
History(InHistory),
NextTokenIndex(0),
NextFullLengthIndex(0)
{}
/** Gets the length value from NextTokenIndex + Offset into history. */
uint32 GetValue(int32 Offset)
{
int32 CurrentOffset = Offset;
// Move to the desired offset
while (CurrentOffset > 0)
{
StepForward();
CurrentOffset--;
}
while (CurrentOffset < 0)
{
StepBackward();
CurrentOffset++;
}
check(CurrentOffset == 0);
// Decode
const int8 Token = History.GetToken(NextTokenIndex);
const uint32 Value = Token == 0 ? History.FullLengths[NextFullLengthIndex] : (int32)Token;
// Restore state
while (CurrentOffset < Offset)
{
StepBackward();
CurrentOffset++;
}
while (CurrentOffset > Offset)
{
StepForward();
CurrentOffset--;
}
check(CurrentOffset == Offset);
return Value;
}
FORCEINLINE void StepForward()
{
const int8 Token = History.GetToken(NextTokenIndex);
if (Token == 0)
{
checkSlow(NextFullLengthIndex - 1 < History.FullLengths.Num());
NextFullLengthIndex++;
}
// Not supporting seeking past the front most serialization in the history
checkSlow(NextTokenIndex - 1 < History.NumTokens);
NextTokenIndex++;
}
void StepBackward()
{
// Not supporting seeking outside of the history tracked
check(NextTokenIndex > 0);
NextTokenIndex--;
const int8 Token = History.GetToken(NextTokenIndex);
if (Token == 0)
{
check(NextFullLengthIndex > 0);
NextFullLengthIndex--;
}
}
};
/** Archive used when saving shaders, which generates data used to detect serialization mismatches on load. */
class FShaderSaveArchive final : public FArchiveProxy
{
public:
FShaderSaveArchive(FArchive& Archive, FSerializationHistory& InHistory) :
FArchiveProxy(Archive),
HistoryTraversalState(InHistory),
History(InHistory)
{
OriginalPosition = Archive.Tell();
}
virtual ~FShaderSaveArchive()
{
// Seek back to the original archive position so we can undo any serializations that went through this archive
InnerArchive.Seek(OriginalPosition);
}
virtual void Serialize( void* V, int64 Length )
{
if (HistoryTraversalState.NextTokenIndex < HistoryTraversalState.History.NumTokens)
{
// We are no longer appending (due to a seek), make sure writes match up in size with what's already been written
check(Length == HistoryTraversalState.GetValue(0));
}
else
{
// Appending to the archive, track the size of this serialization
check(Length >= 0 && Length <= TNumericLimits<uint32>::Max());
History.AddValue((uint32)Length);
}
HistoryTraversalState.StepForward();
if (V)
{
FArchiveProxy::Serialize(V, Length);
}
}
virtual void Seek( int64 InPos )
{
int64 Offset = InPos - Tell();
if (Offset <= 0)
{
// We're seeking backward, walk backward through the serialization history while updating NextSerialization
while (Offset < 0)
{
Offset += HistoryTraversalState.GetValue(-1);
HistoryTraversalState.StepBackward();
}
}
else
{
// We're seeking forward, walk forward through the serialization history while updating NextSerialization
while (Offset > 0)
{
Offset -= HistoryTraversalState.GetValue(-1);
HistoryTraversalState.StepForward();
}
HistoryTraversalState.StepForward();
}
check(Offset == 0);
FArchiveProxy::Seek(InPos);
}
FSerializationHistoryTraversalState HistoryTraversalState;
FSerializationHistory& History;
private:
/** Stored off position of the original archive we are wrapping. */
int64 OriginalPosition;
};
/**
* 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<FShader>& 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);