在 Vulkan 的 Acquire-Present 场景中使用信号量“同步”渲染通道布局转换

lis*_*reg 3 synchronization memory-model vulkan

所以有这个官方示例https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples#combined-graphicspresent-queue

\n\n
/* Only need a dependency coming in to ensure that the first\n   layout transition happens at the right time.\n   Second external dependency is implied by having a different\n   finalLayout and subpass layout. */\nVkSubpassDependency dependency = {\n    .srcSubpass = VK_SUBPASS_EXTERNAL,\n    .dstSubpass = 0,\n    // .srcStageMask needs to be a part of pWaitDstStageMask in the WSI semaphore.\n    .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,\n    .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,\n    .srcAccessMask = 0,\n    .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,\n    .dependencyFlags = 0};\n
Run Code Online (Sandbox Code Playgroud)\n\n

有人可以向我提供规范中的相关部分,这些部分结合了保证(构成推理链),即在发出队列的等待信号量(获取的图像)信号之前,这种依赖关系布局转换才会发生?

\n\n

特别是我找不到如何解释这种“同一阶段对其自身的依赖性”。

\n\n

要明确的是。我发现了很多与此相关的地方。我已经阅读文档一个多月了,但我正在努力寻找它们的连贯性。

\n\n

例如,何时(根据规范)发生可用性操作?相关内存依赖操作何时提交(按提交顺序)?如果是,那么提交子通道依赖项时?或者它是否位于源范围指令和目标范围指令之间(如 中If srcSubpass is equal to VK_SUBPASS_EXTERNAL, the first synchronization scope includes commands that occur earlier in submission order than the vkCmdBeginRenderPass)。如果是,srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT上面示例中的指令指的是哪些指令?

\n\n

在 krOoze 的回答后编辑

\n\n

我想我会写在这里。一是因为评论太长,二是因为我相信它对其他人可能有用。

\n\n

我承认,我误解了规范中有关执行依赖链的部分。

\n\n

所以总结一下。为了根据规范定义相关机制,我们有以下内容:

\n\n
    \n
  1. 等待信号量操作发生在子通道依赖操作之前(这里实际上我遇到了一些麻烦):

    \n\n
    \n

    6.4.2. 信号量等待*
    \n 信号量等待操作发生在执行依赖项中的第一组操作之后,并且发生在执行依赖项中的第二组操作之前。

    \n
    \n\n

    但是如何确定我们的子通道依赖操作在第二组中呢?它在同一批次中,它没有定义关于子通道依赖项的提交顺序(至少我看不到),并且信号量第二同步范围的定义没有帮助,因为我们的子通道依赖项没有\不会发生在 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT 管道阶段(这是 vkQueueSubmit 情况下第二个同步范围的限制)。更重要的是,同步范围无论如何都不会定义第二组操作。这是一个独特的术语。但我发现了另外一个可能有用的声明(好吧,如果我们同意子通道依赖项是工作项的一部分):

    \n\n
    \n

    4.3.5. 队列提交
    \n每个批次由三个不同的部分组成:

    \n\n
      \n
    1. 在执行批处理的其余部分之前要等待零个或多个信号量。
    2. \n
    3. 要执行的零个或多个工作项。
    4. \n
    5. 零个或多个信号量在工作项完成时发出信号。
    6. \n
    \n
    \n\n

    我们需要确保构建执行依赖链的顺序:

  2. \n
  3. 等待信号量和子通道依赖构成执行依赖链:

    \n\n
    \n

    6.1. 执行和内存依赖关系
    \n 执行依赖关系链是一系列执行依赖关系,它在第一个依赖关系\xe2\x80\x99s A\' 和最后一个依赖关系\xe2\x80\x99s B\' 之间形成发生前关系。对于每对连续的执行依赖关系,如果第一个依赖关系中的B S和第二个依赖关系中的 A S的交集不是空集,则存在链。

    \n
    \n\n

    (详情请参阅 krOoze 的回答)

    \n\n

    由此我们知道,子通道依赖项的目标范围将在发出信号量信号后发生(信号操作位于信号量等待操作的源范围内)。
    \n现在我们应该可以使用布局转换规则:

  4. \n
  5. 布局转换发生在我们的子通道依赖项的可用性操作之后:

    \n\n
    \n

    7.1. 渲染通道创建
    \n在 srcSubpass 等于 VK_SUBPASS_EXTERNAL 的所有依赖项的可用性操作之后,会发生自动布局从初始布局转换,其中 dstSubpass 使用将转换的附件。

    \n
    \n\n

    老实说,我仍然缺少规范中信号量发信号和可用性操作部分之间的顺序,但我认为可以假设。
    \n(上面的方法可以工作,因为可用性操作是内存依赖操作的一部分:

    \n\n
    \n

    执行内存依赖性的操作会生成:
    \n \xe2\x80\xa2 可用性操作,其所有写入的源范围位于依赖性的第一个访问范围中,目标范围位于设备域中。

    \n
    \n\n

    好吧,我们的第一个访问范围是空的,但它仍然是一个可用性操作,对吧?)

  6. \n
\n\n

还有这样的说法:

\n\n
\n

然而,对于附件,子通道依赖项的工作方式更像是与上面的 VkMemoryBarrier 类似定义的 VkImageMemoryBarrier,队列族索引设置为 VK_QUEUE_FAMILY_IGNORED,布局如下:
\n \xe2\x80\xa2 与 oldLayout 等效的是 Attachment\xe2\x80 \x99s 根据 srcSubpass 的子通道描述进行布局。
\n \xe2\x80\xa2 与 newLayout 等效的是根据 dstSubpass 的子通道描述的attachment\xe2\x80\x99s 布局。

\n
\n\n

...这带来了另一个分析范围,但我已经头痛了。当我对上述想法进行一些审查时,我将非常乐意对其进行更多编辑。

\n\n

*所有规范均引用自“Vulkan\xc2\xae 1.2.132 - A 规范(包含所有已注册的 Vulkan 扩展)”

\n

krO*_*oze 6

我在krOoze/Hello_Triangle/doc对此进行了一些讨论。在这种情况下应该发生的是:

\n

在此输入图像描述

\n
\n

特别是我找不到如何解释这种“同一阶段对其自身的依赖性”。

\n
\n

现在,我们首先来解决这个问题。这就是我所说的同步系统的本末倒置的直觉

\n

您不会同步阶段”或类似的事情。这种直觉只会让你感到困惑。您同步范围。

\n

人们还会将管道与流程图混淆。直觉上存在巨大差异。在流程图中,您从头开始,然后按顺序经历所有阶段,然后就完成了,永远完成了。这不是管道。它永远不会开始,也永远不会结束。管道就是。它就像一个桌面游戏板。你通过管道填充命令,它们就像板上的钉子一样经历各个阶段。

\n

同步命令引入了两个事物之间的依赖关系:源同步范围目标同步范围之间之间。它保证 src 范围发生在 dst 范围之前。

\n

范围是队列操作的一些子集,以及它们当前的执行可以处于哪个阶段。

\n

所以,有了这种更好的直觉,

\n
    .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,\n    .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,\n
Run Code Online (Sandbox Code Playgroud)\n

这是一件完全正常的事情。这意味着源范围内的命令(对于屏障,是较早记录的命令,或更正式地说是“提交顺序较早的命令”)COLOR_ATTACHMENT在目标范围内的任何命令到达COLOR_ATTACHMENT阶段之前到达阶段。(相反,如果没有依赖性,则意味着任何命令都可以在任何给定时间处于其执行的任何阶段)。

\n
\n

例如,当(根据规范)发生可用性操作时。

\n
\n

这些在某种程度上插入了屏障定义的依赖关系中。假设您包含内存依赖项在屏障中

\n

可用性操作(如果有)发生在源同步范围之后。然后发生布局转换(如果有)。然后发生可见性操作(如果有)。只有在此之后,目标同步作用域才能执行。

\n
\n

有人可以向我提供规范中组合保证的相关部分(构成推理链)

\n
\n

我现在只想拍拍你的头,因为你想要权威信息......:D

\n

因此,您需要了解形式主义和术语。这是所有同步原语所描述的内容。虽然只有一页,但读起来却相对困难。我尝试解释上面的重要部分。我不会在这里引用它,它是6.1。执行和内存依赖性章节。

\n

现在,信号量等待有了自己的章节。重要的是要注意它对于其他命令的行为与对于vkQueueSubmit(这很烦人)的行为略有不同。无论如何(6.4.2. 信号量等待):

\n
\n

第二同步范围包括同一批次提交的所有命令。在 的情况下vkQueueSubmit,第二同步范围仅限于由 的相应元素指定的目标阶段掩码pWaitDstStageMask确定的流水线阶段上的操作。此外,在 的情况下vkQueueSubmit,第二个同步范围还包括按提交顺序稍后发生的所有命令稍后出现的所有命令。

\n

第二个访问范围包括设备执行的所有内存访问。

\n
\n

(for vkQueueSubmit)是单个的VkSubmitInfo提交顺序也有自己的章节;基本上,它的意思是“提交数组中稍后的所有其他批次,以及vkQueueSubmit同一队列中的任何未来批次”。

\n

因此,这意味着:“如果您等待信号量,则只有在信号量发出信号后,所有命令VkSubmitInfo才能到达某个阶段”。pWaitDstStageMask

\n

现在了解渲染通道的作用非常重要。除了记录的命令之外,它还有其他“可同步的”:自动布局转换、加载操作和存储操作。

\n

自动布局转换:

\n
\n

自动布局转换为srcSubpass 等于 的所有依赖项的可用性操作initialLayout后发生的情况,其中 dstSubpass 使用将转换的附件VK_SUBPASS_EXTERNAL

\n

自动布局转换为子通道中使用的布局发生在该子通道作为dstSubpass.

\n
\n

因此,简单来说,布局转换隐藏在VkSubpassDependency您列表定义的依赖项中。它发生在.srcStageMaskwith之后.srcAccessMask。它发生在.dstSubpass.dstStageMask之前.dstAccessMask

\n

负载操作:

\n
\n

附件中每个样本的加载操作发生在任何访问使用附件的第一个子通道中的样本的记录命令之前。[...] 具有颜色格式的附件的加载操作在管道阶段执行VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT

\n

VK_ATTACHMENT_LOAD_OP_LOAD[...] 对于具有颜色格式的附件,这使用访问类型VK_ACCESS_COLOR_ATTACHMENT_READ_BIT

\n

VK_ATTACHMENT_LOAD_OP_CLEAR(或VK_ATTACHMENT_LOAD_OP_DONT_CARE)[...] 对于具有颜色格式的附件,这使用访问类型VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT

\n
\n

加载操作作为使用附件(您的.dstSubpass)的第一个子通道的一部分发生。以上明确地确定了您的.dstStageMask.dstAccessMask)

\n

现在,我们要选择pWaitDstStageMask.srcStageMask.srcAccessMask你列出来的pWaitDstStageMask = COLOR_ATTACHMENT_OUTPUT.srcStageMask = COLOR_ATTACHMENT_OUTPUT.srcAccessMask = 0

\n

信号量等待操作必须发生在这些VkSubpassDependency东西之前。这被指定为依赖链

\n
\n

执行依赖链是一系列执行依赖关系,它们在第一个依赖项\xe2\x80\x99s A\'和最后一个依赖项\xe2\x80\x99s B\'之间形成发生前关系。对于每对连续的执行依赖关系,如果第一个依赖关系中的B S和第二个依赖关系中的A S的交集不是空集,则存在链。

\n
\n

即,两个后续的同步原语也彼此同步并形成过渡属性。我们这里的A\'是信号量信号,我们这里的B\'是 的 dst 作用域VkSubpassDependency。我们这里BS是信号量dst范围,即pWaitDstStageMask。而我们的A S就是我们的 src 范围VkSubpassDependency

\n

所以我们pWaitDstStageMask与 的交集.srcStageMask仍然是COLOR_ATTACHMENT_OUTPUT。因此,形成了一个依赖链,保证信号量信号发生在渲染通道子通道COLOR_ATTACHMENT_OUTPUT中的命令之前。0

\n

现在,将它们放在一起:来自的信号量信号vkAcquireNextImage使交换链图像可以从表示引擎的读取中获得。信号量等待vkQueueSubmit使交换链图像对批量中的所有命令可见,COLOR_ATTACHMENT_OUTPUT仅限于. 该信号量的链VkSubpassDependency等待。该图像仍然可见,因此不需要额外的内存依赖性,因此我们的图像是可见.srcAccessMask0。布局转换写入图像并使其(隐式)布局转换中使用,并且对提供给..dst*VkSubpassDependency

\n