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

1
.gitignore vendored
View File

@@ -35,6 +35,7 @@ bld/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/
.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot # Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/ #wwwroot/

21
.vscode/tasks.json vendored
View File

@@ -1,21 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build Ghost.Graphics with RenderGraph",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"Ghost.Graphics/Ghost.Graphics.csproj",
"-c",
"Debug"
],
"group": "build",
"problemMatcher": [
"$msCompile"
],
"isBackground": false
}
]
}

View File

@@ -117,6 +117,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
throw new NotImplementedException(); throw new NotImplementedException();
} }
// TODO: Batch draw calls by material to minimize state changes
public void DrawMesh(Mesh mesh, Material material) public void DrawMesh(Mesh mesh, Material material)
{ {
// Bind the bindless material (sets up root signature, pipeline state, and descriptor heaps) // Bind the bindless material (sets up root signature, pipeline state, and descriptor heaps)

View File

@@ -1,5 +1,8 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.Data;
using Ghost.Graphics.RHI;
using System.Runtime.CompilerServices;
using Win32.Graphics.Direct3D12; using Win32.Graphics.Direct3D12;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
@@ -7,13 +10,13 @@ namespace Ghost.Graphics.D3D12;
/// <summary> /// <summary>
/// D3D12 implementation of descriptor allocator that manages different types of descriptor heaps. /// D3D12 implementation of descriptor allocator that manages different types of descriptor heaps.
/// </summary> /// </summary>
internal unsafe class D3D12DescriptorAllocator : IDisposable internal unsafe class D3D12DescriptorAllocator : IDescriptorAllocator, IDisposable
{ {
private readonly DescriptorHeapAllocator _rtvHeap; private readonly D3D12DescriptorHeap _rtvHeap;
private readonly DescriptorHeapAllocator _dsvHeap; private readonly D3D12DescriptorHeap _dsvHeap;
private readonly DescriptorHeapAllocator _srvHeap; private readonly D3D12DescriptorHeap _srvHeap;
private readonly DescriptorHeapAllocator _samplerHeap; private readonly D3D12DescriptorHeap _samplerHeap;
private readonly BindlessDescriptorHeapAllocator _bindlessHeap; private readonly BindlessDescriptorHeap _bindlessHeap;
private bool _disposed; private bool _disposed;
@@ -21,11 +24,11 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
{ {
var pDevice = device.NativeDevice; var pDevice = device.NativeDevice;
_rtvHeap = new DescriptorHeapAllocator("rtv", pDevice, DescriptorHeapType.Rtv, initialRtvCount); _rtvHeap = new D3D12DescriptorHeap("rtv", pDevice, DescriptorHeapType.Rtv, initialRtvCount);
_dsvHeap = new DescriptorHeapAllocator("dsv", pDevice, DescriptorHeapType.Dsv, initialDsvCount); _dsvHeap = new D3D12DescriptorHeap("dsv", pDevice, DescriptorHeapType.Dsv, initialDsvCount);
_srvHeap = new DescriptorHeapAllocator("srv", pDevice, DescriptorHeapType.CbvSrvUav, initialSrvCount); _srvHeap = new D3D12DescriptorHeap("srv", pDevice, DescriptorHeapType.CbvSrvUav, initialSrvCount);
_samplerHeap = new DescriptorHeapAllocator("sampler", pDevice, DescriptorHeapType.Sampler, initialSamplerCount); _samplerHeap = new D3D12DescriptorHeap("sampler", pDevice, DescriptorHeapType.Sampler, initialSamplerCount);
_bindlessHeap = new BindlessDescriptorHeapAllocator(pDevice, initialBindlessCount); _bindlessHeap = new BindlessDescriptorHeap(pDevice, initialBindlessCount);
} }
~D3D12DescriptorAllocator() ~D3D12DescriptorAllocator()
@@ -45,8 +48,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
throw new InvalidOperationException("Failed to allocate RTV descriptor"); throw new InvalidOperationException("Failed to allocate RTV descriptor");
} }
var cpuHandle = _rtvHeap.GetCpuHandle(index); return new RenderTargetDescriptor { Index = index };
return new RenderTargetDescriptor(index, cpuHandle);
} }
public RenderTargetDescriptor[] AllocateRTVs(uint count) public RenderTargetDescriptor[] AllocateRTVs(uint count)
@@ -63,30 +65,33 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
for (uint i = 0; i < count; i++) for (uint i = 0; i < count; i++)
{ {
var index = baseIndex + i; var index = baseIndex + i;
var cpuHandle = _rtvHeap.GetCpuHandle(index); descriptors[i] = new RenderTargetDescriptor { Index = index };
descriptors[i] = new RenderTargetDescriptor(index, cpuHandle);
} }
return descriptors; return descriptors;
} }
public void ReleaseRTV(RenderTargetDescriptor descriptor) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public CpuDescriptorHandle GetCpuHandle(RenderTargetDescriptor descriptor)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return _rtvHeap.GetCpuHandle(descriptor.Index);
if (descriptor is RenderTargetDescriptor d3d12Descriptor)
{
_rtvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
}
} }
public void ReleaseRTVs(RenderTargetDescriptor[] descriptors) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Release(RenderTargetDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_rtvHeap.ReleaseDescriptor(descriptor.Index);
}
public void Release(ReadOnlySpan<RenderTargetDescriptor> descriptors)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors) foreach (var descriptor in descriptors)
{ {
ReleaseRTV(descriptor); Release(descriptor);
} }
} }
@@ -104,8 +109,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
throw new InvalidOperationException("Failed to allocate DSV descriptor"); throw new InvalidOperationException("Failed to allocate DSV descriptor");
} }
var cpuHandle = _dsvHeap.GetCpuHandle(index); return new DepthStencilDescriptor { Index = index };
return new DepthStencilDescriptor(index, cpuHandle);
} }
public DepthStencilDescriptor[] AllocateDSVs(uint count) public DepthStencilDescriptor[] AllocateDSVs(uint count)
@@ -122,30 +126,31 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
for (uint i = 0; i < count; i++) for (uint i = 0; i < count; i++)
{ {
var index = baseIndex + i; var index = baseIndex + i;
var cpuHandle = _dsvHeap.GetCpuHandle(index); descriptors[i] = new DepthStencilDescriptor { Index = index };
descriptors[i] = new DepthStencilDescriptor(index, cpuHandle);
} }
return descriptors; return descriptors;
} }
public void ReleaseDSV(DepthStencilDescriptor descriptor) public CpuDescriptorHandle GetCpuHandle(DepthStencilDescriptor descriptor)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return _dsvHeap.GetCpuHandle(descriptor.Index);
if (descriptor is DepthStencilDescriptor d3d12Descriptor)
{
_dsvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
}
} }
public void ReleaseDSVs(DepthStencilDescriptor[] descriptors) public void Release(DepthStencilDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_dsvHeap.ReleaseDescriptor(descriptor.Index);
}
public void Release(ReadOnlySpan<DepthStencilDescriptor> descriptors)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors) foreach (var descriptor in descriptors)
{ {
ReleaseDSV(descriptor); Release(descriptor);
} }
} }
@@ -163,13 +168,8 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
throw new InvalidOperationException("Failed to allocate SRV descriptor"); throw new InvalidOperationException("Failed to allocate SRV descriptor");
} }
var cpuHandle = _srvHeap.GetCpuHandle(index);
var gpuHandle = _srvHeap.GetGpuHandle(index);
// Copy to shader visible heap
_srvHeap.CopyToShaderVisibleHeap(index); _srvHeap.CopyToShaderVisibleHeap(index);
return new ShaderResourceDescriptor { Index = index };
return new ShaderResourceDescriptor(index, cpuHandle, gpuHandle);
} }
public ShaderResourceDescriptor[] AllocateSRVs(uint count) public ShaderResourceDescriptor[] AllocateSRVs(uint count)
@@ -186,34 +186,38 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
for (uint i = 0; i < count; i++) for (uint i = 0; i < count; i++)
{ {
var index = baseIndex + i; var index = baseIndex + i;
var cpuHandle = _srvHeap.GetCpuHandle(index); descriptors[i] = new ShaderResourceDescriptor { Index = index };
var gpuHandle = _srvHeap.GetGpuHandle(index);
descriptors[i] = new ShaderResourceDescriptor(index, cpuHandle, gpuHandle);
} }
// Copy all descriptors to shader visible heap
_srvHeap.CopyToShaderVisibleHeap(baseIndex, count); _srvHeap.CopyToShaderVisibleHeap(baseIndex, count);
return descriptors; return descriptors;
} }
public void ReleaseSRV(ShaderResourceDescriptor descriptor) public CpuDescriptorHandle GetCpuHandle(ShaderResourceDescriptor descriptor)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return _srvHeap.GetCpuHandle(descriptor.Index);
if (descriptor is ShaderResourceDescriptor d3d12Descriptor)
{
_srvHeap.ReleaseDescriptor(d3d12Descriptor.Index);
}
} }
public void ReleaseSRVs(ShaderResourceDescriptor[] descriptors) public GpuDescriptorHandle GetGpuHandle(ShaderResourceDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _srvHeap.GetGpuHandle(descriptor.Index);
}
public void Release(ShaderResourceDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_srvHeap.ReleaseDescriptor(descriptor.Index);
}
public void Release(ReadOnlySpan<ShaderResourceDescriptor> descriptors)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors) foreach (var descriptor in descriptors)
{ {
ReleaseSRV(descriptor); Release(descriptor);
} }
} }
@@ -231,13 +235,8 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
throw new InvalidOperationException("Failed to allocate Sampler descriptor"); throw new InvalidOperationException("Failed to allocate Sampler descriptor");
} }
var cpuHandle = _samplerHeap.GetCpuHandle(index);
var gpuHandle = _samplerHeap.GetGpuHandle(index);
// Copy to shader visible heap
_samplerHeap.CopyToShaderVisibleHeap(index); _samplerHeap.CopyToShaderVisibleHeap(index);
return new SamplerDescriptor { Index = index };
return new SamplerDescriptor(index, cpuHandle, gpuHandle);
} }
public SamplerDescriptor[] AllocateSamplers(uint count) public SamplerDescriptor[] AllocateSamplers(uint count)
@@ -254,34 +253,38 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
for (uint i = 0; i < count; i++) for (uint i = 0; i < count; i++)
{ {
var index = baseIndex + i; var index = baseIndex + i;
var cpuHandle = _samplerHeap.GetCpuHandle(index); descriptors[i] = new SamplerDescriptor { Index = index };
var gpuHandle = _samplerHeap.GetGpuHandle(index);
descriptors[i] = new SamplerDescriptor(index, cpuHandle, gpuHandle);
} }
// Copy all descriptors to shader visible heap
_samplerHeap.CopyToShaderVisibleHeap(baseIndex, count); _samplerHeap.CopyToShaderVisibleHeap(baseIndex, count);
return descriptors; return descriptors;
} }
public void ReleaseSampler(SamplerDescriptor descriptor) public CpuDescriptorHandle GetCpuHandle(SamplerDescriptor descriptor)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
return _samplerHeap.GetCpuHandle(descriptor.Index);
if (descriptor is SamplerDescriptor d3d12Descriptor)
{
_samplerHeap.ReleaseDescriptor(d3d12Descriptor.Index);
}
} }
public void ReleaseSamplers(SamplerDescriptor[] descriptors) public GpuDescriptorHandle GetGpuHandle(SamplerDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _samplerHeap.GetGpuHandle(descriptor.Index);
}
public void Release(SamplerDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_samplerHeap.ReleaseDescriptor(descriptor.Index);
}
public void Release(ReadOnlySpan<SamplerDescriptor> descriptors)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors) foreach (var descriptor in descriptors)
{ {
ReleaseSampler(descriptor); Release(descriptor);
} }
} }
@@ -303,10 +306,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
throw new InvalidOperationException("Failed to allocate bindless descriptor"); throw new InvalidOperationException("Failed to allocate bindless descriptor");
} }
var cpuHandle = _bindlessHeap.GetCpuHandle(index); return new BindlessDescriptor { Index = index };
var gpuHandle = _bindlessHeap.GetGpuHandle(index);
return new BindlessDescriptor(index, cpuHandle, gpuHandle);
} }
/// <summary> /// <summary>
@@ -326,37 +326,43 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
for (uint i = 0; i < count; i++) for (uint i = 0; i < count; i++)
{ {
var index = baseIndex + i; var index = baseIndex + i;
var cpuHandle = _bindlessHeap.GetCpuHandle(index); descriptors[i] = new BindlessDescriptor { Index = index };
var gpuHandle = _bindlessHeap.GetGpuHandle(index);
descriptors[i] = new BindlessDescriptor(index, cpuHandle, gpuHandle);
} }
return descriptors; return descriptors;
} }
public CpuDescriptorHandle GetCpuHandle(BindlessDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _bindlessHeap.GetCpuHandle(descriptor.Index);
}
public GpuDescriptorHandle GetGpuHandle(BindlessDescriptor descriptor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _bindlessHeap.GetGpuHandle(descriptor.Index);
}
/// <summary> /// <summary>
/// Releases a bindless descriptor. /// Releases a bindless descriptor.
/// </summary> /// </summary>
public void ReleaseBindless(BindlessDescriptor descriptor) public void Release(BindlessDescriptor descriptor)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
_bindlessHeap.ReleaseDescriptor(descriptor.Index);
if (descriptor is BindlessDescriptor d3d12Descriptor)
{
_bindlessHeap.ReleaseDescriptor(d3d12Descriptor.Index);
}
} }
/// <summary> /// <summary>
/// Releases multiple bindless descriptors. /// Releases multiple bindless descriptors.
/// </summary> /// </summary>
public void ReleaseBindless(BindlessDescriptor[] descriptors) public void Release(ReadOnlySpan<BindlessDescriptor> descriptors)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
foreach (var descriptor in descriptors) foreach (var descriptor in descriptors)
{ {
ReleaseBindless(descriptor); Release(descriptor);
} }
} }

View File

@@ -9,16 +9,16 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
#endif #endif
private readonly D3D12RenderDevice _device; private readonly D3D12RenderDevice _device;
private readonly D3D12PipelineStateController _stateController; private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12ResourceAllocator _resourceAllocator; private readonly D3D12ResourceAllocator _resourceAllocator;
private readonly D3D12PipelineStateController _pipelineState; private readonly D3D12PipelineStateController _stateController;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
public IRenderDevice Device => _device; public IRenderDevice Device => _device;
public IPipelineStateController PipelineStateController => _stateController;
public IResourceAllocator ResourceAllocator => _resourceAllocator; public IResourceAllocator ResourceAllocator => _resourceAllocator;
public IPipelineStateController PipelineStateController => _stateController;
public D3D12GraphicsEngine(RenderSystem renderSystem) public D3D12GraphicsEngine(RenderSystem renderSystem)
{ {
#if DEBUG #if DEBUG
@@ -26,11 +26,10 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
#endif #endif
_device = new(); _device = new();
_stateController = new(_device);
_resourceAllocator = new(_device, renderSystem);
_pipelineState = new(_device);
_descriptorAllocator = new(_device); _descriptorAllocator = new(_device);
_resourceAllocator = new(renderSystem, _device, _descriptorAllocator);
_stateController = new(_device);
} }
public IRenderer CreateRenderer() public IRenderer CreateRenderer()
@@ -50,6 +49,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
public void Dispose() public void Dispose()
{ {
_stateController.Dispose();
_descriptorAllocator.Dispose(); _descriptorAllocator.Dispose();
_resourceAllocator.Dispose(); _resourceAllocator.Dispose();
_device.Dispose(); _device.Dispose();

View File

@@ -25,10 +25,6 @@ internal class D3D12ShaderPipeline : IShaderPipeline
internal unsafe class D3D12PipelineStateController : IPipelineStateController, IDisposable internal unsafe class D3D12PipelineStateController : IPipelineStateController, IDisposable
{ {
private const string _VS_ENTRY_POINT = "VSMain";
private const string _PS_ENTRY_POINT = "PSMain";
private const string _PROFILE_VS_6_6 = "vs_6_6";
private readonly ID3D12Device14* _device; private readonly ID3D12Device14* _device;
private readonly Dictionary<Shader, D3D12ShaderPipeline> _shaderPipelines; private readonly Dictionary<Shader, D3D12ShaderPipeline> _shaderPipelines;
@@ -55,8 +51,8 @@ internal unsafe class D3D12PipelineStateController : IPipelineStateController, I
{ {
foreach (var kvp in _shaderPipelines) foreach (var kvp in _shaderPipelines)
{ {
var vsResult = D3D12ShaderCompiler.CompileDXC(kvp.Key, _VS_ENTRY_POINT, _PROFILE_VS_6_6); var vsResult = D3D12ShaderCompiler.Compile(kvp.Key, D3D12ShaderCompiler.ShaderStage.VertexShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
var psResult = D3D12ShaderCompiler.CompileDXC(kvp.Key, _PS_ENTRY_POINT, _PROFILE_VS_6_6); var psResult = D3D12ShaderCompiler.Compile(kvp.Key, D3D12ShaderCompiler.ShaderStage.PixelShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
kvp.Value.vsResult = vsResult; kvp.Value.vsResult = vsResult;
kvp.Value.psResult = psResult; kvp.Value.psResult = psResult;

View File

@@ -7,7 +7,6 @@ using Win32.Graphics.Direct3D12;
using Win32.Graphics.Dxgi; using Win32.Graphics.Dxgi;
using Win32.Graphics.Dxgi.Common; using Win32.Graphics.Dxgi.Common;
using static Win32.Graphics.D3D12MemoryAllocator.Apis; using static Win32.Graphics.D3D12MemoryAllocator.Apis;
using ResourceHandle = Ghost.Graphics.Data.ResourceHandle;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
@@ -43,8 +42,12 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
private const uint _MAX_TEXTURE2D_DIMENSION = 16384u; private const uint _MAX_TEXTURE2D_DIMENSION = 16384u;
private const uint _MAX_TEXTURE3D_DIMENSION = 2048u; private const uint _MAX_TEXTURE3D_DIMENSION = 2048u;
private readonly RenderSystem _renderSystem; private readonly ID3D12Device14* _device;
private readonly Allocator _allocator; private readonly Allocator _allocator;
private readonly RenderSystem _renderSystem;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private UnsafeList<AllocationInfo> _allocations = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); private UnsafeList<AllocationInfo> _allocations = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
private UnsafeQueue<int> _freeSlots = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); private UnsafeQueue<int> _freeSlots = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
private UnsafeQueue<ResourceHandle> _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); private UnsafeQueue<ResourceHandle> _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
@@ -60,10 +63,8 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
} }
} }
public D3D12ResourceAllocator(D3D12RenderDevice device, RenderSystem renderSystem) public D3D12ResourceAllocator(RenderSystem renderSystem, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator)
{ {
_renderSystem = renderSystem;
var desc = new AllocatorDesc var desc = new AllocatorDesc
{ {
pAdapter = (IDXGIAdapter*)device.Adapter, pAdapter = (IDXGIAdapter*)device.Adapter,
@@ -72,6 +73,10 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
}; };
CreateAllocator(in desc, out _allocator); CreateAllocator(in desc, out _allocator);
_device = device.NativeDevice;
_renderSystem = renderSystem;
_descriptorAllocator = descriptorAllocator;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -173,13 +178,55 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator<ID3D12Resource
Allocation allocation = default; Allocation allocation = default;
ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDescription, initialState, null, &allocation, IID_NULL, null)); ThrowIfFailed(_allocator.CreateResource(&allocationDesc, in resourceDescription, initialState, null, &allocation, IID_NULL, null));
return new(TrackResource(in allocation, tempResource)); var handle = TrackResource(in allocation, tempResource);
if (desc.Usage.HasFlag(BufferUsage.ShaderResource) && desc.CreationFlags.HasFlag(BufferCreationFlags.Bindless))
{
var isRaw = desc.Usage.HasFlag(BufferUsage.Raw);
var descriptorHandle = _descriptorAllocator.AllocateBindless();
var srvDesc = new ShaderResourceViewDescription
{
ViewDimension = SrvDimension.Buffer,
Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
};
if (isRaw)
{
srvDesc.Format = Format.R32Typeless;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Buffer.NumElements = (uint)(desc.Size / 4);
srvDesc.Buffer.StructureByteStride = 0;
srvDesc.Buffer.Flags = BufferSrvFlags.Raw;
}
else
{
srvDesc.Format = Format.Unknown;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Buffer.NumElements = (uint)(desc.Size / desc.Stride);
srvDesc.Buffer.StructureByteStride = desc.Stride;
srvDesc.Buffer.Flags = BufferSrvFlags.None;
}
_device->CreateShaderResourceView(allocation.Resource, &srvDesc, _descriptorAllocator.GetCpuHandle(descriptorHandle));
return new(handle, descriptorHandle);
}
return new(handle);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferHandle CreateUploadBuffer(uint sizeInBytes, bool tempResource = false) public BufferHandle CreateUploadBuffer(uint sizeInBytes, bool tempResource = false)
{ {
var desc = new BufferDesc(sizeInBytes, BufferUsage.Upload, MemoryType.Upload); var desc = new BufferDesc
{
Size = sizeInBytes,
Usage = BufferUsage.Upload,
MemoryType = MemoryType.Upload
};
return CreateBufferHandle(in desc, tempResource); return CreateBufferHandle(in desc, tempResource);
} }

View File

@@ -11,6 +11,20 @@ namespace Ghost.Graphics.D3D12;
internal unsafe static class D3D12ShaderCompiler internal unsafe static class D3D12ShaderCompiler
{ {
public enum CompilerVersion
{
SM_6_6,
SM_7_0
}
public enum ShaderStage
{
VertexShader,
PixelShader,
MeshShader,
ComputeShader
}
public struct CompileResult : IDisposable public struct CompileResult : IDisposable
{ {
public UnsafeArray<byte> bytecode; public UnsafeArray<byte> bytecode;
@@ -23,7 +37,35 @@ internal unsafe static class D3D12ShaderCompiler
} }
} }
public static CompileResult CompileDXC(Shader shader, string entryPoint, string profile) private static string GetProfileString(ShaderStage stage, CompilerVersion version)
{
return (stage, version) switch
{
(ShaderStage.VertexShader, CompilerVersion.SM_6_6) => "vs_6_6",
(ShaderStage.PixelShader, CompilerVersion.SM_6_6) => "ps_6_6",
(ShaderStage.MeshShader, CompilerVersion.SM_6_6) => "ms_6_6",
(ShaderStage.ComputeShader, CompilerVersion.SM_6_6) => "cs_6_6",
(ShaderStage.VertexShader, CompilerVersion.SM_7_0) => "vs_7_0",
(ShaderStage.PixelShader, CompilerVersion.SM_7_0) => "ps_7_0",
(ShaderStage.MeshShader, CompilerVersion.SM_7_0) => "ms_7_0",
(ShaderStage.ComputeShader, CompilerVersion.SM_7_0) => "cs_7_0",
_ => throw new ArgumentOutOfRangeException(nameof(stage), "Unsupported shader stage or compiler version")
};
}
private static string GetEntryPoint(ShaderStage stage)
{
return stage switch
{
ShaderStage.VertexShader => "VSMain",
ShaderStage.PixelShader => "PSMain",
ShaderStage.MeshShader => "MSMain",
ShaderStage.ComputeShader => "CSMain",
_ => throw new ArgumentOutOfRangeException(nameof(stage), "Unsupported shader stage")
};
}
public static CompileResult Compile(Shader shader, ShaderStage stage, CompilerVersion version)
{ {
using ComPtr<IDxcCompiler3> compiler = default; using ComPtr<IDxcCompiler3> compiler = default;
using ComPtr<IDxcUtils> utils = default; using ComPtr<IDxcUtils> utils = default;
@@ -43,12 +85,12 @@ internal unsafe static class D3D12ShaderCompiler
// Prepare compilation arguments - NOTE: NO -Qstrip_reflect to keep reflection data // Prepare compilation arguments - NOTE: NO -Qstrip_reflect to keep reflection data
var argsArray = new string[] var argsArray = new string[]
{ {
"-T", profile, // Target profile (vs_6_6, ps_6_6) "-T", GetProfileString(stage, version), // Target profile (vs_6_6, ps_6_6)
"-E", entryPoint, // Entry point "-E", GetEntryPoint(stage), // Entry point
"-HV", "2021", // HLSL version 2021 (required for SM 6.6) "-HV", "2021", // HLSL version 2021 (required for SM 6.6)
"-enable-16bit-types", // Enable 16-bit types "-enable-16bit-types", // Enable 16-bit types
"-O3", // Optimization level "-O3", // Optimization level
"-Qstrip_debug" // Strip debug info but KEEP reflection "-Qstrip_debug" // Strip debug info but KEEP reflection
}; };
// Convert to wide strings (DXC expects LPCWSTR) // Convert to wide strings (DXC expects LPCWSTR)

View File

@@ -1,132 +0,0 @@
using Win32.Graphics.Direct3D12;
namespace Ghost.Graphics.D3D12;
/// <summary>
/// Implementation of render target view (RTV) descriptor.
/// </summary>
public readonly struct RenderTargetDescriptor
{
public uint Index
{
get;
}
public CpuDescriptorHandle CpuHandle
{
get;
}
public RenderTargetDescriptor(uint index, CpuDescriptorHandle cpuHandle)
{
Index = index;
CpuHandle = cpuHandle;
}
}
/// <summary>
/// Implementation of depth stencil view (DSV) descriptor.
/// </summary>
public readonly struct DepthStencilDescriptor
{
public uint Index
{
get;
}
public CpuDescriptorHandle CpuHandle
{
get;
}
public DepthStencilDescriptor(uint index, CpuDescriptorHandle cpuHandle)
{
Index = index;
CpuHandle = cpuHandle;
}
}
/// <summary>
/// Implementation of shader resource view (SRV) descriptor.
/// </summary>
public sealed class ShaderResourceDescriptor
{
public uint Index
{
get;
}
public CpuDescriptorHandle CpuHandle
{
get;
}
public GpuDescriptorHandle GpuHandle
{
get;
}
public ShaderResourceDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
{
Index = index;
CpuHandle = cpuHandle;
GpuHandle = gpuHandle;
}
}
/// <summary>
/// Implementation of sampler descriptor.
/// </summary>
public sealed class SamplerDescriptor
{
public uint Index
{
get;
}
public CpuDescriptorHandle CpuHandle
{
get;
}
public GpuDescriptorHandle GpuHandle
{
get;
}
public SamplerDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
{
Index = index;
CpuHandle = cpuHandle;
GpuHandle = gpuHandle;
}
}
/// <summary>
/// Implementation of bindless descriptor for SM 6.6 rendering.
/// This descriptor maintains a 1:1 relationship between allocation indices and shader indices.
/// </summary>
public sealed class BindlessDescriptor
{
public uint Index
{
get;
}
public CpuDescriptorHandle CpuHandle
{
get;
}
public GpuDescriptorHandle GpuHandle
{
get;
}
public BindlessDescriptor(uint index, CpuDescriptorHandle cpuHandle, GpuDescriptorHandle gpuHandle)
{
Index = index;
CpuHandle = cpuHandle;
GpuHandle = gpuHandle;
}
}

View File

@@ -64,7 +64,7 @@ internal unsafe class Renderer
commandAllocator.Dispose(); commandAllocator.Dispose();
commandList.Dispose(); commandList.Dispose();
backBuffer.Dispose(); backBuffer.Dispose();
GraphicsPipeline.DescriptorAllocator.ReleaseRTV(rtvDescriptor); GraphicsPipeline.DescriptorAllocator.Release(rtvDescriptor);
} }
} }
@@ -226,7 +226,7 @@ internal unsafe class Renderer
if (frameResource.backBuffer.Get() is not null) if (frameResource.backBuffer.Get() is not null)
{ {
var c = frameResource.backBuffer.Reset(); var c = frameResource.backBuffer.Reset();
GraphicsPipeline.DescriptorAllocator.ReleaseRTV(frameResource.rtvDescriptor); GraphicsPipeline.DescriptorAllocator.Release(frameResource.rtvDescriptor);
} }
frameResource.fenceValue = _frameResources[_backBufferIndex].fenceValue; frameResource.fenceValue = _frameResources[_backBufferIndex].fenceValue;

View File

@@ -10,7 +10,7 @@ namespace Ghost.Graphics.D3D12.Utilities;
/// Specialized descriptor heap allocator for SM 6.6 bindless rendering with ResourceDescriptorHeap[index]. /// 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. /// This allocator maintains a 1:1 relationship between allocation indices and shader indices.
/// </summary> /// </summary>
internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable internal unsafe struct BindlessDescriptorHeap : IDisposable
{ {
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u; private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
@@ -42,7 +42,7 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
public readonly ConstPtr<ID3D12DescriptorHeap> BindlessHeap => new(_bindlessHeap.Get()); public readonly ConstPtr<ID3D12DescriptorHeap> BindlessHeap => new(_bindlessHeap.Get());
public BindlessDescriptorHeapAllocator(ComPtr<ID3D12Device14> device, uint numDescriptors = 10000) public BindlessDescriptorHeap(ComPtr<ID3D12Device14> device, uint numDescriptors = 10000)
{ {
_device = device; _device = device;
device.Get()->AddRef(); device.Get()->AddRef();
@@ -67,7 +67,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
// Try to grow the heap // Try to grow the heap
if (!Grow(NumDescriptors * 2)) if (!Grow(NumDescriptors * 2))
{ {
Debug.WriteLine("ERROR: Failed to grow bindless descriptor heap!");
return _INVALID_DESCRIPTOR_INDEX; return _INVALID_DESCRIPTOR_INDEX;
} }
} }
@@ -88,7 +87,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
var newSize = Math.Max(NumDescriptors * 2, NumDescriptors + count); var newSize = Math.Max(NumDescriptors * 2, NumDescriptors + count);
if (!Grow(newSize)) if (!Grow(newSize))
{ {
Debug.WriteLine("ERROR: Failed to grow bindless descriptor heap!");
return _INVALID_DESCRIPTOR_INDEX; return _INVALID_DESCRIPTOR_INDEX;
} }
} }
@@ -110,7 +108,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
{ {
if (index >= NumDescriptors) if (index >= NumDescriptors)
{ {
Debug.WriteLine("Error: Attempted to release an invalid descriptor index");
return; return;
} }
@@ -128,7 +125,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
var index = baseIndex + i; var index = baseIndex + i;
if (index >= NumDescriptors) if (index >= NumDescriptors)
{ {
Debug.WriteLine("Error: Attempted to release an invalid descriptor index");
continue; continue;
} }
@@ -139,19 +135,19 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable
} }
} }
public CpuDescriptorHandle GetCpuHandle(DescriptorIndex index) public readonly CpuDescriptorHandle GetCpuHandle(DescriptorIndex index)
{ {
var handle = _startCpuHandle; var handle = _startCpuHandle;
return handle.Offset((int)index, _stride); return handle.Offset((int)index, _stride);
} }
public GpuDescriptorHandle GetGpuHandle(DescriptorIndex index) public readonly GpuDescriptorHandle GetGpuHandle(DescriptorIndex index)
{ {
var handle = _startGpuHandle; var handle = _startGpuHandle;
return handle.Offset((int)index, _stride); return handle.Offset((int)index, _stride);
} }
public GpuDescriptorHandle GetGpuHandleStart() public readonly GpuDescriptorHandle GetGpuHandleStart()
{ {
return _startGpuHandle; return _startGpuHandle;
} }

View File

@@ -6,11 +6,11 @@ using DescriptorIndex = System.UInt32;
namespace Ghost.Graphics.D3D12.Utilities; namespace Ghost.Graphics.D3D12.Utilities;
internal unsafe struct DescriptorHeapAllocator : IDisposable internal unsafe struct D3D12DescriptorHeap : IDisposable
{ {
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u; private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
private ComPtr<ID3D12Device14> _device; private readonly ID3D12Device14* _pDevice;
private ComPtr<ID3D12DescriptorHeap> _heap; private ComPtr<ID3D12DescriptorHeap> _heap;
private ComPtr<ID3D12DescriptorHeap> _shaderVisibleHeap; private ComPtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
@@ -50,15 +50,14 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
public readonly ID3D12DescriptorHeap* Heap => _heap.Get(); public readonly ID3D12DescriptorHeap* Heap => _heap.Get();
public readonly ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get(); public readonly ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get();
public DescriptorHeapAllocator(string name, ComPtr<ID3D12Device14> device, DescriptorHeapType type, uint numDescriptors) public D3D12DescriptorHeap(string name, ID3D12Device14* device, DescriptorHeapType type, uint numDescriptors)
{ {
_device = device; _pDevice = device;
device.Get()->AddRef();
HeapType = type; HeapType = type;
NumDescriptors = numDescriptors; NumDescriptors = numDescriptors;
ShaderVisible = type == DescriptorHeapType.CbvSrvUav || type == DescriptorHeapType.Sampler; ShaderVisible = type == DescriptorHeapType.CbvSrvUav || type == DescriptorHeapType.Sampler;
Stride = device.Get()->GetDescriptorHandleIncrementSize(type); Stride = device->GetDescriptorHandleIncrementSize(type);
var success = AllocateResources(numDescriptors); var success = AllocateResources(numDescriptors);
Debug.Assert(success); Debug.Assert(success);
@@ -154,27 +153,27 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
} }
} }
public CpuDescriptorHandle GetCpuHandle(DescriptorIndex index) public readonly CpuDescriptorHandle GetCpuHandle(DescriptorIndex index)
{ {
var handle = _startCpuHandle; var handle = _startCpuHandle;
return handle.Offset((int)index, Stride); return handle.Offset((int)index, Stride);
} }
public CpuDescriptorHandle GetCpuHandleShaderVisible(DescriptorIndex index) public readonly CpuDescriptorHandle GetCpuHandleShaderVisible(DescriptorIndex index)
{ {
var handle = _startCpuHandleShaderVisible; var handle = _startCpuHandleShaderVisible;
return handle.Offset((int)index, Stride); return handle.Offset((int)index, Stride);
} }
public GpuDescriptorHandle GetGpuHandle(DescriptorIndex index) public readonly GpuDescriptorHandle GetGpuHandle(DescriptorIndex index)
{ {
var handle = _startGpuHandleShaderVisible; var handle = _startGpuHandleShaderVisible;
return handle.Offset((int)index, Stride); return handle.Offset((int)index, Stride);
} }
public void CopyToShaderVisibleHeap(DescriptorIndex index, uint count = 1) public readonly void CopyToShaderVisibleHeap(DescriptorIndex index, uint count = 1)
{ {
_device.Get()->CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType); _pDevice->CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
} }
private bool AllocateResources(uint numDescriptors) private bool AllocateResources(uint numDescriptors)
@@ -193,7 +192,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
fixed (void* heapPtr = &_heap) fixed (void* heapPtr = &_heap)
{ {
var hr = _device.Get()->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr); var hr = _pDevice->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
if (hr.Failure) if (hr.Failure)
{ {
return false; return false;
@@ -209,7 +208,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
fixed (void* heapPtr = &_shaderVisibleHeap) fixed (void* heapPtr = &_shaderVisibleHeap)
{ {
var hr = _device.Get()->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr); var hr = _pDevice->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
if (hr.Failure) if (hr.Failure)
{ {
return false; return false;
@@ -235,11 +234,11 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
return false; return false;
} }
_device.Get()->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType); _pDevice->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
if (_shaderVisibleHeap.Get() is not null) if (_shaderVisibleHeap.Get() is not null)
{ {
_device.Get()->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType); _pDevice->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
} }
return true; return true;
@@ -248,8 +247,6 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
_device.Dispose();
_heap.Dispose(); _heap.Dispose();
_shaderVisibleHeap.Dispose(); _shaderVisibleHeap.Dispose();
} }

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.D3D12;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Helpers; using Misaki.HighPerformance.LowLevel.Helpers;
@@ -9,18 +10,15 @@ using Win32.Graphics.Dxgi.Common;
namespace Ghost.Graphics.Data; 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<Vertex> _vertices;
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent); private UnsafeList<int> _indices;
private Bounds _boundingBox; private Bounds _boundingBox;
private GraphicsBuffer? _vertexBuffer; private IBuffer? _vertexBuffer;
private GraphicsBuffer? _indexBuffer; private IBuffer? _indexBuffer;
private BindlessDescriptor? _vertexBufferDescriptor;
private BindlessDescriptor? _indexBufferDescriptor;
public Span<Vertex> Vertices => _vertices.AsSpan(); public Span<Vertex> Vertices => _vertices.AsSpan();
public Span<int> Indices => _indices.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 VertexCount => (uint)_vertices.Count;
public uint IndexCount => (uint)_indices.Count; public uint IndexCount => (uint)_indices.Count;
public uint VertexBufferDescriptorIndex => _vertexBufferDescriptor?.Index ?? throw new InvalidOperationException("Vertex buffer descriptor is not allocated."); public uint VertexBufferDescriptorIndex
public uint IndexBufferDescriptorIndex => _indexBufferDescriptor?.Index ?? throw new InvalidOperationException("Index buffer descriptor is not allocated."); {
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() ~Mesh()
{ {
@@ -302,6 +351,13 @@ public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialInde
device->CreateShaderResourceView(_indexBuffer.NativeResource.Ptr, &indexSrvDesc, _indexBufferDescriptor.CpuHandle); device->CreateShaderResourceView(_indexBuffer.NativeResource.Ptr, &indexSrvDesc, _indexBufferDescriptor.CpuHandle);
} }
internal void MarkNoLongerReadable()
{
_vertices.Dispose();
_indices.Dispose();
}
/// <summary> /// <summary>
/// Clears all vertex and index data and releases associated GPU resources. /// Clears all vertex and index data and releases associated GPU resources.
/// </summar> /// </summar>
@@ -320,14 +376,14 @@ public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialInde
_indexBuffer?.Dispose(); _indexBuffer?.Dispose();
_indexBuffer = null; _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; namespace Ghost.Graphics.Data;
public readonly struct ResourceHandle : IEquatable<ResourceHandle>, IDisposable public readonly struct ResourceHandle : IEquatable<ResourceHandle>
{ {
private const int _INVALID_ID = -1; 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; 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) public bool Equals(ResourceHandle other)
{ {
return id == other.id && generation == other.generation; 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); 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) public static bool operator ==(ResourceHandle left, ResourceHandle right)
{ {
return left.Equals(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; private readonly ResourceHandle _resourceHandle;
@@ -104,11 +78,6 @@ public readonly struct TextureHandle : IEquatable<TextureHandle>, IDisposable
return _resourceHandle.GetHashCode(); return _resourceHandle.GetHashCode();
} }
public void Dispose()
{
_resourceHandle.Dispose();
}
public static bool operator ==(TextureHandle left, TextureHandle right) public static bool operator ==(TextureHandle left, TextureHandle right)
{ {
return left.Equals(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 ResourceHandle _resourceHandle;
private readonly BindlessDescriptor _bindlessDescriptor;
public static BufferHandle Invalid => new(ResourceHandle.Invalid);
public ResourceHandle ResourceHandle => _resourceHandle; public ResourceHandle ResourceHandle => _resourceHandle;
public static BufferHandle Invalid => new(ResourceHandle.Invalid); public BindlessDescriptor BindlessDescriptor => _bindlessDescriptor;
internal BufferHandle(ResourceHandle resourceHandle) internal BufferHandle(ResourceHandle resourceHandle)
{ {
_resourceHandle = resourceHandle; _resourceHandle = resourceHandle;
_bindlessDescriptor = BindlessDescriptor.Invalid;
}
internal BufferHandle(ResourceHandle resourceHandle, BindlessDescriptor descriptor)
{
_resourceHandle = resourceHandle;
_bindlessDescriptor = descriptor;
} }
public bool IsValid => _resourceHandle.IsValid; public bool IsValid => _resourceHandle.IsValid;
@@ -149,11 +128,6 @@ public readonly struct BufferHandle : IEquatable<BufferHandle>, IDisposable
return _resourceHandle.GetHashCode(); return _resourceHandle.GetHashCode();
} }
public void Dispose()
{
_resourceHandle.Dispose();
}
public static bool operator ==(BufferHandle left, BufferHandle right) public static bool operator ==(BufferHandle left, BufferHandle right)
{ {
return left.Equals(right); return left.Equals(right);

View File

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

View File

@@ -142,6 +142,6 @@ public abstract unsafe class Texture : GraphicsResource
public override void Dispose() public override void Dispose()
{ {
base.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.Image;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;

View File

@@ -1,7 +1,7 @@
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
/// <summary> /// <summary>
/// D3D12-style command queue interface /// Command queue interface
/// </summary> /// </summary>
public interface ICommandQueue : IDisposable public interface ICommandQueue : IDisposable
{ {
@@ -46,7 +46,7 @@ public interface ICommandQueue : IDisposable
} }
/// <summary> /// <summary>
/// Command queue types matching D3D12 /// Command queue types
/// </summary> /// </summary>
public enum CommandQueueType public enum CommandQueueType
{ {

View File

@@ -0,0 +1,64 @@
using Ghost.Graphics.Data;
using System.Runtime.CompilerServices;
using Win32.Graphics.Direct3D12;
namespace Ghost.Graphics.RHI;
public interface IDescriptorAllocator
{
public RenderTargetDescriptor AllocateRTV();
public RenderTargetDescriptor[] AllocateRTVs(uint count);
public DepthStencilDescriptor AllocateDSV();
public DepthStencilDescriptor[] AllocateDSVs(uint count);
public ShaderResourceDescriptor AllocateSRV();
public ShaderResourceDescriptor[] AllocateSRVs(uint count);
public SamplerDescriptor AllocateSampler();
public SamplerDescriptor[] AllocateSamplers(uint count);
public BindlessDescriptor AllocateBindless();
public BindlessDescriptor[] AllocateBindless(uint count);
public CpuDescriptorHandle GetCpuHandle(RenderTargetDescriptor descriptor);
public CpuDescriptorHandle GetCpuHandle(DepthStencilDescriptor descriptor);
public CpuDescriptorHandle GetCpuHandle(ShaderResourceDescriptor descriptor);
public GpuDescriptorHandle GetGpuHandle(ShaderResourceDescriptor descriptor);
public CpuDescriptorHandle GetCpuHandle(SamplerDescriptor descriptor);
public GpuDescriptorHandle GetGpuHandle(SamplerDescriptor descriptor);
public CpuDescriptorHandle GetCpuHandle(BindlessDescriptor descriptor);
public GpuDescriptorHandle GetGpuHandle(BindlessDescriptor descriptor);
public void Release(RenderTargetDescriptor descriptor);
public void Release(ReadOnlySpan<RenderTargetDescriptor> descriptors);
public void Release(DepthStencilDescriptor descriptor);
public void Release(ReadOnlySpan<DepthStencilDescriptor> descriptors);
public void Release(ShaderResourceDescriptor descriptor);
public void Release(ReadOnlySpan<ShaderResourceDescriptor> descriptors);
public void Release(SamplerDescriptor descriptor);
public void Release(ReadOnlySpan<SamplerDescriptor> descriptors);
public void Release(BindlessDescriptor descriptor);
public void Release(ReadOnlySpan<BindlessDescriptor> descriptors);
}

View File

@@ -165,7 +165,16 @@ public struct RenderTargetDesc
usage |= TextureUsage.UnorderedAccess; usage |= TextureUsage.UnorderedAccess;
} }
return new TextureDesc(desc.Width, desc.Height, desc.Slice, desc.Format, desc.Dimension, desc.MipLevels, usage); return new TextureDesc
{
Width = desc.Width,
Height = desc.Height,
Slice = desc.Slice,
Format = desc.Format,
Dimension = desc.Dimension,
MipLevels = desc.MipLevels,
Usage = usage
};
} }
} }
@@ -236,19 +245,6 @@ public struct TextureDesc
get; get;
set; set;
} }
public TextureDesc(uint width, uint height, uint slice = 1,
TextureFormat format = TextureFormat.R8G8B8A8_UNorm, TextureDimension dimension = TextureDimension.Texture2D,
uint mipLevels = 0u, TextureUsage usage = TextureUsage.ShaderResource)
{
Width = width;
Height = height;
Slice = slice;
Format = format;
Dimension = dimension;
MipLevels = mipLevels;
Usage = usage;
}
} }
/// <summary> /// <summary>
@@ -265,6 +261,12 @@ public struct BufferDesc
set; set;
} }
public uint Stride
{
get;
set;
}
/// <summary> /// <summary>
/// Buffer usage flags /// Buffer usage flags
/// </summary> /// </summary>
@@ -274,6 +276,12 @@ public struct BufferDesc
set; set;
} }
public BufferCreationFlags CreationFlags
{
get;
set;
}
/// <summary> /// <summary>
/// Memory type for the buffer /// Memory type for the buffer
/// </summary> /// </summary>
@@ -282,13 +290,6 @@ public struct BufferDesc
get; get;
set; set;
} }
public BufferDesc(ulong size, BufferUsage usage, MemoryType memoryType = MemoryType.Default)
{
Size = size;
Usage = usage;
MemoryType = memoryType;
}
} }
/// <summary> /// <summary>

View File

@@ -1,3 +1,5 @@
using Ghost.Graphics.Data;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
/// <summary> /// <summary>
@@ -8,17 +10,26 @@ public interface IResource : IDisposable
/// <summary> /// <summary>
/// Current resource state /// Current resource state
/// </summary> /// </summary>
ResourceState CurrentState { get; } ResourceState CurrentState
{
get;
}
/// <summary> /// <summary>
/// Resource name for debugging /// Resource name for debugging
/// </summary> /// </summary>
string Name { get; set; } string Name
{
get; set;
}
/// <summary> /// <summary>
/// Size of the resource in bytes /// Size of the resource in bytes
/// </summary> /// </summary>
ulong Size { get; } ulong Size
{
get;
}
} }
/// <summary> /// <summary>
@@ -29,22 +40,34 @@ public interface ITexture : IResource
/// <summary> /// <summary>
/// Width of the texture in pixels /// Width of the texture in pixels
/// </summary> /// </summary>
uint Width { get; } uint Width
{
get;
}
/// <summary> /// <summary>
/// Height of the texture in pixels /// Height of the texture in pixels
/// </summary> /// </summary>
uint Height { get; } uint Height
{
get;
}
/// <summary> /// <summary>
/// Texture format /// Texture format
/// </summary> /// </summary>
TextureFormat Format { get; } TextureFormat Format
{
get;
}
/// <summary> /// <summary>
/// Number of mip levels /// Number of mip levels
/// </summary> /// </summary>
uint MipLevels { get; } uint MipLevels
{
get;
}
} }
/// <summary> /// <summary>
@@ -55,18 +78,26 @@ public interface IBuffer : IResource
/// <summary> /// <summary>
/// Buffer usage type /// Buffer usage type
/// </summary> /// </summary>
BufferUsage Usage { get; } public BufferUsage Usage
{
get;
}
public BufferHandle Handle
{
get;
}
/// <summary> /// <summary>
/// Maps the buffer for CPU access /// Maps the buffer for CPU access
/// </summary> /// </summary>
/// <returns>Pointer to mapped memory</returns> /// <returns>Pointer to mapped memory</returns>
unsafe void* Map(); public unsafe void* Map();
/// <summary> /// <summary>
/// Unmaps the buffer from CPU access /// Unmaps the buffer from CPU access
/// </summary> /// </summary>
void Unmap(); public void Unmap();
} }
/// <summary> /// <summary>
@@ -78,7 +109,10 @@ public interface IRenderTarget : ITexture
/// <summary> /// <summary>
/// Type of render target (color or depth) /// Type of render target (color or depth)
/// </summary> /// </summary>
RenderTargetType Type { get; } RenderTargetType Type
{
get;
}
} }
/// <summary> /// <summary>
@@ -117,5 +151,15 @@ public enum BufferUsage
Structured = 1 << 3, Structured = 1 << 3,
Raw = 1 << 4, Raw = 1 << 4,
Upload = 1 << 5, Upload = 1 << 5,
Readback = 1 << 6 Readback = 1 << 6,
IndirectArgument = 1 << 7,
ShaderResource = Vertex | Index | Constant
} }
[Flags]
public enum BufferCreationFlags
{
None = 0,
Bindless = 1 << 0
}

View File

@@ -12,7 +12,7 @@ public enum GraphicsAPI
/// Application-level render system that orchestrates multiple renderers /// Application-level render system that orchestrates multiple renderers
/// and handles frame synchronization /// and handles frame synchronization
/// </summary> /// </summary>
internal class RenderSystem : IDisposable internal class RenderSystem
{ {
private readonly struct FrameResource : IDisposable private readonly struct FrameResource : IDisposable
{ {
@@ -34,15 +34,16 @@ internal class RenderSystem : IDisposable
private const uint _FRAME_COUNT = 2; private const uint _FRAME_COUNT = 2;
private readonly IGraphicsEngine _engine; private readonly IGraphicsEngine _engine = null!;
private readonly FrameResource[] _frameResources; private readonly FrameResource[] _frameResources = null!;
private readonly Thread _renderThread; private readonly Thread _renderThread = null!;
private readonly AutoResetEvent _shutdownEvent; private readonly AutoResetEvent _shutdownEvent = null!;
private ImmutableArray<IRenderer> _renderers; private ImmutableArray<IRenderer> _renderers;
private uint _frameIndex; private uint _frameIndex;
private uint _cpuFenceValue; private uint _cpuFenceValue;
private uint _gpuFenceValue; private uint _gpuFenceValue;
private bool _isRunning; private bool _isRunning;
private bool _disposed; private bool _disposed;
@@ -59,7 +60,7 @@ internal class RenderSystem : IDisposable
_ => throw new NotSupportedException($"Graphics API {api} is not supported.") _ => throw new NotSupportedException($"Graphics API {api} is not supported.")
}; };
_renderers = new(); _renderers = ImmutableArray<IRenderer>.Empty;
_shutdownEvent = new(false); _shutdownEvent = new(false);
// Create frame resources for synchronization // Create frame resources for synchronization
@@ -75,15 +76,14 @@ internal class RenderSystem : IDisposable
Name = "Graphics Render Thread", Name = "Graphics Render Thread",
Priority = ThreadPriority.Normal Priority = ThreadPriority.Normal
}; };
}
~RenderSystem() _disposed = true;
{
Dispose();
} }
public IRenderer CreateRenderer() public IRenderer CreateRenderer()
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
var renderer = _engine.CreateRenderer(); var renderer = _engine.CreateRenderer();
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer)); ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer));
return renderer; return renderer;
@@ -91,11 +91,15 @@ internal class RenderSystem : IDisposable
public void RemoveRenderer(IRenderer renderer) public void RemoveRenderer(IRenderer renderer)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Remove(renderer)); ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Remove(renderer));
} }
public void Start() public void Start()
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
if (_isRunning) if (_isRunning)
{ {
return; return;
@@ -107,6 +111,8 @@ internal class RenderSystem : IDisposable
public void Stop() public void Stop()
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
if (!_isRunning) if (!_isRunning)
{ {
return; return;
@@ -123,12 +129,16 @@ internal class RenderSystem : IDisposable
public bool WaitForGPUReady(int timeOut = -1) public bool WaitForGPUReady(int timeOut = -1)
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT); var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT);
return _frameResources[eventIndex].gpuReadyEvent.WaitOne(timeOut); return _frameResources[eventIndex].gpuReadyEvent.WaitOne(timeOut);
} }
public void SignalCPUReady() public void SignalCPUReady()
{ {
ObjectDisposedException.ThrowIf(_disposed, this);
var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT); var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT);
_frameResources[eventIndex].cpuReadyEvent.Set(); _frameResources[eventIndex].cpuReadyEvent.Set();
_cpuFenceValue++; _cpuFenceValue++;
@@ -184,7 +194,5 @@ internal class RenderSystem : IDisposable
_shutdownEvent.Dispose(); _shutdownEvent.Dispose();
_disposed = true; _disposed = true;
GC.SuppressFinalize(this);
} }
} }