DirectX 11桌面复制API捕获帧全为零的问题排查与解决
屏幕捕获帧数据全零问题排查与解决
问题描述
使用C++结合DirectX 11及Desktop Duplication API开发屏幕捕获应用,已完成以下操作:
- 初始化DirectX组件:创建
ID3D11Device、ID3D11DeviceContext及IDXGIOutputDuplication - 调用
AcquireNextFrame捕获屏幕帧 - 将
IDXGIResource转换为ID3D11Texture2D - 通过
CopyResource将捕获纹理复制到暂存纹理 - 映射暂存纹理以访问像素数据
已完成调试验证:启用DirectX调试层、确认捕获纹理与暂存纹理描述匹配、验证暂存纹理初始化正确,但捕获到的帧数据始终全为零,调试无错误输出。
相关代码片段
初始化与帧获取
HRESULT hr; Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice; Microsoft::WRL::ComPtr<ID3D11Device> device; Microsoft::WRL::ComPtr<ID3D11DeviceContext> context; hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &context); device.As(&dxgiDevice); Microsoft::WRL::ComPtr<IDXGIOutput1> dxgiOutput1; Microsoft::WRL::ComPtr<IDXGIOutputDuplication> deskDupl; dxgiOutput1->DuplicateOutput(device.Get(), &deskDupl); Microsoft::WRL::ComPtr<IDXGIResource> desktopResource; DXGI_OUTDUPL_FRAME_INFO frameInfo; deskDupl->AcquireNextFrame(500, &frameInfo, &desktopResource);
资源转纹理
Microsoft::WRL::ComPtr<ID3D11Texture2D> acquiredDesktopImage; desktopResource.As(&acquiredDesktopImage);
复制到暂存纹理
D3D11_TEXTURE2D_DESC desc; desc.Width = 1920; desc.Height = 1080; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_STAGING; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; Microsoft::WRL::ComPtr<ID3D11Texture2D> stagingTex; device->CreateTexture2D(&desc, nullptr, &stagingTex); context->CopyResource(stagingTex.Get(), acquiredDesktopImage.Get());
映射并访问数据
D3D11_MAPPED_SUBRESOURCE mappedResource; context->Map(stagingTex.Get(), 0, D3D11_MAP_READ, 0, &mappedResource); std::vector<BYTE> buffer(mappedResource.RowPitch * desc.Height); memcpy(buffer.data(), mappedResource.pData, buffer.size()); context->Unmap(stagingTex.Get(), 0);
输出信息
Output Device: \.\DISPLAY6 Frame acquired successfully. Resource converted to texture successfully. Acquired Texture Width: 1920 Height: 1080 Acquired Texture Format: 87 CopyResource called. Staging Texture Width: 1920 Height: 1080 RowPitch: 7680 First 64 bytes of mapped data after CopyResource: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
原因分析与解决方案
1. 未释放前序帧资源
每次调用AcquireNextFrame后必须调用ReleaseFrame释放当前帧资源,否则后续捕获的帧会被阻塞,无法获取有效数据。
解决代码:
// 处理完帧数据后添加释放逻辑 context->Unmap(stagingTex.Get(), 0); deskDupl->ReleaseFrame(); // 释放当前帧,确保下一帧可正常捕获
2. 硬编码暂存纹理参数导致不匹配
代码中硬编码了暂存纹理的宽高和格式,若捕获的实际纹理参数存在隐性差异,会导致复制失败。应从捕获的纹理动态获取参数。
解决代码:
// 从捕获的纹理获取原始描述 D3D11_TEXTURE2D_DESC acquiredDesc; acquiredDesktopImage->GetDesc(&acquiredDesc); // 构造暂存纹理描述,仅修改必要参数 D3D11_TEXTURE2D_DESC stagingDesc = acquiredDesc; stagingDesc.Usage = D3D11_USAGE_STAGING; stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; stagingDesc.BindFlags = 0; // 暂存纹理无需绑定标志 stagingDesc.MiscFlags = 0; // 创建匹配的暂存纹理 Microsoft::WRL::ComPtr<ID3D11Texture2D> stagingTex; device->CreateTexture2D(&stagingDesc, nullptr, &stagingTex); context->CopyResource(stagingTex.Get(), acquiredDesktopImage.Get());
3. 未等待GPU完成复制操作
CopyResource是异步GPU操作,若直接映射纹理,可能读取到未更新的初始零数据。需强制GPU完成当前队列操作后再访问数据。
解决代码:
context->CopyResource(stagingTex.Get(), acquiredDesktopImage.Get()); context->Flush(); // 等待GPU完成复制操作 // 之后再映射纹理读取数据 D3D11_MAPPED_SUBRESOURCE mappedResource; context->Map(stagingTex.Get(), 0, D3D11_MAP_READ, 0, &mappedResource);
4. 未处理帧元数据与初始帧同步
Desktop Duplication API的初始帧可能为空白,且若帧包含更新区域(脏矩形、移动矩形),需先处理元数据确保帧数据有效;同时可连续捕获2-3帧跳过空白初始帧。
解决代码:
DXGI_OUTDUPL_FRAME_INFO frameInfo; deskDupl->AcquireNextFrame(500, &frameInfo, &desktopResource); // 处理帧元数据(若存在) if (frameInfo.TotalMetadataBufferSize > 0) { std::vector<BYTE> metadataBuffer(frameInfo.TotalMetadataBufferSize); UINT bufferSize = frameInfo.TotalMetadataBufferSize; deskDupl->GetFrameMetadata(bufferSize, metadataBuffer.data(), &bufferSize); } // 可选:连续捕获并释放2次初始帧,避免空白数据 for (int i = 0; i < 2; ++i) { deskDupl->ReleaseFrame(); deskDupl->AcquireNextFrame(500, &frameInfo, &desktopResource); }
5. 未正确启用DirectX调试层
调试层能输出详细API错误信息,帮助定位隐性问题。原代码未启用调试层,需修改设备创建参数。
解决代码:
hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_DEBUG, // 启用调试层 nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &context );
内容的提问来源于stack exchange,提问作者ObemeVortep
相关产品推荐
相关产品推荐

