Files
UnrealEngine/Engine/Source/Runtime/RHI/Public/RHIShaderParameters.h
2025-05-18 13:04:45 +08:00

444 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Containers/Array.h"
#include "RHIResources.h"
#include "RHIResourceCollection.h"
#define RHI_VALIDATE_BATCHED_SHADER_PARAMETERS DO_CHECK
class FRHICommandList;
class FRHIComputeCommandList;
/** Compact representation of a bound shader parameter (read: value). Its offsets are for referencing their data in an associated blob. */
struct FRHIShaderParameter
{
FRHIShaderParameter(uint16 InBufferIndex, uint16 InBaseIndex, uint16 InByteOffset, uint16 InByteSize)
: BufferIndex(InBufferIndex)
, BaseIndex(InBaseIndex)
, ByteOffset(InByteOffset)
, ByteSize(InByteSize)
{
}
uint16 BufferIndex;
uint16 BaseIndex;
uint16 ByteOffset;
uint16 ByteSize;
};
/** Compact representation of a bound resource parameter (Texture, SRV, UAV, SamplerState, or UniformBuffer) */
struct FRHIShaderParameterResource
{
enum class EType : uint8
{
Texture,
ResourceView,
UnorderedAccessView,
Sampler,
UniformBuffer,
ResourceCollection,
};
FRHIShaderParameterResource() = default;
FRHIShaderParameterResource(EType InType, FRHIResource* InResource, uint16 InIndex)
: Resource(InResource)
, Index(InIndex)
, Type(InType)
{
}
FRHIShaderParameterResource(FRHITexture* InTexture, uint16 InIndex)
: FRHIShaderParameterResource(FRHIShaderParameterResource::EType::Texture, InTexture, InIndex)
{
}
FRHIShaderParameterResource(FRHIShaderResourceView* InView, uint16 InIndex)
: FRHIShaderParameterResource(FRHIShaderParameterResource::EType::ResourceView, InView, InIndex)
{
}
FRHIShaderParameterResource(FRHIUnorderedAccessView* InUAV, uint16 InIndex)
: FRHIShaderParameterResource(FRHIShaderParameterResource::EType::UnorderedAccessView, InUAV, InIndex)
{
}
FRHIShaderParameterResource(FRHISamplerState* InSamplerState, uint16 InIndex)
: FRHIShaderParameterResource(FRHIShaderParameterResource::EType::Sampler, InSamplerState, InIndex)
{
}
FRHIShaderParameterResource(FRHIUniformBuffer* InUniformBuffer, uint16 InIndex)
: FRHIShaderParameterResource(FRHIShaderParameterResource::EType::UniformBuffer, InUniformBuffer, InIndex)
{
}
FRHIShaderParameterResource(FRHIResourceCollection* InResourceCollection, uint16 InIndex)
: FRHIShaderParameterResource(FRHIShaderParameterResource::EType::ResourceCollection, InResourceCollection, InIndex)
{
}
bool operator == (const FRHIShaderParameterResource& Other) const
{
return Resource == Other.Resource
&& Index == Other.Index
&& Type == Other.Type;
}
FRHIResource* Resource = nullptr;
uint16 Index = 0;
EType Type = EType::Texture;
};
struct FRHIBatchedShaderParameters;
enum class ERHIBatchedShaderParameterAllocatorPageSize
{
Small,
Large
};
class FRHIBatchedShaderParametersAllocator
{
public:
FRHIBatchedShaderParametersAllocator* Next;
FRHICommandListBase& RHICmdList;
private:
friend class FRHICommandListBase;
friend struct FRHIBatchedShaderParameters;
FMemStackBase ParametersData;
FMemStackBase Parameters;
FMemStackBase ResourceParameters;
FMemStackBase BindlessParameters;
FRHIBatchedShaderParametersAllocator(FRHIBatchedShaderParametersAllocator*& InOutRootListLink, FRHICommandListBase& InRHICmdList, ERHIBatchedShaderParameterAllocatorPageSize PageSize)
: FRHIBatchedShaderParametersAllocator(InOutRootListLink, InRHICmdList, PageSize == ERHIBatchedShaderParameterAllocatorPageSize::Small ? FMemStackBase::EPageSize::Small : FMemStackBase::EPageSize::Large)
{}
FRHIBatchedShaderParametersAllocator(FRHIBatchedShaderParametersAllocator*& InOutRootListLink, FRHICommandListBase& InRHICmdList, FMemStackBase::EPageSize PageSize)
: Next(InOutRootListLink)
, RHICmdList(InRHICmdList)
, ParametersData(PageSize)
, Parameters(PageSize)
, ResourceParameters(PageSize)
, BindlessParameters(PageSize)
{
InOutRootListLink = this;
}
FORCEINLINE void Attach(const FRHIBatchedShaderParameters* InParameters)
{
#if RHI_VALIDATE_BATCHED_SHADER_PARAMETERS
if (AttachedParameters != InParameters)
{
checkf(!AttachedParameters, TEXT("Only one FRHIBatchedShaderParameters instance can be used at a time with this allocator. You must call FRHIBatchedShaderParameters::{Reset, Finish} to start processing a new one."));
AttachedParameters = InParameters;
}
#endif
}
FORCEINLINE void Detach()
{
#if RHI_VALIDATE_BATCHED_SHADER_PARAMETERS
AttachedParameters = nullptr;
#endif
}
template <typename... ArgsType>
FORCEINLINE void EmplaceParameter(TArrayView<FRHIShaderParameter>& InOutArray, ArgsType&& ...Args)
{
Emplace(Parameters, InOutArray, Forward<ArgsType&&>(Args)...);
}
template <typename... ArgsType>
FORCEINLINE void AddResourceParameter(TArrayView<FRHIShaderParameterResource>& InOutArray, ArgsType&& ...Args)
{
Emplace(ResourceParameters, InOutArray, Forward<ArgsType&&>(Args)...);
}
template <typename... ArgsType>
FORCEINLINE void AddBindlessParameter(TArrayView<FRHIShaderParameterResource>& InOutArray, ArgsType&& ...Args)
{
Emplace(BindlessParameters, InOutArray, Forward<ArgsType&&>(Args)...);
}
#if RHI_VALIDATE_BATCHED_SHADER_PARAMETERS
const FRHIBatchedShaderParameters* AttachedParameters = nullptr;
#endif
template <typename ElementType, typename... ArgsType>
void Emplace(FMemStackBase& MemStack, TArrayView<ElementType>& InOutArray, ArgsType&& ...Args)
{
static_assert(sizeof(ElementType) % alignof(ElementType) == 0, "Element size must be a multiple of its alignment");
const size_t ElementSize = sizeof(ElementType);
const size_t Alignment = alignof(ElementType);
const int32 NumElements = InOutArray.Num() + 1;
ElementType* Elements = InOutArray.GetData();
if (InOutArray.IsEmpty())
{
Elements = new (MemStack.Alloc(ElementSize, Alignment)) ElementType(Forward<ArgsType&>(Args)...);
}
else
{
// Sanity check that the top of the stack contains the last element that was allocated.
check(MemStack.GetTop() == (uint8*)(InOutArray.GetData() + InOutArray.Num()));
// Try to extend the size of the current array without resizing.
if (MemStack.CanFitInPage(ElementSize, 1))
{
new (MemStack.Alloc(ElementSize, 1)) ElementType(Forward<ArgsType&>(Args)...);
}
// Reached the end of the page. Reallocate the entire array into a new page.
else
{
Elements = reinterpret_cast<ElementType*>(MemStack.Alloc(NumElements * ElementSize, Alignment));
ElementType* LastElement = Elements;
for (int32 Index = 0; Index < InOutArray.Num(); ++Index, ++LastElement)
{
new (LastElement) ElementType(MoveTemp(InOutArray[Index]));
}
new (LastElement) ElementType(Forward<ArgsType&>(Args)...);
}
}
InOutArray = TArrayView<ElementType>(Elements, NumElements);
}
void AppendParametersData(TArrayView<uint8>& InOutArray, uint32 NumBytes, const uint8* Bytes)
{
constexpr size_t Alignment = 1;
const int32 NumArrayBytes = InOutArray.Num() + NumBytes;
uint8* ArrayBytes = InOutArray.GetData();
if (InOutArray.IsEmpty())
{
ArrayBytes = (uint8*)ParametersData.Alloc(NumBytes, Alignment);
FMemory::Memcpy(ArrayBytes, Bytes, NumBytes);
}
else
{
// Sanity check that the top of the stack contains the last element that was allocated.
check(ParametersData.GetTop() == InOutArray.GetData() + InOutArray.Num());
// Try to extend the size of the current array without resizing.
if (ParametersData.CanFitInPage(NumBytes, Alignment))
{
FMemory::Memcpy(ParametersData.Alloc(NumBytes, Alignment), Bytes, NumBytes);
}
// Reached the end of the page. Reallocate the entire array into a new page.
else
{
ArrayBytes = (uint8*)ParametersData.Alloc(NumArrayBytes, Alignment);
FMemory::Memcpy(ArrayBytes, InOutArray.GetData(), InOutArray.Num());
FMemory::Memcpy(ArrayBytes + InOutArray.Num(), Bytes, NumBytes);
}
}
InOutArray = TArrayView<uint8>(ArrayBytes, NumArrayBytes);
}
};
/** Collection of parameters to set in the RHI. These parameters aren't bound to any specific shader until SetBatchedShaderParameters is called. */
struct FRHIBatchedShaderParameters
{
FRHIBatchedShaderParametersAllocator& Allocator;
TArrayView<uint8> ParametersData;
TArrayView<FRHIShaderParameter> Parameters;
TArrayView<FRHIShaderParameterResource> ResourceParameters;
TArrayView<FRHIShaderParameterResource> BindlessParameters;
FRHIBatchedShaderParameters(FRHIBatchedShaderParametersAllocator& InAllocator)
: Allocator(InAllocator)
{}
inline bool HasParameters() const
{
return (Parameters.Num() + ResourceParameters.Num() + BindlessParameters.Num()) > 0;
}
// Marks the parameters as complete and retains the parameter contents.
void Finish()
{
Allocator.Detach();
}
// Resets the parameters back to an empty state.
void Reset()
{
Allocator.Detach();
ParametersData = {};
Parameters = {};
ResourceParameters = {};
BindlessParameters = {};
}
template <typename... ArgsType>
FORCEINLINE_DEBUGGABLE void AddResourceParameter(ArgsType&& ...Args)
{
Allocator.Attach(this);
Allocator.AddResourceParameter(ResourceParameters, Forward<ArgsType&>(Args)...);
}
template <typename... ArgsType>
FORCEINLINE_DEBUGGABLE void AddBindlessParameter(ArgsType&& ...Args)
{
Allocator.Attach(this);
Allocator.AddBindlessParameter(BindlessParameters, Forward<ArgsType&>(Args)...);
}
FORCEINLINE_DEBUGGABLE void SetShaderParameter(uint32 BufferIndex, uint32 BaseIndex, uint32 NumBytes, const void* NewValue)
{
const int32 DestDataOffset = ParametersData.Num();
Allocator.Attach(this);
Allocator.AppendParametersData(ParametersData, NumBytes, (const uint8*)NewValue);
Allocator.EmplaceParameter(Parameters, (uint16)BufferIndex, (uint16)BaseIndex, (uint16)DestDataOffset, (uint16)NumBytes);
}
FORCEINLINE_DEBUGGABLE void SetShaderUniformBuffer(uint32 Index, FRHIUniformBuffer* UniformBuffer)
{
Allocator.Attach(this);
AddResourceParameter(UniformBuffer, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetShaderTexture(uint32 Index, FRHITexture* Texture)
{
AddResourceParameter(Texture, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetShaderResourceViewParameter(uint32 Index, FRHIShaderResourceView* SRV)
{
AddResourceParameter(SRV, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetShaderSampler(uint32 Index, FRHISamplerState* State)
{
AddResourceParameter(State, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetUAVParameter(uint32 Index, FRHIUnorderedAccessView* UAV)
{
AddResourceParameter(UAV, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetResourceCollection(uint32 Index, FRHIResourceCollection* ResourceCollection)
{
AddResourceParameter(ResourceCollection, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetBindlessTexture(uint32 Index, FRHITexture* Texture)
{
AddBindlessParameter(Texture, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetBindlessResourceView(uint32 Index, FRHIShaderResourceView* SRV)
{
AddBindlessParameter(SRV, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetBindlessSampler(uint32 Index, FRHISamplerState* State)
{
AddBindlessParameter(State, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetBindlessUAV(uint32 Index, FRHIUnorderedAccessView* UAV)
{
AddBindlessParameter(UAV, (uint16)Index);
}
FORCEINLINE_DEBUGGABLE void SetBindlessResourceCollection(uint32 Index, FRHIResourceCollection* ResourceCollection)
{
AddBindlessParameter(ResourceCollection, (uint16)Index);
}
};
/** Compact representation of a resource parameter unbind, limited to SRVs and UAVs */
struct FRHIShaderParameterUnbind
{
enum class EType : uint8
{
ResourceView,
UnorderedAccessView,
};
FRHIShaderParameterUnbind() = default;
FRHIShaderParameterUnbind(EType InType, uint16 InIndex)
: Index(InIndex)
, Type(InType)
{
}
uint16 Index = 0;
EType Type = EType::ResourceView;
};
/** Collection of parameters to unbind in the RHI. These unbinds aren't tied to any specific shader until SetBatchedShaderUnbinds is called. */
struct FRHIBatchedShaderUnbinds
{
TArray<FRHIShaderParameterUnbind> Unbinds;
bool HasParameters() const
{
return Unbinds.Num() > 0;
}
void Reset()
{
Unbinds.Reset();
}
void UnsetSRV(uint32 Index)
{
Unbinds.Emplace(FRHIShaderParameterUnbind::EType::ResourceView, (uint16)Index);
}
void UnsetUAV(uint32 Index)
{
Unbinds.Emplace(FRHIShaderParameterUnbind::EType::UnorderedAccessView, (uint16)Index);
}
};
struct FRHIShaderBundleComputeDispatch
{
uint32 RecordIndex = ~uint32(0u);
class FComputePipelineState* PipelineState = nullptr;
FRHIComputeShader* Shader = nullptr;
FRHIWorkGraphShader* WorkGraphShader = nullptr;
FRHIComputePipelineState* RHIPipeline = nullptr;
TOptional<FRHIBatchedShaderParameters> Parameters;
FUint32Vector4 Constants;
inline bool IsValid() const
{
return RecordIndex != ~uint32(0u);
}
};
struct FRHIShaderBundleGraphicsState
{
FIntRect ViewRect;
float DepthMin = 0.0f;
float DepthMax = 1.0f;
float BlendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
EPrimitiveType PrimitiveType = PT_TriangleList;
uint8 StencilRef = 0;
};
struct FRHIShaderBundleGraphicsDispatch
{
uint32 RecordIndex = ~uint32(0u);
class FGraphicsPipelineState* PipelineState = nullptr;
FRHIGraphicsPipelineState* RHIPipeline = nullptr;
FGraphicsPipelineStateInitializer PipelineInitializer;
TOptional<FRHIBatchedShaderParameters> Parameters_MSVS;
TOptional<FRHIBatchedShaderParameters> Parameters_PS;
FUint32Vector4 Constants;
inline bool IsValid() const
{
return RecordIndex != ~uint32(0u);
}
};