WSI 同步子通道依赖和 link 颜色附件输出
WSI synchronization subpass dependency and link to color attachment output
我想我确实了解 Vulkan 同步的工作原理,但我在理解与 WSI 的同步方面存在问题。
Synchronization Examples,我们可以找到这段代码
/* 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};
在我看来应该是这样的:
VkSubpassDependency dependency = {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
// .srcStageMask needs to be a part of pWaitDstStageMask in the WSI semaphore.
.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstAccessMask = 0,
.dependencyFlags = 0};
确实,由于我们要写入附件,因此无需在 dstAccessMask
.
中使用 WRITE_BIT
(表示使写入可用)
但真正的问题在于 srcStageMask
。
我明白为什么 dstStageMask
是 COLOR_ATTACHMENT_OUTPUT_BIT
。这是因为我们不接触附件,所以前面的阶段可以正常工作。
然而,对于 srcStageMask
,我没有看到任何关于 WSI 和 COLOR_ATTACHMENT_OUTPUT_BIT
之间的 link。对我来说,布局转换必须出现在演示文稿的末尾,恰好在 COLOR_ATTACHMENT_OUTPUT
阶段开始之前。
对我来说,演示结束应该用 BOTTOM_OF_PIPE
表示,而不是 COLOR_ATTACHMENT_OUTPUT
我哪里弄错了?
Indeed, since we are going to WRITE
into the attachment, there is no need to use a WRITE_BIT
(meaning make writes available) in the dstAccessMask.
我不太确定你的逻辑。应该是 WRITE
因为我们要写附件。
这可能有助于描述这里发生的事情(来自 krOoze/Hello_Triangle/doc):
信号量的 VkSubpassDependency
链(通过 pWaitDstStageMask
)。然后它执行其布局转换。然后它与 Load Op 同步。 (然后 Load Op 发生在 subpass 中。然后 subpass vkDraw
s 一些东西到交换链图像中。)
现在假设(这是第一次使用交换链图像的典型情况)我们的加载操作是 VK_ATTACHMENT_LOAD_OP_CLEAR
。这意味着 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
访问。
所以:
Presentation 将读取交换链图像,并提交信号量信号。
我们将 VkSubpassDependency
链接到信号(通过 pWaitDstStageMask
)。信号量信号已经涵盖所有内存访问,因此我们的 srcAccessMask = 0
.
Dependency 执行其对 Layout Transition 的隐式依赖(获取你的 src
,并发明一些内部 dst
),然后 Layout Transition 发生,它读取图像和写它回来,然后依赖执行另一个隐式依赖(发明一些src
覆盖布局转换,并使用你的dst
)。
Load Op 发生在 subpass 中,你必须明确地确保它发生在上面的所有事情之后。所以你的 Dependency 中的 dst
必须是 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
来覆盖 Load Op.
现在内存hazard分为三种:读→写、写→写、写→读(读→读是非危险)。在 WWH 中,有些人写资源,而其他人也写资源。不清楚哪个写入最后发生,因此内存内容变成垃圾。在 RWH 和 WRH 中,读和写可能同时发生。不清楚读取看到的是未修改的内存,还是写入的内存(即读取垃圾)。
在没有显式依赖的情况下,Layout Transition 和后续的 Load Op 形成了写→写的风险。因此 dstAccessMask
必须 不是 0
以解决危险并确保一个写入发生在第二个之前。
(也许值得注意的是,我们引入 VkSubpassDependency
仅仅是为了布局转换。否则信号量等待就已经是所有需要的了。默认是 srcStageMask = TOP_OF_PIPE
,这意味着没有显式依赖,布局转换可能发生在信号量等待之前,即在演示文稿完成读取之前;这将是读→写的危险)。
To me, the layout transition must appear at the end of the presentation, and just before the beginning of the COLOR_ATTACHMENT_OUTPUT
stage. And the end of presentation, for me, should be represented by BOTTOM_OF_PIPE
and not COLOR_ATTACHMENT_OUTPUT
我们在这里有一点选择:pWaitDstStageMask = dependency.srcStageMask = ?
现在我们的情况是这样的:
vkBeginCommandBuffer();
[possibly vkCmdDispatch()?]
vkBeginRenderPass();
vkCmdDraw(); // does vertex shading + fragment shading, then color writes
vkEndRenderPass();
vkEndCommandBuffer();
vkQueueSubmit(.pwaitDstStageMask = ?);
如果我们使用VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
,那么信号量等待不会阻止启动假设的vkCmdDispatch()
(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
)。并且 Subpass Dependency src
也不会强制它完成。太棒了!
使用的阶段不应早于VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
。例如。对于 VK_PIPELINE_STAGE_ALL_COMMANDS
,信号量等待会不必要地阻塞 vkCmdDispatch()
以及 vkCmdDraw
的顶点和片段着色器。同时,只有当我们实际需要在 Load Op 写入它时,我们才需要交换链图像(你可以想象当 vkCmdDraw()
到达它需要执行颜色写入的点时发生)。
现在,我们可以选择VK_PIPELINE_STAGE_BOTTOM_OF_PIPE
。信号量不阻塞任何东西(dstStage\pWaitDstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE
与 "nothing" 的意思相同)。伟大的!但是 Subpass Dependency 现在会阻止所有内容(srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE
与 VK_PIPELINE_STAGE_ALL_COMMANDS
的含义相同)。这意味着我们的 vkCmdDispatch
必须在我们的子通道开始之前完成。不太好...
所以,最好的做法是使用pWaitDstStageMask = dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
。
我想我确实了解 Vulkan 同步的工作原理,但我在理解与 WSI 的同步方面存在问题。
Synchronization Examples,我们可以找到这段代码
/* 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};
在我看来应该是这样的:
VkSubpassDependency dependency = {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
// .srcStageMask needs to be a part of pWaitDstStageMask in the WSI semaphore.
.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstAccessMask = 0,
.dependencyFlags = 0};
确实,由于我们要写入附件,因此无需在 dstAccessMask
.
WRITE_BIT
(表示使写入可用)
但真正的问题在于 srcStageMask
。
我明白为什么 dstStageMask
是 COLOR_ATTACHMENT_OUTPUT_BIT
。这是因为我们不接触附件,所以前面的阶段可以正常工作。
然而,对于 srcStageMask
,我没有看到任何关于 WSI 和 COLOR_ATTACHMENT_OUTPUT_BIT
之间的 link。对我来说,布局转换必须出现在演示文稿的末尾,恰好在 COLOR_ATTACHMENT_OUTPUT
阶段开始之前。
对我来说,演示结束应该用 BOTTOM_OF_PIPE
表示,而不是 COLOR_ATTACHMENT_OUTPUT
我哪里弄错了?
Indeed, since we are going to
WRITE
into the attachment, there is no need to use aWRITE_BIT
(meaning make writes available) in the dstAccessMask.
我不太确定你的逻辑。应该是 WRITE
因为我们要写附件。
这可能有助于描述这里发生的事情(来自 krOoze/Hello_Triangle/doc):
信号量的 VkSubpassDependency
链(通过 pWaitDstStageMask
)。然后它执行其布局转换。然后它与 Load Op 同步。 (然后 Load Op 发生在 subpass 中。然后 subpass vkDraw
s 一些东西到交换链图像中。)
现在假设(这是第一次使用交换链图像的典型情况)我们的加载操作是 VK_ATTACHMENT_LOAD_OP_CLEAR
。这意味着 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
访问。
所以:
Presentation 将读取交换链图像,并提交信号量信号。
我们将
VkSubpassDependency
链接到信号(通过pWaitDstStageMask
)。信号量信号已经涵盖所有内存访问,因此我们的srcAccessMask = 0
.Dependency 执行其对 Layout Transition 的隐式依赖(获取你的
src
,并发明一些内部dst
),然后 Layout Transition 发生,它读取图像和写它回来,然后依赖执行另一个隐式依赖(发明一些src
覆盖布局转换,并使用你的dst
)。Load Op 发生在 subpass 中,你必须明确地确保它发生在上面的所有事情之后。所以你的 Dependency 中的
dst
必须是VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
+VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
来覆盖 Load Op.
现在内存hazard分为三种:读→写、写→写、写→读(读→读是非危险)。在 WWH 中,有些人写资源,而其他人也写资源。不清楚哪个写入最后发生,因此内存内容变成垃圾。在 RWH 和 WRH 中,读和写可能同时发生。不清楚读取看到的是未修改的内存,还是写入的内存(即读取垃圾)。
在没有显式依赖的情况下,Layout Transition 和后续的 Load Op 形成了写→写的风险。因此 dstAccessMask
必须 不是 0
以解决危险并确保一个写入发生在第二个之前。
(也许值得注意的是,我们引入 VkSubpassDependency
仅仅是为了布局转换。否则信号量等待就已经是所有需要的了。默认是 srcStageMask = TOP_OF_PIPE
,这意味着没有显式依赖,布局转换可能发生在信号量等待之前,即在演示文稿完成读取之前;这将是读→写的危险)。
To me, the layout transition must appear at the end of the presentation, and just before the beginning of the
COLOR_ATTACHMENT_OUTPUT
stage. And the end of presentation, for me, should be represented byBOTTOM_OF_PIPE
and notCOLOR_ATTACHMENT_OUTPUT
我们在这里有一点选择:pWaitDstStageMask = dependency.srcStageMask = ?
现在我们的情况是这样的:
vkBeginCommandBuffer();
[possibly vkCmdDispatch()?]
vkBeginRenderPass();
vkCmdDraw(); // does vertex shading + fragment shading, then color writes
vkEndRenderPass();
vkEndCommandBuffer();
vkQueueSubmit(.pwaitDstStageMask = ?);
如果我们使用VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
,那么信号量等待不会阻止启动假设的vkCmdDispatch()
(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
)。并且 Subpass Dependency src
也不会强制它完成。太棒了!
使用的阶段不应早于VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
。例如。对于 VK_PIPELINE_STAGE_ALL_COMMANDS
,信号量等待会不必要地阻塞 vkCmdDispatch()
以及 vkCmdDraw
的顶点和片段着色器。同时,只有当我们实际需要在 Load Op 写入它时,我们才需要交换链图像(你可以想象当 vkCmdDraw()
到达它需要执行颜色写入的点时发生)。
现在,我们可以选择VK_PIPELINE_STAGE_BOTTOM_OF_PIPE
。信号量不阻塞任何东西(dstStage\pWaitDstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE
与 "nothing" 的意思相同)。伟大的!但是 Subpass Dependency 现在会阻止所有内容(srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE
与 VK_PIPELINE_STAGE_ALL_COMMANDS
的含义相同)。这意味着我们的 vkCmdDispatch
必须在我们的子通道开始之前完成。不太好...
所以,最好的做法是使用pWaitDstStageMask = dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
。