首页 > 解决方案 > Order of action commands using subpass dependency?

问题描述

From what I have read so far, commands in a single command buffer can be out of order without explicit synchronization. Here is what the vulkan spec says (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."

Edit: At first I thought that set state commands would act as some kind of barrier to ensure that draw commands are in order. I have already been explained that this is wrong. So I look at this example of bloom effect in 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]);

Here are the 2 subpass dependencies used by both render passes

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;

My understanding then becomes that these 2 subpass dependencies are responsible for the execution ordering of the render pass but I'm not sure how yet since I'm still fuzzy about subpass dependency. If I'm correct in my understanding can you explain to me why the subpass dependency helps order the draw command? If I'm wrong then what is ensuring the draw command order?

标签: graphicsvulkan

解决方案


所以发生的事情是某些东西被渲染到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更重要的是(且方便),存储操作发生在颜色附件的同一阶段。也总是VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT不管是STORE还是DONT_CARE

VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT并且VK_ACCESS_SHADER_READ_BIT再次非常适合图像采样(在片段着色器中)。

所以这意味着img1在第二个渲染通道实例对其进行采样之前,它是从第一个渲染通道实例完全渲染和存储的。

这也意味着img2在被第三个渲染通道实例采样之前,从第二个渲染通道实例完全渲染和存储。


这是一个高级示例,您应该已经了解同步。

状态命令不受同步。它们仅在引入后续动作命令后立即更改其上下文,并且通常持续到命令缓冲区结束或再次更改状态。

子通道依赖和屏障以这种方式定义依赖:同步范围在同步范围开始执行src之前完成执行。dst

子通道依赖项和障碍几乎相同。屏障通常在渲染通道外部使用,而子通道依赖关系在其中。子通道彼此无序,因此子通道依赖项还具有*Subpass参数,并且同步范围仅限于指定的子通道。VK_SUBPASS_EXTERNAL意味着vkCmdBeginRenderPass\ after之前的东西vkCmdEndRenderPass是同步范围的一部分。

了解同步系统需要时间,我无法在此正确介绍。在Using pipeline barrier instead of semaphores上,我对障碍有更多的扩展答案,否则互联网上也充满了资源。


推荐阅读