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

如何使用IDXGIOutputDuplication实现多显示器屏幕捕获

基于IDXGIOutputDuplication实现多显示器屏幕捕获方案

完全可以不依赖GDI,仅通过Windows桌面复制API实现多显示器配置下的屏幕捕获,这也是目前Windows平台性能表现最好的屏幕捕获方案,相比传统GDI捕获CPU占用更低、延迟更小。

你当前单屏实现的核心逻辑是正确的——IDXGIOutputDuplication 本身设计上就是和单个适配器下的单个输出端(即单个显示器)强绑定的,不存在可以直接覆盖整个跨显示器虚拟桌面的Duplication实例,这是API层面的固有设计。

你提到的多线程方案的问题

你最初考虑的「为每个显示器创建独立线程、跑单独的API调用逻辑」是可运行的,但不是最优解,存在几个明显缺陷:

  • 多线程频繁上下文切换会带来不必要的性能开销
  • 若每个显示器都单独创建D3D设备,会造成GPU显存和驱动资源的浪费
  • 多线程独立取帧很难对齐不同显示器的帧时间戳,拼接出来的全屏画面容易出现不同步的问题

更优雅、性能更优的实现路径

初始化阶段优化

同一个GPU适配器下的所有显示器,完全可以共用同一个ID3D11Device实例,只有当显示器挂在不同物理GPU(比如笔记本核显接主屏、独显接副屏的混合输出场景)时,才需要为对应适配器单独创建D3D设备,这一步可以减少一半以上的资源占用。
具体初始化流程:

  1. 枚举系统内所有IDXGIAdapter,对每个适配器尝试创建ID3D11Device,创建成功的适配器保留,跳过重复设备创建
  2. 对每个保留的适配器,枚举其下挂的所有IDXGIOutput,查询IDXGIOutput1接口后为每个输出创建对应的IDXGIOutputDuplication实例
  3. 将每个Duplication实例、对应的DXGI_OUTPUT_DESC(存储显示器分辨率、虚拟桌面偏移坐标、旋转角度等信息)、预创建的Staging纹理、帧等待事件句柄存在统一的结构体中管理

注意不要像你当前代码一样每次捕获都新建Staging纹理,初始化阶段根据每个输出端的纹理格式提前创建好可复用的Staging纹理,能省掉大量GPU资源创建销毁的开销,捕获性能可提升20%以上。

捕获阶段优化

不需要为每个Duplication实例开独立线程,用单捕获线程配合事件等待即可实现高效的多屏帧获取:

  1. 对每个Duplication实例,调用GetFrameLatencyWaitableObject拿到对应的帧可用等待事件句柄
  2. 把所有事件句柄传入WaitForMultipleObjects,阻塞等待任意显示器有新帧到达,避免空转占满CPU
  3. 被触发的事件对应显示器有新帧,调用AcquireNextFrame拿到帧纹理,拷贝到预创建的Staging纹理后做后续处理
  4. 处理完帧数据后必须调用ReleaseFrame释放帧锁,否则后续取帧会失败(你当前贴的代码漏了这一步)
  5. 取帧时可以通过DXGI_OUTDUPL_FRAME_INFO里的LastPresentTime做时间戳对齐,保证多屏画面的时间一致性

常见踩坑点

  • AcquireNextFrame的超时参数不要设为0,配合事件等待时设置100ms左右的超时即可,避免无效空转
  • 遇到DXGI_ERROR_ACCESS_LOST错误一般是显示器热插拔、分辨率调整、显示模式切换导致的,只需要释放对应失效的Duplication实例,重新枚举输出创建新实例即可,不需要重启整个捕获流程
  • 系统全局最多允许同时运行8个使用Desktop Duplication API的应用,自己的进程内不要对同一个输出端重复创建Duplication实例,否则会触发DXGI_ERROR_NOT_CURRENTLY_AVAILABLE错误
  • 跨GPU适配器的显示器纹理不能直接拷贝,需要先拷贝到对应适配器的Staging纹理做CPU侧映射,再拷贝到目标设备,这类场景占比不高,单独做分支处理即可

内容的提问来源于stack exchange,提问作者bluetooth12

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:39:22