You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 09:20:57