Update rendering architecture and resource management
Added a new `Ref<T>` struct for reference semantics. Added the `RenderGraph` system for managing rendering passes. Added the `RenderTexture` class for encapsulating GPU resources. Added `GraphicsBuffer` class for effective GPU resource management. Changed `CommandList` methods from public to internal for visibility control. Changed `IRenderPass` interface from internal to public for accessibility. Changed `GetData<T>()` in `ComponentObject.cs` to return `CompRef<T>`. Changed `GetComponent<T>()` in `EntityManager.cs` to return `CompRef<T>`. Changed `GetSingleton<T>()` in `World.cs` to use `CompRef<T>`. Changed `IQueryTypeParameter` to use `CompRef<T>` for consistency. Changed `QueryItem<T0>` and related structs to use `CompRef<T>`. Changed `Material` class to support bindless textures. Changed `Shader` class to support bindless rendering. Changed `Mesh` class to support bindless vertex and index buffer access. Updated documentation to reflect the new bindless rendering architecture.
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
using Ghost.Core;
|
||||
using System.Diagnostics;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using DescriptorIndex = System.UInt32;
|
||||
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Specialized descriptor heap allocator for SM 6.6 bindless rendering with ResourceDescriptorHeap[index].
|
||||
/// This allocator maintains a 1:1 relationship between allocation indices and shader indices.
|
||||
/// </summary>
|
||||
internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
|
||||
{
|
||||
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
||||
|
||||
private readonly ConstPtr<ID3D12Device14> _device;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private ComPtr<ID3D12DescriptorHeap> _bindlessHeap;
|
||||
private CpuDescriptorHandle _startCpuHandle;
|
||||
private GpuDescriptorHandle _startGpuHandle;
|
||||
private Queue<uint> _freeDescriptors;
|
||||
private uint _stride;
|
||||
|
||||
public DescriptorHeapType HeapType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public uint NumDescriptors
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public uint NumAllocatedDescriptors
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public uint Stride => _stride;
|
||||
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> BindlessHeap => new(_bindlessHeap.Get());
|
||||
|
||||
public BindlessDescriptorHeapAllocator(ConstPtr<ID3D12Device14> device, uint numDescriptors = 10000)
|
||||
{
|
||||
_device = device;
|
||||
HeapType = DescriptorHeapType.CbvSrvUav;
|
||||
NumDescriptors = numDescriptors;
|
||||
_stride = device.Ptr->GetDescriptorHandleIncrementSize(DescriptorHeapType.CbvSrvUav);
|
||||
_freeDescriptors = new Queue<uint>();
|
||||
|
||||
var success = AllocateResources(numDescriptors);
|
||||
Debug.Assert(success);
|
||||
|
||||
_bindlessHeap.Get()->SetName("bindless");
|
||||
}
|
||||
|
||||
public DescriptorIndex AllocateDescriptor()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_freeDescriptors.Count == 0)
|
||||
{
|
||||
// Try to grow the heap
|
||||
if (!Grow(NumDescriptors * 2))
|
||||
{
|
||||
Debug.WriteLine("ERROR: Failed to grow bindless descriptor heap!");
|
||||
return _INVALID_DESCRIPTOR_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
var index = _freeDescriptors.Dequeue();
|
||||
NumAllocatedDescriptors++;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
public DescriptorIndex AllocateDescriptors(uint count)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_freeDescriptors.Count < count)
|
||||
{
|
||||
// Try to grow the heap
|
||||
var newSize = Math.Max(NumDescriptors * 2, NumDescriptors + count);
|
||||
if (!Grow(newSize))
|
||||
{
|
||||
Debug.WriteLine("ERROR: Failed to grow bindless descriptor heap!");
|
||||
return _INVALID_DESCRIPTOR_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
var baseIndex = _freeDescriptors.Dequeue();
|
||||
for (uint i = 1; i < count; i++)
|
||||
{
|
||||
_freeDescriptors.Dequeue();
|
||||
}
|
||||
|
||||
NumAllocatedDescriptors += count;
|
||||
return baseIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseDescriptor(DescriptorIndex index)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (index >= NumDescriptors)
|
||||
{
|
||||
Debug.WriteLine("Error: Attempted to release an invalid descriptor index");
|
||||
return;
|
||||
}
|
||||
|
||||
_freeDescriptors.Enqueue(index);
|
||||
NumAllocatedDescriptors--;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseDescriptors(DescriptorIndex baseIndex, uint count = 1)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
if (index >= NumDescriptors)
|
||||
{
|
||||
Debug.WriteLine("Error: Attempted to release an invalid descriptor index");
|
||||
continue;
|
||||
}
|
||||
|
||||
_freeDescriptors.Enqueue(index);
|
||||
}
|
||||
|
||||
NumAllocatedDescriptors -= count;
|
||||
}
|
||||
}
|
||||
|
||||
public CpuDescriptorHandle GetCpuHandle(DescriptorIndex index)
|
||||
{
|
||||
var handle = _startCpuHandle;
|
||||
return handle.Offset((int)index, _stride);
|
||||
}
|
||||
|
||||
public GpuDescriptorHandle GetGpuHandle(DescriptorIndex index)
|
||||
{
|
||||
var handle = _startGpuHandle;
|
||||
return handle.Offset((int)index, _stride);
|
||||
}
|
||||
|
||||
public GpuDescriptorHandle GetGpuHandleStart()
|
||||
{
|
||||
return _startGpuHandle;
|
||||
}
|
||||
|
||||
private bool AllocateResources(uint numDescriptors)
|
||||
{
|
||||
NumDescriptors = numDescriptors;
|
||||
_bindlessHeap.Dispose();
|
||||
|
||||
var heapDesc = new DescriptorHeapDescription
|
||||
{
|
||||
Type = HeapType,
|
||||
NumDescriptors = numDescriptors,
|
||||
Flags = DescriptorHeapFlags.ShaderVisible, // Must be shader visible for SM 6.6
|
||||
NodeMask = 0
|
||||
};
|
||||
|
||||
fixed (void* heapPtr = &_bindlessHeap)
|
||||
{
|
||||
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||
if (hr.Failure)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_startCpuHandle = _bindlessHeap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||
_startGpuHandle = _bindlessHeap.Get()->GetGPUDescriptorHandleForHeapStart();
|
||||
|
||||
// Initialize free descriptor queue
|
||||
_freeDescriptors.Clear();
|
||||
for (uint i = 0; i < numDescriptors; i++)
|
||||
{
|
||||
_freeDescriptors.Enqueue(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Grow(uint minRequiredSize)
|
||||
{
|
||||
var oldSize = NumDescriptors;
|
||||
var newSize = Math.Max(minRequiredSize, oldSize * 2);
|
||||
|
||||
var oldHeap = _bindlessHeap;
|
||||
|
||||
if (!AllocateResources(newSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy old descriptors to new heap
|
||||
if (oldHeap.Get() is not null)
|
||||
{
|
||||
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_bindlessHeap.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> Heap => new(_heap.Get());
|
||||
public readonly ConstPtr<ID3D12DescriptorHeap> ShaderVisibleHeap => new(_shaderVisibleHeap.Get());
|
||||
|
||||
public DescriptorHeapAllocator(ConstPtr<ID3D12Device14> device, DescriptorHeapType type, uint numDescriptors)
|
||||
public DescriptorHeapAllocator(string name, ConstPtr<ID3D12Device14> device, DescriptorHeapType type, uint numDescriptors)
|
||||
{
|
||||
_device = device;
|
||||
HeapType = type;
|
||||
@@ -60,6 +60,12 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
|
||||
|
||||
var success = AllocateResources(numDescriptors);
|
||||
Debug.Assert(success);
|
||||
|
||||
_heap.Get()->SetName(name);
|
||||
if (ShaderVisible)
|
||||
{
|
||||
_shaderVisibleHeap.Get()->SetName($"{name} Shader Visible");
|
||||
}
|
||||
}
|
||||
|
||||
public DescriptorIndex AllocateDescriptor() => AllocateDescriptors(1);
|
||||
|
||||
Reference in New Issue
Block a user