I'm trying to convert from a Direct3D11CaptureFrame, obtained as in the example from Microsoft's Screen Capture documentation (and verified to be correct by saving it on disk), to a cv::Mat. I am converting the frame's surface into a ID3D11Texture2D in the following way, and it seems to work:
void processFrame(const winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame &frame) {
winrt::com_ptr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> access =
frame.Surface().as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<ID3D11Texture2D> frameSurface;
winrt::check_hresult(access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), frameSurface.put_void()));
(...)
}
From this point, looking at opencv's d3d interop samples, I see I have two options. The first one consists in using cv::directx::convertFromD3D11Texture2D such as
void processFrame(const winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame &frame) {
winrt::com_ptr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> access =
frame.Surface().as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<ID3D11Texture2D> frameSurface;
winrt::check_hresult(access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), frameSurface.put_void()));
cv::Mat img;
// throws exception in the following line
//
cv::directx::convertFromD3D11Texture2D(frameSurface.get(), img);
}
But it throws an exception after calling the destructor of the input array (frameSurface.get()). This method is used for GPU work in the code examples, but I haven't seen it stated as exclusively for GPU in the documentation.
The second option goes through a couple more hoops:
void processFrame(const winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame &frame, int height, int width) {
auto access =
frame.Surface().as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<ID3D11Texture2D> frameSurface;
winrt::check_hresult(
access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), frameSurface.put_void()));
ID3D11Device *deviceD3D;
frameSurface->GetDevice(&deviceD3D);
ID3D11DeviceContext *m_pD3D11Ctx;
deviceD3D->GetImmediateContext(&m_pD3D11Ctx);
auto subResource = ::D3D11CalcSubresource(0, 0, 1);
D3D11_MAPPED_SUBRESOURCE mappedTex;
auto r = m_pD3D11Ctx->Map(frameSurface.get(), subResource, D3D11_MAP_WRITE_DISCARD, 0, &mappedTex);
// FAILS here
//
if (FAILED(r)) {
throw std::runtime_error("surface mapping failed!");
}
cv::Mat m(height, width, CV_8UC4, mappedTex.pData, mappedTex.RowPitch);
}
... but it fails when mapping the context to the subresource.
I have been tweaking everything that I could think of given my inexperience with DirectX and trying to see what I am missing, but I can't find a solution for any of the two options. Does anyone see my mistakes or has a better solution for my problem?