如何从交换链获取 DirectX 12 命令队列

mor*_*din 5 directx directx-12

假设 DX12 被挂钩以进行叠加渲染的场景,看起来最好的挂钩函数是 IDXGISwapChain::Present,与 DX11 的做法相同。将此函数挂钩后,交换链就可用,并且可以从中检索设备以创建资源。有了这些资源,它也可以记录渲染命令。当我们尝试执行渲染命令时,就会出现问题,因为没有选项可以从交换链中检索关联的命令队列,因此不会出现这样的情况:

\n\n
CComPtr<ID3D12Device> pD3D12Device;\nif (pSwapChain->GetDevice(__uuidof(ID3D12Device), (void**)(&pD3D12Device)) == S_OK)\n{\n    pD3D12Device->GetCommandQueueForSwapChain( swapChain )->ExecuteCommandLists(\xe2\x80\xa6);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

另一个选项是创建一个新的命令队列来执行,如下所示:

\n\n
CComPtr<ID3D12Device> pD3D12Device;\nif (pSwapChain->GetDevice(__uuidof(ID3D12Device), (void**)(&pD3D12Device)) == S_OK)\n{\n    D3D12_COMMAND_QUEUE_DESC queue_desc = {};\n    queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;\n    queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;\n    HRESULT commandQueueRes = _device->CreateCommandQueue( &queue_desc, IID_PPV_ARGS( &_commandQueue ) );\n\n    _commandQueue->ExecuteCommandLists( ... );\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这会导致错误并导致随后的设备删除。请参阅下面的错误消息。

\n\n
\n

D3D12 错误:ID3D12CommandQueue::ExecuteCommandLists:写入交换链后台缓冲区的命令列表只能在与该缓冲区关联的命令队列上执行。[状态设置错误#907:执行命令列表_WRONGSWAPCHAINBUFFERREFERENCE]

\n
\n\n

即使 ID3D12CommandQueue::ExecuteCommandLists 也被挂钩,问题也没有解决,因为也无法从命令队列中检索关联的交换链。

\n\n

所以我的问题是,在交换链创建发生在挂钩可能发生之前的情况下,处理此问题的推荐方法是什么?

\n

mor*_*din 1

如果有人正在寻找答案,这就是我发现的。没有官方方法可以做到这一点,对于覆盖渲染,推荐的方法是使用 DirectComposition,但这会产生性能后果,这对于游戏覆盖来说不是很好。

稍微研究一下内存,有一个可能的解决方案可以从交换链中获取 CommandQueue,如下所示:

#ifdef _M_X64
    size_t* pOffset = (size_t*)((BYTE*)swapChain + 216);
#else
    size_t* pOffset = (size_t*)((BYTE*)swapChain + 132);
#endif
    *(&_commandQueue) = reinterpret_cast<ID3D12CommandQueue*>(*pOffset);
Run Code Online (Sandbox Code Playgroud)

显然,不推荐此解决方案,但如果有人只想进行一些调试,它可能会很有用。

我的最终解决方案是挂钩一个使用 CommandQueue 的函数(我使用 ExecuteCommandLists)并在那里获取指针,并在稍后使用它来渲染覆盖层。它并不完全令人满意,但只要没有多个交换链它就可以工作。