Files
UnrealEngine/Engine/Source/Runtime/RHI/Public/RHIUniformBufferLayoutInitializer.h
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

211 lines
7.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "RHIDefinitions.h"
#include "RHIMemoryLayout.h"
#include "RHIResources.h"
#include "Serialization/MemoryImage.h"
#include "Serialization/MemoryLayout.h"
/** Data structure to store information about resource parameter in a shader parameter structure. */
struct FRHIUniformBufferResourceInitializer
{
DECLARE_EXPORTED_TYPE_LAYOUT(FRHIUniformBufferResourceInitializer, RHI_API, NonVirtual);
/** Byte offset to each resource in the uniform buffer memory. */
LAYOUT_FIELD(uint16, MemberOffset);
/** Type of the member that allow (). */
LAYOUT_FIELD(EUniformBufferBaseType, MemberType);
/** Compare two uniform buffer layout resources. */
friend inline bool operator==(const FRHIUniformBufferResourceInitializer& A, const FRHIUniformBufferResourceInitializer& B)
{
return A.MemberOffset == B.MemberOffset
&& A.MemberType == B.MemberType;
}
};
inline FArchive& operator<<(FArchive& Ar, FRHIUniformBufferResourceInitializer& Ref)
{
uint8 Type = (uint8)Ref.MemberType;
Ar << Ref.MemberOffset;
Ar << Type;
Ref.MemberType = (EUniformBufferBaseType)Type;
return Ar;
}
/** Initializer for the layout of a uniform buffer in memory. */
struct FRHIUniformBufferLayoutInitializer
{
DECLARE_EXPORTED_TYPE_LAYOUT(FRHIUniformBufferLayoutInitializer, RHI_API, NonVirtual);
FRHIUniformBufferLayoutInitializer() = default;
explicit FRHIUniformBufferLayoutInitializer(const TCHAR* InName)
: Name(InName)
{}
explicit FRHIUniformBufferLayoutInitializer(const TCHAR* InName, uint32 InConstantBufferSize)
: Name(InName)
, ConstantBufferSize(InConstantBufferSize)
{
ComputeHash();
}
inline uint32 GetHash() const
{
checkSlow(Hash != 0);
return Hash;
}
/**
* This hash is stored with compiled shaders and is used for two things.
* 1. Validation that the data layout did not change between compilation and runtime.
* 2. Passing to FindUniformBufferStructByLayoutHash() to find the associated uniform buffer layouts (and specifically the runtime slot mappings) for the shader.
* Possibly it would be better to separate those concerns, but that would require more data stored per shader.
*/
void ComputeHash()
{
uint32 TmpHash = 0;
TmpHash |= (ConstantBufferSize << 16);
TmpHash |= (static_cast<uint32>(BindingFlags) << 8);
// Static slot is not stable. Just track whether we have one at all.
TmpHash |= (StaticSlot != MAX_UNIFORM_BUFFER_STATIC_SLOTS ? 1 << 0 : 0);
// These flags aren't based on the resources, so make sure they change the hash
TmpHash |= (EnumHasAnyFlags(Flags, ERHIUniformBufferFlags::NoEmulatedUniformBuffer) ? 1 << 1 : 0);
TmpHash |= (EnumHasAnyFlags(Flags, ERHIUniformBufferFlags::NeedsReflectedMembers) ? 1 << 2 : 0);
TmpHash |= (EnumHasAnyFlags(Flags, ERHIUniformBufferFlags::UniformView) ? 1 << 3 : 0);
for (int32 ResourceIndex = 0; ResourceIndex < Resources.Num(); ResourceIndex++)
{
// Offset and therefore hash must be the same regardless of pointer size
checkSlow(Resources[ResourceIndex].MemberOffset == Align(Resources[ResourceIndex].MemberOffset, SHADER_PARAMETER_POINTER_ALIGNMENT));
TmpHash ^= Resources[ResourceIndex].MemberOffset;
}
uint32 N = Resources.Num();
while (N >= 4)
{
TmpHash ^= (Resources[--N].MemberType << 0);
TmpHash ^= (Resources[--N].MemberType << 8);
TmpHash ^= (Resources[--N].MemberType << 16);
TmpHash ^= (Resources[--N].MemberType << 24);
}
while (N >= 2)
{
TmpHash ^= Resources[--N].MemberType << 0;
TmpHash ^= Resources[--N].MemberType << 16;
}
while (N > 0)
{
TmpHash ^= Resources[--N].MemberType;
}
// Name is necessary to distinguish different uniform buffer declarations with matching layouts.
// Without it FindUniformBufferStructByLayoutHash() is vulnerable to hash collisions.
TmpHash ^= FCrc::StrCrc32(*Name);
Hash = TmpHash;
}
void CopyFrom(const FRHIUniformBufferLayoutInitializer& Source)
{
ConstantBufferSize = Source.ConstantBufferSize;
StaticSlot = Source.StaticSlot;
BindingFlags = Source.BindingFlags;
Flags = Source.Flags;
Resources = Source.Resources;
Name = Source.Name;
Hash = Source.Hash;
}
const FMemoryImageString& GetDebugName() const
{
return Name;
}
bool HasRenderTargets() const
{
return RenderTargetsOffset != kUniformBufferInvalidOffset;
}
bool HasStaticSlot() const
{
return IsUniformBufferStaticSlotValid(StaticSlot);
}
friend FArchive& operator<<(FArchive& Ar, FRHIUniformBufferLayoutInitializer& Ref)
{
Ar << Ref.ConstantBufferSize;
Ar << Ref.StaticSlot;
Ar << Ref.RenderTargetsOffset;
Ar << Ref.BindingFlags;
Ar << Ref.Flags;
Ar << Ref.Resources;
Ar << Ref.GraphResources;
Ar << Ref.GraphTextures;
Ar << Ref.GraphBuffers;
Ar << Ref.GraphUniformBuffers;
Ar << Ref.UniformBuffers;
Ar << Ref.Name;
Ar << Ref.Hash;
return Ar;
}
private:
// for debugging / error message
LAYOUT_FIELD(FMemoryImageString, Name);
public:
/** The list of all resource inlined into the shader parameter structure. */
LAYOUT_FIELD(TMemoryImageArray<FRHIUniformBufferResourceInitializer>, Resources);
/** The list of all RDG resource references inlined into the shader parameter structure. */
LAYOUT_FIELD(TMemoryImageArray<FRHIUniformBufferResourceInitializer>, GraphResources);
/** The list of all RDG texture references inlined into the shader parameter structure. */
LAYOUT_FIELD(TMemoryImageArray<FRHIUniformBufferResourceInitializer>, GraphTextures);
/** The list of all RDG buffer references inlined into the shader parameter structure. */
LAYOUT_FIELD(TMemoryImageArray<FRHIUniformBufferResourceInitializer>, GraphBuffers);
/** The list of all RDG uniform buffer references inlined into the shader parameter structure. */
LAYOUT_FIELD(TMemoryImageArray<FRHIUniformBufferResourceInitializer>, GraphUniformBuffers);
/** The list of all non-RDG uniform buffer references inlined into the shader parameter structure. */
LAYOUT_FIELD(TMemoryImageArray<FRHIUniformBufferResourceInitializer>, UniformBuffers);
private:
LAYOUT_FIELD_INITIALIZED(uint32, Hash, 0);
public:
/** The size of the constant buffer in bytes. */
LAYOUT_FIELD_INITIALIZED(uint32, ConstantBufferSize, 0);
/** The render target binding slots offset, if it exists. */
LAYOUT_FIELD_INITIALIZED(uint16, RenderTargetsOffset, kUniformBufferInvalidOffset);
/** The static slot (if applicable). */
LAYOUT_FIELD_INITIALIZED(FUniformBufferStaticSlot, StaticSlot, MAX_UNIFORM_BUFFER_STATIC_SLOTS);
/** The binding flags describing how this resource can be bound to the RHI. */
LAYOUT_FIELD_INITIALIZED(EUniformBufferBindingFlags, BindingFlags, EUniformBufferBindingFlags::Shader);
/** Flags for the uniform buffer */
LAYOUT_FIELD_INITIALIZED(ERHIUniformBufferFlags, Flags, ERHIUniformBufferFlags::None);
/** Compare two uniform buffer layout initializers. */
friend inline bool operator==(const FRHIUniformBufferLayoutInitializer& A, const FRHIUniformBufferLayoutInitializer& B)
{
return A.ConstantBufferSize == B.ConstantBufferSize
&& A.StaticSlot == B.StaticSlot
&& A.BindingFlags == B.BindingFlags
&& A.Resources == B.Resources;
}
};