27

在 Vulkan 中有几种处理同步的方法。我是这样理解的:

  • 栅栏是 GPU 到 CPU 的同步。
  • 信号量是 GPU 到 GPU 的同步,它们用于同步队列提交(在相同或不同的队列上)。
  • 事件更通用,在 CPU 和 GPU 上都可以重置和检查。
  • 屏障用于命令缓冲区内的同步。

就我而言,我有两个命令缓冲区。我希望第二个命令缓冲区在第一个命令缓冲区之后执行。

submitInfo.pCommandBuffers = &firstCommandBuffer;
vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);

// wait for first command buffer to finish
submitInfo.pCommandBuffers = &secondCommandBuffer;
vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);

什么样的同步最适合这个?如果我使用vkQueueWaitIdle(queue)),它与使用栅栏是一样的,还是应该为此使用事件或信号量?

如果我同时将多个命令缓冲区发送到队列:

std::vector<VkCommandBuffer> submitCmdBuffers = {
        firstCommandBuffer,
        secondCommandBuffer
    };
    submitInfo.commandBufferCount = submitCmdBuffers.size();
    submitInfo.pCommandBuffers = submitCmdBuffers.data();

还有一种方法可以在第一个和第二个之间进行同步吗?

4

2 回答 2

18

第一个命令缓冲区是打开深度测试的渲染对象。第二个命令缓冲区是在深度测试关闭的情况下渲染网格的轮廓。因为它必须在其他对象之上。

对于这种情况,您需要什么取决于这些命令缓冲区是什么。

如果这些是在同一个渲染过程实例中执行的辅助命令缓冲区,那么您不需要任何同步。除非您手动读取辅助命令缓冲区中的深度纹理,否则不会。为什么?

因为第 2.2.1 节的API 排序可以保护您。渲染通道实例中的深度测试和深度写入将始终按 API 顺序进行。因此,以后的命令,无论是在同一个 CB 中还是在不同的 CB 中,都将根据深度测试/写入进行排序。

但是,如果您需要从着色器中读取该深度缓冲区,或者您的命令缓冲区位于不同的渲染通道实例中,那么您需要通过事件进行显式同步。

在这种情况下,vkCmdSetEvent命令的阶段掩码应该是写入深度值的阶段。这可能是EARLY_FRAGMENT_TESTS_BITLATE_FRAGMENT_TESTS_BIT。为了安全起见,两者都用。但是,由于您可能正在更新相同的颜色缓冲区,因此您还需要COLOR_ATTACHMENT_OUTPUT_BIT舞台。将此命令插入到第一个命令缓冲区的末尾(或在完成所有深度写入之后)。

对于vkCmdWaitEvent,您希望等待需要它的管道阶段。在您的情况下,这又是片段测试和颜色附件。但是,如果着色器阶段要读取深度,您还需要在等待命令中使用该阶段。

由于涉及内存,您vkCmdWaitEvent还需要使用对深度和颜色缓冲区的内存依赖性。

但实际上,所有这些复杂性都是为什么您应该尽可能将这些命令缓冲区放在同一个渲染通道实例中。您无法这样做的唯一原因是您需要从着色器中的深度缓冲区中读取数据。

于 2016-05-27T13:44:10.057 回答
6

对于您的场景,您应该使用事件。这些应该是按给定顺序同步执行两个命令缓冲区的最轻量级的同步对象,即使您一次提交它们也是如此。但请注意,事件不适用于不同的队列。如果您只使用一个,请使用事件并尽量保持 src 和 dst 管道阶段掩码尽可能窄。

信号量是同步命令缓冲区执行的另一种方式,但它们仅适用于队列提交,因此它们比事件更重量级。

于 2016-05-27T11:59:41.077 回答