Direct3D 12多线程录制性能增益因等待栅栏/呈现丢失的问题
问题描述
我编写了一个Direct3D 12多线程命令列表录制程序,发现多线程录制节省的时间被栅栏等待和Present操作完全抵消,单线程与多线程渲染单循环的总耗时完全一致。
经耗时测量,多线程录制本身是有效的:单线程录制耗时0.0015秒,8线程各自录制耗时0.0003秒,整体录制时间确实缩短。
最初核心问题是等待栅栏完成耗时0.008秒,于是我将交换链缓冲区和栅栏数量从2改为8,栅栏等待时间降至0.0000003秒,但此时IDXGISwapChain::Present每次调用耗时0.009秒——我原本以为该方法是异步的。尝试设置DXGI_PRESENT_DO_NOT_WAIT标志后,Present耗时下降,但栅栏等待时间又回到0.008秒。
2个后台缓冲区时的交换链描述如下:
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc1 { 800, 600, DXGI_FORMAT_R8G8B8A8_UNORM, 0, { 1, 0 }, DXGI_USAGE_RENDER_TARGET_OUTPUT, 2, DXGI_SCALING_NONE, DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_ALPHA_MODE_UNSPECIFIED, 0 };
每个命令列表调用DrawInstanced 2000次制造录制负载,代码参考了微软的D3D12Multithreading示例。
原因分析
- 2缓冲区时的栅栏等待问题:仅2个后台缓冲区时,GPU处理完一帧后需等待缓冲区被
Present交换,CPU提交新命令列表前必须等待GPU释放对应缓冲区,导致栅栏等待耗时极高,抵消了多线程录制的时间收益。 - 8缓冲区时的
Present阻塞:Present并非完全异步——DXGI会限制缓冲区积压数量,避免GPU和系统内存资源耗尽。当缓冲区数量远超过GPU处理吞吐量时,Present会内部等待GPU消耗旧缓冲区,防止过多未处理帧积压,这就导致Present调用变为同步阻塞,耗时剧增。 DXGI_PRESENT_DO_NOT_WAIT的副作用:设置该标志后,Present会立即返回,但此时CPU提交新命令时,栅栏会检测到对应缓冲区仍被GPU占用,因此再次回到等待状态,总耗时回升。
解决方案
- 匹配缓冲区数量与GPU吞吐量:不要盲目增加缓冲区数量,测试3-4个缓冲区的配置,找到栅栏等待与
Present耗时的平衡点。多数场景下,3-4个缓冲区足以覆盖CPU录制与GPU执行的重叠时间,同时避免Present阻塞。 - 重叠CPU录制与GPU执行:不要等所有线程的命令列表录制完成再提交给GPU,而是每个线程录制完一个命令列表就立即提交,让GPU提前开始执行,最大化CPU与GPU的并行度,减少整体等待时间。
- 优化
Present同步行为:- 若无需垂直同步,设置
DXGI_PRESENT_INTERVAL_0,同时开启DXGI_PRESENT_ALLOW_TEARING(需显卡和驱动支持),可减少Present阻塞。 - 避免在
Present后立即等待栅栏,而是在提交下一帧命令前再检查栅栏状态,给GPU足够时间处理当前帧。
- 若无需垂直同步,设置
- 精细化栅栏管理:不要用全局栅栏跟踪所有命令队列,为每个命令队列或帧分配独立栅栏,仅等待当前帧所需的GPU操作完成,避免不必要的全局等待。
内容的提问来源于stack exchange,提问作者YoonSeok OH
相关产品推荐
相关产品推荐

