Desktop Duplication 创建失败需确保使用集成显卡或独显直连输出、桌面活动、禁用PerMonitorV2 DPI感知、进程有桌面交互权限;帧读取慢因未用STAGING纹理;延迟优化需零超时Acquire、双缓冲池、NV12格式;画面撕裂因未校验FrameInfo及遗漏ReleaseFrame。

Desktop Duplication 创建失败:DXGI_ERROR_UNSUPPORTED 或 DXGI_ERROR_ACCESS_DENIED 怎么办
常见错误是直接用普通 IDXGIAdapter 调用 CreateDesktopDuplication,但 Desktop Duplication 只支持「集成显卡」或「独显的显示输出接口所连接的桌面」,且必须满足:
- 桌面处于「活动显示模式」(不能是远程桌面、锁屏、多用户会话)
- 应用以非高 DPI 缩放兼容模式运行(manifest 中禁用
dpiAwareness="PerMonitorV2"可能触发限制) - 进程需有桌面交互权限(不能在服务进程或无桌面会话的环境下运行)
- Windows 10 1809+ 才完整支持 NV12 输出格式;旧系统只支持
DXGI_FORMAT_B8G8R8A8_UNORM
实操建议:先用 EnumAdapters 遍历所有适配器,对每个调用 EnumOutputs,再对每个 IDXGIOutput 调用 GetDisplayModeList 确认是否为当前主显输出;最终只对匹配的 IDXGIOutput 创建 IDXGIOutput1,再调用 CreateDesktopDuplication。
CopyResource 后 Map 读取数据慢如 CPU 拷贝?GPU 到 CPU 传输瓶颈在哪
根本原因不是 API 慢,而是默认创建的 ID3D11Texture2D 目标纹理用了 D3D11_USAGE_DEFAULT,无法 CPU 直接映射。必须用 D3D11_USAGE_STAGING + D3D11_CPU_ACCESS_READ 创建 staging 纹理,再通过 CopyResource 把帧从 duplication 纹理拷到 staging 纹理,最后 Map staging 纹理。
关键点:
立即学习“C++免费学习笔记(深入)”;
- staging 纹理不能设
BindFlags(否则创建失败) -
CopyResource必须在同一个ID3D11DeviceContext上执行,且不能跨设备 - 若启用
DXGI_OUTPUT_DUPL_FLAG_ALLOW_TEARING,需检查AcquireNextFrame返回的DXGI_OUTDUPL_FRAME_INFO中LastPresentTime是否变化,避免重复拷贝同一帧 - 多线程读取时,
Unmap后才能再次Map,否则返回E_INVALIDARG
怎么让每帧延迟压到 1~2ms?避免 Present/Acquire 同步等待
Desktop Duplication 天然带帧同步语义,但默认行为是阻塞式 AcquireNextFrame。要榨干性能:
- 使用
AcquireNextFrame(0, ...)(超时设为 0),配合轮询 +Sleep(0),避免线程挂起 - 维护一个双缓冲 staging 纹理池(至少 2 张),每次
AcquireNextFrame后立即CopyResource到空闲 staging 纹理,然后异步交给编码线程(如 libx264 的x264_picture_t输入) - 关闭 VSync:确保 DXGI 输出不参与 DWM 合成(全屏独占或使用
DXGI_SWAP_EFFECT_DISCARD的 swap chain 不相关,但 duplication 本身不受 swap effect 影响) - 若目标是 H.264 编码,优先用 NV12 格式(Windows 10 1809+ 支持),省去 RGB→YUV 转换开销;否则需用 compute shader 或
ID3D11VideoProcessor做格式转换,后者更稳但引入额外 GPU 队列等待
为什么录出来画面撕裂或跳帧?AcquireNextFrame 返回值没校验
AcquireNextFrame 成功只表示“拿到了一帧”,不代表这帧是新内容:
-
FrameInfo.LastPresentTime.QuadPart == 0表示尚未发生任何呈现(初始帧) -
FrameInfo.AccumulatedFrames == 0表示本帧无更新(可能桌面静止) -
FrameInfo.TotalMetadataBufferSize > 0才值得调用GetFrameMoveRects/GetFrameDirtyRects - 最关键:每次
AcquireNextFrame后必须调用ReleaseFrame,否则后续调用会失败或卡死;哪怕只读元数据也必须释放
容易被忽略的是:如果上一帧的 ReleaseFrame 被遗漏,下一次 AcquireNextFrame 会永远阻塞(即使超时设为 0),且不报错——这是最隐蔽的卡顿源头。建议把 acquire/release 封装进 RAII 类,构造时 acquire,析构时强制 release。


















