using Ghost.Graphics.Data; using Ghost.Graphics.RHI; using Win32; using Win32.Graphics.Direct3D12; namespace Ghost.Graphics.D3D12; /// /// D3D12 implementation of buffer interface using resource handles /// internal unsafe class D3D12Buffer : IBuffer { private readonly BufferHandle _handle; private readonly D3D12ResourceAllocator? _allocator; private readonly ComPtr _externalResource; // For externally managed resources private ResourceState _currentState; private void* _mappedPtr; private bool _disposed; public BufferUsage Usage { get; } public MemoryType MemoryType { get; } public string Name { get => field; set { field = value; NativeResource->SetName(field); } } = string.Empty; public ulong Size { get; } public BufferHandle Handle => _handle; public ResourceState CurrentState => _currentState; public ID3D12Resource* NativeResource => _externalResource.Get() == null ? _allocator!.GetResource(_handle.ResourceHandle) : _externalResource.Get(); /// /// Constructor for wrapping existing D3D12 resources /// public D3D12Buffer(ComPtr resource, ulong size, BufferUsage usage, MemoryType memoryType) { _handle = BufferHandle.Invalid; _allocator = null; _externalResource = resource.Move(); Size = size; Usage = usage; MemoryType = memoryType; _currentState = ResourceState.Common; } /// /// Constructor for allocator-managed buffers /// public D3D12Buffer(BufferHandle handle, ref readonly BufferDesc desc, D3D12ResourceAllocator allocator) { _handle = handle; _allocator = allocator; _externalResource = default; Size = desc.Size; Usage = desc.Usage; MemoryType = desc.MemoryType; _currentState = ResourceState.Common; } public void* Map() { if (_mappedPtr != null) { return _mappedPtr; } if (MemoryType != MemoryType.Upload && MemoryType != MemoryType.Readback) { throw new InvalidOperationException("Only upload and readback buffers can be mapped"); } var range = new Win32.Graphics.Direct3D12.Range { Begin = 0, End = 0 }; fixed (void** ptr = &_mappedPtr) { NativeResource->Map(0, &range, ptr); } return _mappedPtr; } public void Unmap() { if (_mappedPtr != null) { NativeResource->Unmap(0, null); _mappedPtr = null; } } public void SetCurrentState(ResourceState state) { _currentState = state; } public void Dispose() { if (_disposed) { return; } Unmap(); if (_handle.IsValid) { // Release resource via allocator _allocator?.ReleaseResource(_handle.ResourceHandle); } else { // Release external resource _externalResource.Dispose(); } _disposed = true; } }