Desktop Duplication 初始化失败主因是DXGI设备不支持:需D3D11CreateDevice创建、WDDM 1.2+驱动、非WARP GPU、线程具桌面交互权限;捕获卡顿因帧合并,建议非阻塞Acquire+时间戳去重;零拷贝共享纹理可避免Map延迟,FFmpeg 5.0+支持直接消费;多屏/DPI/旋转需用IDXGIOutput::GetDesc获取物理坐标。

Desktop Duplication 初始化失败:IDXGIDesktopDuplication 为空?
常见现象是 CreateDesktopDuplication 返回 E_NOINTERFACE 或 QueryInterface 拿不到 IDXGIDesktopDuplication。根本原因不是代码写错,而是 DXGI 设备不支持桌面复制——必须用 D3D11CreateDevice 创建的设备,且需满足:驱动支持 WDDM 1.2+、GPU 非软件渲染器(如 WARP)、调用线程拥有桌面交互权限(不能在无窗口服务进程里跑)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 检查
D3D_FEATURE_LEVEL至少设为D3D_FEATURE_LEVEL_10_0,低于此值可能被拒绝创建桌面复制接口 - 创建设备时传入
D3D11_CREATE_DEVICE_VIDEO_SUPPORT标志,否则部分驱动会静默禁用 Desktop Duplication 功能 - 确保调用前已调用
SetThreadDpiAwarenessContext(Win10 1703+),否则高 DPI 缩放下可能初始化失败 - 不要复用
ID3D11Device实例:如果该设备之前用于 Direct2D 或视频解码,最好新建一个专用于捕获的设备
捕获帧卡顿或延迟突增:AcquireNextFrame 超时或返回重复帧?
AcquireNextFrame 默认阻塞等待新帧,但实际中常遇到等不到新帧(超时返回 DXGI_ERROR_WAIT_TIMEOUT)或连续几次拿到同一帧(DirtyRects 为空、FrameInfo.TotalMetadataBufferSize 为 0)。这不是丢帧,而是 Desktop Duplication 的“帧合并”行为:系统只在真正有变化时才推送新帧。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 把
AcquireNextFrame的超时设为0(非阻塞),自己用std::this_thread::sleep_for(1ms)控制轮询节奏,避免线程饿死 - 检查
FrameInfo.LastPresentTime和上一帧时间戳是否相同——相同即为重复帧,直接跳过处理,不送入编码/显示流水线 - 若需强制逐帧捕获(比如录屏需要固定 FPS),启用
IDXGIOutputDuplication::GetFrameDirtyRects并结合全屏CopyResource,但代价是 GPU 带宽翻倍 - 避免在 Acquire 后立刻调用
ReleaseFrame:必须等 CPU 完成对该帧的读取(如Map+ memcpy)后再释放,否则下一帧可能被覆盖
内存拷贝慢、CPU 占用高:如何绕过 Map/Unmap?
最常踩的坑是用 Map 把 GPU 纹理映射到 CPU 可读内存,再 memcpy 出来——这触发隐式同步,每帧多出 1–3ms 延迟,且占用大量 CPU 时间。Desktop Duplication 原生支持零拷贝共享:它返回的 ID3D11Texture2D 是可跨进程共享的句柄,只要目标模块(如编码器)也支持 D3D11 纹理输入,就完全不需要 CPU 搬运。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 调用
OpenSharedResource获取共享纹理后,立刻用GetDesc检查Usage是否为D3D11_USAGE_DEFAULT;如果是D3D11_USAGE_STAGING,说明已被降级,必须重走 Map 路径 - FFmpeg 5.0+ 支持
dxva2和d3d11va硬编码器,传入AV_PIX_FMT_D3D11并设置hw_frames_ctx,即可直接消费共享纹理 - 若必须转为 CPU 内存(如喂给 OpenCV),改用
CopySubresourceRegion+ staging texture 异步拷贝,再用Map读 staging,比直接 Map 输出纹理快 2–4 倍 - 禁用
D3D11_MAP_FLAG_DO_NOT_WAIT:它在资源忙时直接失败,不如让Map等一会更稳
多显示器/缩放/旋转下捕获区域错位?
Desktop Duplication 默认捕获整个输出(output),但 IDXGIOutputDuplication::AcquireNextFrame 返回的纹理尺寸是物理分辨率(如 3840×2160),而你用 GetDisplaySurfaceData 或 CopyResource 时若按逻辑坐标裁剪(如 1920×1080),就会偏移或拉伸。尤其 Win10/11 多 DPI 混合场景下,每个显示器有自己的缩放比例和旋转状态。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 调用
IDXGIOutput::GetDesc获取DesktopCoordinates,它给出的是该输出在虚拟桌面中的绝对像素位置(已考虑缩放和旋转) - 不要依赖
GetDpiForWindow或GetDpiForSystem:它们返回的是当前线程 DPI,不是输出本身的 DPI;应调用IDXGIOutput5::GetDesc1中的Scaling和Rotation字段 - 若只捕获某个窗口区域,别用
GetWindowRect;改用GetPhysicalMonitorDescription+IDXGIOutput6::DuplicateOutput绑定到特定显示器,再用GetDisplaySurfaceData裁剪 - 旋转 90°/270° 时,纹理宽高互换,且
CopySubresourceRegion的dstX/dstY需按旋转后坐标系重新计算,否则图像倒置
Desktop Duplication 的性能天花板不在 API 调用本身,而在你是否让 GPU 纹理全程留在 GPU 内存里流转。一旦引入 CPU Map,再快的 i9 也扛不住每秒 60 次的 PCIe 往返。真正的高速,是连 memcpy 都不出现。


















