using Ghost.Core; using Ghost.Graphics.Data; using System.Collections.Immutable; using Win32; using Win32.Graphics.Direct3D; using Win32.Graphics.Direct3D12; using Win32.Graphics.Dxgi; namespace Ghost.Graphics.D3D12; internal unsafe class GraphicsDevice { #if DEBUG private readonly DebugLayer _debugLayer; #endif private ComPtr _dxgiFactory; private ComPtr _device; private ComPtr _commandQueue; private ImmutableArray _initializeQueue; private ImmutableArray _renderers; private bool _disposed; public ReadOnlySpan InitializeQueue => _initializeQueue.AsSpan(); public ReadOnlySpan Renderers => _renderers.AsSpan(); public ConstPtr NativeDevice => new(_device.Get()); public ConstPtr DXGIFactory => new(_dxgiFactory.Get()); public ConstPtr CommandQueue => new(_commandQueue.Get()); public GraphicsDevice() { #if DEBUG _debugLayer = new DebugLayer(); #endif InitializeDevice(); InitializeCommandQueue(); _initializeQueue = ImmutableArray.Empty; _renderers = ImmutableArray.Empty; } private void InitializeDevice() { #if DEBUG CreateDXGIFactory2(true, __uuidof(), _dxgiFactory.GetVoidAddressOf()); #else CreateDXGIFactory2(false, __uuidof(), _dxgiFactory.GetVoidAddressOf()); #endif using ComPtr adapter = default; for (uint adapterIndex = 0; _dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, GpuPreference.HighPerformance, __uuidof(), adapter.ReleaseAndGetVoidAddressOf()).Success; adapterIndex++) { AdapterDescription1 desc = default; adapter.Get()->GetDesc1(&desc); // Don't select the Basic Render Driver adapter. if ((desc.Flags & AdapterFlags.Software) != AdapterFlags.None) { continue; } if (D3D12CreateDevice((IUnknown*)adapter.Get(), FeatureLevel.Level_12_0, __uuidof(), _device.GetVoidAddressOf()).Success) { break; } } if (_device.Get() == null) { throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0"); } } private void InitializeCommandQueue() { var queueDesc = new CommandQueueDescription { Type = CommandListType.Direct, Priority = (int)CommandQueuePriority.High, Flags = CommandQueueFlags.None, }; fixed (void* queuePtr = &_commandQueue) { _device.Get()->CreateCommandQueue(&queueDesc, __uuidof(), (void**)queuePtr); } } public Renderer CreateRenderer(in SwapChainPresenter presenter) { var renderView = new Renderer(this, in presenter); ImmutableInterlocked.Update(ref _initializeQueue, old => old.Add(renderView)); return renderView; } public void RemoveRenderer(Renderer renderer) { if (renderer is Renderer dx12RenderView) { dx12RenderView.Dispose(); var index = _initializeQueue.IndexOf(dx12RenderView); if (index > -1) { ImmutableInterlocked.Update(ref _initializeQueue, old => old.RemoveAt(index)); } else { ImmutableInterlocked.Update(ref _renderers, old => old.Remove(dx12RenderView)); } } } public void InitializePendingRenderers() { if (_initializeQueue.IsEmpty) { return; } foreach (var renderer in _initializeQueue.AsSpan()) { renderer.Initialize(); } ImmutableInterlocked.Update(ref _renderers, old => old.AddRange(_initializeQueue)); _initializeQueue = _initializeQueue.Clear(); } public void Dispose() { if (_disposed) { return; } foreach (var renderer in _renderers) { renderer.Dispose(); } _commandQueue.Dispose(); _device.Reset(); _dxgiFactory.Dispose(); #if DEBUG _debugLayer.Dispose(); #endif _disposed = true; } }