Add support for gpu descriptor heap compaction amongst other things (#4219)

* Primary Support to compact shader visible Cbv/Srv/Uav heap if it fragments enough to run out of descriptor handles

- Split the main heap into two sections - static handles and dynamic handles for descriptor tables
- Add support to data drive number of allowed static handles
- Add support to data drive ability to compact the relevant shader visible heap as there is overhead associated with compaction
- If compaction is enabled we create two shader visible ehaps and ping pong between them since you can not compact an active heap as it needs to stay active for 3 frames atleast
- As part of compaction we copy over the static handles as is but recreate all the dynamic section(i.e descriptaor tables) by tracking all the active SRGs, reallocating the descriptor tables and updating them from the non-shader visible heap
- Enabled 3 fences for cpu/gpu synchronization so that cpu can get 3 frames ahead of gpu
- Use fixed_wstring for commandlist name as a perf optimization

Signed-off-by: moudgils <moudgils@amazon.com>

* - Disable Heap compaction by default
- Disable PSO caching when WARP is enabled
- Misc cleanup

Signed-off-by: moudgils <moudgils@amazon.com>

* Address Feedback

Signed-off-by: moudgils <moudgils@amazon.com>

* Address feedback

Signed-off-by: moudgils <moudgils@amazon.com>
This commit is contained in:
moudgils
2021-09-27 09:38:56 -07:00
committed by GitHub
parent c75f3690da
commit 5b6e342fe1
24 changed files with 958 additions and 327 deletions
@@ -28,25 +28,19 @@ namespace AZ
size_t m_accumulatedInBytes = 0;
};
/**
* Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints:
*
* 1) Reserved <= Budget (unless the budget is 0).
* 2) Resident <= Reserved.
*/
//! Tracks memory usage for a specific heap in the system. The data is expected to adhere to the following constraints:
//! 1) Reserved <= Budget (unless the budget is 0).
//! 2) Resident <= Reserved.
struct HeapMemoryUsage
{
HeapMemoryUsage() = default;
HeapMemoryUsage(const HeapMemoryUsage&);
HeapMemoryUsage& operator=(const HeapMemoryUsage&);
/**
* This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely
* reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected.
*
* @param sizeInBytes The amount of bytes to reserve.
* @return Whether the reservation was successful.
*/
//! This helper reserves memory in a thread-safe fashion. If the result exceeds the budget, the reservation is safely
//! reverted and false is returned. otherwise, true is returned. Only m_reservedInBytes is affected.
//! @param sizeInBytes The amount of bytes to reserve.
//! @return Whether the reservation was successful.
bool TryReserveMemory(size_t sizeInBytes)
{
const size_t reservationInBytes = (m_reservedInBytes += sizeInBytes);
@@ -60,45 +54,41 @@ namespace AZ
return true;
}
/**
* Helper function to validate sizes
*/
//! Helper function to validate sizes
void Validate()
{
if (Validation::IsEnabled())
{
AZ_Assert(m_budgetInBytes >= m_reservedInBytes, "Reserved memory is larger than memory budget");
AZ_Assert(m_reservedInBytes >= m_residentInBytes, "Resident memory is larger than reserved memory");
AZ_Assert(
m_budgetInBytes >= m_reservedInBytes,
"Reserved memory is larger than memory budget. Memory budget %zu Reserved %zu", m_budgetInBytes, m_reservedInBytes.load());
AZ_Assert(
m_reservedInBytes >= m_residentInBytes,
"Resident memory is larger than reserved memory. Reserved Memory %zu Resident memory %zu", m_reservedInBytes.load(),
m_residentInBytes.load());
}
}
/**
* The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests
* once the budget is exceeded. A zero budget effectively disables this check. On certain platforms,
* it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain
* heaps.
*/
// The budget for the heap in bytes. A non-zero budget means the pool will reject reservation requests
// once the budget is exceeded. A zero budget effectively disables this check. On certain platforms,
// it may be unnecessary to budget certain heaps. Other platforms may require a non-zero budget for certain
// heaps.
size_t m_budgetInBytes = 0;
/**
* Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for
* the platform. It is validated against the budget and may not exceed it.
*/
// Number of bytes reserved on the heap for allocations. This value represents the allocation capacity for
// the platform. It is validated against the budget and may not exceed it.
AZStd::atomic_size_t m_reservedInBytes{ 0 };
/**
* Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms
* may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver.
*/
// Number of bytes physically allocated on the heap. This may not exceed the reservation. Certain platforms
// may choose to transfer memory down the heap level hierarchy in response to memory trim events from the driver.
AZStd::atomic_size_t m_residentInBytes{ 0 };
};
/**
* Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single
* device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms
* may not require one or the other. In this case, the memory usage / budget will report empty values for
* that heap type.
*/
//!
//! Describes memory usage metrics of a resource pool. Resource pools *can* associate with a single
//! device memory heap (i.e. a single GPU) and the host memory heap. Certain pools on specific platforms
//! may not require one or the other. In this case, the memory usage / budget will report empty values for
//! that heap type.
struct PoolMemoryUsage
{
PoolMemoryUsage() = default;
+38 -43
View File
@@ -13,13 +13,12 @@ namespace AZ
{
namespace RHI
{
/**
* A virtual address which may be relative to a base resource. This means
* 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value).
* To account for this, VirtualAddress::Null is used instead. Check validity of the address
* using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized
* to Null, so returning the default constructor is sufficient to represent an invalid address.
*/
//! A virtual address which may be relative to a base resource. This means
//! 0 might be a valid address (dependent on the Allocator::Descriptor::m_addressBase value).
//! To account for this, VirtualAddress::Null is used instead. Check validity of the address
//! using IsValid or IsNull instead of checking for 0. VirtualAddress is initialized
//! to Null, so returning the default constructor is sufficient to represent an invalid address.
class VirtualAddress
{
static const VirtualAddress Null;
@@ -29,13 +28,13 @@ namespace AZ
static VirtualAddress CreateNull();
/// Creates a valid address with a zero offset.
//! Creates a valid address with a zero offset.
static VirtualAddress CreateZero();
/// Creates an address from a pointer.
//! Creates an address from a pointer.
static VirtualAddress CreateFromPointer(void* ptr);
/// Creates an address from an offset from a base pointer.
//! Creates an address from an offset from a base pointer.
static VirtualAddress CreateFromOffset(uint64_t offset);
inline bool IsValid() const
@@ -51,15 +50,13 @@ namespace AZ
uintptr_t m_ptr;
};
/**
* An allocator interface used for external GPU allocations. The allocator
* does not manage the host memory. Instead, the user specifies a base address
* (which may be 0, in order to allocate offsets from a base resource). The allocator
* interface also provides an API for garbage collection. If used to manage GPU resources,
* these are often deferred-released after N frames. The user may provide a garbage collection
* latency, which controls the number of GarbageCollect calls that must occur before an allocation
* is actually reclaimed. The intended use case is to garbage collect at the end of each frame.
*/
//! An allocator interface used for external GPU allocations. The allocator
//! does not manage the host memory. Instead, the user specifies a base address
//! (which may be 0, in order to allocate offsets from a base resource). The allocator
//! interface also provides an API for garbage collection. If used to manage GPU resources,
//! these are often deferred-released after N frames. The user may provide a garbage collection
//! latency, which controls the number of GarbageCollect calls that must occur before an allocation
//! is actually reclaimed. The intended use case is to garbage collect at the end of each frame.
class Allocator
{
public:
@@ -86,44 +83,42 @@ namespace AZ
virtual void Shutdown() = 0;
/**
* Allocates a virtual address relative to the base address provided at initialization time.
* @param byteCount The number of bytes to allocate.
* @param byteAlignement The alignment used to align the allocation.
*/
//! Allocates a virtual address relative to the base address provided at initialization time.
//! @param byteCount The number of bytes to allocate.
//! @param byteAlignement The alignment used to align the allocation.
virtual VirtualAddress Allocate(size_t byteCount, size_t byteAlignment) = 0;
/**
* Deallocates an allocation. The memory is not reclaimed until garbage collect is called.
* Depending on the garbage collection latency, it may take several garbage collection cycles
* before the memory is reclaimed.
*/
//! Deallocates an allocation. The memory is not reclaimed until garbage collect is called.
//! Depending on the garbage collection latency, it may take several garbage collection cycles
//! before the memory is reclaimed.
virtual void DeAllocate(VirtualAddress offset) = 0;
/// Allocations are deferred-released until a specific number of GC cycles have occurred. This
/// is useful for allocations actively being consumed by the GPU.
//! Allocations are deferred-released until a specific number of GC cycles have occurred. This
//! is useful for allocations actively being consumed by the GPU.
virtual void GarbageCollect() = 0;
/// Forces garbage collection of all allocations, regardless of the GC latency.
//! Forces garbage collection of all allocations, regardless of the GC latency.
virtual void GarbageCollectForce() = 0;
/**
* Returns the number of allocations active for this allocator. This includes
* allocations that are pending garbage collection.
*/
//! Returns the number of allocations active for this allocator. This includes
//! allocations that are pending garbage collection.
virtual size_t GetAllocationCount() const { return 0; }
/**
* Returns the number of bytes used by the allocator. This includes
* allocations that are pending garbage collection.
*/
//! Returns the number of bytes used by the allocator. This includes
//! allocations that are pending garbage collection.
virtual size_t GetAllocatedByteCount() const { return 0; }
/// Returns the descriptor used to initialize the allocator.
//! Returns the descriptor used to initialize the allocator.
virtual const Descriptor& GetDescriptor() const = 0;
/// Helper for converting agnostic VirtualAddress type to pointer type. Will convert
/// VirtualAddress::Null to nullptr.
//! Clone the current allocator to the new allocator passed in
virtual void Clone([[maybe_unused]] RHI::Allocator* newAllocator)
{
AZ_Assert(false, "Not Implemented");
};
//! Helper for converting agnostic VirtualAddress type to pointer type. Will convert
//! VirtualAddress::Null to nullptr.
template <typename T>
T* AllocateAs(size_t byteCount, size_t byteAlignment)
{
@@ -139,6 +139,12 @@ namespace AZ
//! Notifies after all objects currently in the platform release queue are released
virtual void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) = 0;
//! Allows the back-ends to compact SRG related memory if applicable
virtual RHI::ResultCode CompactSRGMemory()
{
return RHI::ResultCode::Success;
};
protected:
DeviceFeatures m_features;
DeviceLimits m_limits;
@@ -112,6 +112,9 @@ namespace AZ
//! Returns true if Pix dll is loaded
static bool IsPixModuleLoaded();
//! Returns true if Warp is enabled
static bool UsingWarpDevice();
//! Returns the name of the Factory.
virtual Name GetName() = 0;
@@ -55,6 +55,7 @@ namespace AZ
size_t GetAllocationCount() const override;
size_t GetAllocatedByteCount() const override;
const Descriptor& GetDescriptor() const override;
void Clone(RHI::Allocator* newAllocator) override;
//////////////////////////////////////////////////////////////////////////
private:
+10 -1
View File
@@ -8,12 +8,12 @@
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/ResourceInvalidateBus.h>
#include <Atom/RHI/RHIUtils.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Component/TickBus.h>
#if defined(USE_RENDERDOC) || defined(USE_PIX)
#include <AzCore/Module/DynamicModuleHandle.h>
#include <Atom/RHI/RHIUtils.h>
#include <Atom_RHI_Traits_Platform.h>
#endif
@@ -28,6 +28,8 @@ static AZStd::unique_ptr<AZ::DynamicModuleHandle> s_pixModule;
static bool s_isPixGpuCaptureDllLoaded = false;
#endif
static bool s_usingWarpDevice = false;
namespace AZ
{
namespace RHI
@@ -55,6 +57,8 @@ namespace AZ
Factory::Factory()
{
AZStd::string preferredUserAdapterName = RHI::GetCommandLineValue("forceAdapter");
s_usingWarpDevice = preferredUserAdapterName == "Microsoft Basic Render Driver";
#if defined(USE_RENDERDOC)
// If RenderDoc is requested, we need to load the library as early as possible (before device queries/factories are made)
bool enableRenderDoc = RHI::QueryCommandLineOption("enableRenderDoc");
@@ -197,5 +201,10 @@ namespace AZ
return false;
#endif
}
bool Factory::UsingWarpDevice()
{
return s_usingWarpDevice;
}
}
}
@@ -314,6 +314,11 @@ namespace AZ
resourcePoolDatabase.ForEachShaderResourceGroupPool<decltype(compileAllLambda)>(compileAllLambda);
}
//It is possible for certain back ends to run out of SRG memory (due to fragmentation) in which case
//we try to compact and re-compile SRGs.
RHI::ResultCode resultCode = m_device->CompactSRGMemory();
AZ_Assert(resultCode == RHI::ResultCode::Success, "SRG compaction failed and this can lead to a gpu crash.");
}
void FrameScheduler::BuildRayTracingShaderTables()
@@ -344,5 +344,17 @@ namespace AZ
handle = node.m_nextFree;
}
}
void FreeListAllocator::Clone(RHI::Allocator* newAllocator)
{
FreeListAllocator* newFreeListAllocator = static_cast<FreeListAllocator*>(newAllocator);
newFreeListAllocator->m_headHandle = m_headHandle;
newFreeListAllocator->m_nodeFreeList = m_nodeFreeList;
newFreeListAllocator->m_nodes = m_nodes;
newFreeListAllocator->m_allocations = m_allocations;
newFreeListAllocator->m_garbage = m_garbage;
newFreeListAllocator->m_garbageCollectCycle = m_garbageCollectCycle;
newFreeListAllocator->m_byteCountTotal = m_byteCountTotal;
}
}
}
@@ -64,6 +64,12 @@ namespace AZ
//! int array: Max count for descriptors
AZStd::unordered_map<AZStd::string, AZStd::array<uint32_t, NumHeapFlags>> m_descriptorHeapLimits;
// Number of max static handles for shader visible srv/uav/cbv views
uint32_t m_numShaderVisibleCbvSrvUavStaticHandles = 2000;
//Bool to indicate allowing compaction of shader visible srv/uav/cbv heap in case of fragmentation
bool m_allowDescriptorHeapCompaction = false;
FrameGraphExecuterData m_frameGraphExecuterData;
void LoadPlatformLimitsDescriptor(const char* rhiName) override;
@@ -19,8 +19,10 @@ namespace AZ
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<PlatformLimitsDescriptor, Base>()
->Version(0)
->Version(1)
->Field("DescriptorHeapLimits", &PlatformLimitsDescriptor::m_descriptorHeapLimits)
->Field("NumShaderVisibleCbvSrvUavStaticHandles", &PlatformLimitsDescriptor::m_numShaderVisibleCbvSrvUavStaticHandles)
->Field("AllowDescriptorHeapCompaction", &PlatformLimitsDescriptor::m_allowDescriptorHeapCompaction)
->Field("FrameGraphExecuterData", &PlatformLimitsDescriptor::m_frameGraphExecuterData)
;
}
@@ -54,7 +56,7 @@ namespace AZ
// Map default value must be initialized after attempting to serialize (and result in failure).
// Otherwise, serialization won't overwrite the default values.
m_descriptorHeapLimits = AZStd::unordered_map<AZStd::string, AZStd::array<uint32_t, NumHeapFlags>>({
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 1000000, 1000000 } },
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV"), { 100000, 1000000 } },
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_SAMPLER"), { 2048, 2048 } },
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_RTV"), { 2048, 0 } },
{ AZStd::string("DESCRIPTOR_HEAP_TYPE_DSV"), { 2048, 0 } }
@@ -438,7 +438,7 @@ namespace AZ
switch (pipelineType)
{
case RHI::PipelineStateType::Draw:
if (binding.m_resourceTable.IsValid())
if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr)
{
GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle);
}
@@ -448,14 +448,15 @@ namespace AZ
GetCommandList()->SetGraphicsRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress);
}
if (binding.m_samplerTable.IsValid())
if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr)
{
GetCommandList()->SetGraphicsRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle);
}
for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex)
{
if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid())
if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() &&
compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr)
{
GetCommandList()->SetGraphicsRootDescriptorTable(
binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(),
@@ -465,7 +466,7 @@ namespace AZ
break;
case RHI::PipelineStateType::Dispatch:
if (binding.m_resourceTable.IsValid())
if (binding.m_resourceTable.IsValid() && compiledData.m_gpuViewsDescriptorHandle.ptr)
{
GetCommandList()->SetComputeRootDescriptorTable(binding.m_resourceTable.GetIndex(), compiledData.m_gpuViewsDescriptorHandle);
}
@@ -475,14 +476,15 @@ namespace AZ
GetCommandList()->SetComputeRootConstantBufferView(binding.m_constantBuffer.GetIndex(), compiledData.m_gpuConstantAddress);
}
if (binding.m_samplerTable.IsValid())
if (binding.m_samplerTable.IsValid() && compiledData.m_gpuSamplersDescriptorHandle.ptr)
{
GetCommandList()->SetComputeRootDescriptorTable(binding.m_samplerTable.GetIndex(), compiledData.m_gpuSamplersDescriptorHandle);
}
for (uint32_t unboundedArrayIndex = 0; unboundedArrayIndex < ShaderResourceGroupCompiledData::MaxUnboundedArrays; ++unboundedArrayIndex)
{
if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid())
if (binding.m_unboundedArrayResourceTables[unboundedArrayIndex].IsValid() &&
compiledData.m_gpuUnboundedArraysDescriptorHandles[unboundedArrayIndex].ptr)
{
GetCommandList()->SetComputeRootDescriptorTable(
binding.m_unboundedArrayResourceTables[unboundedArrayIndex].GetIndex(),
@@ -50,7 +50,7 @@ namespace AZ
void CommandListBase::SetNameInternal(const AZStd::string_view& name)
{
AZStd::wstring wname;
AZStd::fixed_wstring<256> wname;
AZStd::to_wstring(wname, name.data());
GetCommandList()->SetName(wname.data());
}
@@ -39,7 +39,7 @@ namespace AZ
{
Device& device = static_cast<Device&>(deviceBase);
m_currentFrameIndex = 0;
m_frameFences.resize(RHI::Limits::Device::FrameCountMax - 1);
m_frameFences.resize(RHI::Limits::Device::FrameCountMax);
for (FenceSet& fences : m_frameFences)
{
fences.Init(device.GetDevice(), RHI::FenceState::Signaled);
@@ -10,7 +10,9 @@
#include <RHI/Conversions.h>
#include <RHI/Device.h>
#include <RHI/Image.h>
#include <RHI/ShaderResourceGroupPool.h>
#include <Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h>
#include <Atom/RHI/ShaderResourceGroupPool.h>
namespace AZ
{
@@ -40,7 +42,7 @@ namespace AZ
for (D3D12_SRV_DIMENSION dimension : validSRVDimensions)
{
DescriptorHandle srvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
DescriptorHandle srvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {};
desc.Format = DXGI_FORMAT_R32_UINT;
@@ -62,7 +64,7 @@ namespace AZ
for (D3D12_UAV_DIMENSION dimension : UAVDimensions)
{
DescriptorHandle uavDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
DescriptorHandle uavDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {};
desc.Format = DXGI_FORMAT_R32_UINT;
@@ -75,14 +77,14 @@ namespace AZ
void DescriptorContext::CreateNullDescriptorsCBV()
{
D3D12_CONSTANT_BUFFER_VIEW_DESC constantBufferDesc = {};
DescriptorHandle cbvDescriptorHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
DescriptorHandle cbvDescriptorHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
m_device->CreateConstantBufferView(&constantBufferDesc, GetCpuPlatformHandle(cbvDescriptorHandle));
m_nullDescriptorCBV = cbvDescriptorHandle;
}
void DescriptorContext::CreateNullDescriptorsSampler()
{
m_nullSamplerDescriptor = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
m_nullSamplerDescriptor = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
D3D12_SAMPLER_DESC samplerDesc = {};
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
@@ -102,7 +104,7 @@ namespace AZ
AZ_Assert(platformLimitsDescriptor.get(), "Platform limits information is missing");
m_platformLimitsDescriptor = platformLimitsDescriptor;
m_allowDescriptorHeapCompaction = m_platformLimitsDescriptor->m_allowDescriptorHeapCompaction;
for (const auto& itr : platformLimitsDescriptor->m_descriptorHeapLimits)
{
for (uint32_t shaderVisibleIdx = 0; shaderVisibleIdx < PlatformLimitsDescriptor::NumHeapFlags; ++shaderVisibleIdx)
@@ -114,11 +116,33 @@ namespace AZ
if (descriptorCountMax)
{
GetPool(static_cast<uint32_t>(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax);
if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flags))
{
//Init the two heaps to help support compaction after fragmentation
m_shaderVisibleCbvSrvUavPools[0].Init(
m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles);
m_shaderVisibleCbvSrvUavPools[1].Init(
m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
descriptorCountMax, platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles);
}
else
{
GetPool(static_cast<uint32_t>(heapTypeIdx.value()), shaderVisibleIdx).Init(m_device.get(), type, flags, descriptorCountMax, descriptorCountMax);
}
}
}
}
if (m_allowDescriptorHeapCompaction)
{
m_backupStaticHandles.Init(
m_device.get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles,
platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles);
}
CreateNullDescriptors();
}
@@ -129,7 +153,7 @@ namespace AZ
{
if (constantBufferView.IsNull())
{
constantBufferView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
constantBufferView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(constantBufferView);
@@ -145,7 +169,7 @@ namespace AZ
{
if (shaderResourceView.IsNull())
{
shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView);
@@ -165,7 +189,7 @@ namespace AZ
{
if (unorderedAccessView.IsNull())
{
unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView);
@@ -176,7 +200,24 @@ namespace AZ
// Copy the UAV descriptor into the GPU-visible version for clearing.
if (unorderedAccessViewClear.IsNull())
{
unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset();
unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1);
if (unorderedAccessViewClear.IsNull())
{
AZ_Assert(
false,
"Descriptor heap ran out of memory for static handles. Please consider increasing the value of NumShaderVisibleCbvSrvUavStaticHandles"
"within platformlimits.azasset file for dx12.");
return;
}
if (m_allowDescriptorHeapCompaction)
{
//We make a copy of static handles in case we need to compact and recreate the shader visible heap
m_device->CopyDescriptorsSimple(
1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor,
unorderedAccessViewClear.m_type);
}
}
CopyDescriptor(unorderedAccessViewClear, unorderedAccessView);
}
@@ -188,7 +229,7 @@ namespace AZ
{
if (shaderResourceView.IsNull())
{
shaderResourceView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
shaderResourceView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE descriptorHandle = GetCpuPlatformHandle(shaderResourceView);
@@ -205,7 +246,7 @@ namespace AZ
{
if (unorderedAccessView.IsNull())
{
unorderedAccessView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
unorderedAccessView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE unorderedAccessDescriptor = GetCpuPlatformHandle(unorderedAccessView);
@@ -216,7 +257,24 @@ namespace AZ
// Copy the UAV descriptor into the GPU-visible version for clearing.
if (unorderedAccessViewClear.IsNull())
{
unorderedAccessViewClear = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1).GetOffset();
unorderedAccessViewClear = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 1);
if (unorderedAccessViewClear.IsNull())
{
AZ_Assert(
false,
"Descriptor heap ran out of memory for static handles. Please consider increasing the value of "
"NumShaderVisibleCbvSrvUavStaticHandles within platformlimits.azasset file for dx12.");
return;
}
if (m_allowDescriptorHeapCompaction)
{
// We make a copy of static handles in case we need to compact and recreate the shader visible heap
m_device->CopyDescriptorsSimple(
1, m_backupStaticHandles.GetCpuPlatformHandle(unorderedAccessViewClear), unorderedAccessDescriptor,
unorderedAccessViewClear.m_type);
}
}
CopyDescriptor(unorderedAccessViewClear, unorderedAccessView);
}
@@ -228,7 +286,7 @@ namespace AZ
{
if (renderTargetView.IsNull())
{
renderTargetView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
renderTargetView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor = GetCpuPlatformHandle(renderTargetView);
@@ -245,13 +303,13 @@ namespace AZ
{
if (depthStencilView.IsNull())
{
depthStencilView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
depthStencilView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE depthStencilDescriptor = GetCpuPlatformHandle(depthStencilView);
if (depthStencilReadView.IsNull())
{
depthStencilReadView = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
depthStencilReadView = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_CPU_DESCRIPTOR_HANDLE depthStencilReadDescriptor = GetCpuPlatformHandle(depthStencilReadView);
@@ -274,7 +332,7 @@ namespace AZ
{
if (samplerHandle.IsNull())
{
samplerHandle = Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1).GetOffset();
samplerHandle = AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
}
D3D12_SAMPLER_DESC samplerDesc;
@@ -286,17 +344,49 @@ namespace AZ
{
if (!descriptorHandle.IsNull())
{
ReleaseDescriptorTable(DescriptorTable(descriptorHandle, 1));
GetPool(descriptorHandle.m_type, descriptorHandle.m_flags).ReleaseHandle(descriptorHandle);
}
}
DescriptorTable DescriptorContext::CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType,
uint32_t descriptorCount)
D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg)
{
return Allocate(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, descriptorCount);
if (m_allowDescriptorHeapCompaction && !m_compactionInProgress)
{
// Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap
AZStd::scoped_lock lock{ m_srgMapMutex };
auto iter = m_srgAllocations.find(srg);
if (iter == m_srgAllocations.end())
{
m_srgAllocations.emplace(srg, 1);
}
else
{
m_srgAllocations[srg]++;
}
}
return GetPool(descriptorHeapType, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE).AllocateTable(descriptorCount);
}
void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table, ShaderResourceGroup* srg)
{
if (m_allowDescriptorHeapCompaction && !m_compactionInProgress)
{
//Track active SRGs in case we need to compact the shader visible cbv_srv_uav heap
AZStd::scoped_lock lock{ m_srgMapMutex };
auto iter = m_srgAllocations.find(srg);
AZ_Assert(iter != m_srgAllocations.end(), "Srg entry not found");
m_srgAllocations[srg]--;
if (m_srgAllocations[srg] == 0)
{
m_srgAllocations.erase(srg);
}
}
GetPool(table.GetType(), table.GetFlags()).ReleaseTable(table);
}
void DescriptorContext::UpdateDescriptorTableRange(
DescriptorTable gpuDestinationTable,
const DescriptorHandle* cpuSourceDescriptors,
@@ -313,14 +403,12 @@ namespace AZ
}
// Resolve destination descriptor to platform handle.
D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandle(gpuDestinationTable.GetOffset());
D3D12_CPU_DESCRIPTOR_HANDLE gpuDestinationHandle = GetCpuPlatformHandleForTable(gpuDestinationTable);
// An array of descriptor sizes for each range. We just want N ranges with 1 descriptor each.
AZStd::vector<uint32_t> rangeCounts(DescriptorCount, 1);
/**
* We are gathering N source descriptors into a contiguous destination table.
*/
//We are gathering N source descriptors into a contiguous destination table.
m_device->CopyDescriptors(
1, // Number of destination ranges.
&gpuDestinationHandle, // Destination range array.
@@ -353,19 +441,24 @@ namespace AZ
}
}
}
if (m_allowDescriptorHeapCompaction)
{
m_backupStaticHandles.GarbageCollect();
}
}
DescriptorTable DescriptorContext::Allocate(
DescriptorTable DescriptorContext::AllocateTable(
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t count)
{
return GetPool(type, flags).Allocate(count);
return GetPool(type, flags).AllocateTable(count);
}
void DescriptorContext::ReleaseDescriptorTable(DescriptorTable table)
DescriptorHandle DescriptorContext::AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count)
{
GetPool(table.GetType(), table.GetFlags()).Release(table);
return GetPool(type, flags).AllocateHandle(count);
}
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandle(DescriptorHandle handle) const
@@ -378,6 +471,16 @@ namespace AZ
return GetPool(handle.m_type, handle.m_flags).GetGpuPlatformHandle(handle);
}
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorContext::GetCpuPlatformHandleForTable(DescriptorTable descTable) const
{
return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetCpuPlatformHandleForTable(descTable);
}
D3D12_GPU_DESCRIPTOR_HANDLE DescriptorContext::GetGpuPlatformHandleForTable(DescriptorTable descTable) const
{
return GetPool(descTable.GetOffset().m_type, descTable.GetOffset().m_flags).GetGpuPlatformHandleForTable(descTable);
}
DescriptorHandle DescriptorContext::GetNullHandleSRV(D3D12_SRV_DIMENSION dimension) const
{
auto iter = m_nullDescriptorsSRV.find(dimension);
@@ -431,14 +534,88 @@ namespace AZ
{
AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type);
AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag);
return m_pools[type][flag];
if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag))
{
return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex];
}
else
{
return m_pools[type][flag];
}
}
const DescriptorPool& DescriptorContext::GetPool(uint32_t type, uint32_t flag) const
{
AZ_Assert(type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, "Trying to get pool with invalid type: [%d]", type);
AZ_Assert(flag < NumHeapFlags, "Trying to get pool with invalid flag: [%d]", flag);
return m_pools[type][flag];
if (m_allowDescriptorHeapCompaction && IsShaderVisibleCbvSrvUavHeap(type, flag))
{
return m_shaderVisibleCbvSrvUavPools[m_currentHeapIndex];
}
else
{
return m_pools[type][flag];
}
}
RHI::ResultCode DescriptorContext::CompactDescriptorHeap()
{
//Check if heap compaction is enabled by the user. Since there is an overhead associated with heap compaction it is not enabled by default
if(!m_allowDescriptorHeapCompaction)
{
AZ_Assert(
false,
"Descriptor heap Compaction not allowed. Please consider increasing number of handles allowed for the second value"
"of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV or enabling AllowDescriptorHeapCompaction within platformlimits.azasset file for dx12.");
return RHI::ResultCode::OutOfMemory;
}
//We need to ping-pong between two heaps as we cannot compact the active heap without updating it and that is not allowed as
//we need to keep that gpu memory untouched until GPU is finished consuming which can take up to 3 frames.
m_compactionInProgress = true;
DescriptorPool& srcPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
//Update the currently active heap index
m_currentHeapIndex = !m_currentHeapIndex;
DescriptorPool& destPool = GetPool(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
//Copy over all the static handles first
for (size_t i = 0; i < m_platformLimitsDescriptor->m_numShaderVisibleCbvSrvUavStaticHandles; i++)
{
DescriptorHandle srcHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, static_cast<uint32_t>(i));
DescriptorHandle destHandle(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, static_cast<uint32_t>(i));
m_device->CopyDescriptorsSimple(1, destPool.GetCpuPlatformHandle(destHandle), m_backupStaticHandles.GetCpuPlatformHandle(srcHandle), destHandle.m_type);
}
//Clone the allocator of the source pool into the destination pool
srcPool.CloneAllocator(destPool.GetAllocator());
{
//The mutex is here 'just in case' Compaction is called from more than one thread.
AZStd::scoped_lock lock{ m_srgMapMutex };
//Re-update all the descriptor tables associated with active SRGs
for (const auto& [srg, numAllocations] : m_srgAllocations)
{
RHI::ResultCode resultCode = static_cast<ShaderResourceGroupPool*>(srg->GetPool())->UpdateDescriptorTableAfterCompaction(*srg, srg->GetData());
if (resultCode != RHI::ResultCode::Success)
{
return resultCode;
}
}
}
//Clear the allocator of the source pool
srcPool.ClearAllocator();
m_compactionInProgress = false;
return RHI::ResultCode::Success;
}
bool DescriptorContext::IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const
{
return type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV && flag == D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
}
}
}
@@ -15,6 +15,8 @@
#include <Atom/RHI.Reflect/SamplerState.h>
#include <Atom/RHI/Buffer.h>
#include <Atom/RHI/Image.h>
#include <AzCore/std/containers/unordered_map.h>
#include <RHI/ShaderResourceGroup.h>
namespace AZ
{
@@ -82,12 +84,14 @@ namespace AZ
//! Creates a GPU-visible descriptor table.
//! @param descriptorHeapType The descriptor heap to allocate from.
//! @param descriptorCount The number of descriptors to allocate.
//! @param srg Shader resource group with which the descriptor table is associated with
DescriptorTable CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType,
uint32_t descriptorCount);
void ReleaseDescriptorTable(DescriptorTable descriptorTable);
D3D12_DESCRIPTOR_HEAP_TYPE descriptorHeapType, uint32_t descriptorCount, ShaderResourceGroup* srg);
//! Releases a GPU-visible descriptor table.
//! @param descriptorHeapType The descriptor heap to allocate from.
//! @param srg Shader resource group with which the descriptor table is associated with
void ReleaseDescriptorTable(DescriptorTable descriptorTable, ShaderResourceGroup* srg);
//! Performs a gather of disjoint CPU-side descriptors and copies to a contiguous GPU-side descriptor table.
//! @param gpuDestinationTable The destination descriptor table that the descriptors will be uploaded to.
@@ -110,6 +114,8 @@ namespace AZ
D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const;
D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const;
D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable descTable) const;
D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable descTable) const;
void SetDescriptorHeaps(ID3D12GraphicsCommandList* commandList) const;
@@ -117,6 +123,12 @@ namespace AZ
ID3D12DeviceX* GetDevice();
//! Since we are only allowed one shader visible CbvSrvUav heap of a limited size in certain hardware, it is possible that
//! it can get fragmented by constant alloc/de-alloc of descriptor tables related to direct views or unbounded resource views within a SRG. We use two
//! heaps to ping pong during compaction as fragmentation can occur many times. It copies static handles directly and for all the
//! dynamic handles we re-update the new heap by copying over the handles from the 'non-shader visible' heap.
RHI::ResultCode CompactDescriptorHeap();
private:
void CopyDescriptor(DescriptorHandle dst, DescriptorHandle src);
@@ -129,10 +141,13 @@ namespace AZ
DescriptorPool& GetPool(uint32_t type, uint32_t flag);
const DescriptorPool& GetPool(uint32_t type, uint32_t flag) const;
DescriptorTable Allocate(
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t count);
//! Allocates a Descriptor table which describes a contiguous range of descriptor handles
DescriptorTable AllocateTable(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count);
//! Allocates a single descriptor handle
DescriptorHandle AllocateHandle(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t count);
bool IsShaderVisibleCbvSrvUavHeap(uint32_t type, uint32_t flag) const;
static const uint32_t NumHeapFlags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE + 1;
static const uint32_t s_descriptorCountMax[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES][NumHeapFlags];
@@ -147,6 +162,25 @@ namespace AZ
DescriptorHandle m_nullSamplerDescriptor;
RHI::ConstPtr<PlatformLimitsDescriptor> m_platformLimitsDescriptor;
// Use 2 heaps below in order to ping-pong between shader visible CbvSrvUav heap when one of them fragments and run out of memory.
static const uint32_t MaxShaderVisibleCbvSrvUavHeaps = 2;
DescriptorPoolShaderVisibleCbvSrvUav m_shaderVisibleCbvSrvUavPools[MaxShaderVisibleCbvSrvUavHeaps];
//This pool stores a copy of static handles that can later be used to recreate the compacted shader visible CbvSrvUav heap.
DescriptorPool m_backupStaticHandles;
//Boolean to dictate when compaction was in progress
bool m_compactionInProgress = false;
//Boolean to dictate if we should support compaction for shader visible CbvSrvUav heap
bool m_allowDescriptorHeapCompaction = false;
//Map to store active SRGs and the number of associated descriptor tables. This is used to recreate the new compacted heap when we switch heaps
AZStd::unordered_map<ShaderResourceGroup*, uint32_t> m_srgAllocations;
AZStd::mutex m_srgMapMutex;
//Index that holds the currently active shader visible CbvSrvUav heap
uint32_t m_currentHeapIndex = 0;
};
}
}
@@ -18,34 +18,38 @@ namespace AZ
ID3D12DeviceX* device,
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t descriptorCount)
uint32_t descriptorCountForHeap,
uint32_t descriptorCountForAllocator)
{
m_Desc.Type = type;
m_Desc.Flags = flags;
m_Desc.NumDescriptors = descriptorCount;
m_Desc.NodeMask = 1;
m_desc.Type = type;
m_desc.Flags = flags;
m_desc.NumDescriptors = descriptorCountForHeap;
m_desc.NodeMask = 1;
ID3D12DescriptorHeap* heap;
DX12::AssertSuccess(device->CreateDescriptorHeap(&m_Desc, IID_GRAPHICS_PPV_ARGS(&heap)));
DX12::AssertSuccess(device->CreateDescriptorHeap(&m_desc, IID_GRAPHICS_PPV_ARGS(&heap)));
heap->SetName(L"DescriptorHeap");
m_DescriptorHeap.Attach(heap);
m_Stride = device->GetDescriptorHandleIncrementSize(m_Desc.Type);
m_descriptorHeap.Attach(heap);
m_stride = device->GetDescriptorHandleIncrementSize(m_desc.Type);
m_CpuStart = heap->GetCPUDescriptorHandleForHeapStart();
m_GpuStart = {};
if (RHI::CheckBitsAny(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE))
{
m_GpuStart = heap->GetGPUDescriptorHandleForHeapStart();
}
m_cpuStart = heap->GetCPUDescriptorHandleForHeapStart();
m_gpuStart = {};
const bool isGpuVisible = RHI::CheckBitsAll(flags, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
if (isGpuVisible)
{
m_gpuStart = heap->GetGPUDescriptorHandleForHeapStart();
}
if (isGpuVisible)
{
RHI::FreeListAllocator::Descriptor descriptor;
descriptor.m_alignmentInBytes = 1;
descriptor.m_capacityInBytes = descriptorCount;
//It is possible for descriptorCountForAllocator to not match descriptorCountForHeap for DescriptorPoolShaderVisibleCbvSrvUav
//heaps in which case descriptorCountForAllocator defines the number of static handles
descriptor.m_capacityInBytes = aznumeric_cast<uint32_t>(descriptorCountForAllocator);
descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax;
RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator();
@@ -56,10 +60,11 @@ namespace AZ
{
// Non-shader-visible heaps don't require contiguous descriptors. Therefore, we can allocate
// them using a block allocator.
RHI::PoolAllocator::Descriptor descriptor;
descriptor.m_alignmentInBytes = 1;
descriptor.m_elementSize = 1;
descriptor.m_capacityInBytes = descriptorCount;
descriptor.m_capacityInBytes = aznumeric_cast<uint32_t>(descriptorCountForAllocator);
descriptor.m_garbageCollectLatency = 0;
RHI::PoolAllocator* allocator = aznew RHI::PoolAllocator();
@@ -68,7 +73,7 @@ namespace AZ
}
}
DescriptorTable DescriptorPool::Allocate(uint32_t count)
DescriptorHandle DescriptorPool::AllocateHandle(uint32_t count)
{
RHI::VirtualAddress address;
{
@@ -78,24 +83,34 @@ namespace AZ
if (address.IsValid())
{
DescriptorHandle handle(m_Desc.Type, m_Desc.Flags, static_cast<uint32_t>(address.m_ptr));
return DescriptorTable(handle, static_cast<uint16_t>(count));
DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast<uint32_t>(address.m_ptr));
return handle;
}
else
{
return DescriptorTable{};
return DescriptorHandle{};
}
}
void DescriptorPool::Release(DescriptorTable table)
void DescriptorPool::ReleaseHandle(DescriptorHandle handle)
{
if (table.IsNull())
if (handle.IsNull())
{
return;
}
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index));
m_allocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(handle.m_index));
}
DescriptorTable DescriptorPool::AllocateTable(uint32_t count)
{
return DescriptorTable(AllocateHandle(count), static_cast<uint16_t>(count));
}
void DescriptorPool::ReleaseTable(DescriptorTable table)
{
ReleaseHandle(table.GetOffset());
}
void DescriptorPool::GarbageCollect()
@@ -106,20 +121,134 @@ namespace AZ
ID3D12DescriptorHeap* DescriptorPool::GetPlatformHeap() const
{
return m_DescriptorHeap.Get();
return m_descriptorHeap.Get();
}
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandle(DescriptorHandle handle) const
{
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
return D3D12_CPU_DESCRIPTOR_HANDLE{ m_CpuStart.ptr + handle.m_index * m_Stride };
return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride };
}
D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandle(DescriptorHandle handle) const
{
AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible");
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
return D3D12_GPU_DESCRIPTOR_HANDLE{ m_GpuStart.ptr + handle.m_index * m_Stride };
return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) };
}
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPool::GetCpuPlatformHandleForTable(DescriptorTable descTable) const
{
DescriptorHandle handle = descTable.GetOffset();
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + handle.m_index * m_stride };
}
D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPool::GetGpuPlatformHandleForTable(DescriptorTable descTable) const
{
DescriptorHandle handle = descTable.GetOffset();
AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible");
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (handle.m_index * m_stride) };
}
void DescriptorPool::CloneAllocator(RHI::Allocator* newAllocator)
{
m_allocator->Clone(newAllocator);
}
void DescriptorPool::ClearAllocator()
{
AZ_Assert(m_gpuStart.ptr, "Clearing the allocator is only supported for the gpu visible heap as only this heap can be compacted");
static_cast<RHI::FreeListAllocator*>(m_allocator.get())
->Init(static_cast<RHI::FreeListAllocator*>(m_allocator.get())->GetDescriptor());
}
RHI::Allocator* DescriptorPool::GetAllocator() const
{
return m_allocator.get();
}
void DescriptorPoolShaderVisibleCbvSrvUav::Init(
ID3D12DeviceX* device,
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t descriptorCount,
uint32_t staticHandlesCount)
{
//This pool manages two allocators. The allocator in the base class manages static handles
Base::Init(device, type, flags, descriptorCount, staticHandlesCount);
//This allocator manages dynamic handles associated with descriptor tables. This allows us to
//reconstruct the full heap in a compact manner if it ever fragments.
RHI::FreeListAllocator::Descriptor descriptor;
descriptor.m_alignmentInBytes = 1;
descriptor.m_capacityInBytes = aznumeric_cast<uint32_t>(descriptorCount - staticHandlesCount);
descriptor.m_garbageCollectLatency = RHI::Limits::Device::FrameCountMax;
RHI::FreeListAllocator* allocator = aznew RHI::FreeListAllocator();
allocator->Init(descriptor);
m_unboundedArrayAllocator.reset(allocator);
//Cache the starting point of the dynamic section of the heap
m_startingHandleIndex = staticHandlesCount;
}
DescriptorTable DescriptorPoolShaderVisibleCbvSrvUav::AllocateTable(uint32_t count)
{
RHI::VirtualAddress address;
{
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
address = m_unboundedArrayAllocator->Allocate(count, 1);
}
if (address.IsValid())
{
DescriptorHandle handle(m_desc.Type, m_desc.Flags, static_cast<uint32_t>(address.m_ptr));
return DescriptorTable(handle, static_cast<uint16_t>(count));
}
else
{
return DescriptorTable{};
}
}
void DescriptorPoolShaderVisibleCbvSrvUav::ReleaseTable(DescriptorTable table)
{
if (table.IsNull())
{
return;
}
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
m_unboundedArrayAllocator->DeAllocate(RHI::VirtualAddress::CreateFromOffset(table.GetOffset().m_index));
}
void DescriptorPoolShaderVisibleCbvSrvUav::GarbageCollect()
{
Base::GarbageCollect();
m_unboundedArrayAllocator->GarbageCollect();
}
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetCpuPlatformHandleForTable(DescriptorTable descTable) const
{
DescriptorHandle handle = descTable.GetOffset();
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
return D3D12_CPU_DESCRIPTOR_HANDLE{ m_cpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) };
}
D3D12_GPU_DESCRIPTOR_HANDLE DescriptorPoolShaderVisibleCbvSrvUav::GetGpuPlatformHandleForTable(DescriptorTable descTable) const
{
DescriptorHandle handle = descTable.GetOffset();
AZ_Assert(handle.IsShaderVisible(), "Handle is not shader visible");
AZ_Assert(handle.m_index != DescriptorHandle::NullIndex, "Index is invalid");
return D3D12_GPU_DESCRIPTOR_HANDLE{ m_gpuStart.ptr + (m_startingHandleIndex * m_stride) + (handle.m_index * m_stride) };
}
void DescriptorPoolShaderVisibleCbvSrvUav::ClearAllocator()
{
Base::ClearAllocator();
static_cast<RHI::FreeListAllocator*>(m_unboundedArrayAllocator.get())->Init(static_cast<RHI::FreeListAllocator*>(m_unboundedArrayAllocator.get())->GetDescriptor());
}
}
}
@@ -18,37 +18,91 @@ namespace AZ
{
namespace DX12
{
//! This class defines a Descriptor pool which manages all the descriptors used for binding resources
class DescriptorPool
{
public:
DescriptorPool() = default;
virtual ~DescriptorPool() = default;
void Init(
//! Initialize the native heap as well as init the allocators tracking the memory for descriptor handles
virtual void Init(
ID3D12DeviceX* device,
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t descriptorCount);
uint32_t descriptorCountForHeap,
uint32_t descriptorCountForAllocator);
ID3D12DescriptorHeap* GetPlatformHeap() const;
DescriptorTable Allocate(uint32_t count = 1);
//! Allocate a Descriptor handles
DescriptorHandle AllocateHandle(uint32_t count = 1);
//! Release a descriptor handle
void ReleaseHandle(DescriptorHandle table);
//! Allocate a range contiguous handles (i.e Descriptor table)
virtual DescriptorTable AllocateTable(uint32_t count = 1);
//! Release a range contiguous handles (i.e Descriptor table)
virtual void ReleaseTable(DescriptorTable table);
//! Garbage collection for freed handles or tables
virtual void GarbageCollect();
//Get native pointers from the heap
virtual D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const;
virtual D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const;
//Clear the tracking allocator
virtual void ClearAllocator();
void Release(DescriptorTable table);
void GarbageCollect();
D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandle(DescriptorHandle handle) const;
D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandle(DescriptorHandle handle) const;
private:
D3D12_CPU_DESCRIPTOR_HANDLE m_CpuStart = {};
D3D12_GPU_DESCRIPTOR_HANDLE m_GpuStart = {};
D3D12_CPU_DESCRIPTOR_HANDLE m_NullDescriptor = {};
uint32_t m_Stride = 0;
D3D12_DESCRIPTOR_HEAP_DESC m_Desc;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_DescriptorHeap;
//Clone the tracking allocator
void CloneAllocator(RHI::Allocator* newAllocator);
RHI::Allocator* GetAllocator() const;
protected:
D3D12_DESCRIPTOR_HEAP_DESC m_desc;
AZStd::mutex m_mutex;
D3D12_CPU_DESCRIPTOR_HANDLE m_cpuStart = {};
D3D12_GPU_DESCRIPTOR_HANDLE m_gpuStart = {};
uint32_t m_stride = 0;
private:
// Native heap
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptorHeap;
// Allocator used to manage the whole native heap. In the case of DescriptorPoolShaderVisibleCbvSrvUav this allocator
// is used to manage the part of the heap that only manages static handles.
AZStd::unique_ptr<RHI::Allocator> m_allocator;
};
//! A specialized pool that specifically handles Descriptor tables for Cbv/Srv/Uav views and allows for Compaction
//! Specifically this pool handles the dynamic part of the heap
class DescriptorPoolShaderVisibleCbvSrvUav : public DescriptorPool
{
using Base = DescriptorPool;
public:
void Init(
ID3D12DeviceX* device,
D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t descriptorCount,
uint32_t staticHandlesCount);
DescriptorTable AllocateTable(uint32_t count = 1) override;
void ReleaseTable(DescriptorTable table) override;
void GarbageCollect() override;
D3D12_CPU_DESCRIPTOR_HANDLE GetCpuPlatformHandleForTable(DescriptorTable handle) const override;
D3D12_GPU_DESCRIPTOR_HANDLE GetGpuPlatformHandleForTable(DescriptorTable handle) const override;
void ClearAllocator() override;
private:
// A separate allocator that handles descriptor tables which are dynamic in nature and may fragment and require compaction
AZStd::unique_ptr<RHI::Allocator> m_unboundedArrayAllocator;
//Starting index of the dynamic part of the heap
uint32_t m_startingHandleIndex = 0;
};
}
}
@@ -625,5 +625,20 @@ namespace AZ
{
return m_isAftermathInitialized;
}
RHI::ResultCode Device::CompactSRGMemory()
{
if (m_isDescriptorHeapCompactionNeeded)
{
m_isDescriptorHeapCompactionNeeded = false;
return m_descriptorContext->CompactDescriptorHeap();
}
return RHI::ResultCode::Success;
}
void Device::DescriptorHeapCompactionNeeded()
{
m_isDescriptorHeapCompactionNeeded = true;
}
}
}
+19 -23
View File
@@ -98,39 +98,27 @@ namespace AZ
D3D12_RESOURCE_STATES initialState,
ImageTileLayout& imageTilingInfo);
/**
* Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed
* through the GPU.
*/
//! Queues a DX12 COM object for release (by taking a reference) after the current frame has flushed
//! through the GPU.
void QueueForRelease(RHI::Ptr<ID3D12Object> dx12Object);
/**
* Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the
* current frame has flushed through the GPU. The reference on the MemoryView itself is not released.
*/
//! Queues the backing Memory instance of a MemoryView for release (by taking a reference) after the
//! current frame has flushed through the GPU. The reference on the MemoryView itself is not released.
void QueueForRelease(const MemoryView& memoryView);
/**
* Allocates host memory from the internal frame allocator that is suitable for staging
* uploads to the GPU for the current frame. The memory is valid for the lifetime of
* the frame and is automatically reclaimed after the frame has completed on the GPU.
*/
//! Allocates host memory from the internal frame allocator that is suitable for staging
//! uploads to the GPU for the current frame. The memory is valid for the lifetime of
//! the frame and is automatically reclaimed after the frame has completed on the GPU.
MemoryView AcquireStagingMemory(size_t size, size_t alignment);
/**
* Acquires a pipeline layout from the internal cache.
*/
//! Acquires a pipeline layout from the internal cache.
RHI::ConstPtr<PipelineLayout> AcquirePipelineLayout(const RHI::PipelineLayoutDescriptor& descriptor);
/**
* Acquires a new command list for the frame given the hardware queue class. The command list is
* automatically reclaimed after the current frame has flushed through the GPU.
*/
//! Acquires a new command list for the frame given the hardware queue class. The command list is
//! automatically reclaimed after the current frame has flushed through the GPU.
CommandList* AcquireCommandList(RHI::HardwareQueueClass hardwareQueueClass);
/**
* Acquires a sampler from the internal cache.
*/
//! Acquires a sampler from the internal cache.
RHI::ConstPtr<Sampler> AcquireSampler(const RHI::SamplerState& state);
const PhysicalDevice& GetPhysicalDevice() const;
@@ -146,6 +134,10 @@ namespace AZ
AsyncUploadQueue& GetAsyncUploadQueue();
bool IsAftermathInitialized() const;
//! Indicate that we need to compact the shader visible srv/uav/cbv shader visible heap.
void DescriptorHeapCompactionNeeded();
private:
Device();
@@ -167,6 +159,7 @@ namespace AZ
RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::ImageDescriptor & descriptor) override;
RHI::ResourceMemoryRequirements GetResourceMemoryRequirements(const RHI::BufferDescriptor & descriptor) override;
void ObjectCollectionNotify(RHI::ObjectCollectorNotifyFunction notifyFunction) override;
RHI::ResultCode CompactSRGMemory() override;
//////////////////////////////////////////////////////////////////////////
RHI::ResultCode InitSubPlatform(RHI::PhysicalDevice& physicalDevice);
@@ -198,6 +191,9 @@ namespace AZ
AZStd::mutex m_samplerCacheMutex;
bool m_isAftermathInitialized = false;
// Boolean used to compact the view specific shader visible heap
bool m_isDescriptorHeapCompactionNeeded = false;
};
}
}
@@ -49,9 +49,11 @@ namespace AZ
AZStd::array_view<uint8_t> bytes;
bool shouldCreateLibFromSerializedData = true;
if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded())
if (RHI::Factory::Get().IsRenderDocModuleLoaded() ||
RHI::Factory::Get().IsPixModuleLoaded() ||
RHI::Factory::Get().UsingWarpDevice())
{
// CreatePipelineLibrary api does not function properly if Renderdoc or Pix is enabled
// CreatePipelineLibrary api does not function properly if Renderdoc, Pix or Warp is enabled
shouldCreateLibFromSerializedData = false;
}
@@ -215,9 +217,11 @@ namespace AZ
RHI::ResultCode PipelineLibrary::MergeIntoInternal([[maybe_unused]] AZStd::array_view<const RHI::PipelineLibrary*> pipelineLibraries)
{
if (RHI::Factory::Get().IsRenderDocModuleLoaded() || RHI::Factory::Get().IsPixModuleLoaded())
if (RHI::Factory::Get().IsRenderDocModuleLoaded() ||
RHI::Factory::Get().IsPixModuleLoaded() ||
RHI::Factory::Get().UsingWarpDevice())
{
// StorePipeline api does not function properly if RenderDoc or Pix is enabled
// StorePipeline api does not function properly if RenderDoc, Pix or Warp is enabled
return RHI::ResultCode::Fail;
}
@@ -51,6 +51,7 @@ namespace AZ
ShaderResourceGroup() = default;
friend class ShaderResourceGroupPool;
friend class DescriptorContext;
/// The current index into the compiled data array.
uint32_t m_compiledDataIndex = 0;
@@ -132,33 +132,17 @@ namespace AZ
compiledData.m_cpuConstantAddress = cpuAddress + m_constantBufferSize * i;
}
}
if (m_viewsDescriptorTableSize)
{
group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize);
if (!group.m_viewsDescriptorTable.IsValid())
{
AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate view descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12");
return RHI::ResultCode::OutOfMemory;
}
for (uint32_t i = 0; i < copyCount; ++i)
{
const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i;
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i];
compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle);
}
}
if (m_samplersDescriptorTableSize)
{
group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize);
group.m_samplersDescriptorTable = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, m_samplersDescriptorTableRingSize, &group);
if (!group.m_samplersDescriptorTable.IsValid())
{
AZ_Error("ShaderResourceGroupPool", false, "Descriptor context failed to allocate sampler descriptor table. Try increasing the limits specified in platformlimits.azasset file for dx12.");
AZ_Error(
"ShaderResourceGroupPool", false,
"Descriptor context failed to allocate sampler descriptor table. Please consider increasing number of handles "
"allowed for the second value of DESCRIPTOR_HEAP_TYPE_SAMPLER within platformlimits.azasset file for dx12.");
return RHI::ResultCode::OutOfMemory;
}
@@ -167,7 +151,7 @@ namespace AZ
const DescriptorHandle descriptorHandle = group.m_samplersDescriptorTable.GetOffset() + m_samplersDescriptorTableSize * i;
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i];
compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandle(descriptorHandle);
compiledData.m_gpuSamplersDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable(DescriptorTable(descriptorHandle, static_cast<uint16_t>(m_samplersDescriptorTableSize)));
}
}
@@ -186,19 +170,25 @@ namespace AZ
if (m_viewsDescriptorTableSize)
{
m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable);
if (group.m_viewsDescriptorTable.IsValid())
{
m_descriptorContext->ReleaseDescriptorTable(group.m_viewsDescriptorTable, &group);
}
}
if (m_samplersDescriptorTableSize)
{
m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable);
if (group.m_viewsDescriptorTable.IsValid())
{
m_descriptorContext->ReleaseDescriptorTable(group.m_samplersDescriptorTable, &group);
}
}
for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex)
{
if (group.m_unboundedDescriptorTables[unboundedArrayindex].IsValid())
{
m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex]);
m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[unboundedArrayindex], &group);
}
}
@@ -213,6 +203,7 @@ namespace AZ
const RHI::ShaderResourceGroupData& groupData)
{
ShaderResourceGroup& group = static_cast<ShaderResourceGroup&>(groupBase);
auto& device = static_cast<Device&>(GetDevice());
group.m_compiledDataIndex = (group.m_compiledDataIndex + 1) % RHI::Limits::Device::FrameCountMax;
if (m_constantBufferSize)
@@ -222,6 +213,22 @@ namespace AZ
if (m_viewsDescriptorTableSize)
{
//Lazy initialization for cbv/srv/uav Descriptor Tables
if (!group.m_viewsDescriptorTable.IsValid())
{
group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group);
if (!group.m_viewsDescriptorTable.IsValid())
{
//We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that.
device.DescriptorHeapCompactionNeeded();
return RHI::ResultCode::Success;
}
CacheGpuHandlesForViews(group);
}
const DescriptorTable descriptorTable(
group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize,
static_cast<uint16_t>(m_viewsDescriptorTableSize));
@@ -246,6 +253,18 @@ namespace AZ
return RHI::ResultCode::Success;
}
void ShaderResourceGroupPool::CacheGpuHandlesForViews(ShaderResourceGroup& group)
{
for (uint32_t i = 0; i < RHI::Limits::Device::FrameCountMax; ++i)
{
const DescriptorHandle descriptorHandle = group.m_viewsDescriptorTable.GetOffset() + m_viewsDescriptorTableSize * i;
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[i];
compiledData.m_gpuViewsDescriptorHandle = m_descriptorContext->GetGpuPlatformHandleForTable(
DescriptorTable(descriptorHandle, static_cast<uint16_t>(m_viewsDescriptorTableSize)));
}
}
void ShaderResourceGroupPool::UpdateViewsDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData)
{
const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout();
@@ -261,27 +280,27 @@ namespace AZ
AZStd::vector<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER);
break;
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews< RHI::BufferView, BufferView> (bufferViews, D3D12_SRV_DIMENSION_BUFFER);
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::BufferView, BufferView>(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_CBV:
{
descriptorHandles = GetCBVsFromBufferViews(bufferViews);
break;
}
default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::BufferView, BufferView>(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_CBV:
{
descriptorHandles = GetCBVsFromBufferViews(bufferViews);
break;
}
default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex);
UpdateDescriptorTableRange(descriptorTable, descriptorHandles, bufferInputIndex);
++shaderInputIndex;
}
@@ -297,23 +316,24 @@ namespace AZ
AZStd::vector<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(shaderInputImage.m_type));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertUAVDimension(shaderInputImage.m_type));
break;
}
default:
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles =
GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(shaderInputImage.m_type));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles =
GetUAVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertUAVDimension(shaderInputImage.m_type));
break;
}
default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
UpdateDescriptorTableRange(descriptorTable, descriptorHandles, imageInputIndex);
++shaderInputIndex;
}
}
@@ -334,7 +354,7 @@ namespace AZ
void ShaderResourceGroupPool::UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData)
{
const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout();
auto& device = static_cast<Device&>(GetDevice());
uint32_t shaderInputIndex = 0;
// process buffer unbounded arrays
@@ -350,50 +370,30 @@ namespace AZ
{
if (group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]);
m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group);
group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{};
}
if (!bufferViews.empty())
{
group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast<uint32_t>(bufferViews.size()));
AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory.");
group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast<uint32_t>(bufferViews.size()), &group);
if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
// We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that.
device.DescriptorHeapCompactionNeeded();
return;
}
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset());
compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
}
}
++shaderInputIndex;
if (bufferViews.empty())
{
// we don't need to update descriptors since the buffer list is empty
continue;
}
D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(shaderInputBufferUnboundedArray.m_access);
AZStd::vector<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::BufferView, BufferView>(bufferViews, D3D12_SRV_DIMENSION_BUFFER);
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::BufferView, BufferView>(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
break;
}
default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast<uint16_t>(bufferViews.size()));
m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access);
++shaderInputIndex;
}
// process image unbounded arrays
@@ -407,55 +407,223 @@ namespace AZ
// resize the descriptor table allocation if necessary
if (group.m_unboundedDescriptorTables[tableIndex].GetSize() != imageViews.size())
{
if (group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex]);
m_descriptorContext->ReleaseDescriptorTable(group.m_unboundedDescriptorTables[tableIndex], &group);
group.m_unboundedDescriptorTables[tableIndex] = DescriptorTable{};
}
if (!imageViews.empty())
{
group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast<uint32_t>(imageViews.size()));
AZ_Assert(group.m_unboundedDescriptorTables[tableIndex].IsValid(), "Descriptor context failed to allocate unbounded array descriptor table, most likely out of memory.");
group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast<uint32_t>(imageViews.size()), &group);
if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
// We have support for compacting D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV (if applicable) so try that
device.DescriptorHeapCompactionNeeded();
return;
}
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandle(group.m_unboundedDescriptorTables[tableIndex].GetOffset());
compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
}
}
++shaderInputIndex;
if (imageViews.empty())
{
// we don't need to update descriptors since the image list is empty
continue;
}
D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(shaderInputImageUnboundedArray.m_access);
AZStd::vector<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(shaderInputImageUnboundedArray.m_type));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertUAVDimension(shaderInputImageUnboundedArray.m_type));
break;
}
default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast<uint16_t>(imageViews.size()));
m_descriptorContext->UpdateDescriptorTableRange(descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type);
++shaderInputIndex;
}
}
void ShaderResourceGroupPool::UpdateUnboundedBuffersDescTable(
DescriptorTable descriptorTable,
const RHI::ShaderResourceGroupData& groupData,
uint32_t shaderInputIndex,
RHI::ShaderInputBufferAccess bufferAccess)
{
const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex);
AZStd::array_view<RHI::ConstPtr<RHI::BufferView>> bufferViews =
groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex);
if (bufferViews.empty())
{
// we don't need to update descriptors since the buffer list is empty
return;
}
D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputBufferAccess(bufferAccess);
AZStd::vector<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::BufferView, BufferView>(bufferViews, D3D12_SRV_DIMENSION_BUFFER);
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::BufferView, BufferView>(bufferViews, D3D12_UAV_DIMENSION_BUFFER);
break;
}
default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
m_descriptorContext->UpdateDescriptorTableRange(
descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
void ShaderResourceGroupPool::UpdateUnboundedImagesDescTable(
DescriptorTable descriptorTable,
const RHI::ShaderResourceGroupData& groupData,
uint32_t shaderInputIndex,
RHI::ShaderInputImageAccess imageAccess,
RHI::ShaderInputImageType imageType)
{
const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex);
AZStd::array_view<RHI::ConstPtr<RHI::ImageView>> imageViews =
groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex);
if (imageViews.empty())
{
// we don't need to update descriptors since the image list is empty
return;
}
D3D12_DESCRIPTOR_RANGE_TYPE descriptorRangeType = ConvertShaderInputImageAccess(imageAccess);
AZStd::vector<DescriptorHandle> descriptorHandles;
switch (descriptorRangeType)
{
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
{
descriptorHandles = GetSRVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertSRVDimension(imageType));
break;
}
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
{
descriptorHandles = GetUAVsFromImageViews<RHI::ImageView, ImageView>(imageViews, ConvertUAVDimension(imageType));
break;
}
default:
AZ_Assert(false, "Unhandled D3D12_DESCRIPTOR_RANGE_TYPE enumeration");
break;
}
m_descriptorContext->UpdateDescriptorTableRange(
descriptorTable, descriptorHandles.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
RHI::ResultCode ShaderResourceGroupPool::UpdateDescriptorTableAfterCompaction(
RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData)
{
// Since we are trying to compact we will re-create all the descriptor tables and re-update them all
ShaderResourceGroup& group = static_cast<ShaderResourceGroup&>(groupBase);
if (m_viewsDescriptorTableSize)
{
group.m_viewsDescriptorTable = m_descriptorContext->CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_viewsDescriptorTableRingSize, &group);
if (!group.m_viewsDescriptorTable.IsValid())
{
AZ_Assert(
false,
"Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value"
"of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12.");
return RHI::ResultCode::OutOfMemory;
}
CacheGpuHandlesForViews(group);
const DescriptorTable descriptorTable(
group.m_viewsDescriptorTable.GetOffset() + group.m_compiledDataIndex * m_viewsDescriptorTableSize,
static_cast<uint16_t>(m_viewsDescriptorTableSize));
UpdateViewsDescriptorTable(descriptorTable, groupData);
}
if (m_unboundedArrayCount)
{
//Reset all the old descriptor tables as the previous heap is gone.
for (uint32_t unboundedArrayindex = 0; unboundedArrayindex < (ShaderResourceGroupCompiledData::MaxUnboundedArrays * RHI::Limits::Device::FrameCountMax); ++unboundedArrayindex)
{
group.m_unboundedDescriptorTables[unboundedArrayindex] = DescriptorTable{};
}
const RHI::ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout();
uint32_t shaderInputIndex = 0;
// process buffer unbounded arrays
for (const RHI::ShaderInputBufferUnboundedArrayDescriptor& shaderInputBufferUnboundedArray : groupLayout.GetShaderInputListForBufferUnboundedArrays())
{
const RHI::ShaderInputBufferUnboundedArrayIndex bufferUnboundedArrayInputIndex(shaderInputIndex);
AZStd::array_view<RHI::ConstPtr<RHI::BufferView>> bufferViews = groupData.GetBufferViewUnboundedArray(bufferUnboundedArrayInputIndex);
uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex;
if (!bufferViews.empty())
{
group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast<uint32_t>(bufferViews.size()), &group);
if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
AZ_Assert(
false,
"Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value"
"of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12.");
return RHI::ResultCode::OutOfMemory;
}
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
const DescriptorTable descriptorTable(
group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast<uint16_t>(bufferViews.size()));
UpdateUnboundedBuffersDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputBufferUnboundedArray.m_access);
}
shaderInputIndex++;
}
// process image unbounded arrays
for (const RHI::ShaderInputImageUnboundedArrayDescriptor& shaderInputImageUnboundedArray :
groupLayout.GetShaderInputListForImageUnboundedArrays())
{
const RHI::ShaderInputImageUnboundedArrayIndex imageUnboundedArrayInputIndex(shaderInputIndex);
AZStd::array_view<RHI::ConstPtr<RHI::ImageView>> imageViews =
groupData.GetImageViewUnboundedArray(imageUnboundedArrayInputIndex);
uint32_t tableIndex = shaderInputIndex * RHI::Limits::Device::FrameCountMax + group.m_compiledDataIndex;
if (!imageViews.empty())
{
group.m_unboundedDescriptorTables[tableIndex] = m_descriptorContext->CreateDescriptorTable(
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, static_cast<uint32_t>(imageViews.size()), &group);
if (!group.m_unboundedDescriptorTables[tableIndex].IsValid())
{
AZ_Assert(
false,
"Descriptor heap ran out of memory. Please consider increasing number of handles allowed for the second value"
"of DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV within platformlimits.azasset file for dx12.");
return RHI::ResultCode::OutOfMemory;
}
ShaderResourceGroupCompiledData& compiledData = group.m_compiledData[group.m_compiledDataIndex];
compiledData.m_gpuUnboundedArraysDescriptorHandles[shaderInputIndex] = m_descriptorContext->GetGpuPlatformHandleForTable(group.m_unboundedDescriptorTables[tableIndex]);
const DescriptorTable descriptorTable(group.m_unboundedDescriptorTables[tableIndex].GetOffset(), static_cast<uint16_t>(imageViews.size()));
UpdateUnboundedImagesDescTable(descriptorTable, groupData, shaderInputIndex, shaderInputImageUnboundedArray.m_access, shaderInputImageUnboundedArray.m_type);
}
shaderInputIndex++;
}
}
return RHI::ResultCode::Success;
}
void ShaderResourceGroupPool::OnFrameEnd()
{
m_constantAllocator.GarbageCollect();
@@ -30,6 +30,9 @@ namespace AZ
static RHI::Ptr<ShaderResourceGroupPool> Create();
//! Re-Update the descriptor tables for all the cbv/srv/uav views (direct and via unbounded array)
RHI::ResultCode UpdateDescriptorTableAfterCompaction(RHI::ShaderResourceGroup& groupBase, const RHI::ShaderResourceGroupData& groupData);
private:
ShaderResourceGroupPool() = default;
@@ -51,6 +54,21 @@ namespace AZ
void UpdateSamplersDescriptorTable(DescriptorTable descriptorTable, const RHI::ShaderResourceGroupData& groupData);
void UpdateUnboundedArrayDescriptorTables(ShaderResourceGroup& group, const RHI::ShaderResourceGroupData& groupData);
//! Update all the buffer views for the unbounded array
void UpdateUnboundedBuffersDescTable(
DescriptorTable descriptorTable,
const RHI::ShaderResourceGroupData& groupData,
uint32_t shaderInputIndex,
RHI::ShaderInputBufferAccess bufferAccess);
//! Update all the image views for the unbounded array
void UpdateUnboundedImagesDescTable(
DescriptorTable descriptorTable,
const RHI::ShaderResourceGroupData& groupData,
uint32_t shaderInputIndex,
RHI::ShaderInputImageAccess imageAccess,
RHI::ShaderInputImageType imageType);
void UpdateDescriptorTableRange(
DescriptorTable descriptorTable,
const AZStd::vector<DescriptorHandle>& descriptors,
@@ -66,6 +84,9 @@ namespace AZ
RHI::ShaderInputSamplerIndex samplerIndex,
AZStd::array_view<RHI::SamplerState> samplerStates);
//Cache all the gpu handles for the Descriptor tables related to all the views
void CacheGpuHandlesForViews(ShaderResourceGroup& group);
DescriptorTable GetBufferTable(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const;
DescriptorTable GetBufferTableUnbounded(DescriptorTable descriptorTable, RHI::ShaderInputBufferIndex bufferIndex) const;
DescriptorTable GetImageTable(DescriptorTable descriptorTable, RHI::ShaderInputImageIndex imageIndex) const;
@@ -79,7 +100,6 @@ namespace AZ
AZStd::vector<DescriptorHandle> GetCBVsFromBufferViews(const AZStd::array_view<RHI::ConstPtr<RHI::BufferView>>& bufferViews);
AZStd::mutex m_constantAllocatorMutex;
MemoryPoolSubAllocator m_constantAllocator;
DescriptorContext* m_descriptorContext = nullptr;
uint32_t m_constantBufferSize = 0;
@@ -21,11 +21,13 @@
"$type": "AZ::DX12::PlatformLimitsDescriptor",
"DescriptorHeapLimits":
{
"DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [1000000, 1000000],
"DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV": [100000, 1000000],
"DESCRIPTOR_HEAP_TYPE_SAMPLER": [2048, 2048],
"DESCRIPTOR_HEAP_TYPE_RTV": [2048, 0],
"DESCRIPTOR_HEAP_TYPE_DSV": [2048, 0]
}
},
"NumShaderVisibleCbvSrvUavStaticHandles": 2000,
"AllowDescriptorHeapCompaction": false
},
"vulkan":
{