使用 subpass 依赖的动作命令顺序?
Order of action commands using subpass dependency?
根据我目前所读的内容,如果没有显式同步,单个命令缓冲区中的命令可能会乱序。这是 vulkan 规范所说的 (https://vulkan.lunarg.com/doc/view/1.0.26.0/linux/vkspec.chunked/ch02s02.html#fundamentals-queueoperation-commandorder)
"The work involved in performing action commands is often allowed to overlap or to be reordered, but doing so must not alter the state to be used by each action command. In general, action commands are those commands that alter framebuffer attachments, read/write buffer or image memory, or write to query pools."
编辑:起初我认为设置状态命令会作为某种屏障来确保绘制命令是有序的。我已经被解释说这是错误的。所以我看一下 Vulkan 中的绽放效果示例
https://github.com/SaschaWillems/Vulkan/blob/master/examples/bloom/bloom.cpp
/*First render pass: Render glow parts of the model (separate mesh) to an offscreen frame buffer*/
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.glowPass);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.ufoGlow.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufoGlow.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufoGlow.indexCount, 1, 0, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
/*Second render pass: Vertical blur
Render contents of the first pass into a second framebuffer and apply a vertical blur
This is the first blur pass, the horizontal blur is applied when rendering on top of the scene*/
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[1].framebuffer;
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurVert, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurVert);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
这是两个渲染通道使用的 2 个子通道依赖关系
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
然后我的理解是这 2 个子通道依赖关系负责渲染通道的执行顺序,但我不确定是怎么回事,因为我仍然对子通道依赖关系很模糊。如果我的理解是正确的,你能向我解释为什么 subpass 依赖项有助于命令绘制命令吗?如果我错了,那么是什么确保了绘制命令的顺序?
所以发生的事情是将某些东西渲染到 img1
(作为颜色附件)。然后
img1
被采样,东西被写入 img2
(作为颜色附件)。然后 img2
被采样并写入交换链图像。
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstSubpass = 0;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
对于第一个和第二个渲染过程实例,这可能会阻止资源的某些先前采样。可能是从上一帧开始的。假设后续帧之间没有其他同步。
dependencies[1].srcSubpass = 0;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
现在颜色附件是用 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
编写的,更重要的是(也很方便),存储操作发生在颜色附件的同一阶段。无论是 STORE
还是 DONT_CARE
.
,它也总是 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
和 VK_ACCESS_SHADER_READ_BIT
再次适合图像采样(在片段着色器中)。
所以这意味着 img1
在被第二个渲染通道实例采样之前从第一个渲染通道实例完全渲染和存储。
这也意味着 img2
在被第三个渲染通道实例采样之前从第二个渲染通道实例完全渲染和存储。
这是一个高级示例,您应该已经了解同步。
状态命令不受同步影响。它们只会在引入后立即更改后续操作命令的上下文,并且通常会持续到命令缓冲区结束,或者直到状态再次更改。
子通道依赖关系和障碍以这种方式定义依赖关系:src
同步作用域在 dst
同步作用域开始执行之前完成执行。
Subpass 依赖项和障碍几乎相同。障碍通常在渲染通道外使用,而子通道依赖于它内部。子通道彼此无序,因此子通道依赖项还具有 *Subpass
参数,并且同步范围仅限于指定的子通道。 VK_SUBPASS_EXTERNAL
表示 vkCmdBeginRenderPass
之前 \ vkCmdEndRenderPass
之后的内容是同步范围的一部分。
了解同步系统需要时间,我不能在这里适当地介绍它。我在 Using pipeline barriers instead of semaphores 上有更多关于障碍的扩展答案,否则互联网上也有很多资源。
根据我目前所读的内容,如果没有显式同步,单个命令缓冲区中的命令可能会乱序。这是 vulkan 规范所说的 (https://vulkan.lunarg.com/doc/view/1.0.26.0/linux/vkspec.chunked/ch02s02.html#fundamentals-queueoperation-commandorder)
"The work involved in performing action commands is often allowed to overlap or to be reordered, but doing so must not alter the state to be used by each action command. In general, action commands are those commands that alter framebuffer attachments, read/write buffer or image memory, or write to query pools."
编辑:起初我认为设置状态命令会作为某种屏障来确保绘制命令是有序的。我已经被解释说这是错误的。所以我看一下 Vulkan 中的绽放效果示例 https://github.com/SaschaWillems/Vulkan/blob/master/examples/bloom/bloom.cpp
/*First render pass: Render glow parts of the model (separate mesh) to an offscreen frame buffer*/
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.glowPass);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.ufoGlow.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufoGlow.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufoGlow.indexCount, 1, 0, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
/*Second render pass: Vertical blur
Render contents of the first pass into a second framebuffer and apply a vertical blur
This is the first blur pass, the horizontal blur is applied when rendering on top of the scene*/
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[1].framebuffer;
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurVert, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurVert);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
这是两个渲染通道使用的 2 个子通道依赖关系
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
然后我的理解是这 2 个子通道依赖关系负责渲染通道的执行顺序,但我不确定是怎么回事,因为我仍然对子通道依赖关系很模糊。如果我的理解是正确的,你能向我解释为什么 subpass 依赖项有助于命令绘制命令吗?如果我错了,那么是什么确保了绘制命令的顺序?
所以发生的事情是将某些东西渲染到 img1
(作为颜色附件)。然后
img1
被采样,东西被写入 img2
(作为颜色附件)。然后 img2
被采样并写入交换链图像。
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstSubpass = 0;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
对于第一个和第二个渲染过程实例,这可能会阻止资源的某些先前采样。可能是从上一帧开始的。假设后续帧之间没有其他同步。
dependencies[1].srcSubpass = 0;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
现在颜色附件是用 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
编写的,更重要的是(也很方便),存储操作发生在颜色附件的同一阶段。无论是 STORE
还是 DONT_CARE
.
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
和 VK_ACCESS_SHADER_READ_BIT
再次适合图像采样(在片段着色器中)。
所以这意味着 img1
在被第二个渲染通道实例采样之前从第一个渲染通道实例完全渲染和存储。
这也意味着 img2
在被第三个渲染通道实例采样之前从第二个渲染通道实例完全渲染和存储。
这是一个高级示例,您应该已经了解同步。
状态命令不受同步影响。它们只会在引入后立即更改后续操作命令的上下文,并且通常会持续到命令缓冲区结束,或者直到状态再次更改。
子通道依赖关系和障碍以这种方式定义依赖关系:src
同步作用域在 dst
同步作用域开始执行之前完成执行。
Subpass 依赖项和障碍几乎相同。障碍通常在渲染通道外使用,而子通道依赖于它内部。子通道彼此无序,因此子通道依赖项还具有 *Subpass
参数,并且同步范围仅限于指定的子通道。 VK_SUBPASS_EXTERNAL
表示 vkCmdBeginRenderPass
之前 \ vkCmdEndRenderPass
之后的内容是同步范围的一部分。
了解同步系统需要时间,我不能在这里适当地介绍它。我在 Using pipeline barriers instead of semaphores 上有更多关于障碍的扩展答案,否则互联网上也有很多资源。