Refactor and enhance resource management and rendering

Updated multiple components to improve encapsulation, maintainability, and performance. Key changes include:

- Upgraded package dependencies in project files.
- Refactored `Mesh` and `RenderingContext` to use properties and added support for per-object constant buffers.
- Improved resource management in `D3D12CommandBuffer`, `D3D12CommandQueue`, and `D3D12ResourceAllocator` with better encapsulation and disposal handling.
- Added validation for constant buffer sizes in `D3D12PipelineLibrary`.
- Simplified `MeshBuilder` methods to accept allocators and removed hardcoded values.
- Enhanced debugging with `GPUResourceLeakException` and resource tracking updates.
- Updated shaders and rendering logic for testing, including hardcoded triangle rendering.
- Removed redundant base classes and interfaces for cleaner code structure.
This commit is contained in:
2025-11-26 01:48:24 +09:00
parent dfe786a2aa
commit 0720444c2c
40 changed files with 1008 additions and 903 deletions

View File

@@ -8,14 +8,16 @@ using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static TerraFX.Aliases.D3D_Alias;
using static TerraFX.Aliases.D3D12_Alias;
using static TerraFX.Aliases.DXGI_Alias;
namespace Ghost.Graphics.D3D12;
internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList10>, ICommandBuffer
internal unsafe class D3D12CommandBuffer : ICommandBuffer
{
private UniquePtr<ID3D12GraphicsCommandList10> _commandList;
private UniquePtr<ID3D12CommandAllocator> _allocator;
private readonly D3D12PipelineLibrary _pipelineLibrary;
@@ -26,12 +28,28 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
private ushort _commandCount;
private bool _isRecording;
private bool _disposed;
public ID3D12GraphicsCommandList10* NativeCommandList => nativeObject.Get();
public SharedPtr<ID3D12GraphicsCommandList10> NativeCommandList => _commandList.Get();
public CommandBufferType Type => _type;
public bool IsEmpty => _commandCount == 0;
public string Name
{
get => field;
set
{
if (field == value)
{
return;
}
field = value;
_commandList.Get()->SetName(value);
}
} = string.Empty;
public D3D12CommandBuffer(
D3D12RenderDevice device,
D3D12PipelineLibrary stateController,
@@ -46,11 +64,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ID3D12GraphicsCommandList10* pCommandList = default;
var commandListType = ConvertCommandBufferType(type);
device.NativeDevice->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
device.NativeDevice->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
device.NativeDevice.Get()->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
device.NativeDevice.Get()->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
_allocator.Attach(pAllocator);
nativeObject.Attach(pCommandList);
_commandList.Attach(pCommandList);
_pipelineLibrary = stateController;
_resourceDatabase = resourceDatabase;
@@ -60,6 +78,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
_isRecording = false;
}
~D3D12CommandBuffer()
{
Dispose();
}
private static D3D12_COMMAND_LIST_TYPE ConvertCommandBufferType(CommandBufferType type)
{
return type switch
@@ -71,6 +94,12 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfRecording()
{
@@ -100,7 +129,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
void ResetCommandList()
{
ThrowIfFailed(_allocator.Get()->Reset());
ThrowIfFailed(nativeObject.Get()->Reset(_allocator.Get(), null));
ThrowIfFailed(_commandList.Get()->Reset(_allocator.Get(), null));
}
void SetBindlessHeap()
@@ -108,14 +137,18 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var heaps = stackalloc ID3D12DescriptorHeap*[2];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
nativeObject.Get()->SetDescriptorHeaps(2, heaps);
_commandList.Get()->SetDescriptorHeaps(2, heaps);
}
ThrowIfDisposed();
ThrowIfRecording();
ResetCommandList();
SetBindlessHeap();
if (Type == CommandBufferType.Graphics || Type == CommandBufferType.Compute)
{
SetBindlessHeap();
}
_commandCount = 0;
_isRecording = true;
@@ -126,7 +159,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfDisposed();
ThrowIfNotRecording();
nativeObject.Get()->Close();
_commandList.Get()->Close();
_isRecording = false;
}
@@ -137,7 +170,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
IncrementCommandCount();
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
nativeObject.Get()->RSSetScissorRects(1, &d3d12Rect);
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
}
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState before, ResourceState after)
@@ -150,7 +183,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(d3d12Resource,
before.ToD3D12States(), after.ToD3D12States());
nativeObject.Get()->ResourceBarrier(1, &barrier);
_commandList.Get()->ResourceBarrier(1, &barrier);
}
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
@@ -168,17 +201,17 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
}
var descriptor = _resourceDatabase.GetResourceInfo(handle.AsResource()).viewGroup;
var descriptor = _resourceDatabase.GetResourceRecord(handle.AsResource()).viewGroup;
pRtvHandles[i] = _descriptorAllocator.GetCpuHandle(descriptor.rtv);
}
var pDsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
if (pDsvHandle != null)
{
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceInfo(depthTarget.AsResource()).viewGroup.dsv);
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceRecord(depthTarget.AsResource()).viewGroup.dsv);
}
nativeObject.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle);
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle);
}
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
@@ -196,7 +229,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
}
var resourceInfo = _resourceDatabase.GetResourceInfo(rtDesc.Texture.AsResource());
var resourceInfo = _resourceDatabase.GetResourceRecord(rtDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.rtv);
var desc = new D3D12_RENDER_PASS_RENDER_TARGET_DESC
@@ -212,6 +245,10 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
Format = resourceInfo.desc.TextureDescription.Format.ToDXGIFormat(),
}
}
},
EndingAccess = new D3D12_RENDER_PASS_ENDING_ACCESS
{
Type = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE
}
};
@@ -226,7 +263,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var pDsvDesc = stackalloc D3D12_RENDER_PASS_DEPTH_STENCIL_DESC[depthDesc.Texture.IsValid ? 1 : 0];
if (pDsvDesc != null)
{
var resourceInfo = _resourceDatabase.GetResourceInfo(depthDesc.Texture.AsResource());
var resourceInfo = _resourceDatabase.GetResourceRecord(depthDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.dsv);
var desc = new D3D12_RENDER_PASS_DEPTH_STENCIL_DESC
@@ -253,7 +290,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
pDsvDesc[0] = desc;
}
nativeObject.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
_commandList.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
allowUAVWrites ? D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES : D3D12_RENDER_PASS_FLAG_NONE);
}
@@ -263,7 +300,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->EndRenderPass();
_commandList.Get()->EndRenderPass();
}
public void SetViewport(ViewportDesc viewport)
@@ -272,8 +309,8 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var d3d12Viewport = new D3D12_VIEWPORT(viewport.Width, viewport.Height, viewport.X, viewport.Y, viewport.MinDepth, viewport.MaxDepth);
nativeObject.Get()->RSSetViewports(1, &d3d12Viewport);
var d3d12Viewport = new D3D12_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
}
public void SetPipelineState(GraphicsPipelineKey pipelineKey)
@@ -288,9 +325,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
#if DEBUG || GHOST_EDITOR
Logger.LogError($"Failed to get graphics pipeline state object for key {pipelineKey}: {psor.Status}");
#endif
return;
}
nativeObject.Get()->SetPipelineState(psor.Value);
_commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
_commandList.Get()->SetPipelineState(psor.Value);
}
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
@@ -300,7 +339,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
IncrementCommandCount();
var resource = _resourceDatabase.GetResource(buffer.AsResource());
nativeObject.Get()->SetGraphicsRootConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, resource->GetGPUVirtualAddress());
_commandList.Get()->SetGraphicsRootConstantBufferView(slot, resource.Get()->GetGPUVirtualAddress());
}
public void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0)
@@ -309,15 +348,15 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
var resource = _resourceDatabase.GetResource(buffer.AsResource());
var vbView = new D3D12_VERTEX_BUFFER_VIEW
{
BufferLocation = pResource->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(pResource->GetDesc().Width - offset),
BufferLocation = resource.Get()->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(resource.Get()->GetDesc().Width - offset),
StrideInBytes = _resourceDatabase.GetResourceDescription(buffer.AsResource()).BufferDescription.Stride
};
nativeObject.Get()->IASetVertexBuffers(slot, 1, &vbView);
_commandList.Get()->IASetVertexBuffers(slot, 1, &vbView);
}
public void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0)
@@ -326,15 +365,15 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
var resource = _resourceDatabase.GetResource(buffer.AsResource());
var ibView = new D3D12_INDEX_BUFFER_VIEW
{
BufferLocation = pResource->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(pResource->GetDesc().Width - offset),
BufferLocation = resource.Get()->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(resource.Get()->GetDesc().Width - offset),
Format = type == IndexType.UInt16 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT
};
nativeObject.Get()->IASetIndexBuffer(&ibView);
_commandList.Get()->IASetIndexBuffer(&ibView);
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
@@ -351,7 +390,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
_ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST
};
nativeObject.Get()->IASetPrimitiveTopology(d3d12Topology);
_commandList.Get()->IASetPrimitiveTopology(d3d12Topology);
}
public void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0)
@@ -360,7 +399,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
_commandList.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
}
public void DrawIndexed(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0)
@@ -369,7 +408,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
}
public void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
@@ -378,7 +417,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
@@ -387,7 +426,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
nativeObject.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchRay()
@@ -398,7 +437,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
// ThrowIfNotRecording();
// IncrementCommandCount();
// nativeObject.Get()->DispatchRays();
// _device.Get()->DispatchRays();
}
public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, ReadOnlySpan<T> data)
@@ -411,19 +450,19 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var sizeInBytes = (uint)(data.Length * sizeof(T));
var uploadHandle = _resourceAllocator.CreateUploadBuffer(sizeInBytes);
var pUploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
void* pMappedData;
pUploadResource->Map(0, null, &pMappedData);
uploadResource.Get()->Map(0, null, &pMappedData);
fixed (T* pData = data)
{
MemoryUtility.MemCpy(pMappedData, pData, sizeInBytes);
MemoryUtility.MemCpy(pData, pMappedData, sizeInBytes);
}
pUploadResource->Unmap(0, null);
uploadResource.Get()->Unmap(0, null);
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
nativeObject.Get()->CopyBufferRegion(pResource, 0, pUploadResource, 0, sizeInBytes);
_commandList.Get()->CopyBufferRegion(pResource, 0, uploadResource, 0, sizeInBytes);
}
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)
@@ -432,10 +471,10 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
ThrowIfNotRecording();
IncrementCommandCount();
var pResource = _resourceDatabase.GetResource(texture.AsResource());
var resource = _resourceDatabase.GetResource(texture.AsResource());
var resourceDesc = pResource->GetDesc();
var requiredSize = GetRequiredIntermediateSize(pResource, 0, (uint)subresources.Length);
var resourceDesc = resource.Get()->GetDesc();
var requiredSize = GetRequiredIntermediateSize(resource, 0, (uint)subresources.Length);
var uploadHandle = _resourceAllocator.CreateUploadBuffer(requiredSize);
var pUploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
@@ -452,8 +491,8 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
}
UpdateSubresources(
(ID3D12GraphicsCommandList*)nativeObject.Get(),
pResource,
(ID3D12GraphicsCommandList*)_commandList.Get(),
resource,
pUploadResource,
0,
0,
@@ -476,17 +515,17 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
if (numBytes == 0)
{
nativeObject.Get()->CopyResource(pDestResource, pSrcResource);
_commandList.Get()->CopyResource(pDestResource, pSrcResource);
}
else
{
nativeObject.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
_commandList.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
}
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
@@ -496,9 +535,11 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
throw new InvalidOperationException("Command buffer is still recording");
}
_commandList.Dispose();
_allocator.Dispose();
_commandCount = 0;
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -1,5 +1,4 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using TerraFX.Interop.DirectX;
@@ -10,18 +9,21 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of command queue interface
/// </summary>
internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, ICommandQueue
internal unsafe class D3D12CommandQueue : ICommandQueue
{
private UniquePtr<ID3D12CommandQueue> _commandQueue;
private UniquePtr<ID3D12Fence1> _fence;
private readonly AutoResetEvent _fenceEvent;
private ulong _fenceValue;
private bool _disposed;
public CommandQueueType Type
{
get;
}
public ID3D12CommandQueue* NativeQueue => nativeObject.Get();
public SharedPtr<ID3D12CommandQueue> NativeQueue => _commandQueue.Get();
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
{
@@ -41,10 +43,15 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
nativeObject.Attach(pQueue);
_commandQueue.Attach(pQueue);
_fence.Attach(pFence);
}
~D3D12CommandQueue()
{
Dispose();
}
private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
{
return type switch
@@ -58,7 +65,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
public void Submit(ICommandBuffer commandBuffer)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
if (commandBuffer.IsEmpty)
{
@@ -68,8 +75,8 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer)
{
var commandList = d3d12CommandBuffer.NativeCommandList;
var commandListPtr = (ID3D12CommandList*)commandList;
nativeObject.Get()->ExecuteCommandLists(1, &commandListPtr);
var commandListPtr = (ID3D12CommandList*)commandList.Get();
_commandQueue.Get()->ExecuteCommandLists(1, &commandListPtr);
}
else
{
@@ -79,7 +86,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
executableIndices.Fill(-1);
@@ -107,7 +114,7 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
if (commandBuffers[cmdIndex] is D3D12CommandBuffer d3d12CommandBuffer)
{
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList;
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList.Get();
}
else
{
@@ -117,21 +124,21 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
currentIndex++;
}
nativeObject.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
_commandQueue.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
}
public ulong Signal(ulong value)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
_fenceValue = value;
nativeObject.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue);
return _fenceValue;
}
public void WaitForValue(ulong value)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
if (_fence.Get()->GetCompletedValue() < value)
{
@@ -145,28 +152,30 @@ internal unsafe class D3D12CommandQueue : D3D12Object<ID3D12CommandQueue>, IComm
public ulong GetCompletedValue()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
return _fence.Get()->GetCompletedValue();
}
public void WaitIdle()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
WaitForValue(fenceValue);
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
_fenceEvent?.Dispose();
_commandQueue.Dispose();
_fence.Dispose();
_fenceEvent?.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -105,7 +105,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
public ISwapChain CreateSwapChain(SwapChainDesc desc)
{
ThrowIfDisposed();
return new D3D12SwapChain(_resourceDatabase, _device.DXGIFactory, ((D3D12CommandQueue)_device.GraphicsQueue).NativeQueue, desc);
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc);
}
public void BeginFrame()
@@ -152,6 +152,7 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
_resourceDatabase.Dispose();
_descriptorAllocator.Dispose();
_shaderCompiler.Dispose();
_device.Dispose();
#if DEBUG
_debugLayer.Dispose();

View File

@@ -1,132 +0,0 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.D3D12;
internal abstract class D3D12RHIObject : IRHIObject
{
public string Name
{
get; set;
} = string.Empty;
}
internal abstract unsafe class D3D12Object<T> : IRHIObject, IDisposable
where T : unmanaged, ID3D12Object.Interface
{
private bool _disposed;
private string _name = string.Empty;
protected UniquePtr<T> nativeObject;
protected bool Disposed => _disposed;
public string Name
{
get => _name;
set
{
if (_name == value)
{
return;
}
_name = value;
if (nativeObject.Get() != null)
{
nativeObject.Get()->SetName(value);
}
}
}
~D3D12Object()
{
Dispose(false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
nativeObject.Dispose();
_disposed = true;
}
}
internal abstract class IUnknownObject<T> : IRHIObject, IDisposable
where T : unmanaged, IUnknown.Interface
{
private bool _disposed;
private string _name = string.Empty;
protected UniquePtr<T> nativeObject;
protected bool Disposed => _disposed;
public string Name
{
get => _name;
set
{
if (_name == value)
{
return;
}
_name = value;
}
}
~IUnknownObject()
{
Dispose(false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
nativeObject.Dispose();
_disposed = true;
}
}

View File

@@ -31,7 +31,7 @@ internal struct D3D12PipelineState : IDisposable
}
}
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
{
private readonly D3D12RenderDevice _device;
private readonly D3D12ResourceDatabase _resourceDatabase;
@@ -153,7 +153,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
}
ID3D12RootSignature* pRootSignature = default;
ThrowIfFailed(_device.NativeDevice->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(),
ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(),
__uuidof(pRootSignature), (void**)&pRootSignature));
_defaultRootSignature.Attach(pRootSignature);
@@ -183,12 +183,12 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var fileBytes = File.ReadAllBytes(filePath!);
fixed (byte* pFileBytes = fileBytes)
{
ThrowIfFailed(_device.NativeDevice->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
}
}
else
{
ThrowIfFailed(_device.NativeDevice->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
}
_library.Attach(pLibrary);
@@ -213,18 +213,26 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
private static Result<CBufferInfo> ValidateReflectionData(ShaderReflectionData reflectionData)
{
CBufferInfo cbufferInfo;
var cbufferInfo = default(CBufferInfo);
foreach (var info in reflectionData.ResourcesBindings)
{
if (info.BindPoint > 3)
if (info.BindPoint >= RootSignatureLayout.ROOT_PARAMETER_COUNT)
{
return Result.Failure($"Resource binding point {info.BindPoint} is out of range. Only binding points 0-3 are supported in the current root signature.");
}
if (info.Type != ShaderInputType.ConstantBuffer)
{
return Result.Failure($"Resource binding type {info.Type} is not supported. Only constant buffers are supported in the current root signature.");
return Result.Failure($"Resource binding type {info.Type} is not supported. Please consider using bindless resources for buffers, textures and samplers.");
}
if (info.BindPoint == RootSignatureLayout.PER_OBJECT_BUFFER_SLOT)
{
if (info.Size != sizeof(PerObjectData))
{
return Result.Failure($"Per-object constant buffer size mismatch. Expected size: {sizeof(PerObjectData)}, Actual size: {info.Size}");
}
}
if (info.BindPoint == RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT)
@@ -237,19 +245,15 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
SizeInBytes = info.Size,
Properties = info.Properties ?? Array.Empty<CBufferPropertyInfo>(),
};
return Result.Success(cbufferInfo);
}
}
return Result.Failure("Per-material constant buffer not found in shader reflection data.");
// TODO: Validate Cbuffer sizes and bindings.
return Result.Success(cbufferInfo);
}
private static D3D12_COMPARISON_FUNC ToD3DCompare(ZTestOptions z) => z switch
{
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_ALWAYS,
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_NEVER,
ZTestOptions.Less => D3D12_COMPARISON_FUNC_LESS,
ZTestOptions.LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL,
ZTestOptions.Equal => D3D12_COMPARISON_FUNC_EQUAL,
@@ -284,7 +288,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
return Result.Failure("Validation of pixel shader reflection data failed: " + psr.Message);
}
if (msr.Value != psr.Value)
if (msr.Value.Properties != null
&& msr.Value.SizeInBytes != psr.Value.SizeInBytes)
{
return Result.Failure("Mesh shader and pixel shader constant buffer layouts do not match.");
}
@@ -297,12 +302,14 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
return Result.Failure("Validation of task shader reflection data failed: " + tsr.Message);
}
if (tsr.Value != msr.Value)
if (tsr.Value.Properties != null
&& tsr.Value.SizeInBytes != psr.Value.SizeInBytes)
{
return Result.Failure("Task shader and mesh shader constant buffer layouts do not match.");
return Result.Failure("Task shader and pixel shader constant buffer layouts do not match.");
}
}
// ts and ms may not use per material cbuffer at all, so we return the psr value.
return psr.Value;
}
@@ -396,7 +403,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
if (hr == E.E_INVALIDARG)
{
// Pipeline not found in the library, create a new one.
ThrowIfFailed(_device.NativeDevice->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
ThrowIfFailed(_library.Get()->StorePipeline(pKeyStr, pPipelineState));
}
else

View File

@@ -12,8 +12,9 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of the render device interface
/// </summary>
internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDevice
internal unsafe class D3D12RenderDevice : IRenderDevice
{
private UniquePtr<ID3D12Device14> _device;
private UniquePtr<IDXGIFactory7> _dxgiFactory;
private UniquePtr<IDXGIAdapter1> _adapter;
@@ -21,21 +22,35 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
private readonly D3D12CommandQueue _computeQueue;
private readonly D3D12CommandQueue _copyQueue;
private bool _disposed;
public ICommandQueue GraphicsQueue => _graphicsQueue;
public ICommandQueue ComputeQueue => _computeQueue;
public ICommandQueue CopyQueue => _copyQueue;
public IDXGIFactory7* DXGIFactory => _dxgiFactory.Get();
public ID3D12Device14* NativeDevice => nativeObject.Get();
public IDXGIAdapter1* Adapter => _adapter.Get();
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Get();
public SharedPtr<ID3D12Device14> NativeDevice => _device.Get();
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Get();
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeQueue;
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeQueue;
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeQueue;
public D3D12RenderDevice()
{
IDXGIFactory7* pFactory = default;
#if DEBUG
ThrowIfFailed(CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory));
#else
ThrowIfFailed(CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory));
#endif
_dxgiFactory.Attach(pFactory);
InitializeDevice();
_graphicsQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Graphics);
_computeQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Compute);
_copyQueue = new D3D12CommandQueue(nativeObject.Get(), CommandQueueType.Copy);
_graphicsQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Graphics);
_computeQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Compute);
_copyQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Copy);
}
~D3D12RenderDevice()
@@ -45,15 +60,6 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
private void InitializeDevice()
{
IDXGIFactory7* pFactory = default;
#if DEBUG
CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory);
#else
CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory);
#endif
_dxgiFactory.Attach(pFactory);
ID3D12Device14* pDevice = default;
IDXGIAdapter1* pAdapter = default;
@@ -86,17 +92,17 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
}
nativeObject.Attach(pDevice);
_device.Attach(pDevice);
}
public FeatureSupport GetFeatureSupport()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
FeatureSupport support = FeatureSupport.None;
D3D12_FEATURE_DATA_D3D12_OPTIONS options = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
{
if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3)
{
@@ -105,7 +111,7 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
}
D3D12_FEATURE_DATA_D3D12_OPTIONS5 options5 = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
{
if (options5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
{
@@ -114,7 +120,7 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
}
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
{
if (options6.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
{
@@ -123,7 +129,7 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
}
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = default;
if (nativeObject.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
{
if (options7.MeshShaderTier != D3D12_MESH_SHADER_TIER_NOT_SUPPORTED)
{
@@ -139,9 +145,9 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
return support;
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
@@ -150,9 +156,11 @@ internal unsafe class D3D12RenderDevice : D3D12Object<ID3D12Device14>, IRenderDe
_computeQueue.Dispose();
_copyQueue.Dispose();
_device.Dispose();
_dxgiFactory.Dispose();
_adapter.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -10,16 +10,17 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of the renderer interface using RHI abstractions
/// </summary>
internal unsafe class D3D12Renderer : IRenderer
internal class D3D12Renderer : IRenderer
{
private struct FrameResource : IDisposable
{
public ICommandBuffer commandBuffer;
public ulong fenceValue;
public FrameResource(D3D12GraphicsEngine graphicsEngine)
public FrameResource(D3D12GraphicsEngine graphicsEngine, int index)
{
commandBuffer = graphicsEngine.CreateCommandBuffer();
commandBuffer.Name = $"Frame Command Buffer {index}";
fenceValue = 0;
}
@@ -67,7 +68,7 @@ internal unsafe class D3D12Renderer : IRenderer
_frameResources = new FrameResource[D3D12PipelineResource.BACK_BUFFER_COUNT];
for (var i = 0; i < _frameResources.Length; i++)
{
_frameResources[i] = new FrameResource(graphicsEngine);
_frameResources[i] = new FrameResource(graphicsEngine, i);
}
_renderTarget = Handle<Texture>.Invalid;
@@ -108,6 +109,12 @@ internal unsafe class D3D12Renderer : IRenderer
CreateOffScreenRenderTarget(swapChain.Width, swapChain.Height);
}
var newSize = swapChain != null ? new uint2(swapChain.Width, swapChain.Height) : _currentSize;
if (!math.all(newSize == _currentSize))
{
RequestResize(newSize);
}
_swapChain = swapChain;
}
@@ -171,9 +178,22 @@ internal unsafe class D3D12Renderer : IRenderer
frame.commandBuffer.Begin();
// NOTE: Temperary solution: render directly to the swap chain back buffer if available.
// HACK: This is hard coded for testing purposes only.
var rt = _swapChain?.GetCurrentBackBuffer() ?? _renderTarget;
if(_swapChain != null)
{
frame.commandBuffer.ResourceBarrier(rt.AsResource(), ResourceState.Present, ResourceState.RenderTarget);
}
RenderScene(rt, frame.commandBuffer);
if (_swapChain != null)
{
frame.commandBuffer.ResourceBarrier(rt.AsResource(), ResourceState.RenderTarget, ResourceState.Present);
}
// if (_swapChain != null)
// {
// var backBufferRT = _swapChain.GetCurrentBackBuffer();
@@ -196,12 +216,14 @@ internal unsafe class D3D12Renderer : IRenderer
{
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
Span<PassRenderTargetDesc> rtDesc = stackalloc PassRenderTargetDesc[1];
rtDesc[0] = new PassRenderTargetDesc
{
Texture = target,
ClearColor = clearColor,
};
Span<PassRenderTargetDesc> rtDesc =
[
new PassRenderTargetDesc
{
Texture = target,
ClearColor = clearColor,
},
];
var depthDesc = new PassDepthStencilDesc
{
@@ -217,11 +239,10 @@ internal unsafe class D3D12Renderer : IRenderer
_pass.Initialize(ref ctx);
}
cmd.BeginRenderPass(rtDesc, depthDesc, false);
var viewport = new ViewportDesc { Width = _currentSize.x, Height = _currentSize.y, MinDepth = 0, MaxDepth = 1 };
var scissor = new RectDesc { Right = _currentSize.x, Bottom = _currentSize.y };
cmd.BeginRenderPass(rtDesc, depthDesc, false);
cmd.SetViewport(viewport);
cmd.SetScissorRect(scissor);

View File

@@ -3,6 +3,7 @@ using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.Mathematics;
using System.Runtime.CompilerServices;
@@ -132,13 +133,13 @@ internal sealed unsafe partial class D3D12ResourceAllocator
var resourceDesc = pResource->GetDesc();
var srvDesc = new D3D12_SHADER_RESOURCE_VIEW_DESC
{
Format = resourceDesc.Format,
ViewDimension = D3D12_SRV_DIMENSION_BUFFER,
Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
};
if (isRaw)
{
srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Buffer.NumElements = (uint)(resourceDesc.Width / 4u);
srvDesc.Buffer.StructureByteStride = 0;
@@ -146,6 +147,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator
}
else // Assumes Structured
{
srvDesc.Format = resourceDesc.Format;
srvDesc.Buffer.FirstElement = 0;
srvDesc.Buffer.NumElements = (uint)(resourceDesc.Width / stride);
srvDesc.Buffer.StructureByteStride = stride;
@@ -406,12 +408,12 @@ internal sealed unsafe partial class D3D12ResourceAllocator
var resourceDesc = pResource->GetDesc();
var uavDesc = new D3D12_UNORDERED_ACCESS_VIEW_DESC
{
Format = resourceDesc.Format,
ViewDimension = D3D12_UAV_DIMENSION_BUFFER,
};
if (isRaw)
{
uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = (uint)(resourceDesc.Width / 4u);
uavDesc.Buffer.StructureByteStride = 0;
@@ -419,6 +421,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator
}
else // Assumes Structured
{
uavDesc.Format = resourceDesc.Format;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = (uint)(resourceDesc.Width / stride);
uavDesc.Buffer.StructureByteStride = stride;
@@ -521,6 +524,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator
// Default to Common, but check for specific roles
var state = D3D12_RESOURCE_STATE_COMMON;
return state;
if (usage.HasFlag(BufferUsage.Vertex) || usage.HasFlag(BufferUsage.Constant))
{
@@ -591,8 +595,10 @@ internal sealed unsafe partial class D3D12ResourceAllocator
// TODO: Thread safety for resource allocator
// A common solution is to use ticket. Each pAllocation request create a ticket and put it into a thread-safe queue. A dedicated thread process the queue and fulfill the requests.
internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D12MA_Allocator>, IResourceAllocator
internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
{
private UniquePtr<D3D12MA_Allocator> _d3d12MA;
private readonly IFenceSynchronizer _fenceSynchronizer;
private readonly D3D12RenderDevice _device;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
@@ -601,6 +607,8 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
private UnsafeQueue<Handle<GPUResource>> _temResources;
private bool _disposed;
public D3D12ResourceAllocator(
IFenceSynchronizer fenceSynchronizer,
D3D12RenderDevice device,
@@ -610,14 +618,14 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
{
var desc = new D3D12MA_ALLOCATOR_DESC
{
pAdapter = (IDXGIAdapter*)device.Adapter,
pDevice = (ID3D12Device*)device.NativeDevice,
pAdapter = (IDXGIAdapter*)device.Adapter.Get(),
pDevice = (ID3D12Device*)device.NativeDevice.Get(),
Flags = D3D12MA_ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED | D3D12MA_ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED,
};
D3D12MA_Allocator* pAllocator = default;
ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator));
nativeObject.Attach(pAllocator);
_d3d12MA.Attach(pAllocator);
_fenceSynchronizer = fenceSynchronizer;
_device = device;
@@ -648,7 +656,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool isTemp = false)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
CheckTexture2DSize(desc.Width, desc.Height);
@@ -706,7 +714,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var initialState = DetermineInitialTextureState(desc.Usage);
D3D12MA_Allocation* pAllocation = default;
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
var resourceDescriptor = ResourceViewGroup.Invalid;
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
@@ -717,7 +725,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
var srvDesc = CreateTextureSrvDesc(pAllocation->GetResource(), mipLevels, desc.Slice, isCubeMap);
_device.NativeDevice->CreateShaderResourceView(pAllocation->GetResource(), &srvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateShaderResourceView(pAllocation->GetResource(), &srvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(TextureUsage.RenderTarget))
@@ -726,7 +734,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv);
var rtvDesc = CreateRtvDesc(pAllocation->GetResource());
_device.NativeDevice->CreateRenderTargetView(pAllocation->GetResource(), &rtvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateRenderTargetView(pAllocation->GetResource(), &rtvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(TextureUsage.DepthStencil))
@@ -735,7 +743,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv);
var dsvDesc = CreateDsvDesc(pAllocation->GetResource());
_device.NativeDevice->CreateDepthStencilView(pAllocation->GetResource(), &dsvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateDepthStencilView(pAllocation->GetResource(), &dsvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
@@ -744,7 +752,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
var uavDesc = CreateTextureUavDesc(pAllocation->GetResource());
_device.NativeDevice->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle);
_device.NativeDevice.Get()->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle);
}
var handle = TrackResource(pAllocation, initialState, resourceDescriptor, ResourceDesc.Texture(desc), isTemp);
@@ -754,24 +762,27 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, bool isTemp = false)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var textureDesc = desc.ToTextureDescripton();
return CreateTexture(ref textureDesc, isTemp);
return CreateTexture(in textureDesc, isTemp);
}
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, bool isTemp = false)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
CheckBufferSize(desc.Size);
var resourceDescription = D3D12_RESOURCE_DESC.Buffer(desc.Size, ConvertBufferUsage(desc.Usage));
var isRaw = desc.Usage.HasFlag(BufferUsage.Raw);
if (isRaw)
var alignedSize = desc.Size;
if (desc.Usage.HasFlag(BufferUsage.Constant))
{
resourceDescription.Format = DXGI_FORMAT_R32_TYPELESS;
// D3D12 CBV size must be 256-byte aligned
alignedSize = (uint)(desc.Size + 255) & ~255u;
}
var resourceDescription = D3D12_RESOURCE_DESC.Buffer(alignedSize, ConvertBufferUsage(desc.Usage));
var isRaw = desc.Usage.HasFlag(BufferUsage.Raw);
var allocationDesc = new D3D12MA_ALLOCATION_DESC
{
HeapType = ConvertMemoryType(desc.MemoryType),
@@ -781,42 +792,41 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
var initialState = DetermineInitialBufferState(desc.Usage, desc.MemoryType);
D3D12MA_Allocation* pAllocation = default;
ThrowIfFailed(nativeObject.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, &pAllocation, Win32Utility.IID_NULL, null));
var iid = IID.IID_NULL;
ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, &pAllocation, &iid, null));
var resourceDescriptor = ResourceViewGroup.Invalid;
var pResource = pAllocation->GetResource();
if (desc.Usage.HasFlag(BufferUsage.Constant))
{
// D3D12 CBV size must be 256-byte aligned
var alignedSize = (uint)(desc.Size + 255) & ~255u;
resourceDescriptor.cbv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.cbv);
var cbvDesc = new D3D12_CONSTANT_BUFFER_VIEW_DESC
{
BufferLocation = pResource->GetGPUVirtualAddress(),
SizeInBytes = alignedSize
SizeInBytes = (uint)alignedSize
};
_device.NativeDevice->CreateConstantBufferView(&cbvDesc, _descriptorAllocator.GetCpuHandle(resourceDescriptor.cbv));
_device.NativeDevice.Get()->CreateConstantBufferView(&cbvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(BufferUsage.ShaderResource))
{
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.srv);
var srvDesc = CreateBufferSrvDesc(pAllocation->GetResource(), desc.Stride, isRaw);
_device.NativeDevice->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
_device.NativeDevice.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
}
if (desc.Usage.HasFlag(BufferUsage.UnorderedAccess))
{
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.uav);
var uavDesc = CreateBufferUavDesc(pAllocation->GetResource(), desc.Stride, isRaw);
_device.NativeDevice->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
_device.NativeDevice.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
}
var handle = TrackResource(pAllocation, initialState, resourceDescriptor, ResourceDesc.Buffer(desc), isTemp);
@@ -825,7 +835,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public Handle<GraphicsBuffer> CreateUploadBuffer(ulong size, bool isTemp = true)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var desc = new BufferDesc
{
@@ -834,18 +844,18 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
MemoryType = ResourceMemoryType.Upload,
};
return CreateBuffer(ref desc, isTemp);
return CreateBuffer(in desc, isTemp);
}
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var vertexBufferDesc = new BufferDesc
{
Size = (uint)(vertices.Count * Unsafe.SizeOf<Vertex>()),
Stride = (uint)Unsafe.SizeOf<Vertex>(),
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource,
Size = (uint)(vertices.Count * sizeof(Vertex)),
Stride = (uint)sizeof(Vertex),
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
@@ -853,37 +863,47 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
{
Size = (uint)(indices.Count * sizeof(uint)),
Stride = sizeof(uint),
Usage = BufferUsage.Index | BufferUsage.ShaderResource,
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default,
};
var vertexBuffer = CreateBuffer(ref vertexBufferDesc);
var indexBuffer = CreateBuffer(ref indexBufferDesc);
var objectBufferDesc = new BufferDesc
{
Size = (uint)sizeof(PerObjectData),
Stride = (uint)sizeof(PerObjectData),
Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default,
};
var vertexBuffer = CreateBuffer(in vertexBufferDesc);
var indexBuffer = CreateBuffer(in indexBufferDesc);
var objectBuffer = CreateBuffer(in objectBufferDesc);
var data = new Mesh
{
vertices = vertices,
indices = indices,
vertexBuffer = vertexBuffer,
indexBuffer = indexBuffer,
Vertices = vertices,
Indices = indices,
VertexBuffer = vertexBuffer,
IndexBuffer = indexBuffer,
ObjectDataBuffer = objectBuffer,
};
return _resourceDatabase.AddMesh(ref data);
return _resourceDatabase.AddMesh(in data);
}
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var material = new Material();
material.SetShader(shader, this, _resourceDatabase);
return _resourceDatabase.AddMaterial(ref material);
return _resourceDatabase.AddMaterial(in material);
}
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var shader = new Shader(descriptor);
foreach (var pass in descriptor.passes)
@@ -893,6 +913,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
continue;
}
// TODO: Cache the pass key because we hash it multiple times right now.
var passKey = new ShaderPassKey(fullPass.Identifier);
var cbr = _pipelineLibrary.GetCBufferInfo(passKey);
if (cbr.Status != ResultStatus.Success)
@@ -908,7 +929,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
public void ReleaseTempResources()
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
while (_temResources.Count > 0)
{
@@ -933,9 +954,9 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
}
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
@@ -952,8 +973,10 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IUnknownObject<D3D
_resourceDatabase.ReleaseResource(handle);
}
_d3d12MA.Dispose();
_temResources.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -8,11 +8,10 @@ using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.D3D12;
internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
internal class D3D12ResourceDatabase : IResourceDatabase
{
internal unsafe struct ResourceRecord
{
@@ -56,12 +55,12 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
this.desc = desc;
}
public ResourceRecord(ID3D12Resource* resource, ResourceState state)
public ResourceRecord(ID3D12Resource* resource, ResourceState state, ResourceViewGroup viewGroup)
{
this.resourceUnion = new ResourceUnion(resource);
this.isExternal = true;
this.viewGroup = default;
this.viewGroup = viewGroup;
this.cpuFenceValue = ~0u;
this.state = state;
this.desc = ResourceDesc.FromD3D12(resource->GetDesc());
@@ -80,13 +79,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
{
refCount = resourceUnion.allocation.Get()->Release();
}
resourceUnion = default;
viewGroup = default;
}
descriptorAllocator.Release(viewGroup);
resourceUnion = default;
viewGroup = default;
return refCount;
}
}
@@ -107,15 +106,15 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
{
_resources = new(64, Allocator.Persistent, AllocationOption.Clear);
_resources = new UnsafeSlotMap<ResourceRecord>(64, Allocator.Persistent, AllocationOption.Clear);
#if DEBUG || GHOST_EDITOR
_resourceName = new(64);
_resourceName = new Dictionary<Handle<GPUResource>, string>(64);
#endif
_meshes = new(64, Allocator.Persistent, AllocationOption.Clear);
_materials = new(16, Allocator.Persistent, AllocationOption.Clear);
_shaders = new(16);
_shaderPasses = new(16);
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent, AllocationOption.Clear);
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
_shaders = new DynamicArray<Shader?>(16);
_shaderPasses = new Dictionary<ShaderPassKey, ShaderPass>(16);
_descriptorAllocator = descriptorAllocator;
}
@@ -132,11 +131,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
resource = default!;
}
public unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, string? name = null)
public unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, ResourceViewGroup viewGroup, string? name = null)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = _resources.Add(new ResourceRecord(pResource, initialState), out var generation);
var id = _resources.Add(new ResourceRecord(pResource, initialState, viewGroup), out var generation);
var handle = new Handle<GPUResource>(id, generation);
#if DEBUG || GHOST_EDITOR
@@ -172,7 +171,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
return _resources.Contains(handle.id, handle.generation);
}
public ref ResourceRecord GetResourceInfo(Handle<GPUResource> handle)
public ref ResourceRecord GetResourceRecord(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -191,11 +190,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist);
}
public unsafe ID3D12Resource* GetResource(Handle<GPUResource> handle)
public unsafe SharedPtr<ID3D12Resource> GetResource(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ref var info = ref GetResourceInfo(handle);
ref var info = ref GetResourceRecord(handle);
if (!info.Allocated)
{
return null;
@@ -207,7 +206,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ResourceState GetResourceState(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return GetResourceInfo(handle).state;
return GetResourceRecord(handle).state;
}
public void SetResourceState(Handle<GPUResource> handle, ResourceState state)
@@ -226,7 +225,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public ResourceDesc GetResourceDescription(Handle<GPUResource> handle)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return GetResourceInfo(handle).desc;
return GetResourceRecord(handle).desc;
}
public int GetBindlessIndex(Handle<GPUResource> handle)
@@ -273,10 +272,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
var refCount = info.Release(_descriptorAllocator);
#if DEBUG || GHOST_EDITOR
_resourceName.Remove(handle, out var name);
if (refCount > 0)
{
throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource");
}
//if (refCount > 0)
//{
// throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource");
//}
//Debug.Assert(refCount == 0, "Resource released with non-zero reference count.");
#endif
_resources.Remove(handle.id, handle.generation);
@@ -435,10 +435,9 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
public void Dispose()
{
[Conditional("DEBUG"), Conditional("GHOST_EDITOR")]
static void ThrowMemoryLeakException(string resourceType, int count)
{
throw new InvalidOperationException($"ResourceAllocator is being disposed with {count} {resourceType} still registered. Ensure all resources are released before disposing.");
throw new MemoryLeakException($"ResourceAllocator is being disposed with {count} {resourceType} still registered. Ensure all resources are released before disposing.");
}
if (_disposed)

View File

@@ -1,9 +1,10 @@
using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
@@ -17,12 +18,19 @@ namespace Ghost.Graphics.D3D12;
/// <summary>
/// D3D12 implementation of swap chain interface
/// </summary>
internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapChain
internal unsafe class D3D12SwapChain : ISwapChain
{
private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12RenderDevice _renderDevice;
private UniquePtr<IDXGISwapChain4> _swapChain;
private UnsafeArray<Handle<Texture>> _backBuffers;
private object? _compositionSurface;
private bool _disposed;
public uint Width
{
get; private set;
@@ -38,9 +46,11 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
get;
}
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, IDXGIFactory7* pFactory, ID3D12CommandQueue* pCommandQueue, SwapChainDesc desc)
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc)
{
_resourceDatabase = resourceDatabase;
_descriptorAllocator = descriptorAllocator;
_renderDevice = device;
_backBuffers = new UnsafeArray<Handle<Texture>>(D3D12PipelineResource.BACK_BUFFER_COUNT, Allocator.Persistent);
@@ -48,11 +58,18 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
Height = desc.height;
BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT;
CreateSwapChain(pFactory, pCommandQueue, desc);
CreateSwapChain(desc);
CreateBackBuffers();
_compositionSurface = desc.target.compositionSurface;
}
private void CreateSwapChain(IDXGIFactory7* pFactory, ID3D12CommandQueue* commandQueue, SwapChainDesc desc)
~D3D12SwapChain()
{
Dispose();
}
private void CreateSwapChain(SwapChainDesc desc)
{
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
{
@@ -71,10 +88,13 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
IDXGISwapChain1* pTempSwapChain = default;
var pFactory = _renderDevice.DXGIFactory.Get();
var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get();
switch (desc.target.type)
{
case SwapChainTargetType.Composition:
ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)commandQueue, &swapChainDesc, null, &pTempSwapChain));
ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)pCommandQueue, &swapChainDesc, null, &pTempSwapChain));
// Set the composition surface
if (desc.target.compositionSurface != null)
@@ -91,7 +111,7 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
};
pFactory->CreateSwapChainForHwnd(
(IUnknown*)commandQueue,
(IUnknown*)pCommandQueue,
new HWND(desc.target.windowHandle.ToPointer()),
&swapChainDesc,
&swapChainFullscreenDesc,
@@ -107,7 +127,7 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
pTempSwapChain->Release();
nativeObject.Attach(pSwapChain);
_swapChain.Attach(pSwapChain);
}
private void CreateBackBuffers()
@@ -115,33 +135,37 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
for (uint i = 0; i < BufferCount; i++)
{
ID3D12Resource* pBackBuffer = default;
nativeObject.Get()->GetBuffer(i, __uuidof(pBackBuffer), (void**)&pBackBuffer);
ThrowIfFailed(_swapChain.Get()->GetBuffer(i, __uuidof(pBackBuffer), (void**)&pBackBuffer));
pBackBuffer->SetName($"SwapChain_BackBuffer_{i}");
_backBuffers[i] = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present).AsTexture();
var rtv = _descriptorAllocator.AllocateRTV();
_renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, _descriptorAllocator.GetCpuHandle(rtv));
var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present, new ResourceViewGroup() { rtv = rtv });
_backBuffers[i] = handle.AsTexture();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Handle<Texture> GetCurrentBackBuffer()
{
ThrowIfDisposed();
return _backBuffers[nativeObject.Get()->GetCurrentBackBufferIndex()];
ObjectDisposedException.ThrowIf(_disposed, this);
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
}
public void Present(bool vsync = true)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
var presentFlags = 0u;
var syncInterval = vsync ? 1u : 0u;
ThrowIfFailed(nativeObject.Get()->Present(syncInterval, presentFlags));
ThrowIfFailed(_swapChain.Get()->Present(syncInterval, presentFlags));
}
public void Resize(uint width, uint height)
{
ThrowIfDisposed();
ObjectDisposedException.ThrowIf(_disposed, this);
if (Width == width && Height == height)
{
@@ -155,7 +179,7 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
}
// Resize the swap chain
if (nativeObject.Get()->ResizeBuffers(BufferCount, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING).FAILED)
if (_swapChain.Get()->ResizeBuffers(BufferCount, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING).FAILED)
{
throw new InvalidOperationException("Failed to resize swap chain buffers.");
}
@@ -167,20 +191,28 @@ internal unsafe class D3D12SwapChain : IUnknownObject<IDXGISwapChain4>, ISwapCha
CreateBackBuffers();
}
protected override void Dispose(bool disposing)
public void Dispose()
{
if (Disposed)
if (_disposed)
{
return;
}
if (_compositionSurface != null)
{
using var panelNative = ISwapChainPanelNative.FromSwapChainPanel(_compositionSurface);
panelNative.SetSwapChain(IntPtr.Zero);
}
for (var i = 0; i < _backBuffers.Count; i++)
{
_resourceDatabase.ReleaseResource(_backBuffers[i].AsResource());
}
_backBuffers.Dispose();
_swapChain.Dispose();
base.Dispose(disposing);
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -66,7 +66,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
HeapType = type;
NumDescriptors = numDescriptors;
ShaderVisible = type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
Stride = device.NativeDevice->GetDescriptorHandleIncrementSize(type);
Stride = device.NativeDevice.Get()->GetDescriptorHandleIncrementSize(type);
_dynamicHeapStart = Math.Clamp(dynamicHeapStart, 0, numDescriptors);
_currentDynamicOffset = 0;
@@ -254,14 +254,14 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
}
var newLocation = AllocateDescriptors(count);
_device.NativeDevice->CopyDescriptorsSimple((uint)count, GetCpuHandle(index), GetCpuHandle(newLocation), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandle(index), GetCpuHandle(newLocation), HeapType);
return newLocation;
}
public readonly void CopyToShaderVisibleHeap(int index, int count = 1)
{
_device.NativeDevice->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
}
private bool AllocateResources(int numDescriptors)
@@ -279,7 +279,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
};
ID3D12DescriptorHeap* pHeap = default;
var hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
var hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
if (hr.FAILED)
{
return false;
@@ -291,11 +291,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
if (!_allocatedDescriptors.IsCreated)
{
_allocatedDescriptors = new UnsafeArray<bool>(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
_allocatedDescriptors = new UnsafeArray<bool>(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption.Clear);
}
else
{
_allocatedDescriptors.Resize(numDescriptors);
_allocatedDescriptors.Resize(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption.Clear);
}
if (ShaderVisible)
@@ -303,7 +303,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
ID3D12DescriptorHeap* pShaderVisibleHeap = default;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
if (hr.FAILED)
{
return false;
@@ -332,11 +332,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
return false;
}
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
if (_shaderVisibleHeap.Get() != null)
{
_device.NativeDevice->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
}
}
finally
@@ -350,12 +350,7 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
/// <inheritdoc />
public void Dispose()
{
#if DEBUG
if (NumAllocatedDescriptors > 0)
{
Debug.WriteLine($"Warning: Descriptor heap of type {HeapType} is being disposed with {NumAllocatedDescriptors} allocated descriptors.");
}
#endif
Debug.Assert(NumAllocatedDescriptors == 0);
_heap.Dispose();
_shaderVisibleHeap.Dispose();