我找不到一个记录的API来解决这个问题。所以,我钩住了IDXGISwapChain::Present方法并捕获了黑盒窗口框架。
重要的是要记住,在DirectX 11中,当你捕获帧时,必须使用创建交换链时使用的相同的DirectX设备和设备上下文对象。这是可能的。
在DirectX 12中,很难获取刚刚呈现的当前帧。所以,我捕获了之前呈现的帧。对我来说,这不是一个大问题。
DirectX 11钩子算法是:
...
...
std::uintptr_t* swapChainPointer =
static_cast<std::uintptr_t*>(static_cast<void*>
(d3d11Helper.GetSwapChain()));
std::uintptr_t* virtualTablePointer =
reinterpret_cast<std::uintptr_t*>(swapChainPointer[0]);
presentPointer_ = static_cast<std::uint64_t>(virtualTablePointer[8]);
...
DirectX 11 帧捕获算法是:
Microsoft::WRL::ComPtr<ID3D11Device> d3d11Device;
hr = swapChain->GetDevice(__uuidof(ID3D11Device), &d3d11Device);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
d3d11Device->GetImmediateContext(&d3d11DeviceContext);
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11SwapChainTexture;
hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
&d3d11SwapChainTexture);
...
d3d11DeviceContext->CopyResource(d3d11StagingTexture.Get(),
d3d11SwapChainTexture.Get());
D3D11_MAPPED_SUBRESOURCE mappedSubresource;
UINT subresource = D3D11CalcSubresource(0, 0, 0);
hr = d3d11DeviceContext->Map(d3d11StagingTexture.Get(), subresource,
D3D11_MAP_READ_WRITE, 0, &mappedSubresource);
以下是DirectX 12钩子算法的亮点。
std::uintptr_t i = 0;
const char* start = static_cast<char*>(
static_cast<void*>(d3d12Helper.GetSwapChain()));
while (true) {
if (reinterpret_cast<std::uintptr_t>(d3d12Helper.GetCommandQueue()) ==
*static_cast<const std::uintptr_t*>(
static_cast<const void*>(start + i))) {
commandQueueOffset_ = i;
break;
}
++i;
}
以下是DirectX 12捕获算法的亮点。
Microsoft::WRL::ComPtr<ID3D12Resource> readbackResource_;
if (readbackResource_.Get()) {
if (readbackData_ == nullptr) {
hr = readbackResource_->Map(0, nullptr, &readbackData_);
if (FAILED(hr)) {
return;
}
}
} else {
...
}
...
const char* start = static_cast<char*>(static_cast<void*(
swapChain3.Get()));
ID3D12CommandQueue* commandQueue =
reinterpret_cast<ID3D12CommandQueue*>(
*static_cast<const std::uintptr_t*>(
static_cast<const void*>(start + commandQueueOffset_)));
ID3D12CommandList* commandLists[] = {copyCommandList.Get()};
commandQueue->ExecuteCommandLists(ARRAYSIZE(commandLists), commandLists);
请查看并收藏完整的示例项目,链接在这里:
https://github.com/eugen15/directx-present-hook 如果您有任何问题,请不要犹豫与我联系!