479 lines
18 KiB
C++
479 lines
18 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RHIResources.h"
|
|
#include "Containers/ConsumeAllMpmcQueue.h"
|
|
#include "Experimental/Containers/HazardPointer.h"
|
|
#include "Misc/MemStack.h"
|
|
#include "RHICommandList.h"
|
|
#include "RHIUniformBufferLayoutInitializer.h"
|
|
#include "Stats/Stats.h"
|
|
#include "RHIGlobals.h"
|
|
|
|
UE::TConsumeAllMpmcQueue<FRHIResource*> PendingDeletes;
|
|
UE::TConsumeAllMpmcQueue<FRHIResource*> PendingDeletesWithLifetimeExtension;
|
|
|
|
#if DO_CHECK
|
|
// Used to check that we only enter the FRHIResource destructor via the FRHIResource::DeleteResources() function.
|
|
thread_local FRHIResource const* FRHIResource::CurrentlyDeleting = nullptr;
|
|
#endif
|
|
|
|
FRHIResource::FRHIResource(ERHIResourceType InResourceType)
|
|
: ResourceType(InResourceType)
|
|
, bCommitted(true)
|
|
, bAllowExtendLifetime(true)
|
|
#if RHI_ENABLE_RESOURCE_INFO
|
|
, bBeingTracked(false)
|
|
#endif
|
|
{
|
|
#if RHI_ENABLE_RESOURCE_INFO
|
|
BeginTrackingResource(this);
|
|
#endif
|
|
}
|
|
|
|
FRHIResource::~FRHIResource()
|
|
{
|
|
#if DO_CHECK
|
|
check(IsEngineExitRequested() || CurrentlyDeleting == this);
|
|
check(AtomicFlags.GetNumRefs(std::memory_order_relaxed) == 0); // this should not have any outstanding refs
|
|
CurrentlyDeleting = nullptr;
|
|
#endif
|
|
|
|
#if RHI_ENABLE_RESOURCE_INFO
|
|
EndTrackingResource(this);
|
|
#endif
|
|
}
|
|
|
|
void FRHIResource::MarkForDelete() const
|
|
{
|
|
if (!AtomicFlags.MarkForDelete(std::memory_order_release))
|
|
{
|
|
if (bAllowExtendLifetime)
|
|
{
|
|
PendingDeletesWithLifetimeExtension.ProduceItem(const_cast<FRHIResource*>(this));
|
|
}
|
|
else
|
|
{
|
|
PendingDeletes.ProduceItem(const_cast<FRHIResource*>(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRHIResource::DeleteResources(TArray<FRHIResource*> const& Resources)
|
|
{
|
|
for (FRHIResource* Resource : Resources)
|
|
{
|
|
if (Resource->AtomicFlags.Deleting())
|
|
{
|
|
#if DO_CHECK
|
|
CurrentlyDeleting = Resource;
|
|
#endif
|
|
delete Resource;
|
|
|
|
#if DO_CHECK
|
|
check(CurrentlyDeleting == nullptr);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("Gather Deleted Resources"), STAT_GatherDeletedResources, STATGROUP_RHICMDLIST);
|
|
|
|
int32 GRHIResourceLifetimeRefCount = 0;
|
|
|
|
void RHIResourceLifetimeAddRef(int32 NumRefs)
|
|
{
|
|
check(IsInRenderingThread());
|
|
GRHIResourceLifetimeRefCount += NumRefs;
|
|
}
|
|
|
|
void RHIResourceLifetimeReleaseRef(FRHICommandListImmediate& RHICmdList, int32 NumRefs)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
int32 RefCount = GRHIResourceLifetimeRefCount -= NumRefs;
|
|
check(RefCount >= 0);
|
|
|
|
if (!RefCount)
|
|
{
|
|
RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread, ERHISubmitFlags::DeleteResources);
|
|
}
|
|
}
|
|
|
|
void FRHIResource::GatherResourcesToDelete(TArray<FRHIResource*>& OutResources, bool bIncludeExtendedLifetimeResources)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_GatherDeletedResources);
|
|
if (bIncludeExtendedLifetimeResources)
|
|
{
|
|
PendingDeletesWithLifetimeExtension.ConsumeAllLifo([&OutResources](FRHIResource* Resource)
|
|
{
|
|
OutResources.Emplace(Resource);
|
|
});
|
|
}
|
|
|
|
PendingDeletes.ConsumeAllLifo([&OutResources](FRHIResource* Resource)
|
|
{
|
|
OutResources.Emplace(Resource);
|
|
});
|
|
}
|
|
|
|
FRHIBuffer::FRHIBuffer(const FRHIBufferCreateDesc& CreateDesc)
|
|
: FRHIViewableResource(RRT_Buffer, CreateDesc.InitialState, CreateDesc.DebugName, CreateDesc.OwnerName)
|
|
#if ENABLE_RHI_VALIDATION
|
|
, RHIValidation::FBufferResource(CreateDesc)
|
|
#endif
|
|
, Desc(CreateDesc)
|
|
{
|
|
}
|
|
|
|
FRHITexture::FRHITexture(const FRHITextureCreateDesc& CreateDesc)
|
|
: FRHIViewableResource(RRT_Texture, CreateDesc.InitialState, CreateDesc.DebugName, CreateDesc.OwnerName)
|
|
#if ENABLE_RHI_VALIDATION
|
|
, RHIValidation::FTextureResource(CreateDesc)
|
|
#endif
|
|
, TextureDesc(CreateDesc)
|
|
{
|
|
}
|
|
|
|
FRHITexture::FRHITexture(ERHIResourceType InResourceType)
|
|
: FRHIViewableResource(InResourceType, ERHIAccess::Unknown, nullptr, NAME_None)
|
|
{
|
|
check(InResourceType == RRT_TextureReference);
|
|
}
|
|
|
|
void FRHITexture::SetName(FName InName)
|
|
{
|
|
Name = InName;
|
|
}
|
|
|
|
const TCHAR* FRHIViewDesc::GetBufferTypeString(FRHIViewDesc::EBufferType BufferType)
|
|
{
|
|
switch (BufferType)
|
|
{
|
|
case FRHIViewDesc::EBufferType::Unknown: return TEXT("Unknown");
|
|
case FRHIViewDesc::EBufferType::Typed: return TEXT("Typed");
|
|
case FRHIViewDesc::EBufferType::Structured: return TEXT("Structured");
|
|
case FRHIViewDesc::EBufferType::AccelerationStructure: return TEXT("AccelerationStructure");
|
|
case FRHIViewDesc::EBufferType::Raw: return TEXT("Raw");
|
|
default: checkf(false, TEXT("Missing FRHIViewDesc::EBufferType %d"), BufferType);
|
|
}
|
|
return TEXT("");
|
|
}
|
|
|
|
const TCHAR* FRHIViewDesc::GetTextureDimensionString(FRHIViewDesc::EDimension Dimension)
|
|
{
|
|
switch (Dimension)
|
|
{
|
|
case EDimension::Texture2D: return TEXT("Texture2D");
|
|
case EDimension::Texture2DArray: return TEXT("Texture2DArray");
|
|
case EDimension::Texture3D: return TEXT("Texture3D");
|
|
case EDimension::TextureCube: return TEXT("TextureCube");
|
|
case EDimension::TextureCubeArray: return TEXT("TextureCubeArray");
|
|
default: checkf(false, TEXT("Missing FRHIViewDesc::EDimension %d"), Dimension);
|
|
}
|
|
|
|
return TEXT("");
|
|
}
|
|
FRHIViewDesc::FBuffer::FViewInfo FRHIViewDesc::FBuffer::GetViewInfo(FRHIBuffer* TargetBuffer) const
|
|
{
|
|
check(TargetBuffer);
|
|
checkf(BufferType != EBufferType::Unknown, TEXT("A buffer type must be specified when creating a buffer view. Use SetType() or SetTypeFromBuffer()."));
|
|
|
|
FRHIBufferDesc const& Desc = TargetBuffer->GetDesc();
|
|
if (Desc.IsNull())
|
|
{
|
|
FViewInfo Info = {};
|
|
Info.bNullView = true;
|
|
return Info;
|
|
}
|
|
|
|
FViewInfo Info = {};
|
|
Info.BufferType = BufferType;
|
|
Info.Format = Format;
|
|
|
|
// Find the correct stride, and do some validation.
|
|
switch (Info.BufferType)
|
|
{
|
|
default:
|
|
checkNoEntry();
|
|
[[fallthrough]];
|
|
|
|
case EBufferType::Typed:
|
|
checkf(!EnumHasAnyFlags(Desc.Usage, BUF_StructuredBuffer | BUF_AccelerationStructure), TEXT("Cannot create typed views of structured buffers, or ray tracing acceleration structures."));
|
|
checkf(Format != PF_Unknown, TEXT("Format cannot be unknown for typed buffers."));
|
|
checkf(Stride == 0, TEXT("Do not specify a stride for typed buffer views."));
|
|
checkf((OffsetInBytes % RHIGetMinimumAlignmentForBufferBackedSRV(Info.Format)) == 0, TEXT("Buffer OffsetInBytes (%d) must be a multiple of the minimum alignment (%" UINT64_FMT ") supported by the RHI for format (%d: %s)."),
|
|
OffsetInBytes,
|
|
RHIGetMinimumAlignmentForBufferBackedSRV(Info.Format),
|
|
Info.Format,
|
|
GPixelFormats[Info.Format].Name
|
|
);
|
|
|
|
// Stride is determined by the format
|
|
Info.StrideInBytes = GPixelFormats[Info.Format].BlockBytes;
|
|
break;
|
|
|
|
case EBufferType::Structured:
|
|
checkf(EnumHasAnyFlags(Desc.Usage, BUF_StructuredBuffer), TEXT("The buffer descriptor is not a structured buffer, so is incompatible with this view type."));
|
|
checkf(Format == PF_Unknown, TEXT("Structured buffer views should not specify a format."));
|
|
|
|
// Stride is taken from the view, or the underlying buffer if not provided.
|
|
Info.StrideInBytes = Stride == 0 ? Desc.Stride : Stride;
|
|
checkf(Info.StrideInBytes > 0, TEXT("Stride for structured buffers must be set by the view, or on the underlying buffer resource."));
|
|
checkf((OffsetInBytes % Info.StrideInBytes) == 0, TEXT("OffsetInBytes (%d) must be a multiple of element stride (%d)."), OffsetInBytes, Info.StrideInBytes);
|
|
break;
|
|
|
|
case EBufferType::AccelerationStructure:
|
|
checkf(EnumHasAnyFlags(Desc.Usage, BUF_AccelerationStructure), TEXT("The buffer descriptor is not a ray tracing acceleration structure, so is incompatible with this view type."));
|
|
checkf(Format == PF_Unknown, TEXT("Acceleration structure views should not specify a format."));
|
|
checkf(RayTracingScene != nullptr, TEXT("RayTracingScene must be specified when creating view of ray tracing acceleration structures."));
|
|
|
|
// Treat acceleration structures as a byte array.
|
|
Info.StrideInBytes = 1;
|
|
break;
|
|
|
|
case EBufferType::Raw:
|
|
checkf(GRHIGlobals.SupportsRawViewsForAnyBuffer || EnumHasAnyFlags(Desc.Usage, BUF_ByteAddressBuffer), TEXT("The current RHI does not support raw access to buffers created without the BUF_ByteAddressBuffer usage flag."));
|
|
checkf(Format == PF_Unknown, TEXT("Raw buffer views should not specify a format."));
|
|
checkf(Stride == 0, TEXT("Do not specify a stride for raw buffer views."));
|
|
checkf((OffsetInBytes % 16) == 0, TEXT("The byte offset of raw views must be a multiple of 16 (specified offset: %d)."), OffsetInBytes);
|
|
|
|
// Raw buffers are always an array of 32-bit ints.
|
|
Info.StrideInBytes = sizeof(uint32);
|
|
Info.Format = PF_Unknown;
|
|
break;
|
|
}
|
|
|
|
checkf(OffsetInBytes < Desc.Size, TEXT("Buffer byte offset (%d) is out of bounds (size: %d)."), OffsetInBytes, Desc.Size);
|
|
Info.OffsetInBytes = OffsetInBytes;
|
|
|
|
// OffsetInBytes == 0 && NumElements == 0 is a special case to mean "whole resource". If offset is non-zero, we need the caller to pass the required number of elements, except for acceleration structures.
|
|
checkf(Info.BufferType == EBufferType::AccelerationStructure || (OffsetInBytes == 0 || NumElements > 0), TEXT("NumElements field must be non-zero if a byte offset is used."));
|
|
|
|
// If BufferType is AccelerationStructure or NumElements is zero, use "whole buffer".
|
|
Info.NumElements = (Info.BufferType == EBufferType::AccelerationStructure || NumElements == 0) ? (Desc.Size - OffsetInBytes) / Info.StrideInBytes : NumElements;
|
|
Info.SizeInBytes = Info.NumElements * Info.StrideInBytes;
|
|
|
|
checkf(Info.OffsetInBytes + Info.SizeInBytes <= Desc.Size,
|
|
TEXT("The bounds of the view (offset: %d, size in bytes: %d, stride: %d, num elements: %d) exceeds the size of the underlying buffer (%d bytes)."),
|
|
Info.OffsetInBytes,
|
|
Info.SizeInBytes,
|
|
Info.StrideInBytes,
|
|
Info.NumElements,
|
|
Desc.Size);
|
|
|
|
checkf(Info.Format == PF_Unknown || GPixelFormats[Info.Format].Supported, TEXT("Unsupported format (%d: %s) requested in view of buffer resource: %s")
|
|
, Info.Format
|
|
, GPixelFormats[Info.Format].Name
|
|
, *TargetBuffer->GetName().ToString()
|
|
);
|
|
|
|
return Info;
|
|
}
|
|
|
|
RHI_API FRHIViewDesc::FBufferSRV::FViewInfo FRHIViewDesc::FBufferSRV::GetViewInfo(FRHIBuffer* TargetBuffer) const
|
|
{
|
|
check(ViewType == EViewType::BufferSRV);
|
|
return FViewInfo { FBuffer::GetViewInfo(TargetBuffer) };
|
|
}
|
|
|
|
RHI_API FRHIViewDesc::FBufferUAV::FViewInfo FRHIViewDesc::FBufferUAV::GetViewInfo(FRHIBuffer* TargetBuffer) const
|
|
{
|
|
check(ViewType == EViewType::BufferUAV);
|
|
FViewInfo Info = { FBuffer::GetViewInfo(TargetBuffer) };
|
|
|
|
// @todo checks to see if these flags are valid
|
|
Info.bAtomicCounter = bAtomicCounter;
|
|
Info.bAppendBuffer = bAppendBuffer;
|
|
|
|
return Info;
|
|
}
|
|
|
|
FRHIViewDesc::FTexture::FViewInfo FRHIViewDesc::FTexture::GetViewInfo(FRHITexture* TargetTexture) const
|
|
{
|
|
check(TargetTexture);
|
|
checkf(Dimension != EDimension::Unknown, TEXT("A texture dimension must be specified when creating a texture view. Use SetDimension() or SetDimensionFromTexture()."));
|
|
|
|
FRHITextureDesc const& Desc = TargetTexture->GetDesc();
|
|
|
|
checkf(!Desc.IsTexture3D() || Dimension == EDimension::Texture3D, TEXT("Views of 3D textures must use 3D dimension."));
|
|
checkf(Desc.IsTexture3D() || Dimension != EDimension::Texture3D, TEXT("The underlying texture resource must be a 3D texture to create a 3D dimension view."));
|
|
checkf(Desc.IsTextureCube() || (Dimension != EDimension::TextureCube && Dimension != EDimension::TextureCubeArray), TEXT("The underlying texture resource must be a cube (or cube array) to create a cube dimension view."));
|
|
|
|
checkf(MipRange.Num > 0 || MipRange.First == 0, TEXT("MipRange.Num cannot be zero, unless creating a view of the entire range."));
|
|
checkf(MipRange.First + MipRange.Num <= Desc.NumMips, TEXT("Mip range (first: %d, num: %d) is out of bounds for texture description (num mips: %d)."),
|
|
MipRange.First,
|
|
MipRange.Num,
|
|
Desc.NumMips
|
|
);
|
|
|
|
checkf(ArrayRange.Num > 0 || ArrayRange.First == 0, TEXT("ArrayRange.Num cannot be zero, unless creating a view of the entire range."));
|
|
|
|
#if DO_CHECK
|
|
// make sure the view fits in the texture
|
|
{
|
|
uint16 TextureArraySize = Desc.IsTextureCube() ? Desc.ArraySize * 6 : Desc.ArraySize;
|
|
uint16 ViewArrayFirst = ArrayRange.First;
|
|
uint16 ViewArrayNum = ArrayRange.Num == 0 ? Desc.ArraySize : ArrayRange.Num;
|
|
|
|
bool bIsCubeView = false;
|
|
if (Dimension == EDimension::TextureCube || Dimension == EDimension::TextureCubeArray)
|
|
{
|
|
bIsCubeView = true;
|
|
ViewArrayFirst *= 6;
|
|
ViewArrayNum *= 6;
|
|
}
|
|
|
|
uint16 SliceDividerForCheckMessage = bIsCubeView ? 6 : 1; // We want the message to report the number of elements in the units of the view
|
|
checkf(ViewArrayFirst + ViewArrayNum <= TextureArraySize, TEXT("Array range (first: %d, num: %d) is out of bounds for texture description (array size: %d)."),
|
|
ViewArrayFirst / SliceDividerForCheckMessage,
|
|
ViewArrayNum / SliceDividerForCheckMessage,
|
|
TextureArraySize / SliceDividerForCheckMessage
|
|
);
|
|
}
|
|
#endif
|
|
|
|
// When ArrayRange.Num == 0, we use the number of elements from the texture. If the view is a 2D array and the texture a cube (array), we need to do x6 on the number of slices
|
|
// We already checked that we can only create cube views on cube textures, so we only need to take into account the 2D view on cube texture case
|
|
uint16 AdjustedTextureArraySize = Desc.ArraySize;
|
|
if (Dimension == EDimension::Texture2DArray && Desc.IsTextureCube())
|
|
{
|
|
AdjustedTextureArraySize *= 6;
|
|
}
|
|
|
|
FViewInfo Info = {};
|
|
Info.Format = Format == PF_Unknown ? Desc.Format : Format;
|
|
Info.Plane = Plane;
|
|
Info.Dimension = Dimension;
|
|
|
|
switch (Plane)
|
|
{
|
|
default:
|
|
checkNoEntry();
|
|
[[fallthrough]];
|
|
|
|
case ERHITexturePlane::Primary:
|
|
case ERHITexturePlane::PrimaryCompressed:
|
|
// Override the returned plane when using the PF_X24_G8 format.
|
|
// @todo remove use of PF_X24_G8 in the engine.
|
|
if (Format == PF_X24_G8)
|
|
{
|
|
check(EnumHasAnyFlags(Desc.Flags, TexCreate_DepthStencilTargetable));
|
|
Info.Plane = ERHITexturePlane::Stencil;
|
|
}
|
|
break;
|
|
|
|
case ERHITexturePlane::Stencil:
|
|
check(EnumHasAnyFlags(Desc.Flags, TexCreate_DepthStencilTargetable));
|
|
if (Format == PF_Unknown)
|
|
{
|
|
Info.Format = PF_X24_G8;
|
|
}
|
|
break;
|
|
|
|
case ERHITexturePlane::HTile:
|
|
check(EnumHasAnyFlags(Desc.Flags, TexCreate_DepthStencilTargetable));
|
|
break;
|
|
|
|
case ERHITexturePlane::Depth:
|
|
case ERHITexturePlane::FMask:
|
|
case ERHITexturePlane::CMask:
|
|
break;
|
|
}
|
|
|
|
checkf(Info.Format == PF_Unknown || GPixelFormats[Info.Format].Supported, TEXT("Unsupported format (%d: %s) requested in view of texture resource: %s")
|
|
, Info.Format
|
|
, GPixelFormats[Info.Format].Name
|
|
, *TargetTexture->GetName().ToString()
|
|
);
|
|
|
|
Info.ArrayRange.First = ArrayRange.First;
|
|
Info.ArrayRange.Num = ArrayRange.Num == 0 ? AdjustedTextureArraySize : ArrayRange.Num;
|
|
Info.bAllSlices = Info.ArrayRange.First == 0 && Info.ArrayRange.Num == AdjustedTextureArraySize;
|
|
|
|
return Info;
|
|
}
|
|
|
|
FRHIViewDesc::FTextureSRV::FViewInfo FRHIViewDesc::FTextureSRV::GetViewInfo(FRHITexture* TargetTexture) const
|
|
{
|
|
check(ViewType == EViewType::TextureSRV);
|
|
check(TargetTexture);
|
|
FRHITextureDesc const& Desc = TargetTexture->GetDesc();
|
|
|
|
FViewInfo Info = { FTexture::GetViewInfo(TargetTexture) };
|
|
|
|
Info.MipRange.First = MipRange.First;
|
|
Info.MipRange.Num = MipRange.Num == 0 ? Desc.NumMips : MipRange.Num;
|
|
Info.bAllMips = Info.MipRange.First == 0 && Info.MipRange.Num == Desc.NumMips;
|
|
|
|
Info.bSRGB = bDisableSRGB ? false : EnumHasAnyFlags(Desc.Flags, TexCreate_SRGB);
|
|
|
|
return Info;
|
|
}
|
|
|
|
RHI_API FRHIViewDesc::FTextureUAV::FViewInfo FRHIViewDesc::FTextureUAV::GetViewInfo(FRHITexture* TargetTexture) const
|
|
{
|
|
check(ViewType == EViewType::TextureUAV);
|
|
check(TargetTexture);
|
|
FRHITextureDesc const& Desc = TargetTexture->GetDesc();
|
|
|
|
FViewInfo Info = { FTexture::GetViewInfo(TargetTexture) };
|
|
Info.MipLevel = MipRange.First;
|
|
check(MipRange.Num == 1);
|
|
|
|
checkf(Desc.NumSamples == 1, TEXT("Cannot create an unordered access view of a multisampled texture."));
|
|
checkf(Info.MipLevel <= Desc.NumMips, TEXT("Mip level (%d) is out of bounds for texture description (Num Mips: %d)."), Info.MipLevel, Desc.NumMips);
|
|
|
|
// UAVs only cover 1 mip, so bAllMips is true if the base resource only has 1.
|
|
Info.bAllMips = Desc.NumMips == 1;
|
|
|
|
return Info;
|
|
}
|
|
|
|
static_assert(offsetof(FRHIUniformBufferResource, MemberOffset) == offsetof(FRHIUniformBufferResourceInitializer, MemberOffset), "FRHIUniformBufferResource must be identical to FRHIUniformBufferResourceInitializer");
|
|
static_assert(offsetof(FRHIUniformBufferResource, MemberType ) == offsetof(FRHIUniformBufferResourceInitializer, MemberType ), "FRHIUniformBufferResource must be identical to FRHIUniformBufferResourceInitializer");
|
|
static_assert(sizeof(FRHIUniformBufferResource) == sizeof(FRHIUniformBufferResourceInitializer), "FRHIUniformBufferResource must be identical to FRHIUniformBufferResourceInitializer");
|
|
|
|
template <>
|
|
struct TIsBitwiseConstructible<FRHIUniformBufferResource, FRHIUniformBufferResourceInitializer>
|
|
{
|
|
enum { Value = true };
|
|
};
|
|
|
|
FRHIUniformBufferLayout::FRHIUniformBufferLayout(const FRHIUniformBufferLayoutInitializer& Initializer)
|
|
: FRHIResource(RRT_UniformBufferLayout)
|
|
, Name(Initializer.GetDebugName())
|
|
, Resources(TConstArrayView<FRHIUniformBufferResourceInitializer>(Initializer.Resources))
|
|
, GraphResources(TConstArrayView<FRHIUniformBufferResourceInitializer>(Initializer.GraphResources))
|
|
, GraphTextures(TConstArrayView<FRHIUniformBufferResourceInitializer>(Initializer.GraphTextures))
|
|
, GraphBuffers(TConstArrayView<FRHIUniformBufferResourceInitializer>(Initializer.GraphBuffers))
|
|
, GraphUniformBuffers(TConstArrayView<FRHIUniformBufferResourceInitializer>(Initializer.GraphUniformBuffers))
|
|
, UniformBuffers(TConstArrayView<FRHIUniformBufferResourceInitializer>(Initializer.UniformBuffers))
|
|
, Hash(Initializer.GetHash())
|
|
, ConstantBufferSize(Initializer.ConstantBufferSize)
|
|
, RenderTargetsOffset(Initializer.RenderTargetsOffset)
|
|
, StaticSlot(Initializer.StaticSlot)
|
|
, BindingFlags(Initializer.BindingFlags)
|
|
, Flags(Initializer.Flags)
|
|
{
|
|
}
|
|
|
|
uint32 FRayTracingPipelineStateInitializer::GetMaxLocalBindingDataSize() const
|
|
{
|
|
uint32 MaxLocalBindingDataSize = 0;
|
|
|
|
// Take max size of all miss, hit and callable shaders
|
|
for (FRHIRayTracingShader* Shader : MissTable)
|
|
{
|
|
MaxLocalBindingDataSize = FMath::Max(MaxLocalBindingDataSize, Shader->LocalBindingDataSize);
|
|
}
|
|
for (FRHIRayTracingShader* Shader : HitGroupTable)
|
|
{
|
|
MaxLocalBindingDataSize = FMath::Max(MaxLocalBindingDataSize, Shader->LocalBindingDataSize);
|
|
}
|
|
for (FRHIRayTracingShader* Shader : CallableTable)
|
|
{
|
|
MaxLocalBindingDataSize = FMath::Max(MaxLocalBindingDataSize, Shader->LocalBindingDataSize);
|
|
}
|
|
|
|
return MaxLocalBindingDataSize;
|
|
} |