Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

1236 lines
37 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
D3D12Resources.h: D3D resource RHI definitions.
=============================================================================*/
#pragma once
#include "BoundShaderStateCache.h"
#include "D3D12DirectCommandListManager.h"
#include "D3D12NvidiaExtensions.h"
#include "D3D12Residency.h"
#include "D3D12ShaderResources.h"
#include "D3D12State.h"
#include "D3D12Util.h"
#include "RHIPoolAllocator.h"
#include "Templates/UniquePtr.h"
// Forward Decls
class FD3D12CommandList;
class FD3D12Resource;
class FD3D12StateCache;
class FD3D12CommandListManager;
class FD3D12CommandContext;
class FD3D12SegListAllocator;
class FD3D12PoolAllocator;
struct FD3D12ComputePipelineState;
struct FD3D12WorkGraphPipelineState;
struct FD3D12GraphicsPipelineState;
struct FD3D12ResourceDesc;
class FD3D12SyncPoint;
using FD3D12SyncPointRef = TRefCountPtr<FD3D12SyncPoint>;
#if D3D12_RHI_RAYTRACING
class FD3D12RayTracingGeometry;
class FD3D12RayTracingScene;
class FD3D12RayTracingPipelineState;
class FD3D12RayTracingShaderBindingTable;
class FD3D12RayTracingShader;
#endif // D3D12_RHI_RAYTRACING
#ifndef D3D12_WITH_CUSTOM_TEXTURE_LAYOUT
#define D3D12_WITH_CUSTOM_TEXTURE_LAYOUT 0
#endif
#if D3D12_WITH_CUSTOM_TEXTURE_LAYOUT
extern void ApplyCustomTextureLayout(FD3D12ResourceDesc& TextureLayout, FD3D12Adapter& Adapter);
#endif
enum class ED3D12ResourceStateMode
{
Default, //< Decide if tracking is required based on flags
SingleState, //< Force disable state tracking of resource - resource will always be in the initial resource state
MultiState, //< Force enable state tracking of resource
};
class FD3D12PendingResourceBarrier
{
public:
FD3D12Resource* Resource;
D3D12_RESOURCE_STATES State;
uint32 SubResource;
FD3D12PendingResourceBarrier(FD3D12Resource* Resource, D3D12_RESOURCE_STATES State, uint32 SubResource)
: Resource(Resource)
, State(State)
, SubResource(SubResource)
{}
};
class FD3D12Heap : public FThreadSafeRefCountedObject, public FD3D12DeviceChild, public FD3D12MultiNodeGPUObject
{
public:
FD3D12Heap(FD3D12Device* Parent, FRHIGPUMask VisibleNodes, HeapId InTraceParentHeapId = EMemoryTraceRootHeap::VideoMemory);
~FD3D12Heap();
inline ID3D12Heap* GetHeap() const { return Heap.GetReference(); }
void SetHeap(ID3D12Heap* HeapIn, const TCHAR* const InName, bool bTrack = true, bool bForceGetGPUAddress = false);
void BeginTrackingResidency(uint64 Size);
void DisallowTrackingResidency(); // Part of workaround for UE-174791 and UE-202367
void DeferDelete();
inline FName GetName() const { return HeapName; }
inline D3D12_HEAP_DESC GetHeapDesc() const { return HeapDesc; }
inline TConstArrayView<FD3D12ResidencyHandle*> GetResidencyHandles()
{
#if ENABLE_RESIDENCY_MANAGEMENT
if (bRequiresResidencyTracking)
{
checkf(ResidencyHandle, TEXT("Resource requires residency tracking, but BeginTrackingResidency() was not called."));
return MakeArrayView(&ResidencyHandle, 1);
}
else
#endif // ENABLE_RESIDENCY_MANAGEMENT
{
return {};
}
}
inline D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const { return GPUVirtualAddress; }
inline void SetIsTransient(bool bInIsTransient) { bIsTransient = bInIsTransient; }
inline bool GetIsTransient() const { return bIsTransient; }
private:
TRefCountPtr<ID3D12Heap> Heap;
FName HeapName;
bool bTrack = true;
D3D12_HEAP_DESC HeapDesc;
D3D12_GPU_VIRTUAL_ADDRESS GPUVirtualAddress = 0;
FD3D12ResidencyHandle* ResidencyHandle = nullptr; // Residency handle owned by this object
HeapId TraceHeapId;
HeapId TraceParentHeapId;
bool bIsTransient = false; // Whether this is a transient heap
bool bRequiresResidencyTracking = bool(ENABLE_RESIDENCY_MANAGEMENT);
};
// This can be used to guarantee that a struct's padding is zero, which is necessary for hashing in some cases
// NOTE: classes with virtual methods are not supported. The struct must be the first declared base class
// to ensure the correct ordering of the ctor calls
template <typename T>
struct TZeroedStruct
{
TZeroedStruct<T>()
{
FMemory::Memzero(this, sizeof(T));
}
};
struct FD3D12ResourceDesc : public TZeroedStruct<FD3D12ResourceDesc>, D3D12_RESOURCE_DESC
{
FD3D12ResourceDesc() :
TZeroedStruct<FD3D12ResourceDesc>()
{
}
FD3D12ResourceDesc(const CD3DX12_RESOURCE_DESC& Other)
: TZeroedStruct<FD3D12ResourceDesc>()
, D3D12_RESOURCE_DESC(Other)
{
}
// TODO: use this type everywhere and disallow implicit conversion
/*explicit*/ FD3D12ResourceDesc(const D3D12_RESOURCE_DESC& Other)
: TZeroedStruct<FD3D12ResourceDesc>()
, D3D12_RESOURCE_DESC(Other)
{
}
EPixelFormat PixelFormat{ PF_Unknown };
// PixelFormat for the Resource that aliases our current resource.
EPixelFormat UAVPixelFormat{ PF_Unknown };
#if D3D12RHI_NEEDS_VENDOR_EXTENSIONS
bool bRequires64BitAtomicSupport : 1 = false;
#endif
bool bReservedResource : 1 = false;
bool bBackBuffer : 1 = false;
// External resources are owned by another application or middleware, not the Engine
bool bExternal : 1 = false;
// If we support the new format list casting, use the newer APIs; otherwise, fall back to our UAV Aliasing approach.
#if D3D12RHI_SUPPORTS_UNCOMPRESSED_UAV
inline bool NeedsUAVAliasWorkarounds() const { return false; }
inline bool SupportsUncompressedUAV() const { return UAVPixelFormat != PF_Unknown; }
TArray<DXGI_FORMAT, TInlineAllocator<4>> GetCastableFormats() const;
#else
inline bool NeedsUAVAliasWorkarounds() const { return UAVPixelFormat != PF_Unknown; }
inline bool SupportsUncompressedUAV() const { return false; }
#endif
};
class FD3D12Resource : public FThreadSafeRefCountedObject, public FD3D12DeviceChild, public FD3D12MultiNodeGPUObject
{
friend class FD3D12Buffer;
private:
TRefCountPtr<ID3D12Resource> Resource;
TRefCountPtr<FD3D12Heap> Heap;
#if !D3D12RHI_SUPPORTS_UNCOMPRESSED_UAV
// Since certain formats cannot be aliased in D3D12, we create a placed UAV
// that aliases them. This resource description is used to create that UAV.
TUniquePtr<D3D12_RESOURCE_DESC> UAVAccessResourceDesc;
#endif
FD3D12ResidencyHandle* ResidencyHandle = nullptr; // Residency handle owned by this object
D3D12_GPU_VIRTUAL_ADDRESS GPUVirtualAddress{};
void* ResourceBaseAddress{};
#if NV_AFTERMATH
UE::RHICore::Nvidia::Aftermath::D3D12::FResource AftermathHandle{};
#endif
const FD3D12ResourceDesc Desc;
ED3D12Access InitialD3D12Access = ED3D12Access::Unknown;
ED3D12Access DefaultD3D12Access = ED3D12Access::Unknown;
#ifdef PLATFORM_SUPPORTS_RESOURCE_COMPRESSION
D3D12_RESOURCE_STATES CompressedState{ D3D12_RESOURCE_STATE_CORRUPT };
#endif
D3D12_HEAP_TYPE HeapType;
FName DebugName;
int32 NumMapCalls = 0;
uint16 SubresourceCount{};
uint8 PlaneCount;
bool bRequiresResourceStateTracking : 1; //1st bit
bool bRequiresResidencyTracking : 1; //2nd bit
bool bDepthStencil : 1; //3rd bit
bool bDeferDelete : 1; //4th bit
#if UE_BUILD_DEBUG
static int64 TotalResourceCount;
static int64 NoStateTrackingResourceCount;
#endif
struct FD3D12ReservedResourceData
{
TArray<TRefCountPtr<FD3D12Heap>> BackingHeaps;
// Flattened array of residency handles owned by backing heaps, used to support batched GetResidencyHandles()
TArray<FD3D12ResidencyHandle*> ResidencyHandles;
TArray<int32> NumResidencyHandlesPerHeap;
// Tiles currently assigned to the resource
uint32 NumCommittedTiles = 0;
// Available tiles at the end of the last backing heap
uint32 NumSlackTiles = 0;
};
TUniquePtr<FD3D12ReservedResourceData> ReservedResourceData;
public:
FD3D12Resource() = delete;
explicit FD3D12Resource(FD3D12Device* ParentDevice,
FRHIGPUMask VisibleNodes,
ID3D12Resource* InResource,
ED3D12Access InInitialD3D12Access,
FD3D12ResourceDesc const& InDesc,
FD3D12Heap* InHeap = nullptr,
D3D12_HEAP_TYPE InHeapType = D3D12_HEAP_TYPE_DEFAULT);
explicit FD3D12Resource(FD3D12Device* ParentDevice,
FRHIGPUMask VisibleNodes,
ID3D12Resource* InResource,
ED3D12Access InInitialD3D12Access,
ED3D12ResourceStateMode InResourceStateMode,
ED3D12Access InDefaultD3D12Access,
FD3D12ResourceDesc const& InDesc,
FD3D12Heap* InHeap,
D3D12_HEAP_TYPE InHeapType);
virtual ~FD3D12Resource();
operator ID3D12Resource&() { return *Resource; }
ID3D12Resource* GetResource() const { return Resource.GetReference(); }
#if !D3D12RHI_SUPPORTS_UNCOMPRESSED_UAV
const D3D12_RESOURCE_DESC* GetUAVAccessResourceDesc() const
{
return UAVAccessResourceDesc.Get();
}
void SetUAVAccessResourceDesc(const D3D12_RESOURCE_DESC& InUAVAccessResourceDesc)
{
UAVAccessResourceDesc = MakeUnique<D3D12_RESOURCE_DESC>(InUAVAccessResourceDesc);
}
#endif
inline void* Map(const D3D12_RANGE* ReadRange = nullptr)
{
if (NumMapCalls == 0)
{
check(Resource);
check(ResourceBaseAddress == nullptr);
VERIFYD3D12RESULT(Resource->Map(0, ReadRange, &ResourceBaseAddress));
}
else
{
check(ResourceBaseAddress);
}
++NumMapCalls;
return ResourceBaseAddress;
}
inline void Unmap()
{
check(Resource);
check(ResourceBaseAddress);
check(NumMapCalls > 0);
--NumMapCalls;
if (NumMapCalls == 0)
{
Resource->Unmap(0, nullptr);
ResourceBaseAddress = nullptr;
}
}
ID3D12Pageable* GetPageable();
const FD3D12ResourceDesc& GetDesc() const { return Desc; }
D3D12_HEAP_TYPE GetHeapType() const { return HeapType; }
D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const { return GPUVirtualAddress; }
inline void SetGPUVirtualAddress(D3D12_GPU_VIRTUAL_ADDRESS Value) { GPUVirtualAddress = Value; }
void* GetResourceBaseAddress() const { check(ResourceBaseAddress); return ResourceBaseAddress; }
uint16 GetMipLevels() const { return Desc.MipLevels; }
uint16 GetArraySize() const { return (Desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) ? 1 : Desc.DepthOrArraySize; }
uint8 GetPlaneCount() const { return PlaneCount; }
uint16 GetSubresourceCount() const { return SubresourceCount; }
ED3D12Access GetInitialAccess() const { return InitialD3D12Access; }
ED3D12Access GetDefaultAccess() const { return DefaultD3D12Access; }
#ifdef PLATFORM_SUPPORTS_RESOURCE_COMPRESSION
D3D12_RESOURCE_STATES GetCompressedState() const { return CompressedState; }
void SetCompressedState(D3D12_RESOURCE_STATES State) { CompressedState = State; }
#endif
bool RequiresResourceStateTracking() const { return bRequiresResourceStateTracking; }
inline bool IsBackBuffer() const { return Desc.bBackBuffer; }
void SetName(const TCHAR* Name)
{
// Check name before setting it. Saves FName lookup and driver call. Names are frequently the same for pooled buffers
// that end up getting reused for the same purpose every frame (2/3 of calls to this function on a given frame).
if (DebugName != Name)
{
DebugName = FName(Name);
SetD3D12ObjectName(GetResource(), Name);
}
}
FName GetName() const
{
return DebugName;
}
void DoNotDeferDelete()
{
bDeferDelete = false;
}
inline bool ShouldDeferDelete() const { return bDeferDelete; }
void DeferDelete();
inline bool IsReservedResource() const { return ReservedResourceData.IsValid(); }
inline bool IsPlacedResource() const { return Heap.GetReference() != nullptr; }
inline FD3D12Heap* GetHeap() const { return Heap; };
inline bool IsDepthStencilResource() const { return bDepthStencil; }
inline uint64 GetCommittedReservedResourceSize() const
{
check(IsReservedResource());
return ReservedResourceData->NumCommittedTiles * (uint64)GRHIGlobals.ReservedResources.TileSizeInBytes;
}
inline bool NeedsDeferredResidencyUpdate() const { return IsReservedResource(); }
void StartTrackingForResidency();
bool IsResident() const
{
#if ENABLE_RESIDENCY_MANAGEMENT
if (NeedsDeferredResidencyUpdate())
{
// We don't know the state because the set of residency handles is only known on the
// RHI Submission Thread and may change throughout the frame.
return true;
}
TConstArrayView<FD3D12ResidencyHandle*> ResidencyHandles = GetResidencyHandles();
if (ResidencyHandles.IsEmpty())
{
// No residency tracking for this resource
return true;
}
// Treat resource as resident if at least one backing heap is resident.
// Technically we should return a partial residency status.
for (FD3D12ResidencyHandle* Handle : ResidencyHandles)
{
if (Handle->ResidencyStatus == FD3D12ResidencyHandle::RESIDENCY_STATUS::RESIDENT)
{
return true;
}
}
return false;
#else // ENABLE_RESIDENCY_MANAGEMENT
return true;
#endif // ENABLE_RESIDENCY_MANAGEMENT
}
TConstArrayView<FD3D12ResidencyHandle*> GetResidencyHandles() const
{
#if ENABLE_RESIDENCY_MANAGEMENT
if (!bRequiresResidencyTracking)
{
return {};
}
else if (IsPlacedResource())
{
return Heap->GetResidencyHandles();
}
else if (IsReservedResource())
{
return ReservedResourceData->ResidencyHandles;
}
else
{
checkf(ResidencyHandle, TEXT("Resource requires residency tracking, but StartTrackingForResidency() was not called."));
return MakeArrayView(&ResidencyHandle, 1);
}
#else // ENABLE_RESIDENCY_MANAGEMENT
return {};
#endif // ENABLE_RESIDENCY_MANAGEMENT
}
template<typename TLambda>
FORCEINLINE_DEBUGGABLE void GetBackingHeapsGpuAddresses(TLambda&& Lambda) const
{
if (IsReservedResource())
{
for (TRefCountPtr<FD3D12Heap>& BackingHeap: ReservedResourceData->BackingHeaps)
{
Lambda(BackingHeap->GetGPUVirtualAddress(), BackingHeap->GetGPUVirtualAddress(), BackingHeap->GetHeapDesc().SizeInBytes);
}
}
}
struct FD3D12ResourceTypeHelper
{
FD3D12ResourceTypeHelper(const FD3D12ResourceDesc& Desc, D3D12_HEAP_TYPE HeapType) :
bSRV(!EnumHasAnyFlags(Desc.Flags, D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE)),
bDSV(EnumHasAnyFlags(Desc.Flags, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)),
bRTV(EnumHasAnyFlags(Desc.Flags, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)),
bUAV(EnumHasAnyFlags(Desc.Flags, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)),
bWritable(bDSV || bRTV || bUAV),
bSRVOnly(bSRV && !bWritable),
bBuffer(Desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER),
bReadBackResource(HeapType == D3D12_HEAP_TYPE_READBACK)
{}
ED3D12Access GetOptimalInitialD3D12Access(ED3D12Access InD3D12Access, bool bAccurateWriteableStates) const
{
// Ignore the requested resource state for non tracked resource because RHI will assume it's always in default resource
// state then when a transition is required (will transition via scoped push/pop to requested state)
if (!bSRVOnly && InD3D12Access != ED3D12Access::Unknown)
{
return InD3D12Access;
}
if (bSRVOnly)
{
return ED3D12Access::SRVGraphics;
}
else if (bBuffer && !bUAV)
{
return (bReadBackResource) ? ED3D12Access::CopyDest : ED3D12Access::GenericRead;
}
else if (bWritable && bAccurateWriteableStates)
{
if (bDSV)
{
return ED3D12Access::DSVWrite;
}
else if (bRTV)
{
return ED3D12Access::RTV;
}
else if (bUAV)
{
check(InD3D12Access != ED3D12Access::Unknown);
return ED3D12Access::UAVMask;
}
}
return ED3D12Access::Common;
}
const uint32 bSRV : 1;
const uint32 bDSV : 1;
const uint32 bRTV : 1;
const uint32 bUAV : 1;
const uint32 bWritable : 1;
const uint32 bSRVOnly : 1;
const uint32 bBuffer : 1;
const uint32 bReadBackResource : 1;
};
// Note: RequiredCommitSizeInBytes is clamped to the maximum size of the resource.
// Use UINT64_MAX to commit the entire resource.
void CommitReservedResource(ID3D12CommandQueue* D3DCommandQueue, uint64 RequiredCommitSizeInBytes);
private:
void InitializeResourceState(
const D3D12_HEAP_PROPERTIES* InHeapProps,
ED3D12Access InInitialD3D12Access,
ED3D12ResourceStateMode InResourceStateMode,
ED3D12Access InDefaultD3D12Access)
{
check(IsValidAccess(InDefaultD3D12Access));
check(IsValidAccess(InInitialD3D12Access));
SubresourceCount = GetMipLevels() * GetArraySize() * GetPlaneCount();
if (InResourceStateMode == ED3D12ResourceStateMode::SingleState)
{
// make sure a valid default state is set
check(InInitialD3D12Access != ED3D12Access::Unknown);
check(InDefaultD3D12Access != ED3D12Access::Unknown);
#if UE_BUILD_DEBUG
FPlatformAtomics::InterlockedIncrement(&NoStateTrackingResourceCount);
#endif
InitialD3D12Access = InInitialD3D12Access;
DefaultD3D12Access = InDefaultD3D12Access;
bRequiresResourceStateTracking = false;
}
else
{
DetermineResourceStates(InHeapProps, InInitialD3D12Access, InResourceStateMode, InDefaultD3D12Access);
}
if (bRequiresResourceStateTracking)
{
#if D3D12_RHI_RAYTRACING
// No state tracking for acceleration structures because they can't have another state
check(!EnumHasAnyFlags(InInitialD3D12Access, ED3D12Access::BVHRead | ED3D12Access::BVHWrite) ||
!EnumHasAnyFlags(InDefaultD3D12Access, ED3D12Access::BVHRead | ED3D12Access::BVHWrite));
#endif // D3D12_RHI_RAYTRACING
check(InInitialD3D12Access != ED3D12Access::Unknown);
InitialD3D12Access = InInitialD3D12Access;
}
}
void DetermineResourceStates(
const D3D12_HEAP_PROPERTIES* InHeapProps,
ED3D12Access InInitialD3D12Access,
ED3D12ResourceStateMode InResourceStateMode,
ED3D12Access InDefaultD3D12Access)
{
const FD3D12ResourceTypeHelper Type(Desc, HeapType);
bDepthStencil = Type.bDSV;
#ifdef PLATFORM_SUPPORTS_RESOURCE_COMPRESSION
SetCompressedState(D3D12_RESOURCE_STATE_COMMON);
#endif
if (!Type.bWritable && InResourceStateMode != ED3D12ResourceStateMode::MultiState)
{
bRequiresResourceStateTracking = false;
#if UE_BUILD_DEBUG
FPlatformAtomics::InterlockedIncrement(&NoStateTrackingResourceCount);
#endif
if (InDefaultD3D12Access != ED3D12Access::Unknown)
{
DefaultD3D12Access = InDefaultD3D12Access;
}
else if (Type.bBuffer)
{
DefaultD3D12Access = DetermineInitialBufferD3D12Access(HeapType, InHeapProps);
}
else
{
check(Type.bSRVOnly);
DefaultD3D12Access = ED3D12Access::SRVGraphics;
}
check(InInitialD3D12Access != ED3D12Access::Unknown);
InitialD3D12Access = InInitialD3D12Access;
}
}
};
typedef class FD3D12BuddyAllocator FD3D12BaseAllocatorType;
struct FD3D12BuddyAllocatorPrivateData
{
uint32 Offset;
uint32 Order;
void Init()
{
Offset = 0;
Order = 0;
}
};
struct FD3D12BlockAllocatorPrivateData
{
uint64 FrameFence;
uint32 BucketIndex;
uint32 Offset;
FD3D12Resource* ResourceHeap;
void Init()
{
FrameFence = 0;
BucketIndex = 0;
Offset = 0;
ResourceHeap = nullptr;
}
};
struct FD3D12SegListAllocatorPrivateData
{
uint32 Offset;
void Init()
{
Offset = 0;
}
};
struct FD3D12PoolAllocatorPrivateData
{
FRHIPoolAllocationData PoolData;
void Init()
{
PoolData.Reset();
}
};
class FD3D12ResourceAllocator;
class FD3D12BaseShaderResource;
// A very light-weight and cache friendly way of accessing a GPU resource
class FD3D12ResourceLocation : public FRHIPoolResource, public FD3D12DeviceChild, public FNoncopyable
{
friend class FD3D12PoolAllocator;
public:
enum class ResourceLocationType : uint8
{
eUndefined,
eStandAlone,
eSubAllocation,
eFastAllocation,
eMultiFrameFastAllocation,
eAliased, // XR HMDs are the only use cases
eNodeReference,
eHeapAliased,
};
enum EAllocatorType : uint8
{
AT_Default, // FD3D12BaseAllocatorType
AT_SegList, // FD3D12SegListAllocator
AT_Pool, // FD3D12PoolAllocator
AT_Unknown = 0xff
};
FD3D12ResourceLocation(FD3D12Device* Parent);
FD3D12ResourceLocation(FD3D12ResourceLocation&& Other)
: FD3D12ResourceLocation(Other.GetParentDevice())
{
TransferOwnership(*this, Other);
}
~FD3D12ResourceLocation();
void Clear();
// Transfers the contents of 1 resource location to another, destroying the original but preserving the underlying resource
static void TransferOwnership(FD3D12ResourceLocation& Destination, FD3D12ResourceLocation& Source);
// Setters
void SetOwner (FD3D12BaseShaderResource* InOwner) { Owner = InOwner; }
void SetType (ResourceLocationType Value) { Type = Value;}
void SetResource(FD3D12Resource* Value);
void SetAllocator (FD3D12BaseAllocatorType* Value) { Allocator = Value; AllocatorType = AT_Default; }
void SetSegListAllocator(FD3D12SegListAllocator* Value) { SegListAllocator = Value; AllocatorType = AT_SegList; }
void SetPoolAllocator (FD3D12PoolAllocator* Value) { PoolAllocator = Value; AllocatorType = AT_Pool; }
void ClearAllocator() { Allocator = nullptr; AllocatorType = AT_Unknown; }
void SetMappedBaseAddress (void* Value ) { MappedBaseAddress = Value; }
void SetGPUVirtualAddress (D3D12_GPU_VIRTUAL_ADDRESS Value) { GPUVirtualAddress = Value; }
void SetOffsetFromBaseOfResource(uint64 Value ) { OffsetFromBaseOfResource = Value; }
void SetSize (uint64 Value ) { Size = Value; }
// Getters
ResourceLocationType GetType () const { return Type; }
EAllocatorType GetAllocatorType () const { return AllocatorType; }
FD3D12BaseAllocatorType* GetAllocator () { check(AT_Default == AllocatorType); return Allocator; }
FD3D12SegListAllocator* GetSegListAllocator () { check(AT_SegList == AllocatorType); return SegListAllocator; }
FD3D12PoolAllocator* GetPoolAllocator () { check(AT_Pool == AllocatorType); return PoolAllocator; }
FD3D12Resource* GetResource () const { return UnderlyingResource; }
void* GetMappedBaseAddress () const { return MappedBaseAddress; }
D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress () const { return GPUVirtualAddress; }
uint64 GetOffsetFromBaseOfResource () const { return OffsetFromBaseOfResource; }
uint64 GetSize () const { return Size; }
FD3D12BuddyAllocatorPrivateData& GetBuddyAllocatorPrivateData () { return AllocatorData.BuddyAllocatorPrivateData; }
FD3D12BlockAllocatorPrivateData& GetBlockAllocatorPrivateData () { return AllocatorData.BlockAllocatorPrivateData; }
FD3D12SegListAllocatorPrivateData& GetSegListAllocatorPrivateData() { return AllocatorData.SegListAllocatorPrivateData; }
FD3D12PoolAllocatorPrivateData& GetPoolAllocatorPrivateData () { return AllocatorData.PoolAllocatorPrivateData; }
// Pool allocation specific functions
bool OnAllocationMoved(FD3D12ContextArray const& Contexts, FRHIPoolAllocationData* InNewData, ED3D12Access& OutRHIAccess);
void UnlockPoolData();
bool IsValid() const { return Type != ResourceLocationType::eUndefined; }
void AsStandAlone(FD3D12Resource* Resource, uint64 InSize = 0, bool bInIsTransient = false, const D3D12_HEAP_PROPERTIES* CustomHeapProperties = nullptr);
bool IsStandaloneOrPooledPlacedResource() const;
void AsHeapAliased(FD3D12Resource* Resource)
{
check(Resource->GetHeapType() != D3D12_HEAP_TYPE_READBACK);
SetType(FD3D12ResourceLocation::ResourceLocationType::eHeapAliased);
SetResource(Resource);
SetSize(0);
if (IsCPUWritable(Resource->GetHeapType()))
{
D3D12_RANGE range = { 0, 0 };
SetMappedBaseAddress(Resource->Map(&range));
}
SetGPUVirtualAddress(Resource->GetGPUVirtualAddress());
}
void AsFastAllocation(FD3D12Resource* Resource, uint32 BufferSize, D3D12_GPU_VIRTUAL_ADDRESS GPUBase, void* CPUBase, uint64 ResourceOffsetBase, uint64 Offset, bool bMultiFrame = false)
{
if (bMultiFrame)
{
Resource->AddRef();
SetType(ResourceLocationType::eMultiFrameFastAllocation);
}
else
{
SetType(ResourceLocationType::eFastAllocation);
}
SetResource(Resource);
SetSize(BufferSize);
SetOffsetFromBaseOfResource(ResourceOffsetBase + Offset);
if (CPUBase != nullptr)
{
SetMappedBaseAddress((uint8*)CPUBase + Offset);
}
SetGPUVirtualAddress(GPUBase + Offset);
}
// XR plugins alias textures so this allows 2+ resource locations to reference the same underlying
// resource. We should avoid this as much as possible as it requires expensive reference counting and
// it complicates the resource ownership model.
static void Alias(FD3D12ResourceLocation& Destination, FD3D12ResourceLocation& Source);
static void ReferenceNode(FD3D12Device* NodeDevice, FD3D12ResourceLocation& Destination, FD3D12ResourceLocation& Source);
bool IsAliased() const
{
return (Type == ResourceLocationType::eAliased)
|| (Type == ResourceLocationType::eHeapAliased);
}
void SetTransient(bool bInTransient)
{
bTransient = bInTransient;
}
bool IsTransient() const
{
return bTransient;
}
/** Get an address used by LLM to track the GPU allocation that this location represents. */
void* GetAddressForLLMTracking() const
{
return (uint8*)this + 1;
}
private:
template<bool bReleaseResource>
void InternalClear();
void ReleaseResource();
void UpdateStandAloneStats(bool bIncrement);
FD3D12BaseShaderResource* Owner{};
FD3D12Resource* UnderlyingResource{};
// Which allocator this belongs to
union
{
FD3D12BaseAllocatorType* Allocator;
FD3D12SegListAllocator* SegListAllocator;
FD3D12PoolAllocator* PoolAllocator;
};
// Union to save memory
union PrivateAllocatorData
{
FD3D12BuddyAllocatorPrivateData BuddyAllocatorPrivateData;
FD3D12BlockAllocatorPrivateData BlockAllocatorPrivateData;
FD3D12SegListAllocatorPrivateData SegListAllocatorPrivateData;
FD3D12PoolAllocatorPrivateData PoolAllocatorPrivateData;
} AllocatorData;
// Note: These values refer to the start of this location including any padding *NOT* the start of the underlying resource
void* MappedBaseAddress{};
D3D12_GPU_VIRTUAL_ADDRESS GPUVirtualAddress{};
uint64 OffsetFromBaseOfResource{};
// The size the application asked for
uint64 Size{};
ResourceLocationType Type{ ResourceLocationType::eUndefined };
EAllocatorType AllocatorType{ AT_Unknown };
bool bTransient{ false };
};
// Generic interface for every type D3D12 specific allocator
struct ID3D12ResourceAllocator
{
// Helper function for textures to compute the correct size and alignment
void AllocateTexture(
uint32 GPUIndex,
D3D12_HEAP_TYPE InHeapType,
const FD3D12ResourceDesc& InDesc,
EPixelFormat InUEFormat,
ED3D12Access InInitialD3D12Access,
ED3D12ResourceStateMode InResourceStateMode,
ED3D12Access InDefaultD3D12Access,
const D3D12_CLEAR_VALUE* InClearValue,
const TCHAR* InName,
FD3D12ResourceLocation& ResourceLocation);
// Actual pure virtual resource allocation function
virtual void AllocateResource(
uint32 GPUIndex,
D3D12_HEAP_TYPE InHeapType,
const FD3D12ResourceDesc& InDesc,
uint64 InSize,
uint32 InAllocationAlignment,
ED3D12Access InInitialD3D12Access,
ED3D12ResourceStateMode InResourceStateMode,
ED3D12Access InDefaultD3D12Access,
const D3D12_CLEAR_VALUE* InClearValue,
const TCHAR* InName,
FD3D12ResourceLocation& ResourceLocation) = 0;
};
struct FD3D12LockedResource : public FD3D12DeviceChild
{
FD3D12LockedResource(FD3D12Device* Device)
: FD3D12DeviceChild(Device)
, ResourceLocation(Device)
{}
inline void Reset()
{
ResourceLocation.Clear();
bLocked = false;
bLockedForReadOnly = false;
LockOffset = 0;
LockSize = 0;
FMemory::Memzero(Footprint);
}
FD3D12ResourceLocation ResourceLocation;
D3D12_SUBRESOURCE_FOOTPRINT Footprint = {};
uint32 LockOffset = 0;
uint32 LockSize = 0;
uint32 bLocked : 1 = false;
uint32 bLockedForReadOnly : 1 = false;
uint32 bHasNeverBeenLocked : 1 = true;
};
/** Resource which might needs to be notified about changes on dependent resources (Views, RTGeometryObject, Cached binding tables) */
struct FD3D12ShaderResourceRenameListener
{
virtual void ResourceRenamed(FD3D12ContextArray const& Contexts, FD3D12BaseShaderResource* InRenamedResource, FD3D12ResourceLocation* InNewResourceLocation) = 0;
};
/** The base class of resources that may be bound as shader resources (texture or buffer). */
class FD3D12BaseShaderResource : public FD3D12DeviceChild, public IRefCountedObject
{
private:
mutable FCriticalSection RenameListenersCS;
TArray<FD3D12ShaderResourceRenameListener*> RenameListeners;
public:
FD3D12Resource* GetResource() const { return ResourceLocation.GetResource(); }
void AddRenameListener(FD3D12ShaderResourceRenameListener* InRenameListener)
{
FScopeLock Lock(&RenameListenersCS);
check(!RenameListeners.Contains(InRenameListener));
RenameListeners.Add(InRenameListener);
}
void RemoveRenameListener(FD3D12ShaderResourceRenameListener* InRenameListener)
{
FScopeLock Lock(&RenameListenersCS);
uint32 Removed = RenameListeners.Remove(InRenameListener);
checkf(Removed == 1, TEXT("Should have exactly one registered listener during remove (same listener shouldn't registered twice and we shouldn't call this if not registered"));
}
bool HasLinkedViews() const
{
FScopeLock Lock(&RenameListenersCS);
return RenameListeners.Num() != 0;
}
void ResourceRenamed(FD3D12ContextArray const& Contexts)
{
FScopeLock Lock(&RenameListenersCS);
for (FD3D12ShaderResourceRenameListener* RenameListener : RenameListeners)
{
RenameListener->ResourceRenamed(Contexts, this, &ResourceLocation);
}
}
FD3D12ResourceLocation ResourceLocation;
public:
FD3D12BaseShaderResource(FD3D12Device* InParent)
: FD3D12DeviceChild(InParent)
, ResourceLocation(InParent)
{
ResourceLocation.SetOwner(this);
}
~FD3D12BaseShaderResource()
{
check(!HasLinkedViews());
}
};
class FD3D12UniformBuffer;
/** Resource which might needs to be notified about changes on dependent resources (Views, RTGeometryObject, Cached binding tables) */
struct ID3D12UniformBufferUpdateListener
{
virtual void RemoveListener(FD3D12UniformBuffer* InUniformBufferToRemove) = 0;
virtual void UniformBufferUpdated(FRHICommandListBase& CmdList, FD3D12UniformBuffer* InUpdatedUniformBuffer) = 0;
};
/** Uniform buffer resource class. */
class FD3D12UniformBuffer : public FRHIUniformBuffer, public FD3D12DeviceChild, public FD3D12LinkedAdapterObject<FD3D12UniformBuffer>
{
private:
mutable FCriticalSection UpdateListenersCS;
TArray<ID3D12UniformBufferUpdateListener*> UpdateListeners;
public:
#if D3D12RHI_USE_CONSTANT_BUFFER_VIEWS
class FD3D12ConstantBufferView* View = nullptr;
#endif
/** The D3D12 constant buffer resource */
FD3D12ResourceLocation ResourceLocation;
const EUniformBufferUsage UniformBufferUsage;
/** Initialization constructor. */
FD3D12UniformBuffer(class FD3D12Device* InParent, const FRHIUniformBufferLayout* InLayout, EUniformBufferUsage InUniformBufferUsage)
: FRHIUniformBuffer(InLayout)
, FD3D12DeviceChild(InParent)
, ResourceLocation(InParent)
, UniformBufferUsage(InUniformBufferUsage)
{
}
virtual ~FD3D12UniformBuffer();
// Provides public non-const access to ResourceTable.
// @todo refactor uniform buffers to perform updates as a member function, so this isn't necessary.
TArray<TRefCountPtr<FRHIResource>>& GetResourceTable() { return ResourceTable; }
void AddUpdateListener(ID3D12UniformBufferUpdateListener* InUpdateListener)
{
FScopeLock Lock(&UpdateListenersCS);
check(!UpdateListeners.Contains(InUpdateListener));
UpdateListeners.Add(InUpdateListener);
}
void RemoveUpdateListener(ID3D12UniformBufferUpdateListener* InUpdateListener)
{
FScopeLock Lock(&UpdateListenersCS);
uint32 Removed = UpdateListeners.Remove(InUpdateListener);
checkf(Removed == 1, TEXT("Should have exactly one registered listener during remove (same listener shouldn't registered twice and we shouldn't call this if not registered"));
}
bool HasListeners() const
{
FScopeLock Lock(&UpdateListenersCS);
return UpdateListeners.Num() != 0;
}
void UniformBufferUpdated(FRHICommandListBase& CmdList)
{
FScopeLock Lock(&UpdateListenersCS);
for (ID3D12UniformBufferUpdateListener* UpdateListener : UpdateListeners)
{
UpdateListener->UniformBufferUpdated(CmdList, this);
}
}
};
class FD3D12Buffer : public FRHIBuffer, public FD3D12BaseShaderResource, public FD3D12LinkedAdapterObject<FD3D12Buffer>
{
public:
FD3D12Buffer(FD3D12Device* InParent, const FRHIBufferCreateDesc& InCreateDesc)
: FRHIBuffer(InCreateDesc)
, FD3D12BaseShaderResource(InParent)
, LockedData(InParent)
{
}
virtual ~FD3D12Buffer();
static void UploadResourceData(
FD3D12CommandContext& CommandContext,
ED3D12Access DestinationD3D12Access,
FD3D12ResourceLocation& DestinationResourceLocation,
const FD3D12ResourceLocation& SourceResourceLocation,
uint32 Size);
FD3D12SyncPointRef UploadResourceDataViaCopyQueue(FD3D12CommandContext& OwningContext, FResourceArrayUploadInterface* InResourceArray);
// FRHIResource overrides
#if RHI_ENABLE_RESOURCE_INFO
bool GetResourceInfo(FRHIResourceInfo& OutResourceInfo) const override
{
OutResourceInfo = FRHIResourceInfo{};
OutResourceInfo.Name = GetName();
OutResourceInfo.Type = GetType();
if (ResourceLocation.GetResource() && ResourceLocation.GetResource()->IsReservedResource())
{
OutResourceInfo.VRamAllocation.AllocationSize = ResourceLocation.GetResource()->GetCommittedReservedResourceSize();
}
else
{
OutResourceInfo.VRamAllocation.AllocationSize = ResourceLocation.GetSize();
}
OutResourceInfo.IsTransient = ResourceLocation.IsTransient();
OutResourceInfo.bResident = GetResource() && GetResource()->IsResident();
return true;
}
#endif
void Rename(FD3D12ContextArray const& Contexts, FD3D12ResourceLocation& NewLocation);
void RenameLDAChain(FD3D12ContextArray const& Contexts, FD3D12ResourceLocation& NewLocation);
void TakeOwnership(FD3D12Buffer& Other);
void ReleaseOwnership();
// IRefCountedObject interface.
virtual FReturnedRefCountValue AddRef() const
{
return FReturnedRefCountValue{FRHIResource::AddRef()};
}
virtual uint32 Release() const
{
return FRHIResource::Release();
}
virtual uint32 GetRefCount() const
{
return FRHIResource::GetRefCount();
}
static void GetResourceDescAndAlignment(const FRHIBufferCreateDesc& CreateDesc, D3D12_RESOURCE_DESC& ResourceDesc, uint32& Alignment);
// There might be more than 1 VRAM allocation in case of reserved resources
#if ENABLE_LOW_LEVEL_MEM_TRACKER || UE_MEMORY_TRACE_ENABLED
void UpdateAllocationTags() const;
#endif
FD3D12LockedResource LockedData;
uint32 BufferAlignment = 0;
};
namespace D3D12BufferStats
{
void UpdateBufferStats(FD3D12Buffer& Buffer, bool bAllocating);
}
class FD3D12StagingBuffer final : public FRHIStagingBuffer
{
friend class FD3D12CommandContext;
friend class FD3D12DynamicRHI;
public:
FD3D12StagingBuffer(FD3D12Device* InDevice)
: FRHIStagingBuffer()
, ResourceLocation(InDevice)
, ShadowBufferSize(0)
{}
~FD3D12StagingBuffer() override;
void SafeRelease()
{
ResourceLocation.Clear();
}
void* Lock(uint32 Offset, uint32 NumBytes) override;
void Unlock() override;
uint64 GetGPUSizeBytes() const override { return ShadowBufferSize; }
private:
FD3D12ResourceLocation ResourceLocation;
uint32 ShadowBufferSize;
};
class FD3D12ShaderBundle : public FRHIShaderBundle, public FD3D12DeviceChild
{
friend class FD3D12CommandContext;
friend class FD3D12DynamicRHI;
public:
FD3D12ShaderBundle(FD3D12Device* InDevice, const FShaderBundleCreateInfo& CreateInfo)
: FRHIShaderBundle(CreateInfo)
, FD3D12DeviceChild(InDevice)
{
}
};
class FD3D12GPUFence final : public FRHIGPUFence
{
public:
FD3D12GPUFence(FName InName);
virtual void Clear() override;
virtual bool Poll() const override;
virtual bool Poll(FRHIGPUMask GPUMask) const override;
virtual void Wait(FRHICommandListImmediate& RHICmdList, FRHIGPUMask GPUMask) const override;
TArray<FD3D12SyncPointRef, TInlineAllocator<MAX_NUM_GPUS>> SyncPoints;
};
template<>
struct TD3D12ResourceTraits<FRHIUniformBuffer>
{
typedef FD3D12UniformBuffer TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIBuffer>
{
typedef FD3D12Buffer TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHISamplerState>
{
typedef FD3D12SamplerState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIRasterizerState>
{
typedef FD3D12RasterizerState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIDepthStencilState>
{
typedef FD3D12DepthStencilState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIBlendState>
{
typedef FD3D12BlendState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIGraphicsPipelineState>
{
typedef FD3D12GraphicsPipelineState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIComputePipelineState>
{
typedef FD3D12ComputePipelineState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIWorkGraphPipelineState>
{
typedef FD3D12WorkGraphPipelineState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIGPUFence>
{
typedef FD3D12GPUFence TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIStagingBuffer>
{
typedef FD3D12StagingBuffer TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIShaderBundle>
{
typedef FD3D12ShaderBundle TConcreteType;
};
#if D3D12_RHI_RAYTRACING
template<>
struct TD3D12ResourceTraits<FRHIRayTracingScene>
{
typedef FD3D12RayTracingScene TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIRayTracingGeometry>
{
typedef FD3D12RayTracingGeometry TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIRayTracingPipelineState>
{
typedef FD3D12RayTracingPipelineState TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIShaderBindingTable>
{
typedef FD3D12RayTracingShaderBindingTable TConcreteType;
};
template<>
struct TD3D12ResourceTraits<FRHIRayTracingShader>
{
typedef FD3D12RayTracingShader TConcreteType;
};
#endif // D3D12_RHI_RAYTRACING