【问题标题】:"Synchronizing" a render pass layout transition with a semaphore in Acquire-Present scenario in Vulkan在 Vulkan 的 Acquire-Present 场景中“同步”渲染通道布局转换与信号量
【发布时间】:2020-02-21 12:59:50
【问题描述】:

于是就有了这个官方例子https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples#combined-graphicspresent-queue

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

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

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

要清楚。我在这里找到了很多似乎相关的地方。我已经阅读文档一个多月了,但我很难在其中找到连贯性。

例如,当(根据规范)可用性操作确实发生时?什么时候提交了相关的内存依赖操作(按提交顺序)?如果是,那么提交子通道依赖项的时间是什么时候?或者它是在源范围指令和目标范围指令之间的某个地方(如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 指的是什么指令?

在 krOoze 回答后编辑

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

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

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

  1. 等待信号量操作happens-before子通道依赖操作(这里我其实有点麻烦):

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

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

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

    1. 零个或多个信号量等待在执行之前其余批次。
    2. 要执行零个或多个工作项。
    3. 零个或多个信号量在工作项目完成时发出信号。

    并且我们需要确定这个顺序来构建执行依赖链:

  2. 等待信号量和subpass依赖构成一个执行依赖链根据:

    6.1。执行和内存依赖
    执行依赖链是执行依赖的序列,它在第一个依赖的 A' 和最终的依赖的 B' 之间形成发生前的关系。对于每一对连续的执行依赖,如果第一个依赖中的BS和第二个依赖中的AS的交集为不是空集。

    (详见 krOoze 的回答)

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

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

    7.1。渲染通道创建
    对于 srcSubpass 等于 VK_SUBPASS_EXTERNAL 的所有依赖项,在可用性操作之后从 initialLayout 自动布局转换,其中 dstSubpass 使用将要转换的附件。

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

    执行内存依赖的操作会生成:
    • 在依赖项的第一个访问范围和设备域的目标范围内具有所有写入的源范围的可用性操作。

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

还有这样的说法:

但是,对于附件,子通道依赖项的工作方式更像是定义类似于上面的 VkMemoryBarrier 的 VkImageMemoryBarrier,队列系列索引设置为 VK_QUEUE_FAMILY_IGNORED,并且布局如下:
• oldLayout 的等价物是根据 srcSubpass 的子通道描述的附件布局。
• 与newLayout 等效的是根据dstSubpass 的子通道描述的附件布局。

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

*所有规范都来自“Vulkan® 1.2.132 - A Specification (with all registered Vulkan extensions)”

【问题讨论】:

    标签: synchronization vulkan memory-model


    【解决方案1】:

    我在krOoze/Hello_Triangle/doc 上稍微讨论了一下。在这种情况下应该发生的事情是:

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

    现在,让我们先解决这个问题。这就是我喜欢称之为cart-before-horse intuition of the synchronization system

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

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

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

    作用域是队列操作的一些子集,它们当前可以在哪个阶段执行。

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

        .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
    

    是一件非常正常的事情。这意味着源作用域中的命令(对于屏障,较早记录的命令,或更正式的“提交顺序较早”的命令)在目标作用域中的任何命令到达COLOR_ATTACHMENT 阶段之前到达COLOR_ATTACHMENT 阶段。 (相比之下,没有依赖意味着任何命令都可以在任何给定时间处于其执行的任何阶段。

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

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

    可用性操作(如果有)发生在源同步范围之后。然后发生布局转换(如果有)。然后发生 visibility op(如果有的话)。并且只有在此之后才能执行目标同步范围。

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

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

    因此,您需要了解形式和命名法。它是描述所有同步原语的东西。它只有一页,但相对难以阅读。我试着解释上面的重要部分。我不会在这里引用它,这是6.1. Execution and Memory Dependencies章节。

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

    第二个同步范围包括在同一批次中提交的每个命令。在vkQueueSubmit 的情况下,第二个同步范围仅限于对由pWaitDstStageMask 的相应元素指定的destination stage mask 确定的管道阶段的操作。此外,在vkQueueSubmit 的情况下,第二个同步范围还包括在提交顺序中稍后出现的所有命令。

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

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

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

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

    自动布局过渡:

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

    自动布局转换为子通道中使用的布局,发生在 可见性操作之前,该子通道的所有依赖项为 dstSubpass

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

    加载操作:

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

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

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

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

    现在,我们选择pWaitDstStageMask.srcStageMask.srcAccessMask。你列出它是pWaitDstStageMask = COLOR_ATTACHMENT_OUTPUT.srcStageMask = COLOR_ATTACHMENT_OUTPUT.srcAccessMask = 0

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

    执行依赖链是一系列执行依赖,在第一个依赖的A'和最终依赖的B'之间形成了happens-before关系。对于每对连续的执行依赖,如果第一个依赖中的BSAS的交集存在一条链strong> 第二个依赖不是空集。

    即两个后续的同步原语也相互同步并形成一个过渡属性。我们这里的A'是信号量信号,我们这里的B'VkSubpassDependency的dst范围。我们这里的 BS 是信号量 dst 范围,即pWaitDstStageMask。而我们的AS就是我们VkSubpassDependency的src范围。

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

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

    【讨论】:

    • 这是一个很好的答案!谢谢你,虽然我没有得到关于拍我头的部分。 ;) 我已经更新了我的答案。你能再看一遍吗?
    • *我的问题很明显
    • @listerreg TBH 许多人只是想被那些兜售知识的中间人用瓶装知识点的勺子喂饱。这很高兴看到有人像男人一样直接询问规格报价。 :p 就像俗话说的那样,这让我感觉更像是在帮助一个人钓鱼,而不是给他们一条鱼。小心不要过度滥用 SO 系统;这不是reddit。如果可以,请保持问题的原子性,并根据需要创建新问题。我会在答案中说明我能做什么。
    • 谢谢。我知道这不是reddit。我坚信这仍然是同一个问题。 :) 哦,这个“从同一阶段到自身的依赖”是从 6.4.2 中引用的。信号量等待规范的一部分。
    • 等一下(以防你正在写作)我发现我的推理中有很大的缺陷需要纠正。
    【解决方案2】:

    引用自“Vulkan® 1.2.169 - A Specification (with all registered Vulkan extensions)”的所有规范。

    问:

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

    答:

    7.4.2. Semaphore Waiting ,它在注释部分提供了一个示例。

    如果一个可呈现的图像在用于帧缓冲区之前需要对其进行图像布局转换,则可以作为获取图像后提交到队列的第一个操作来执行,并且 不应阻止其他工作与演示操作重叠。例如,VkImageMemoryBarrier 可以使用:

    • srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
    • srcAccessMask = 0
    • dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
    • dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
    • oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
    • newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL

    根据关于VkSubpassDependency 的注释部分,由于子通道依赖项的工作方式类似于VkImageMemoryBarrier

    对于非附件资源,子通道依赖表示的内存依赖几乎与VkMemoryBarrier 的相同...

    但是,对于附件,子通道依赖项的工作方式更像是 VkImageMemoryBarrier,其定义类似于上面的 VkMemoryBarrier ...

    关于示例的进一步说明可以回答部分问题。

    此屏障在之前的表示操作和随后的颜色附件输出操作之间实现了依赖链,并在其间执行了布局转换,并且不会在之前的工作和任何顶点处理阶段之间引入依赖关系。更准确地说,表示操作完成后的信号量信号,信号量等待暂停VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT 阶段,并且在同一阶段之间存在依赖关系,并且在其间执行布局转换。

    【讨论】:

      猜你喜欢
      • 2018-03-31
      • 1970-01-01
      • 2016-11-21
      • 2019-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多