using Ghost.Core;
using Ghost.Graphics.D3D12.Utilities;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using static TerraFX.Aliases.D3D12_Alias;
namespace Ghost.Graphics.D3D12;
///
/// D3D12 implementation of viewGroup allocator that manages different types of viewGroup heaps.
///
internal unsafe class D3D12DescriptorAllocator : IDisposable
{
private readonly D3D12DescriptorHeap _rtvHeap;
private readonly D3D12DescriptorHeap _dsvHeap;
private readonly D3D12DescriptorHeap _cbvSrvUavHeap;
private readonly D3D12DescriptorHeap _samplerHeap;
private bool _disposed;
public unsafe D3D12DescriptorAllocator(D3D12RenderDevice device, int initialRtvCount = 256, int initialDsvCount = 256, int initialSrvCount = 200_000, int initialSamplerCount = 256)
{
_rtvHeap = new D3D12DescriptorHeap("rtv", device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, initialRtvCount, initialRtvCount / 2);
_dsvHeap = new D3D12DescriptorHeap("dsv", device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, initialDsvCount, initialDsvCount / 2);
_cbvSrvUavHeap = new D3D12DescriptorHeap("srv", device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, initialSrvCount, initialSrvCount / 2);
_samplerHeap = new D3D12DescriptorHeap("sampler", device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, initialSamplerCount, initialSamplerCount);
}
~D3D12DescriptorAllocator()
{
Dispose();
}
#region RTV Methods
public Identifier AllocateRTV(bool dynamic = false)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var index = dynamic ? _rtvHeap.AllocateDescriptorDynamic() : _rtvHeap.AllocateDescriptor();
if (index == -1)
{
throw new InvalidOperationException("Failed to allocate RTV descriptor");
}
return new Identifier(index);
}
public Identifier[] AllocateRTVs(int count, bool dynamic = false)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var baseIndex = dynamic ? _rtvHeap.AllocateDescriptorsDynamic(count) : _rtvHeap.AllocateDescriptors(count);
if (baseIndex == -1)
{
throw new InvalidOperationException($"Failed to allocate {count} RTV descriptors");
}
var descriptors = new Identifier[count];
for (var i = 0; i < count; i++)
{
var index = baseIndex + i;
descriptors[i] = new Identifier(index);
}
return descriptors;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _rtvHeap.GetCpuHandle(descriptor.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Release(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_rtvHeap.ReleaseDescriptor(descriptor.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Release(ReadOnlySpan> descriptors)
{
ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors)
{
Release(descriptor);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MakePersistent(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_rtvHeap.CopyToPersistentHeap(descriptor.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MakePersistent(ReadOnlySpan> descriptors)
{
ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors)
{
MakePersistent(descriptor);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetRTVDynamicHeap()
{
ObjectDisposedException.ThrowIf(_disposed, this);
_rtvHeap.ResetDynamicHeap();
}
#endregion
#region DSV Methods
public Identifier AllocateDSV(bool dynamic = false)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var index = dynamic ? _dsvHeap.AllocateDescriptorDynamic() : _dsvHeap.AllocateDescriptor();
if (index == -1)
{
throw new InvalidOperationException("Failed to allocate DSV descriptor");
}
return new Identifier(index);
}
public Identifier[] AllocateDSVs(int count, bool dynamic = false)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var baseIndex = dynamic ? _dsvHeap.AllocateDescriptorsDynamic(count) : _dsvHeap.AllocateDescriptors(count);
if (baseIndex == -1)
{
throw new InvalidOperationException($"Failed to allocate {count} DSV descriptors");
}
var descriptors = new Identifier[count];
for (var i = 0; i < count; i++)
{
var index = baseIndex + i;
descriptors[i] = new Identifier(index);
}
return descriptors;
}
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _dsvHeap.GetCpuHandle(descriptor.value);
}
public void Release(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_dsvHeap.ReleaseDescriptor(descriptor.value);
}
public void Release(ReadOnlySpan> descriptors)
{
ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors)
{
Release(descriptor);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MakePersistent(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_dsvHeap.CopyToPersistentHeap(descriptor.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MakePersistent(ReadOnlySpan> descriptors)
{
ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors)
{
MakePersistent(descriptor);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetDSVDynamicHeap()
{
ObjectDisposedException.ThrowIf(_disposed, this);
_dsvHeap.ResetDynamicHeap();
}
#endregion
#region CBV_SRV_UAV Methods
public Identifier AllocateCbvSrvUav(bool dynamic = false)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var index = dynamic ? _cbvSrvUavHeap.AllocateDescriptorDynamic() : _cbvSrvUavHeap.AllocateDescriptor();
if (index == -1)
{
throw new InvalidOperationException("Failed to allocate CBV/SRV/UAV descriptor");
}
_cbvSrvUavHeap.CopyToShaderVisibleHeap(index);
return new Identifier(index);
}
public Identifier[] AllocateSRVs(int count, bool dynamic = false)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var baseIndex = dynamic ? _cbvSrvUavHeap.AllocateDescriptorsDynamic(count) : _cbvSrvUavHeap.AllocateDescriptors(count);
if (baseIndex == -1)
{
throw new InvalidOperationException($"Failed to allocate {count} CBV/SRV/UAV descriptors");
}
var descriptors = new Identifier[count];
for (var i = 0; i < count; i++)
{
var index = baseIndex + i;
descriptors[i] = new Identifier(index);
}
_cbvSrvUavHeap.CopyToShaderVisibleHeap(baseIndex, count);
return descriptors;
}
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _cbvSrvUavHeap.GetCpuHandle(descriptor.value);
}
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandleShaderVisible(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _cbvSrvUavHeap.GetCpuHandleShaderVisible(descriptor.value);
}
public D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _cbvSrvUavHeap.GetGpuHandle(descriptor.value);
}
public void Release(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_cbvSrvUavHeap.ReleaseDescriptor(descriptor.value);
}
public void Release(ReadOnlySpan> descriptors)
{
ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors)
{
Release(descriptor);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MakePersistent(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_cbvSrvUavHeap.CopyToPersistentHeap(descriptor.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MakePersistent(ReadOnlySpan> descriptors)
{
ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors)
{
MakePersistent(descriptor);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetCbvSrvUavDynamicHeap()
{
ObjectDisposedException.ThrowIf(_disposed, this);
_cbvSrvUavHeap.ResetDynamicHeap();
}
#endregion
#region Sampler Methods
public Identifier AllocateSampler()
{
ObjectDisposedException.ThrowIf(_disposed, this);
var index = _samplerHeap.AllocateDescriptor();
if (index == -1)
{
throw new InvalidOperationException("Failed to allocate Sampler descriptor");
}
_samplerHeap.CopyToShaderVisibleHeap(index);
return new Identifier(index);
}
public Identifier[] AllocateSamplers(int count)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var baseIndex = _samplerHeap.AllocateDescriptors(count);
if (baseIndex == -1)
{
throw new InvalidOperationException($"Failed to allocate {count} Sampler descriptors");
}
var descriptors = new Identifier[count];
for (var i = 0; i < count; i++)
{
var index = baseIndex + i;
descriptors[i] = new Identifier(index);
}
_samplerHeap.CopyToShaderVisibleHeap(baseIndex, count);
return descriptors;
}
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _samplerHeap.GetCpuHandle(descriptor.value);
}
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandleShaderVisible(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _samplerHeap.GetCpuHandleShaderVisible(descriptor.value);
}
public D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _samplerHeap.GetGpuHandle(descriptor.value);
}
public void Release(Identifier descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_samplerHeap.ReleaseDescriptor(descriptor.value);
}
public void Release(ReadOnlySpan> descriptors)
{
ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors)
{
Release(descriptor);
}
}
#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Release(ResourceViewGroup descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
Release(descriptor.rtv);
Release(descriptor.dsv);
Release(descriptor.srv);
Release(descriptor.cbv);
Release(descriptor.uav);
Release(descriptor.sampler);
}
#region Utility Methods
///
/// Gets the RTV heap for binding to the command list.
///
public ID3D12DescriptorHeap* GetRTVHeap() => _rtvHeap.Heap;
///
/// Gets the DSV heap for binding to the command list.
///
public ID3D12DescriptorHeap* GetDSVHeap() => _dsvHeap.Heap;
///
/// Gets the CBV/SRV/UAV heap for binding to the command list.
///
public ID3D12DescriptorHeap* GetCbvSrvUavHeap() => _cbvSrvUavHeap.ShaderVisibleHeap;
///
/// Gets the sampler heap for binding to the command list.
///
public ID3D12DescriptorHeap* GetSamplerHeap() => _samplerHeap.ShaderVisibleHeap;
///
/// Gets the shader visible heaps that need to be bound to the command list.
///
/// An array of two ID3D12DescriptorHeap pointers to receive the CBV/SRV/UAV and Sampler heaps.
public void GetShaderVisibleHeaps(ID3D12DescriptorHeap** ppHeap)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (ppHeap == null)
{
throw new ArgumentNullException(nameof(ppHeap));
}
ppHeap[0] = _cbvSrvUavHeap.ShaderVisibleHeap;
ppHeap[1] = _samplerHeap.ShaderVisibleHeap;
}
#endregion
public void Dispose()
{
if (_disposed)
{
return;
}
_rtvHeap.Dispose();
_dsvHeap.Dispose();
_cbvSrvUavHeap.Dispose();
_samplerHeap.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
}