From 74bb2ccda5eeaaddd65d82ef1c9151ff2b34ee3f Mon Sep 17 00:00:00 2001 From: Misaki Date: Sat, 13 Sep 2025 20:07:29 +0900 Subject: [PATCH] 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. --- .gitignore | 1 + .vscode/tasks.json | 21 -- Ghost.Graphics/D3D12/D3D12CommandBuffer.cs | 1 + .../D3D12/D3D12DescriptorAllocator.cs | 182 +++++++++--------- Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs | 16 +- .../D3D12/D3D12PipelineStateController.cs | 8 +- .../D3D12/D3D12ResourceAllocator.cs | 61 +++++- Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs | 56 +++++- Ghost.Graphics/D3D12/Descriptors.cs | 132 ------------- Ghost.Graphics/D3D12/Renderer.cs | 4 +- ...Allocator.cs => BindlessDescriptorHeap.cs} | 14 +- ...eapAllocator.cs => D3D12DescriptorHeap.cs} | 31 ++- Ghost.Graphics/Data/Descriptors.cs | 78 ++++++++ Ghost.Graphics/Data/Mesh.cs | 84 ++++++-- Ghost.Graphics/Data/ResourceHandle.cs | 54 ++---- Ghost.Graphics/Data/Shader.cs | 7 +- Ghost.Graphics/Data/Texture.cs | 2 +- Ghost.Graphics/Data/Texture2D.cs | 1 - Ghost.Graphics/RHI/ICommandQueue.cs | 4 +- Ghost.Graphics/RHI/IDescriptorAllocator.cs | 64 ++++++ Ghost.Graphics/RHI/IRenderTypes.cs | 43 +++-- Ghost.Graphics/RHI/IResource.cs | 68 +++++-- Ghost.Graphics/RenderSystem.cs | 32 +-- 23 files changed, 561 insertions(+), 403 deletions(-) delete mode 100644 .vscode/tasks.json delete mode 100644 Ghost.Graphics/D3D12/Descriptors.cs rename Ghost.Graphics/D3D12/Utilities/{BindlessDescriptorHeapAllocator.cs => BindlessDescriptorHeap.cs} (88%) rename Ghost.Graphics/D3D12/Utilities/{DescriptorHeapAllocator.cs => D3D12DescriptorHeap.cs} (81%) create mode 100644 Ghost.Graphics/Data/Descriptors.cs create mode 100644 Ghost.Graphics/RHI/IDescriptorAllocator.cs diff --git a/.gitignore b/.gitignore index 9491a2f..52d9dd1 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ bld/ # Visual Studio 2015/2017 cache/options directory .vs/ +.vscode/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 72fd640..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -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 - } - ] -} \ No newline at end of file diff --git a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs index f2d4102..5cf4f09 100644 --- a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs +++ b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs @@ -117,6 +117,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer throw new NotImplementedException(); } + // TODO: Batch draw calls by material to minimize state changes public void DrawMesh(Mesh mesh, Material material) { // Bind the bindless material (sets up root signature, pipeline state, and descriptor heaps) diff --git a/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs b/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs index cd7cec6..286689f 100644 --- a/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs +++ b/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs @@ -1,5 +1,8 @@ using Ghost.Core; using Ghost.Graphics.D3D12.Utilities; +using Ghost.Graphics.Data; +using Ghost.Graphics.RHI; +using System.Runtime.CompilerServices; using Win32.Graphics.Direct3D12; namespace Ghost.Graphics.D3D12; @@ -7,13 +10,13 @@ namespace Ghost.Graphics.D3D12; /// /// D3D12 implementation of descriptor allocator that manages different types of descriptor heaps. /// -internal unsafe class D3D12DescriptorAllocator : IDisposable +internal unsafe class D3D12DescriptorAllocator : IDescriptorAllocator, IDisposable { - private readonly DescriptorHeapAllocator _rtvHeap; - private readonly DescriptorHeapAllocator _dsvHeap; - private readonly DescriptorHeapAllocator _srvHeap; - private readonly DescriptorHeapAllocator _samplerHeap; - private readonly BindlessDescriptorHeapAllocator _bindlessHeap; + private readonly D3D12DescriptorHeap _rtvHeap; + private readonly D3D12DescriptorHeap _dsvHeap; + private readonly D3D12DescriptorHeap _srvHeap; + private readonly D3D12DescriptorHeap _samplerHeap; + private readonly BindlessDescriptorHeap _bindlessHeap; private bool _disposed; @@ -21,11 +24,11 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable { var pDevice = device.NativeDevice; - _rtvHeap = new DescriptorHeapAllocator("rtv", pDevice, DescriptorHeapType.Rtv, initialRtvCount); - _dsvHeap = new DescriptorHeapAllocator("dsv", pDevice, DescriptorHeapType.Dsv, initialDsvCount); - _srvHeap = new DescriptorHeapAllocator("srv", pDevice, DescriptorHeapType.CbvSrvUav, initialSrvCount); - _samplerHeap = new DescriptorHeapAllocator("sampler", pDevice, DescriptorHeapType.Sampler, initialSamplerCount); - _bindlessHeap = new BindlessDescriptorHeapAllocator(pDevice, initialBindlessCount); + _rtvHeap = new D3D12DescriptorHeap("rtv", pDevice, DescriptorHeapType.Rtv, initialRtvCount); + _dsvHeap = new D3D12DescriptorHeap("dsv", pDevice, DescriptorHeapType.Dsv, initialDsvCount); + _srvHeap = new D3D12DescriptorHeap("srv", pDevice, DescriptorHeapType.CbvSrvUav, initialSrvCount); + _samplerHeap = new D3D12DescriptorHeap("sampler", pDevice, DescriptorHeapType.Sampler, initialSamplerCount); + _bindlessHeap = new BindlessDescriptorHeap(pDevice, initialBindlessCount); } ~D3D12DescriptorAllocator() @@ -45,8 +48,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable throw new InvalidOperationException("Failed to allocate RTV descriptor"); } - var cpuHandle = _rtvHeap.GetCpuHandle(index); - return new RenderTargetDescriptor(index, cpuHandle); + return new RenderTargetDescriptor { Index = index }; } public RenderTargetDescriptor[] AllocateRTVs(uint count) @@ -63,30 +65,33 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable for (uint i = 0; i < count; i++) { var index = baseIndex + i; - var cpuHandle = _rtvHeap.GetCpuHandle(index); - descriptors[i] = new RenderTargetDescriptor(index, cpuHandle); + descriptors[i] = new RenderTargetDescriptor { Index = index }; } return descriptors; } - public void ReleaseRTV(RenderTargetDescriptor descriptor) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CpuDescriptorHandle GetCpuHandle(RenderTargetDescriptor descriptor) { ObjectDisposedException.ThrowIf(_disposed, this); - - if (descriptor is RenderTargetDescriptor d3d12Descriptor) - { - _rtvHeap.ReleaseDescriptor(d3d12Descriptor.Index); - } + return _rtvHeap.GetCpuHandle(descriptor.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 descriptors) { ObjectDisposedException.ThrowIf(_disposed, this); 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"); } - var cpuHandle = _dsvHeap.GetCpuHandle(index); - return new DepthStencilDescriptor(index, cpuHandle); + return new DepthStencilDescriptor { Index = index }; } public DepthStencilDescriptor[] AllocateDSVs(uint count) @@ -122,30 +126,31 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable for (uint i = 0; i < count; i++) { var index = baseIndex + i; - var cpuHandle = _dsvHeap.GetCpuHandle(index); - descriptors[i] = new DepthStencilDescriptor(index, cpuHandle); + descriptors[i] = new DepthStencilDescriptor { Index = index }; } return descriptors; } - public void ReleaseDSV(DepthStencilDescriptor descriptor) + public CpuDescriptorHandle GetCpuHandle(DepthStencilDescriptor descriptor) { ObjectDisposedException.ThrowIf(_disposed, this); - - if (descriptor is DepthStencilDescriptor d3d12Descriptor) - { - _dsvHeap.ReleaseDescriptor(d3d12Descriptor.Index); - } + return _dsvHeap.GetCpuHandle(descriptor.Index); } - public void ReleaseDSVs(DepthStencilDescriptor[] descriptors) + public void Release(DepthStencilDescriptor descriptor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + _dsvHeap.ReleaseDescriptor(descriptor.Index); + } + + public void Release(ReadOnlySpan descriptors) { ObjectDisposedException.ThrowIf(_disposed, this); 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"); } - var cpuHandle = _srvHeap.GetCpuHandle(index); - var gpuHandle = _srvHeap.GetGpuHandle(index); - - // Copy to shader visible heap _srvHeap.CopyToShaderVisibleHeap(index); - - return new ShaderResourceDescriptor(index, cpuHandle, gpuHandle); + return new ShaderResourceDescriptor { Index = index }; } public ShaderResourceDescriptor[] AllocateSRVs(uint count) @@ -186,34 +186,38 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable for (uint i = 0; i < count; i++) { var index = baseIndex + i; - var cpuHandle = _srvHeap.GetCpuHandle(index); - var gpuHandle = _srvHeap.GetGpuHandle(index); - descriptors[i] = new ShaderResourceDescriptor(index, cpuHandle, gpuHandle); + descriptors[i] = new ShaderResourceDescriptor { Index = index }; } - // Copy all descriptors to shader visible heap _srvHeap.CopyToShaderVisibleHeap(baseIndex, count); - return descriptors; } - public void ReleaseSRV(ShaderResourceDescriptor descriptor) + public CpuDescriptorHandle GetCpuHandle(ShaderResourceDescriptor descriptor) { ObjectDisposedException.ThrowIf(_disposed, this); - - if (descriptor is ShaderResourceDescriptor d3d12Descriptor) - { - _srvHeap.ReleaseDescriptor(d3d12Descriptor.Index); - } + return _srvHeap.GetCpuHandle(descriptor.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 descriptors) { ObjectDisposedException.ThrowIf(_disposed, this); 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"); } - var cpuHandle = _samplerHeap.GetCpuHandle(index); - var gpuHandle = _samplerHeap.GetGpuHandle(index); - - // Copy to shader visible heap _samplerHeap.CopyToShaderVisibleHeap(index); - - return new SamplerDescriptor(index, cpuHandle, gpuHandle); + return new SamplerDescriptor { Index = index }; } public SamplerDescriptor[] AllocateSamplers(uint count) @@ -254,34 +253,38 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable for (uint i = 0; i < count; i++) { var index = baseIndex + i; - var cpuHandle = _samplerHeap.GetCpuHandle(index); - var gpuHandle = _samplerHeap.GetGpuHandle(index); - descriptors[i] = new SamplerDescriptor(index, cpuHandle, gpuHandle); + descriptors[i] = new SamplerDescriptor { Index = index }; } - // Copy all descriptors to shader visible heap _samplerHeap.CopyToShaderVisibleHeap(baseIndex, count); - return descriptors; } - public void ReleaseSampler(SamplerDescriptor descriptor) + public CpuDescriptorHandle GetCpuHandle(SamplerDescriptor descriptor) { ObjectDisposedException.ThrowIf(_disposed, this); - - if (descriptor is SamplerDescriptor d3d12Descriptor) - { - _samplerHeap.ReleaseDescriptor(d3d12Descriptor.Index); - } + return _samplerHeap.GetCpuHandle(descriptor.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 descriptors) { ObjectDisposedException.ThrowIf(_disposed, this); 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"); } - var cpuHandle = _bindlessHeap.GetCpuHandle(index); - var gpuHandle = _bindlessHeap.GetGpuHandle(index); - - return new BindlessDescriptor(index, cpuHandle, gpuHandle); + return new BindlessDescriptor { Index = index }; } /// @@ -326,37 +326,43 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable for (uint i = 0; i < count; i++) { var index = baseIndex + i; - var cpuHandle = _bindlessHeap.GetCpuHandle(index); - var gpuHandle = _bindlessHeap.GetGpuHandle(index); - descriptors[i] = new BindlessDescriptor(index, cpuHandle, gpuHandle); + descriptors[i] = new BindlessDescriptor { Index = index }; } 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); + } + /// /// Releases a bindless descriptor. /// - public void ReleaseBindless(BindlessDescriptor descriptor) + public void Release(BindlessDescriptor descriptor) { ObjectDisposedException.ThrowIf(_disposed, this); - - if (descriptor is BindlessDescriptor d3d12Descriptor) - { - _bindlessHeap.ReleaseDescriptor(d3d12Descriptor.Index); - } + _bindlessHeap.ReleaseDescriptor(descriptor.Index); } /// /// Releases multiple bindless descriptors. /// - public void ReleaseBindless(BindlessDescriptor[] descriptors) + public void Release(ReadOnlySpan descriptors) { ObjectDisposedException.ThrowIf(_disposed, this); foreach (var descriptor in descriptors) { - ReleaseBindless(descriptor); + Release(descriptor); } } diff --git a/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs b/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs index 60ab9a4..73c9ca4 100644 --- a/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs +++ b/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs @@ -9,16 +9,16 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine #endif private readonly D3D12RenderDevice _device; - private readonly D3D12PipelineStateController _stateController; + private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12ResourceAllocator _resourceAllocator; - private readonly D3D12PipelineStateController _pipelineState; - private readonly D3D12DescriptorAllocator _descriptorAllocator; + private readonly D3D12PipelineStateController _stateController; public IRenderDevice Device => _device; - public IPipelineStateController PipelineStateController => _stateController; public IResourceAllocator ResourceAllocator => _resourceAllocator; + public IPipelineStateController PipelineStateController => _stateController; + public D3D12GraphicsEngine(RenderSystem renderSystem) { #if DEBUG @@ -26,11 +26,10 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine #endif _device = new(); - _stateController = new(_device); - _resourceAllocator = new(_device, renderSystem); - - _pipelineState = new(_device); _descriptorAllocator = new(_device); + _resourceAllocator = new(renderSystem, _device, _descriptorAllocator); + + _stateController = new(_device); } public IRenderer CreateRenderer() @@ -50,6 +49,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine public void Dispose() { + _stateController.Dispose(); _descriptorAllocator.Dispose(); _resourceAllocator.Dispose(); _device.Dispose(); diff --git a/Ghost.Graphics/D3D12/D3D12PipelineStateController.cs b/Ghost.Graphics/D3D12/D3D12PipelineStateController.cs index fc5e770..29aca4b 100644 --- a/Ghost.Graphics/D3D12/D3D12PipelineStateController.cs +++ b/Ghost.Graphics/D3D12/D3D12PipelineStateController.cs @@ -25,10 +25,6 @@ internal class D3D12ShaderPipeline : IShaderPipeline 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 Dictionary _shaderPipelines; @@ -55,8 +51,8 @@ internal unsafe class D3D12PipelineStateController : IPipelineStateController, I { foreach (var kvp in _shaderPipelines) { - var vsResult = D3D12ShaderCompiler.CompileDXC(kvp.Key, _VS_ENTRY_POINT, _PROFILE_VS_6_6); - var psResult = D3D12ShaderCompiler.CompileDXC(kvp.Key, _PS_ENTRY_POINT, _PROFILE_VS_6_6); + var vsResult = D3D12ShaderCompiler.Compile(kvp.Key, D3D12ShaderCompiler.ShaderStage.VertexShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6); + var psResult = D3D12ShaderCompiler.Compile(kvp.Key, D3D12ShaderCompiler.ShaderStage.PixelShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6); kvp.Value.vsResult = vsResult; kvp.Value.psResult = psResult; diff --git a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs index 489bd66..ae2962e 100644 --- a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs +++ b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs @@ -7,7 +7,6 @@ using Win32.Graphics.Direct3D12; using Win32.Graphics.Dxgi; using Win32.Graphics.Dxgi.Common; using static Win32.Graphics.D3D12MemoryAllocator.Apis; -using ResourceHandle = Ghost.Graphics.Data.ResourceHandle; namespace Ghost.Graphics.D3D12; @@ -43,8 +42,12 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator _allocations = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); private UnsafeQueue _freeSlots = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); private UnsafeQueue _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); @@ -60,10 +63,8 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocatorCreateShaderResourceView(allocation.Resource, &srvDesc, _descriptorAllocator.GetCpuHandle(descriptorHandle)); + + return new(handle, descriptorHandle); + } + + return new(handle); } [MethodImpl(MethodImplOptions.AggressiveInlining)] 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); } diff --git a/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs b/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs index c5c1df8..b0c0a9e 100644 --- a/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs +++ b/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs @@ -11,6 +11,20 @@ namespace Ghost.Graphics.D3D12; 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 UnsafeArray 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 compiler = default; using ComPtr utils = default; @@ -43,12 +85,12 @@ internal unsafe static class D3D12ShaderCompiler // Prepare compilation arguments - NOTE: NO -Qstrip_reflect to keep reflection data var argsArray = new string[] { - "-T", profile, // Target profile (vs_6_6, ps_6_6) - "-E", entryPoint, // Entry point - "-HV", "2021", // HLSL version 2021 (required for SM 6.6) - "-enable-16bit-types", // Enable 16-bit types - "-O3", // Optimization level - "-Qstrip_debug" // Strip debug info but KEEP reflection + "-T", GetProfileString(stage, version), // Target profile (vs_6_6, ps_6_6) + "-E", GetEntryPoint(stage), // Entry point + "-HV", "2021", // HLSL version 2021 (required for SM 6.6) + "-enable-16bit-types", // Enable 16-bit types + "-O3", // Optimization level + "-Qstrip_debug" // Strip debug info but KEEP reflection }; // Convert to wide strings (DXC expects LPCWSTR) diff --git a/Ghost.Graphics/D3D12/Descriptors.cs b/Ghost.Graphics/D3D12/Descriptors.cs deleted file mode 100644 index 54a7a76..0000000 --- a/Ghost.Graphics/D3D12/Descriptors.cs +++ /dev/null @@ -1,132 +0,0 @@ -using Win32.Graphics.Direct3D12; - -namespace Ghost.Graphics.D3D12; - -/// -/// Implementation of render target view (RTV) descriptor. -/// -public readonly struct RenderTargetDescriptor -{ - public uint Index - { - get; - } - - public CpuDescriptorHandle CpuHandle - { - get; - } - - public RenderTargetDescriptor(uint index, CpuDescriptorHandle cpuHandle) - { - Index = index; - CpuHandle = cpuHandle; - } -} - -/// -/// Implementation of depth stencil view (DSV) descriptor. -/// -public readonly struct DepthStencilDescriptor -{ - public uint Index - { - get; - } - - public CpuDescriptorHandle CpuHandle - { - get; - } - - public DepthStencilDescriptor(uint index, CpuDescriptorHandle cpuHandle) - { - Index = index; - CpuHandle = cpuHandle; - } -} - -/// -/// Implementation of shader resource view (SRV) descriptor. -/// -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; - } -} - -/// -/// Implementation of sampler descriptor. -/// -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; - } -} - -/// -/// Implementation of bindless descriptor for SM 6.6 rendering. -/// This descriptor maintains a 1:1 relationship between allocation indices and shader indices. -/// -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; - } -} \ No newline at end of file diff --git a/Ghost.Graphics/D3D12/Renderer.cs b/Ghost.Graphics/D3D12/Renderer.cs index dd674d2..0cae5d5 100644 --- a/Ghost.Graphics/D3D12/Renderer.cs +++ b/Ghost.Graphics/D3D12/Renderer.cs @@ -64,7 +64,7 @@ internal unsafe class Renderer commandAllocator.Dispose(); commandList.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) { var c = frameResource.backBuffer.Reset(); - GraphicsPipeline.DescriptorAllocator.ReleaseRTV(frameResource.rtvDescriptor); + GraphicsPipeline.DescriptorAllocator.Release(frameResource.rtvDescriptor); } frameResource.fenceValue = _frameResources[_backBufferIndex].fenceValue; diff --git a/Ghost.Graphics/D3D12/Utilities/BindlessDescriptorHeapAllocator.cs b/Ghost.Graphics/D3D12/Utilities/BindlessDescriptorHeap.cs similarity index 88% rename from Ghost.Graphics/D3D12/Utilities/BindlessDescriptorHeapAllocator.cs rename to Ghost.Graphics/D3D12/Utilities/BindlessDescriptorHeap.cs index 9c48053..24ea516 100644 --- a/Ghost.Graphics/D3D12/Utilities/BindlessDescriptorHeapAllocator.cs +++ b/Ghost.Graphics/D3D12/Utilities/BindlessDescriptorHeap.cs @@ -10,7 +10,7 @@ namespace Ghost.Graphics.D3D12.Utilities; /// 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. /// -internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable +internal unsafe struct BindlessDescriptorHeap : IDisposable { private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u; @@ -42,7 +42,7 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable public readonly ConstPtr BindlessHeap => new(_bindlessHeap.Get()); - public BindlessDescriptorHeapAllocator(ComPtr device, uint numDescriptors = 10000) + public BindlessDescriptorHeap(ComPtr device, uint numDescriptors = 10000) { _device = device; device.Get()->AddRef(); @@ -67,7 +67,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable // Try to grow the heap if (!Grow(NumDescriptors * 2)) { - Debug.WriteLine("ERROR: Failed to grow bindless descriptor heap!"); return _INVALID_DESCRIPTOR_INDEX; } } @@ -88,7 +87,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable var newSize = Math.Max(NumDescriptors * 2, NumDescriptors + count); if (!Grow(newSize)) { - Debug.WriteLine("ERROR: Failed to grow bindless descriptor heap!"); return _INVALID_DESCRIPTOR_INDEX; } } @@ -110,7 +108,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable { if (index >= NumDescriptors) { - Debug.WriteLine("Error: Attempted to release an invalid descriptor index"); return; } @@ -128,7 +125,6 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable var index = baseIndex + i; if (index >= NumDescriptors) { - Debug.WriteLine("Error: Attempted to release an invalid descriptor index"); continue; } @@ -139,19 +135,19 @@ internal unsafe struct BindlessDescriptorHeapAllocator : IDisposable } } - public CpuDescriptorHandle GetCpuHandle(DescriptorIndex index) + public readonly CpuDescriptorHandle GetCpuHandle(DescriptorIndex index) { var handle = _startCpuHandle; return handle.Offset((int)index, _stride); } - public GpuDescriptorHandle GetGpuHandle(DescriptorIndex index) + public readonly GpuDescriptorHandle GetGpuHandle(DescriptorIndex index) { var handle = _startGpuHandle; return handle.Offset((int)index, _stride); } - public GpuDescriptorHandle GetGpuHandleStart() + public readonly GpuDescriptorHandle GetGpuHandleStart() { return _startGpuHandle; } diff --git a/Ghost.Graphics/D3D12/Utilities/DescriptorHeapAllocator.cs b/Ghost.Graphics/D3D12/Utilities/D3D12DescriptorHeap.cs similarity index 81% rename from Ghost.Graphics/D3D12/Utilities/DescriptorHeapAllocator.cs rename to Ghost.Graphics/D3D12/Utilities/D3D12DescriptorHeap.cs index 1e88778..b9a4980 100644 --- a/Ghost.Graphics/D3D12/Utilities/DescriptorHeapAllocator.cs +++ b/Ghost.Graphics/D3D12/Utilities/D3D12DescriptorHeap.cs @@ -6,11 +6,11 @@ using DescriptorIndex = System.UInt32; namespace Ghost.Graphics.D3D12.Utilities; -internal unsafe struct DescriptorHeapAllocator : IDisposable +internal unsafe struct D3D12DescriptorHeap : IDisposable { private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u; - private ComPtr _device; + private readonly ID3D12Device14* _pDevice; private ComPtr _heap; private ComPtr _shaderVisibleHeap; @@ -50,15 +50,14 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable public readonly ID3D12DescriptorHeap* Heap => _heap.Get(); public readonly ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get(); - public DescriptorHeapAllocator(string name, ComPtr device, DescriptorHeapType type, uint numDescriptors) + public D3D12DescriptorHeap(string name, ID3D12Device14* device, DescriptorHeapType type, uint numDescriptors) { - _device = device; - device.Get()->AddRef(); + _pDevice = device; HeapType = type; NumDescriptors = numDescriptors; ShaderVisible = type == DescriptorHeapType.CbvSrvUav || type == DescriptorHeapType.Sampler; - Stride = device.Get()->GetDescriptorHandleIncrementSize(type); + Stride = device->GetDescriptorHandleIncrementSize(type); var success = AllocateResources(numDescriptors); 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; return handle.Offset((int)index, Stride); } - public CpuDescriptorHandle GetCpuHandleShaderVisible(DescriptorIndex index) + public readonly CpuDescriptorHandle GetCpuHandleShaderVisible(DescriptorIndex index) { var handle = _startCpuHandleShaderVisible; return handle.Offset((int)index, Stride); } - public GpuDescriptorHandle GetGpuHandle(DescriptorIndex index) + public readonly GpuDescriptorHandle GetGpuHandle(DescriptorIndex index) { var handle = _startGpuHandleShaderVisible; 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) @@ -193,7 +192,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable fixed (void* heapPtr = &_heap) { - var hr = _device.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(), (void**)heapPtr); + var hr = _pDevice->CreateDescriptorHeap(&heapDesc, __uuidof(), (void**)heapPtr); if (hr.Failure) { return false; @@ -209,7 +208,7 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable fixed (void* heapPtr = &_shaderVisibleHeap) { - var hr = _device.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(), (void**)heapPtr); + var hr = _pDevice->CreateDescriptorHeap(&heapDesc, __uuidof(), (void**)heapPtr); if (hr.Failure) { return false; @@ -235,11 +234,11 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable 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) { - _device.Get()->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType); + _pDevice->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType); } return true; @@ -248,8 +247,6 @@ internal unsafe struct DescriptorHeapAllocator : IDisposable /// public void Dispose() { - _device.Dispose(); - _heap.Dispose(); _shaderVisibleHeap.Dispose(); } diff --git a/Ghost.Graphics/Data/Descriptors.cs b/Ghost.Graphics/Data/Descriptors.cs new file mode 100644 index 0000000..63d849f --- /dev/null +++ b/Ghost.Graphics/Data/Descriptors.cs @@ -0,0 +1,78 @@ +using Win32.Graphics.Direct3D12; + +namespace Ghost.Graphics.Data; + +/// +/// Render target view (RTV) descriptor. +/// +public readonly struct RenderTargetDescriptor +{ + public uint Index + { + get; init; + } + + public static RenderTargetDescriptor Invalid => new() { Index = ~0u }; + + public bool IsValid => Index != ~0u; +} + +/// +/// Depth stencil view (DSV) descriptor. +/// +public readonly struct DepthStencilDescriptor +{ + public uint Index + { + get; init; + } + + public static DepthStencilDescriptor Invalid => new() { Index = ~0u }; + + public bool IsValid => Index != ~0u; +} + +/// +/// Shader resource view (SRV) descriptor. +/// +public readonly struct ShaderResourceDescriptor +{ + public uint Index + { + get; init; + } + + public static ShaderResourceDescriptor Invalid => new() { Index = ~0u }; + + public bool IsValid => Index != ~0u; +} + +/// +/// Sampler descriptor. +/// +public readonly struct SamplerDescriptor +{ + public uint Index + { + get; init; + } + + public static SamplerDescriptor Invalid => new() { Index = ~0u }; + + public bool IsValid => Index != ~0u; +} + +/// +/// Bindless descriptor +/// +public readonly struct BindlessDescriptor +{ + public uint Index + { + get; init; + } + + public static BindlessDescriptor Invalid => new() { Index = ~0u }; + + public bool IsValid => Index != ~0u; +} \ No newline at end of file diff --git a/Ghost.Graphics/Data/Mesh.cs b/Ghost.Graphics/Data/Mesh.cs index 31ccbe6..0994579 100644 --- a/Ghost.Graphics/Data/Mesh.cs +++ b/Ghost.Graphics/Data/Mesh.cs @@ -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 _vertices = new(initialVertexCapacity, Allocator.Persistent); - private UnsafeList _indices = new(initialIndexCapacity, Allocator.Persistent); + private UnsafeList _vertices; + private UnsafeList _indices; private Bounds _boundingBox; - private GraphicsBuffer? _vertexBuffer; - private GraphicsBuffer? _indexBuffer; - - private BindlessDescriptor? _vertexBufferDescriptor; - private BindlessDescriptor? _indexBufferDescriptor; + private IBuffer? _vertexBuffer; + private IBuffer? _indexBuffer; public Span Vertices => _vertices.AsSpan(); public Span 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 vertices, ReadOnlySpan 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(); + } + /// /// Clears all vertex and index data and releases associated GPU resources. /// @@ -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); } } diff --git a/Ghost.Graphics/Data/ResourceHandle.cs b/Ghost.Graphics/Data/ResourceHandle.cs index a7e4a58..cf251f1 100644 --- a/Ghost.Graphics/Data/ResourceHandle.cs +++ b/Ghost.Graphics/Data/ResourceHandle.cs @@ -3,7 +3,7 @@ using Win32.Graphics.D3D12MemoryAllocator; namespace Ghost.Graphics.Data; -public readonly struct ResourceHandle : IEquatable, IDisposable +public readonly struct ResourceHandle : IEquatable { private const int _INVALID_ID = -1; @@ -20,17 +20,6 @@ public readonly struct ResourceHandle : IEquatable, 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, 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, IDisposable } } -public readonly struct TextureHandle : IEquatable, IDisposable +public readonly struct TextureHandle : IEquatable { private readonly ResourceHandle _resourceHandle; @@ -104,11 +78,6 @@ public readonly struct TextureHandle : IEquatable, 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, IDisposable } } -public readonly struct BufferHandle : IEquatable, IDisposable +public readonly struct BufferHandle : IEquatable { 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, IDisposable return _resourceHandle.GetHashCode(); } - public void Dispose() - { - _resourceHandle.Dispose(); - } - public static bool operator ==(BufferHandle left, BufferHandle right) { return left.Equals(right); diff --git a/Ghost.Graphics/Data/Shader.cs b/Ghost.Graphics/Data/Shader.cs index 28cc2a0..b7eabb2 100644 --- a/Ghost.Graphics/Data/Shader.cs +++ b/Ghost.Graphics/Data/Shader.cs @@ -60,10 +60,10 @@ internal readonly struct CBufferInfo } /// -/// 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. /// + +// 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 RegularTextures => _regularTextures; internal Dictionary PropertyNameToIdMap => _propertyNameToIdMap; + // TODO: In real production, we should not load the shader source code directly. internal Shader(string shaderCode) { _source = shaderCode; diff --git a/Ghost.Graphics/Data/Texture.cs b/Ghost.Graphics/Data/Texture.cs index ab7569a..a603a25 100644 --- a/Ghost.Graphics/Data/Texture.cs +++ b/Ghost.Graphics/Data/Texture.cs @@ -142,6 +142,6 @@ public abstract unsafe class Texture : GraphicsResource public override void Dispose() { base.Dispose(); - GraphicsPipeline.DescriptorAllocator.ReleaseBindless(_bindlessDescriptor); + GraphicsPipeline.DescriptorAllocator.Release(_bindlessDescriptor); } } \ No newline at end of file diff --git a/Ghost.Graphics/Data/Texture2D.cs b/Ghost.Graphics/Data/Texture2D.cs index b0dd519..388412d 100644 --- a/Ghost.Graphics/Data/Texture2D.cs +++ b/Ghost.Graphics/Data/Texture2D.cs @@ -1,4 +1,3 @@ -using Ghost.Graphics.D3D12; using Misaki.HighPerformance.Image; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; diff --git a/Ghost.Graphics/RHI/ICommandQueue.cs b/Ghost.Graphics/RHI/ICommandQueue.cs index 91a506c..583e09b 100644 --- a/Ghost.Graphics/RHI/ICommandQueue.cs +++ b/Ghost.Graphics/RHI/ICommandQueue.cs @@ -1,7 +1,7 @@ namespace Ghost.Graphics.RHI; /// -/// D3D12-style command queue interface +/// Command queue interface /// public interface ICommandQueue : IDisposable { @@ -46,7 +46,7 @@ public interface ICommandQueue : IDisposable } /// -/// Command queue types matching D3D12 +/// Command queue types /// public enum CommandQueueType { diff --git a/Ghost.Graphics/RHI/IDescriptorAllocator.cs b/Ghost.Graphics/RHI/IDescriptorAllocator.cs new file mode 100644 index 0000000..b25c945 --- /dev/null +++ b/Ghost.Graphics/RHI/IDescriptorAllocator.cs @@ -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 descriptors); + + public void Release(DepthStencilDescriptor descriptor); + + public void Release(ReadOnlySpan descriptors); + + public void Release(ShaderResourceDescriptor descriptor); + + public void Release(ReadOnlySpan descriptors); + + public void Release(SamplerDescriptor descriptor); + + public void Release(ReadOnlySpan descriptors); + + public void Release(BindlessDescriptor descriptor); + + public void Release(ReadOnlySpan descriptors); +} \ No newline at end of file diff --git a/Ghost.Graphics/RHI/IRenderTypes.cs b/Ghost.Graphics/RHI/IRenderTypes.cs index 279d68b..79beb1e 100644 --- a/Ghost.Graphics/RHI/IRenderTypes.cs +++ b/Ghost.Graphics/RHI/IRenderTypes.cs @@ -165,7 +165,16 @@ public struct RenderTargetDesc 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; 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; - } } /// @@ -265,6 +261,12 @@ public struct BufferDesc set; } + public uint Stride + { + get; + set; + } + /// /// Buffer usage flags /// @@ -274,6 +276,12 @@ public struct BufferDesc set; } + public BufferCreationFlags CreationFlags + { + get; + set; + } + /// /// Memory type for the buffer /// @@ -282,13 +290,6 @@ public struct BufferDesc get; set; } - - public BufferDesc(ulong size, BufferUsage usage, MemoryType memoryType = MemoryType.Default) - { - Size = size; - Usage = usage; - MemoryType = memoryType; - } } /// diff --git a/Ghost.Graphics/RHI/IResource.cs b/Ghost.Graphics/RHI/IResource.cs index 6aca2f8..c0f8910 100644 --- a/Ghost.Graphics/RHI/IResource.cs +++ b/Ghost.Graphics/RHI/IResource.cs @@ -1,3 +1,5 @@ +using Ghost.Graphics.Data; + namespace Ghost.Graphics.RHI; /// @@ -8,17 +10,26 @@ public interface IResource : IDisposable /// /// Current resource state /// - ResourceState CurrentState { get; } + ResourceState CurrentState + { + get; + } /// /// Resource name for debugging /// - string Name { get; set; } + string Name + { + get; set; + } /// /// Size of the resource in bytes /// - ulong Size { get; } + ulong Size + { + get; + } } /// @@ -29,22 +40,34 @@ public interface ITexture : IResource /// /// Width of the texture in pixels /// - uint Width { get; } + uint Width + { + get; + } /// /// Height of the texture in pixels /// - uint Height { get; } + uint Height + { + get; + } /// /// Texture format /// - TextureFormat Format { get; } + TextureFormat Format + { + get; + } /// /// Number of mip levels /// - uint MipLevels { get; } + uint MipLevels + { + get; + } } /// @@ -55,18 +78,26 @@ public interface IBuffer : IResource /// /// Buffer usage type /// - BufferUsage Usage { get; } + public BufferUsage Usage + { + get; + } + + public BufferHandle Handle + { + get; + } /// /// Maps the buffer for CPU access /// /// Pointer to mapped memory - unsafe void* Map(); + public unsafe void* Map(); /// /// Unmaps the buffer from CPU access /// - void Unmap(); + public void Unmap(); } /// @@ -78,7 +109,10 @@ public interface IRenderTarget : ITexture /// /// Type of render target (color or depth) /// - RenderTargetType Type { get; } + RenderTargetType Type + { + get; + } } /// @@ -117,5 +151,15 @@ public enum BufferUsage Structured = 1 << 3, Raw = 1 << 4, 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 +} \ No newline at end of file diff --git a/Ghost.Graphics/RenderSystem.cs b/Ghost.Graphics/RenderSystem.cs index 7d5075a..ecd12ac 100644 --- a/Ghost.Graphics/RenderSystem.cs +++ b/Ghost.Graphics/RenderSystem.cs @@ -12,7 +12,7 @@ public enum GraphicsAPI /// Application-level render system that orchestrates multiple renderers /// and handles frame synchronization /// -internal class RenderSystem : IDisposable +internal class RenderSystem { private readonly struct FrameResource : IDisposable { @@ -34,15 +34,16 @@ internal class RenderSystem : IDisposable private const uint _FRAME_COUNT = 2; - private readonly IGraphicsEngine _engine; - private readonly FrameResource[] _frameResources; - private readonly Thread _renderThread; - private readonly AutoResetEvent _shutdownEvent; + private readonly IGraphicsEngine _engine = null!; + private readonly FrameResource[] _frameResources = null!; + private readonly Thread _renderThread = null!; + private readonly AutoResetEvent _shutdownEvent = null!; private ImmutableArray _renderers; private uint _frameIndex; private uint _cpuFenceValue; private uint _gpuFenceValue; + private bool _isRunning; private bool _disposed; @@ -59,7 +60,7 @@ internal class RenderSystem : IDisposable _ => throw new NotSupportedException($"Graphics API {api} is not supported.") }; - _renderers = new(); + _renderers = ImmutableArray.Empty; _shutdownEvent = new(false); // Create frame resources for synchronization @@ -75,15 +76,14 @@ internal class RenderSystem : IDisposable Name = "Graphics Render Thread", Priority = ThreadPriority.Normal }; - } - ~RenderSystem() - { - Dispose(); + _disposed = true; } public IRenderer CreateRenderer() { + ObjectDisposedException.ThrowIf(_disposed, this); + var renderer = _engine.CreateRenderer(); ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer)); return renderer; @@ -91,11 +91,15 @@ internal class RenderSystem : IDisposable public void RemoveRenderer(IRenderer renderer) { + ObjectDisposedException.ThrowIf(_disposed, this); + ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Remove(renderer)); } public void Start() { + ObjectDisposedException.ThrowIf(_disposed, this); + if (_isRunning) { return; @@ -107,6 +111,8 @@ internal class RenderSystem : IDisposable public void Stop() { + ObjectDisposedException.ThrowIf(_disposed, this); + if (!_isRunning) { return; @@ -123,12 +129,16 @@ internal class RenderSystem : IDisposable public bool WaitForGPUReady(int timeOut = -1) { + ObjectDisposedException.ThrowIf(_disposed, this); + var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT); return _frameResources[eventIndex].gpuReadyEvent.WaitOne(timeOut); } public void SignalCPUReady() { + ObjectDisposedException.ThrowIf(_disposed, this); + var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT); _frameResources[eventIndex].cpuReadyEvent.Set(); _cpuFenceValue++; @@ -184,7 +194,5 @@ internal class RenderSystem : IDisposable _shutdownEvent.Dispose(); _disposed = true; - - GC.SuppressFinalize(this); } }