/// <summary>
    /// Rents a <see cref="ID3D12GraphicsCommandList"/> and <see cref="ID3D12CommandAllocator"/> pair.
    /// </summary>
    /// <param name="d3D12Device">The <see cref="ID3D12Device"/> renting the command list.</param>
    /// <param name="d3D12PipelineState">The <see cref="ID3D12PipelineState"/> instance to use for the new command list.</param>
    /// <param name="d3D12CommandList">The resulting <see cref="ID3D12GraphicsCommandList"/> value.</param>
    /// <param name="d3D12CommandAllocator">The resulting <see cref="ID3D12CommandAllocator"/> value.</param>
    public void Rent(ID3D12Device *d3D12Device, ID3D12PipelineState *d3D12PipelineState, out ID3D12GraphicsCommandList *d3D12CommandList, out ID3D12CommandAllocator *d3D12CommandAllocator)
    {
        lock (this.d3D12CommandListBundleQueue)
        {
            if (this.d3D12CommandListBundleQueue.TryDequeue(out D3D12CommandListBundle d3D12CommandListBundle))
            {
                d3D12CommandList      = d3D12CommandListBundle.D3D12CommandList;
                d3D12CommandAllocator = d3D12CommandListBundle.D3D12CommandAllocator;
            }
            else
            {
                d3D12CommandAllocator = null;
                d3D12CommandList      = null;
            }
        }

        // Reset the command allocator and command list outside of the lock, or create a new pair if one to be reused
        // wasn't available. These operations are relatively expensive, so doing so here reduces thread contention
        // when multiple shader executions are being dispatched in parallel on the same device.
        if (d3D12CommandAllocator is not null)
        {
            d3D12CommandAllocator->Reset().Assert();
            d3D12CommandList->Reset(d3D12CommandAllocator, d3D12PipelineState).Assert();
        }
        else
        {
            CreateCommandListAndAllocator(d3D12Device, d3D12PipelineState, out d3D12CommandList, out d3D12CommandAllocator);
        }
    }
    public static ID3D12PipelineState *GetLatestD3D12PipelineState(ID3D12PipelineState *d3d12PipelineState, out uint d3d12PipelineStateVersion)
    {
        ID3D12PipelineState *result;

        d3d12PipelineStateVersion = 0;
        result = d3d12PipelineState;

        return(result);
    }
    private void CreateCommandListAndAllocator(ID3D12Device *d3D12Device, ID3D12PipelineState *d3D12PipelineState, out ID3D12GraphicsCommandList *d3D12CommandList, out ID3D12CommandAllocator *d3D12CommandAllocator)
    {
        using ComPtr <ID3D12CommandAllocator> d3D12CommandAllocatorComPtr = d3D12Device->CreateCommandAllocator(this.d3D12CommandListType);
        using ComPtr <ID3D12GraphicsCommandList> d3D12CommandListComPtr   = d3D12Device->CreateCommandList(this.d3D12CommandListType, d3D12CommandAllocatorComPtr.Get(), d3D12PipelineState);

        fixed(ID3D12GraphicsCommandList **d3D12CommandListPtr = &d3D12CommandList)
        fixed(ID3D12CommandAllocator **d3D12CommandAllocatorPtr = &d3D12CommandAllocator)
        {
            d3D12CommandListComPtr.CopyTo(d3D12CommandListPtr);
            d3D12CommandAllocatorComPtr.CopyTo(d3D12CommandAllocatorPtr);
        }
    }
Beispiel #4
0
            public ListCreationParams(
                ExecutionContext context,
                ID3D12CommandAllocator *allocator,
                ID3D12PipelineState *pso
                )
            {
                Debug.Assert(allocator != null);

                Type      = (D3D12_COMMAND_LIST_TYPE)context;
                Allocator = allocator;
                Pso       = pso;
            }
Beispiel #5
0
    /// <summary>
    /// Creates a new <see cref="CommandList"/> instance with the specified parameters.
    /// </summary>
    /// <param name="device">The target <see cref="GraphicsDevice"/> instance to use.</param>
    /// <param name="d3D12PipelineState">The <see cref="ID3D12PipelineState"/> instance to use for the new command list.</param>
    public CommandList(GraphicsDevice device, ID3D12PipelineState *d3D12PipelineState)
    {
        this.device = device;
        this.d3D12CommandListType = D3D12_COMMAND_LIST_TYPE_COMPUTE;

        Unsafe.SkipInit(out this.d3D12GraphicsCommandList);
        Unsafe.SkipInit(out this.d3D12CommandAllocator);

        device.GetCommandListAndAllocator(
            d3D12PipelineState,
            out *(ID3D12GraphicsCommandList **)Unsafe.AsPointer(ref this.d3D12GraphicsCommandList),
            out *(ID3D12CommandAllocator **)Unsafe.AsPointer(ref this.d3D12CommandAllocator));

        // Set the heap descriptor for the command list
        device.SetDescriptorHeapForCommandList(this.d3D12GraphicsCommandList);
    }
Beispiel #6
0
 public void ClearState([NativeTypeName("ID3D12PipelineState *")] ID3D12PipelineState *pPipelineState)
 {
     ((delegate * stdcall <ID3D12GraphicsCommandList4 *, ID3D12PipelineState *, void>)(lpVtbl[11]))((ID3D12GraphicsCommandList4 *)Unsafe.AsPointer(ref this), pPipelineState);
 }
Beispiel #7
0
 public int Reset([NativeTypeName("ID3D12CommandAllocator *")] ID3D12CommandAllocator *pAllocator, [NativeTypeName("ID3D12PipelineState *")] ID3D12PipelineState *pInitialState)
 {
     return(((delegate * stdcall <ID3D12GraphicsCommandList4 *, ID3D12CommandAllocator *, ID3D12PipelineState *, int>)(lpVtbl[10]))((ID3D12GraphicsCommandList4 *)Unsafe.AsPointer(ref this), pAllocator, pInitialState));
 }
Beispiel #8
0
 public void SetPipelineState([NativeTypeName("ID3D12PipelineState *")] ID3D12PipelineState *pPipelineState)
 {
     ((delegate * unmanaged <ID3D12GraphicsCommandList1 *, ID3D12PipelineState *, void>)(lpVtbl[25]))((ID3D12GraphicsCommandList1 *)Unsafe.AsPointer(ref this), pPipelineState);
 }
Beispiel #9
0
 /// <summary>
 /// Creates a new <see cref="PipelineData"/> instance with the specified parameters.
 /// </summary>
 /// <param name="d3D12RootSignature">The <see cref="ID3D12RootSignature"/> value for the current shader.</param>
 /// <param name="d3D12PipelineState">The compiled pipeline state to reuse for the current shader.</param>
 public PipelineData(ID3D12RootSignature *d3D12RootSignature, ID3D12PipelineState *d3D12PipelineState)
 {
     this.d3D12RootSignature = new ComPtr <ID3D12RootSignature>(d3D12RootSignature);
     this.d3D12PipelineState = new ComPtr <ID3D12PipelineState>(d3D12PipelineState);
 }
Beispiel #10
0
 public UniqueComPtr <ID3D12GraphicsCommandList> Rent(
     ExecutionContext context,
     ID3D12CommandAllocator *allocator,
     ID3D12PipelineState *pso
     )
 => Rent(new ListCreationParams(context, allocator, pso));
Beispiel #11
0
 public int CreateCommandList([NativeTypeName("UINT")] uint nodeMask, D3D12_COMMAND_LIST_TYPE type, [NativeTypeName("ID3D12CommandAllocator *")] ID3D12CommandAllocator *pCommandAllocator, [NativeTypeName("ID3D12PipelineState *")] ID3D12PipelineState *pInitialState, [NativeTypeName("const IID &")] Guid *riid, [NativeTypeName("void **")] void **ppCommandList)
 {
     return(((delegate * unmanaged <ID3D12Device *, uint, D3D12_COMMAND_LIST_TYPE, ID3D12CommandAllocator *, ID3D12PipelineState *, Guid *, void **, int>)(lpVtbl[12]))((ID3D12Device *)Unsafe.AsPointer(ref this), nodeMask, type, pCommandAllocator, pInitialState, riid, ppCommandList));
 }
Beispiel #12
0
        protected override void DestroyDeviceDependentResources()
        {
            DestroyAssets();

            DestroyGraphicsCommandLists();
            DestroyPipelineState();
            DestroyRootSignature();

            DestroyFenceEvent();
            DestroyFence();

            DestroyCommandAllocators();

            DestroyDescriptorHeaps();

            DestroyCommandQueue();
            DestroyD3DDevice();
            DestroyDxgiAdapter();
            DestroyDxgiFactory();

            void DestroyCommandAllocators()
            {
                for (var i = 0; i < _commandAllocators.Length; i++)
                {
                    var commandAllocator = _commandAllocators[i];

                    if (commandAllocator != null)
                    {
                        _commandAllocators[i] = null;
                        _ = commandAllocator->Release();
                    }
                }
            }

            void DestroyCommandQueue()
            {
                var commandQueue = _commandQueue;

                if (commandQueue != null)
                {
                    _commandQueue = null;
                    _             = commandQueue->Release();
                }
            }

            void DestroyD3DDevice()
            {
                var d3dDevice = _d3dDevice;

                if (d3dDevice != null)
                {
                    _d3dDevice = null;
                    _          = d3dDevice->Release();
                }
            }

            void DestroyDxgiAdapter()
            {
                var dxgiAdapter = _dxgiAdapter;

                if (dxgiAdapter != null)
                {
                    _dxgiAdapter = null;
                    _            = dxgiAdapter->Release();
                }
            }

            void DestroyDxgiFactory()
            {
                var dxgiFactory = _dxgiFactory;

                if (dxgiFactory != null)
                {
                    _dxgiFactory = null;
                    _            = dxgiFactory->Release();
                }
            }

            void DestroyFence()
            {
                var fence = _fence;

                if (fence != null)
                {
                    _fence = null;
                    _      = fence->Release();
                }
            }

            void DestroyFenceEvent()
            {
                var fenceEvent = _fenceEvent;

                if (fenceEvent != IntPtr.Zero)
                {
                    _fenceEvent = IntPtr.Zero;
                    _           = CloseHandle(_fenceEvent);
                }
            }

            void DestroyGraphicsCommandLists()
            {
                for (var i = 0; i < _graphicsCommandLists.Length; i++)
                {
                    var graphicsCommandList = _graphicsCommandLists[i];

                    if (graphicsCommandList != null)
                    {
                        _graphicsCommandLists[i] = null;
                        _ = graphicsCommandList->Release();
                    }
                }
            }

            void DestroyPipelineState()
            {
                var pipelineState = _pipelineState;

                if (pipelineState != null)
                {
                    _pipelineState = null;
                    _ = pipelineState->Release();
                }
            }

            void DestroyRootSignature()
            {
                var rootSignature = _rootSignature;

                if (rootSignature != null)
                {
                    _rootSignature = null;
                    _ = rootSignature->Release();
                }
            }
        }
Beispiel #13
0
        protected override void CreateDeviceDependentResources()
        {
            _dxgiFactory  = CreateDxgiFactory();
            _dxgiAdapter  = GetDxgiAdapter();
            _d3dDevice    = CreateD3DDevice();
            _commandQueue = CreateCommandQueue();

            CreateDescriptorHeaps();

            for (int i = 0; i < FrameCount; i++)
            {
                _commandAllocators[i] = CreateCommandAllocator();
            }

            _fence       = CreateFence();
            _fenceValues = CreateFenceValues();
            _fenceEvent  = CreateFenceEvent();

            _rootSignature        = CreateRootSignature();
            _pipelineState        = CreatePipelineState();
            _graphicsCommandLists = CreateGraphicsCommandLists();

            ThrowIfFailed(nameof(ID3D12CommandAllocator.Reset), CommandAllocator->Reset());
            ThrowIfFailed(nameof(ID3D12GraphicsCommandList.Reset), GraphicsCommandList->Reset(CommandAllocator, PipelineState));

            CreateAssets();

            ID3D12CommandAllocator *CreateCommandAllocator()
            {
                ID3D12CommandAllocator *commandAllocator;

                var iid = IID_ID3D12CommandAllocator;

                ThrowIfFailed(nameof(ID3D12Device.CreateCommandAllocator), D3DDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, &iid, (void **)&commandAllocator));

                return(commandAllocator);
            }

            ID3D12CommandQueue *CreateCommandQueue()
            {
                var queueDesc = new D3D12_COMMAND_QUEUE_DESC();

                ID3D12CommandQueue *commandQueue;

                var iid = IID_ID3D12CommandQueue;

                ThrowIfFailed(nameof(ID3D12Device.CreateCommandQueue), D3DDevice->CreateCommandQueue(&queueDesc, &iid, (void **)&commandQueue));

                return(commandQueue);
            }

            ID3D12Device *CreateD3DDevice()
            {
                ID3D12Device *d3dDevice;

                var iid = IID_ID3D12Device;

                ThrowIfFailed(nameof(D3D12CreateDevice), D3D12CreateDevice((IUnknown *)_dxgiAdapter, D3D_FEATURE_LEVEL_11_0, &iid, (void **)&d3dDevice));

                return(d3dDevice);
            }

            IDXGIFactory4 *CreateDxgiFactory()
            {
                var dxgiFactoryFlags = TryEnableDebugLayer() ? DXGI_CREATE_FACTORY_DEBUG : 0u;

                IDXGIFactory4 *dxgiFactory;

                var iid = IID_IDXGIFactory4;

                ThrowIfFailed(nameof(CreateDXGIFactory2), CreateDXGIFactory2(dxgiFactoryFlags, &iid, (void **)&dxgiFactory));

                return(dxgiFactory);
            }

            ID3D12Fence *CreateFence()
            {
                ID3D12Fence *fence;

                var iid = IID_ID3D12Fence;

                ThrowIfFailed(nameof(ID3D12Device.CreateFence), D3DDevice->CreateFence(InitialValue: 0, D3D12_FENCE_FLAG_NONE, &iid, (void **)&fence));

                return(fence);
            }

            IntPtr CreateFenceEvent()
            {
                var fenceEvent = CreateEventW(lpEventAttributes: null, bManualReset: FALSE, bInitialState: FALSE, lpName: null);

                if (fenceEvent == IntPtr.Zero)
                {
                    var hr = Marshal.GetHRForLastWin32Error();
                    Marshal.ThrowExceptionForHR(hr);
                }

                return(fenceEvent);
            }

            ulong[] CreateFenceValues()
            {
                var fenceValues = new ulong[FrameCount];

                fenceValues[0] = 1;
                return(fenceValues);
            }

            ID3D12GraphicsCommandList *[] CreateGraphicsCommandLists()
            {
                var graphicsCommandLists = new ID3D12GraphicsCommandList *[FrameCount];

                for (uint i = 0u; i < FrameCount; i++)
                {
                    ID3D12GraphicsCommandList *graphicsCommandList;

                    var iid = IID_ID3D12GraphicsCommandList;
                    ThrowIfFailed(nameof(ID3D12Device.CreateCommandList), D3DDevice->CreateCommandList(nodeMask: 0, D3D12_COMMAND_LIST_TYPE_DIRECT, _commandAllocators[i], PipelineState, &iid, (void **)&graphicsCommandList));

                    ThrowIfFailed(nameof(ID3D12GraphicsCommandList.Close), graphicsCommandList->Close());
                    graphicsCommandLists[i] = graphicsCommandList;
                }

                return(graphicsCommandLists);
            }

            IDXGIAdapter1 *GetDxgiAdapter()
            {
                if (UseWarpDevice)
                {
                    IDXGIAdapter1 *adapter;

                    var iid = IID_IDXGIAdapter;
                    ThrowIfFailed(nameof(IDXGIFactory4.EnumWarpAdapter), _dxgiFactory->EnumWarpAdapter(&iid, (void **)&adapter));

                    return(adapter);
                }
                else
                {
                    return(GetHardwareAdapter((IDXGIFactory1 *)_dxgiFactory));
                }
            }

            bool TryEnableDebugLayer()
            {
#if DEBUG
                // Enable the debug layer (requires the Graphics Tools "optional feature").
                // NOTE: Enabling the debug layer after device creation will invalidate the active device.

                using ComPtr <ID3D12Debug> debugController = null;
                var iid = IID_ID3D12Debug;

                if (SUCCEEDED(D3D12GetDebugInterface(&iid, (void **)&debugController)))
                {
                    debugController.Get()->EnableDebugLayer();
                    return(true);
                }
#endif

                return(false);
            }
        }
 public void SetPipelineState(ID3D12PipelineState *pPipelineState)
 {
     ((delegate * unmanaged[Stdcall] < ID3D12GraphicsCommandList *, ID3D12PipelineState *, void >)(lpVtbl[25]))((ID3D12GraphicsCommandList *)Unsafe.AsPointer(ref this), pPipelineState);
 }
Beispiel #15
0
 public int Reset(ID3D12CommandAllocator *pAllocator, ID3D12PipelineState *pInitialState)
 {
     return(((delegate * unmanaged <ID3D12GraphicsCommandList2 *, ID3D12CommandAllocator *, ID3D12PipelineState *, int>)(lpVtbl[10]))((ID3D12GraphicsCommandList2 *)Unsafe.AsPointer(ref this), pAllocator, pInitialState));
 }
Beispiel #16
0
 public void ClearState(ID3D12PipelineState *pPipelineState)
 {
     ((delegate * unmanaged <ID3D12GraphicsCommandList2 *, ID3D12PipelineState *, void>)(lpVtbl[11]))((ID3D12GraphicsCommandList2 *)Unsafe.AsPointer(ref this), pPipelineState);
 }
Beispiel #17
0
        protected override void Dispose(bool isDisposing)
        {
            var swapChain = _swapChain;

            if (swapChain != null)
            {
                _swapChain = null;
                _          = swapChain->Release();
            }

            var device = _device;

            if (device != null)
            {
                _device = null;
                _       = device->Release();
            }

            for (var index = 0; index < FrameCount; index++)
            {
                var renderTarget = _renderTargets[index];

                if (renderTarget != null)
                {
                    _renderTargets[index] = null;
                    _ = renderTarget->Release();
                }
            }

            var commandAllocator = _commandAllocator;

            if (commandAllocator != null)
            {
                _commandAllocator = null;
                _ = commandAllocator->Release();
            }

            var bundleAllocator = _bundleAllocator;

            if (bundleAllocator != null)
            {
                _bundleAllocator = null;
                _ = bundleAllocator->Release();
            }

            var commandQueue = _commandQueue;

            if (commandQueue != null)
            {
                _commandQueue = null;
                _             = commandQueue->Release();
            }

            var rootSignature = _rootSignature;

            if (rootSignature != null)
            {
                _rootSignature = null;
                _ = rootSignature->Release();
            }

            var rtvHeap = _rtvHeap;

            if (rtvHeap != null)
            {
                _rtvHeap = null;
                _        = rtvHeap->Release();
            }

            var pipelineState = _pipelineState;

            if (pipelineState != null)
            {
                _pipelineState = null;
                _ = pipelineState->Release();
            }

            var commandList = _commandList;

            if (commandList != null)
            {
                _commandList = null;
                _            = commandList->Release();
            }

            var bundle = _bundle;

            if (bundle != null)
            {
                _bundle = null;
                _       = bundle->Release();
            }

            var vertexBuffer = _vertexBuffer;

            if (vertexBuffer != null)
            {
                _vertexBuffer = null;
                _             = vertexBuffer->Release();
            }

            var fence = _fence;

            if (fence != null)
            {
                _fence = null;
                _      = fence->Release();
            }

            base.Dispose(isDisposing);
        }
 public int StorePipeline([NativeTypeName("LPCWSTR")] ushort *pName, [NativeTypeName("ID3D12PipelineState *")] ID3D12PipelineState *pPipeline)
 {
     return(((delegate * unmanaged <ID3D12PipelineLibrary *, ushort *, ID3D12PipelineState *, int>)(lpVtbl[8]))((ID3D12PipelineLibrary *)Unsafe.AsPointer(ref this), pName, pPipeline));
 }
Beispiel #19
0
        protected override void CreateDeviceDependentResources()
        {
            _dxgiFactory = CreateDxgiFactory();
            _dxgiAdapter = GetDxgiAdapter();
            _d3dDevice   = CreateD3DDevice();
            StartInfoPump();
            _commandQueue = CreateCommandQueue();

            CreateDescriptorHeaps();

            for (int i = 0; i < FrameCount; i++)
            {
                _commandAllocators[i] = CreateCommandAllocator();
            }

            _fence       = CreateFence();
            _fenceValues = CreateFenceValues();
            _fenceEvent  = CreateFenceEvent();

            _rootSignature        = CreateRootSignature();
            _pipelineState        = CreatePipelineState();
            _graphicsCommandLists = CreateGraphicsCommandLists();

            SilkMarshal.ThrowHResult(CommandAllocator->Reset());
            SilkMarshal.ThrowHResult(GraphicsCommandList->Reset(CommandAllocator, PipelineState));

            CreateAssets();

            ID3D12CommandAllocator *CreateCommandAllocator()
            {
                ID3D12CommandAllocator *commandAllocator;

                var iid = ID3D12CommandAllocator.Guid;

                SilkMarshal.ThrowHResult
                (
                    D3DDevice->CreateCommandAllocator
                        (CommandListType.CommandListTypeDirect, &iid, (void **)&commandAllocator)
                );

                return(commandAllocator);
            }

            ID3D12CommandQueue *CreateCommandQueue()
            {
                var queueDesc = new CommandQueueDesc();

                ID3D12CommandQueue *commandQueue;

                var iid = ID3D12CommandQueue.Guid;

                SilkMarshal.ThrowHResult(D3DDevice->CreateCommandQueue(&queueDesc, &iid, (void **)&commandQueue));

                return(commandQueue);
            }

            ID3D12Device *CreateD3DDevice()
            {
                ID3D12Device *d3dDevice;

                var iid = ID3D12Device.Guid;

                SilkMarshal.ThrowHResult
                (
                    D3D12.CreateDevice
                        ((IUnknown *)_dxgiAdapter, D3DFeatureLevel.D3DFeatureLevel110, &iid, (void **)&d3dDevice)
                );

                return(d3dDevice);
            }

            IDXGIFactory4 *CreateDxgiFactory()
            {
                var dxgiFactoryFlags = TryEnableDebugLayer() ? 0x01 : 0u;

                IDXGIFactory4 *dxgiFactory;

                var iid = IDXGIFactory4.Guid;

                SilkMarshal.ThrowHResult(Dxgi.CreateDXGIFactory2(dxgiFactoryFlags, &iid, (void **)&dxgiFactory));

                return(dxgiFactory);
            }

            ID3D12Fence *CreateFence()
            {
                ID3D12Fence *fence;

                var iid = ID3D12Fence.Guid;

                SilkMarshal.ThrowHResult
                    (D3DDevice->CreateFence(InitialValue: 0, FenceFlags.FenceFlagNone, &iid, (void **)&fence));

                return(fence);
            }

            IntPtr CreateFenceEvent()
            {
                var fenceEvent = SilkMarshal.CreateWindowsEvent(null, false, false, null);

                if (fenceEvent == IntPtr.Zero)
                {
                    var hr = Marshal.GetHRForLastWin32Error();
                    Marshal.ThrowExceptionForHR(hr);
                }

                return(fenceEvent);
            }

            ulong[] CreateFenceValues()
            {
                var fenceValues = new ulong[FrameCount];

                fenceValues[0] = 1;
                return(fenceValues);
            }

            ID3D12GraphicsCommandList *[] CreateGraphicsCommandLists()
            {
                var graphicsCommandLists = new ID3D12GraphicsCommandList *[FrameCount];

                for (uint i = 0u; i < FrameCount; i++)
                {
                    ID3D12GraphicsCommandList *graphicsCommandList;

                    var iid = ID3D12GraphicsCommandList.Guid;
                    SilkMarshal.ThrowHResult
                    (
                        D3DDevice->CreateCommandList
                        (
                            nodeMask: 0, CommandListType.CommandListTypeDirect, _commandAllocators[i], PipelineState,
                            &iid, (void **)&graphicsCommandList
                        )
                    );

                    SilkMarshal.ThrowHResult(graphicsCommandList->Close());
                    graphicsCommandLists[i] = graphicsCommandList;
                }

                return(graphicsCommandLists);
            }

            IDXGIAdapter1 *GetDxgiAdapter()
            {
                if (UseWarpDevice)
                {
                    IDXGIAdapter1 *adapter;

                    var iid = IDXGIAdapter.Guid;
                    SilkMarshal.ThrowHResult(_dxgiFactory->EnumWarpAdapter(&iid, (void **)&adapter));

                    return(adapter);
                }
                else
                {
                    return(GetHardwareAdapter((IDXGIFactory1 *)_dxgiFactory));
                }
            }

            bool TryEnableDebugLayer()
            {
#if DEBUG
                // Enable the debug layer (requires the Graphics Tools "optional feature").
                // NOTE: Enabling the debug layer after device creation will invalidate the active device.

                using ComPtr <ID3D12Debug> debugController = null;
                var iid = ID3D12Debug.Guid;
                var hr  = D3D12.GetDebugInterface(&iid, (void **)&debugController);

                if (HResult.IndicatesSuccess(hr))
                {
                    debugController.Get().EnableDebugLayer();
                    Log.LogInformation("Debug layer enabled");
                    return(_debug = true);
                }
                else
                {
                    Log.LogWarning
                    (
                        Marshal.GetExceptionForHR(hr),
                        $"Failed to enable debug layer, failed with result {hr} (0x{hr:x8})"
                    );
                }
#endif

                return(false);
            }

            void StartInfoPump()
            {
#if DEBUG
                if (!_debug)
                {
                    Log.LogInformation("Skipped creation of info pump due to the debug layer not being enabled.");
                    return;
                }

                var iid = ID3D12InfoQueue.Guid;
                fixed(ID3D12InfoQueue ** @out = &_infoQueue)
                {
                    SilkMarshal.ThrowHResult(D3DDevice->QueryInterface(&iid, (void **)@out));
                }

                _infoPumpCancellationToken = new();
                _infoPump = Task.Run
                            (
                    () =>
                {
                    Log.LogInformation("Info queue pump started");
                    while (!_infoPumpCancellationToken.Token.IsCancellationRequested)
                    {
                        var numMessages = _infoQueue->GetNumStoredMessages();
                        if (numMessages == 0)
                        {
                            continue;
                        }

                        for (var i = 0ul; i < numMessages; i++)
                        {
                            nuint msgByteLength;
                            SilkMarshal.ThrowHResult(_infoQueue->GetMessageA(i, null, &msgByteLength));
                            using var memory = GlobalMemory.Allocate((int)msgByteLength);
                            SilkMarshal.ThrowHResult
                            (
                                _infoQueue->GetMessageA(i, memory.AsPtr <Message>(), &msgByteLength)
                            );

                            ref var msg   = ref memory.AsRef <Message>();
                            var descBytes = new Span <byte>(msg.PDescription, (int)msg.DescriptionByteLength);
                            var desc      = Encoding.UTF8.GetString(descBytes[..^ 1]);
                            var eid       = new EventId((int)msg.ID, msg.ID.ToString()["MessageID".Length..]);