D3D11屏幕桌面复制到ID3D11Texture2D

D3D11 screen desktop copy to ID3D11Texture2D

我正在编写一个 DLL 插件,它将读取桌面帧缓冲区(整个屏幕)并将其直接渲染到传入的 Texture2D 指针中。目标是将所有内容保存在视频内存中(并避免成本复制回系统内存和视频内存)。

我能够传递 Texture2D(显示为 ID3D11Texture2D),但我在使用 D3D11 获取桌面帧缓冲区时遇到问题。 D3D9 提供了 GetFrontBufferData() 但似乎 D3D11 解决方案是使用 GetBuffer()。

我的问题是关于获取 IDXGISwapChain。因为我想读取桌面帧缓冲区,只需通过 GetBuffer() 读取内容并将其显示在 ID3D11Texture2D 上。我得到一个 ID3D11Device 我不明白如何得到它的 IDXGISwapChain。

我有如下代码来获取帧缓冲区并将其放在纹理上:

ID3D11Texture2D* src = (ID3D11Texture2D*)g_TexturePointer;
ID3D11Texture2D* dst = NULL;
HRESULT hr = swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&dst);
g_devCon->CopyResource(dst, src);

这里我实际上使用 D3D11CreateDeviceAndSwapChain() 创建了我自己的交换链,但我想知道是否有必要,因为我已经有一个 ID3D11Device。

CopyResource() 似乎也失败了。

不一定所有桌面内容都可以使用 D3D11 呈现。 DXGI 是 Windows 上所有图形的底层系统,因此您肯定需要以某种方式使用它来捕获桌面。但是,D3D11 是建立在 DXGI 之上的(例如,ID3D11Texture2D 支持 IDXGIResource 接口)。下面的代码示例显示了如何将整个监视器的输出捕获到 D3D11 暂存纹理中:

// IDXGIOutput* poutput = ...; // from DXGIAdapter::EnumOutputs.

// Get description of desktop.
DXGI_OUTPUT_DESC outdesc;
poutput->GetDesc(&outdesc);

// Create destination texture, sized same as desktop.
D3D11_TEXTURE2D_DESC texDesc;
memset(&texDesc, 0, sizeof(texDesc));
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.BindFlags = 0;
texDesc.Width  = outdesc.DesktopCoordinates.right - outdesc.DesktopCoordinates.left;
texDesc.Height = outdesc.DesktopCoordinates.bottom - outdesc.DesktopCoordinates.top;
texDesc.MipLevels = 1;
texDesc.SampleDesc = { 1, 0 };
texDesc.Usage = D3D11_USAGE_STAGING;
texDesc.ArraySize = 1;
ID3D11Texture2D* destinationTexture = 0;
pDevice->CreateTexture2D(&texDesc, 0, &destinationTexture); // check HRESULT.

// Get IDXGIResource from texture.
IDXGIResource* destinationResource=0;
destinationTexture->QueryInterface(IID_PPV_ARGS(&destinationResource)); // check HRESULT.

// Get data.
IDXGIOutput1* poutput1;
poutput->QueryInterface(IID_PPV_ARGS(&poutput1)); // check HRESULT.
poutput1->TakeOwnership(pDevice, TRUE);
poutput1->GetDisplaySurfaceData1(destinationResource); // check HRESULT.
poutput1->ReleaseOwnership();

// Now use destinationTexture, it contains the contents of the desktop.

不幸的是,它有在 IDXGIOutput::TakeOwnership 调用期间将输出变为黑色的令人讨厌的副作用。但是,如果没有此调用,GetDiplaySurfaceData1 将失败。根据您的情况,这可能是可以接受的。