Refactor descriptor handling and shader compilation

Refactored descriptor allocation and release logic by introducing `IDescriptorAllocator` and replacing `DescriptorHeapAllocator` with `D3D12DescriptorHeap`. Updated descriptor structs to include validation properties and improved memory management with `ReadOnlySpan`.

Enhanced shader compilation by introducing `ShaderStage` and `CompilerVersion` enums, enabling more flexible and maintainable shader handling.

Refactored `Mesh` to use `IBuffer` for vertex and index buffers, added bindless descriptor support, and improved resource cleanup.

Updated `RenderSystem` and other components for better initialization, error handling, and disposal logic. General improvements to code readability and maintainability.
This commit is contained in:
2025-09-13 20:07:29 +09:00
parent 1dfed83e38
commit 74bb2ccda5
23 changed files with 561 additions and 403 deletions

View File

@@ -0,0 +1,78 @@
using Win32.Graphics.Direct3D12;
namespace Ghost.Graphics.Data;
/// <summary>
/// Render target view (RTV) descriptor.
/// </summary>
public readonly struct RenderTargetDescriptor
{
public uint Index
{
get; init;
}
public static RenderTargetDescriptor Invalid => new() { Index = ~0u };
public bool IsValid => Index != ~0u;
}
/// <summary>
/// Depth stencil view (DSV) descriptor.
/// </summary>
public readonly struct DepthStencilDescriptor
{
public uint Index
{
get; init;
}
public static DepthStencilDescriptor Invalid => new() { Index = ~0u };
public bool IsValid => Index != ~0u;
}
/// <summary>
/// Shader resource view (SRV) descriptor.
/// </summary>
public readonly struct ShaderResourceDescriptor
{
public uint Index
{
get; init;
}
public static ShaderResourceDescriptor Invalid => new() { Index = ~0u };
public bool IsValid => Index != ~0u;
}
/// <summary>
/// Sampler descriptor.
/// </summary>
public readonly struct SamplerDescriptor
{
public uint Index
{
get; init;
}
public static SamplerDescriptor Invalid => new() { Index = ~0u };
public bool IsValid => Index != ~0u;
}
/// <summary>
/// Bindless descriptor
/// </summary>
public readonly struct BindlessDescriptor
{
public uint Index
{
get; init;
}
public static BindlessDescriptor Invalid => new() { Index = ~0u };
public bool IsValid => Index != ~0u;
}

View File

@@ -1,4 +1,5 @@
using Ghost.Graphics.D3D12;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Helpers;
@@ -9,18 +10,15 @@ using Win32.Graphics.Dxgi.Common;
namespace Ghost.Graphics.Data;
public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512) : IDisposable
public unsafe sealed class Mesh : IDisposable
{
private UnsafeList<Vertex> _vertices = new(initialVertexCapacity, Allocator.Persistent);
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent);
private UnsafeList<Vertex> _vertices;
private UnsafeList<int> _indices;
private Bounds _boundingBox;
private GraphicsBuffer? _vertexBuffer;
private GraphicsBuffer? _indexBuffer;
private BindlessDescriptor? _vertexBufferDescriptor;
private BindlessDescriptor? _indexBufferDescriptor;
private IBuffer? _vertexBuffer;
private IBuffer? _indexBuffer;
public Span<Vertex> Vertices => _vertices.AsSpan();
public Span<int> Indices => _indices.AsSpan();
@@ -29,8 +27,59 @@ public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialInde
public uint VertexCount => (uint)_vertices.Count;
public uint IndexCount => (uint)_indices.Count;
public uint VertexBufferDescriptorIndex => _vertexBufferDescriptor?.Index ?? throw new InvalidOperationException("Vertex buffer descriptor is not allocated.");
public uint IndexBufferDescriptorIndex => _indexBufferDescriptor?.Index ?? throw new InvalidOperationException("Index buffer descriptor is not allocated.");
public uint VertexBufferDescriptorIndex
{
get
{
if (_vertexBuffer == null || !_vertexBuffer.Handle.IsValid)
{
throw new InvalidOperationException("Vertex buffer is not created.");
}
var bindlessDesc = _vertexBuffer.Handle.BindlessDescriptor;
if (!bindlessDesc.IsValid)
{
throw new InvalidOperationException("Vertex buffer is not created with bindless.");
}
return bindlessDesc.Index;
}
}
public uint IndexBufferDescriptorIndex
{
get
{
if (_indexBuffer == null || !_indexBuffer.Handle.IsValid)
{
throw new InvalidOperationException("Index buffer is not created.");
}
var bindlessDesc = _indexBuffer.Handle.BindlessDescriptor;
if (!bindlessDesc.IsValid)
{
throw new InvalidOperationException("Index buffer is not created with bindless.");
}
return bindlessDesc.Index;
}
}
public Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512)
{
_vertices = new(initialVertexCapacity, Allocator.Persistent);
_indices = new(initialIndexCapacity, Allocator.Persistent);
}
public Mesh(ReadOnlySpan<Vertex> vertices, ReadOnlySpan<int> indices)
: this(vertices.Length, indices.Length)
{
_vertices = new(vertices.Length, Allocator.Persistent);
_indices = new(indices.Length, Allocator.Persistent);
_vertices.CopyFrom(vertices);
_indices.CopyFrom(indices);
}
~Mesh()
{
@@ -302,6 +351,13 @@ public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialInde
device->CreateShaderResourceView(_indexBuffer.NativeResource.Ptr, &indexSrvDesc, _indexBufferDescriptor.CpuHandle);
}
internal void MarkNoLongerReadable()
{
_vertices.Dispose();
_indices.Dispose();
}
/// <summary>
/// Clears all vertex and index data and releases associated GPU resources.
/// </summar>
@@ -320,14 +376,14 @@ public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialInde
_indexBuffer?.Dispose();
_indexBuffer = null;
if (_vertexBufferDescriptor != null)
if (_vertexBufferDescriptor.IsValid)
{
GraphicsPipeline.DescriptorAllocator.ReleaseBindless(_vertexBufferDescriptor);
RenderSystem.GraphicsEngine.DescriptorAllocator.Release(_vertexBufferDescriptor);
}
if (_indexBufferDescriptor != null)
if (_indexBufferDescriptor.IsValid)
{
GraphicsPipeline.DescriptorAllocator.ReleaseBindless(_indexBufferDescriptor);
RenderSystem.GraphicsEngine.DescriptorAllocator.Release(_indexBufferDescriptor);
}
}

View File

@@ -3,7 +3,7 @@ using Win32.Graphics.D3D12MemoryAllocator;
namespace Ghost.Graphics.Data;
public readonly struct ResourceHandle : IEquatable<ResourceHandle>, IDisposable
public readonly struct ResourceHandle : IEquatable<ResourceHandle>
{
private const int _INVALID_ID = -1;
@@ -20,17 +20,6 @@ public readonly struct ResourceHandle : IEquatable<ResourceHandle>, IDisposable
public bool IsValid => id != _INVALID_ID && generation >= 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Allocation GetAllocation()
{
if (!IsValid)
{
throw new InvalidOperationException("Cannot get allocation from an invalid AllocationHandle.");
}
return GraphicsPipeline.ResourceAllocator.GetResource(this);
}
public bool Equals(ResourceHandle other)
{
return id == other.id && generation == other.generation;
@@ -49,21 +38,6 @@ public readonly struct ResourceHandle : IEquatable<ResourceHandle>, IDisposable
return obj is ResourceHandle handle && Equals(handle);
}
public void Dispose()
{
GraphicsPipeline.ResourceAllocator.ReleaseResource(this);
}
public static implicit operator Allocation(ResourceHandle handle)
{
if (!handle.IsValid)
{
throw new InvalidOperationException("Cannot convert an invalid AllocationHandle to Allocation.");
}
return handle.GetAllocation();
}
public static bool operator ==(ResourceHandle left, ResourceHandle right)
{
return left.Equals(right);
@@ -75,7 +49,7 @@ public readonly struct ResourceHandle : IEquatable<ResourceHandle>, IDisposable
}
}
public readonly struct TextureHandle : IEquatable<TextureHandle>, IDisposable
public readonly struct TextureHandle : IEquatable<TextureHandle>
{
private readonly ResourceHandle _resourceHandle;
@@ -104,11 +78,6 @@ public readonly struct TextureHandle : IEquatable<TextureHandle>, IDisposable
return _resourceHandle.GetHashCode();
}
public void Dispose()
{
_resourceHandle.Dispose();
}
public static bool operator ==(TextureHandle left, TextureHandle right)
{
return left.Equals(right);
@@ -120,16 +89,26 @@ public readonly struct TextureHandle : IEquatable<TextureHandle>, IDisposable
}
}
public readonly struct BufferHandle : IEquatable<BufferHandle>, IDisposable
public readonly struct BufferHandle : IEquatable<BufferHandle>
{
private readonly ResourceHandle _resourceHandle;
private readonly BindlessDescriptor _bindlessDescriptor;
public static BufferHandle Invalid => new(ResourceHandle.Invalid);
public ResourceHandle ResourceHandle => _resourceHandle;
public static BufferHandle Invalid => new(ResourceHandle.Invalid);
public BindlessDescriptor BindlessDescriptor => _bindlessDescriptor;
internal BufferHandle(ResourceHandle resourceHandle)
{
_resourceHandle = resourceHandle;
_bindlessDescriptor = BindlessDescriptor.Invalid;
}
internal BufferHandle(ResourceHandle resourceHandle, BindlessDescriptor descriptor)
{
_resourceHandle = resourceHandle;
_bindlessDescriptor = descriptor;
}
public bool IsValid => _resourceHandle.IsValid;
@@ -149,11 +128,6 @@ public readonly struct BufferHandle : IEquatable<BufferHandle>, IDisposable
return _resourceHandle.GetHashCode();
}
public void Dispose()
{
_resourceHandle.Dispose();
}
public static bool operator ==(BufferHandle left, BufferHandle right)
{
return left.Equals(right);

View File

@@ -60,10 +60,10 @@ internal readonly struct CBufferInfo
}
/// <summary>
/// Bindless shader implementation using SM 6.6 with ResourceDescriptorHeap
/// and D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED
/// Enhanced to support both bindless and regular texture binding for hybrid materials
/// A representation of a GPU shader, including its metadata about its resources.
/// </summary>
// TODO: Multi pass and keyword support
public unsafe class Shader : IDisposable
{
private readonly string _source;
@@ -82,6 +82,7 @@ public unsafe class Shader : IDisposable
internal List<TextureInfo> RegularTextures => _regularTextures;
internal Dictionary<string, int> PropertyNameToIdMap => _propertyNameToIdMap;
// TODO: In real production, we should not load the shader source code directly.
internal Shader(string shaderCode)
{
_source = shaderCode;

View File

@@ -142,6 +142,6 @@ public abstract unsafe class Texture : GraphicsResource
public override void Dispose()
{
base.Dispose();
GraphicsPipeline.DescriptorAllocator.ReleaseBindless(_bindlessDescriptor);
GraphicsPipeline.DescriptorAllocator.Release(_bindlessDescriptor);
}
}

View File

@@ -1,4 +1,3 @@
using Ghost.Graphics.D3D12;
using Misaki.HighPerformance.Image;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;