同步 CPU 和 GPU
此示例的源代码可以在 Khronos Vulkan 示例 github 存储库 中找到。 |
WaitIdle 或 Fences
同步 CPU 和 GPU 的最简单方法是使用 vkQueueWaitIdle
或 vkDeviceWaitIdle
,这些命令会等待设备或队列完成执行所有分派给它的工作。请注意,当使用单个 VkQueue
时,vkQueueWaitIdle
在功能上等同于 vkDeviceWaitIdle
。虽然此方法可靠,但它比 CPU 和 GPU 之间同步实际需要的要粗糙得多。这会导致 GPU 内出现气泡,阻止它保持完整的管线,从而阻止它并行化来自不同帧的顶点和片元工作,从而导致更高的帧时间和更低的效率。
WaitIdle
的替代方法是使用 Vulkan Fence
对象,这些对象旨在允许 GPU 在完成单个帧的工作负载时通知 CPU,从而允许 CPU 安全地重用该帧的资源。此方法避免了在等待 GPU 完成执行时停顿,因为 CPU 可以继续提交后续帧,而无需等待 GPU,这反过来避免了 GPU 管线耗尽工作。
Wait Idle 示例
此示例提供了两个单选按钮,允许您在 WaitIdle
和 Fence
之间切换。
当选择 WaitIdle
时,该示例在开始每个帧之前调用 vkDeviceWaitIdle
,这会强制 GPU 完成执行所有分派给它的工作,从而耗尽管线内的所有工作。因此,GPU 在创建下一帧的命令缓冲区时处于空闲状态,直到它被分派,这会增加帧时间。
当选择 Fence
时,该示例在创建期间为每个帧分配一个 Fence
,然后调用 vkWaitForFences
并使用下一个要计算的帧的 Fence
。此方法允许 CPU 在 GPU 执行先前帧的工作负载时继续向 GPU 分派工作。
以下是在具有 Mali G76 GPU 的手机上运行的示例的屏幕截图

当使用 WaitIdle
时,平均帧时间为 72 毫秒,但在启用 Fences
时,帧时间会减少 22% 至 56 毫秒。
当使用 Streamline Performance Analyzer 时,性能提升也很明显。
下图显示了当使用 WaitIdle
时,单个帧中的 Job Manager 周期和停顿。

而下图显示了使用 Fences
时,单个帧的 Job Manager 周期。

此输出清楚地表明,WaitIdle
强制 GPU 耗尽所有工作,这导致 GPU 空闲,从而导致更高的帧时间。
最佳实践总结
这样做
-
使用
Fences
异步读取数据回 CPU;不要同步阻塞并导致管线排空。
不要
-
不必要地在 CPU 或 GPU 上等待 GPU 数据。
-
除非绝对必要进行粗粒度同步,否则不要使用
vkQueueWaitIdle()
或vkDeviceWaitIdle()
。
影响
-
影响可能非常小,也可能非常显著,具体取决于排队的各个工作负载的相对大小和顺序。
调试
-
Arm Mobile Studio 可用于可视化 Arm CPU 和 Mali GPU 在两个 GPU 队列上的活动,并可以快速显示 GPU 队列本地调度中的气泡(指示阶段依赖问题)或 CPU 和 GPU 之间的全局气泡(指示正在使用阻塞的 CPU 调用)。