D3D11屏幕桌面复制到ID3D11Texture2D

D3D11 screen desktop copy to ID3D11Texture2D

本文关键字:ID3D11Texture2D 复制 桌面 屏幕 D3D11      更新时间:2023-10-16

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

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

我的问题是关于获得IDXGISwapChain。因为我想读取桌面帧缓冲区,只需通过GetBuffer()读取内容并将其显示在ID3D11Texture2D上。我得到了一个ID3D11设备,我不知道如何获得它的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将失败。根据您的情况,这可能是可以接受的。