So I have a render pass with a single subpass that draws directly to a framebuffer. The specification doesn't force me to use dependencies - if I omit them, the implementation inserts them implicitly (though I don't understand why it uses srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
for the first subpass - this stage means the very beginning, i.e. don't wait for anything).
But as usual with the Vulkan - better to be explicit. And here is the confusion - multiple sources use subpasses differently.
Sdk's cube example does not use them at all.
Vulkan-tutorial uses only one:
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
Why srcAccessMask
is zero here?
API without secrets uses two:
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependency.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
and
dependency.srcSubpass = 0;
dependency.dstSubpass = VK_SUBPASS_EXTERNAL;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependency.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
It's not very clear why srcStageMask
is
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
in the first subpass - isn't
this stage should be used for execution dependencies, but here we
need a memory dependency? The same question about why dstStageMask
is VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
in the second subpass?
Khronos synchronization examples uses one:
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
Why srcAccessMask
is 0?
And here's my attempt with two dependencies:
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // need to wait until
presentation is finished reading the image
dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // we are writing to
the image in this stage
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
and
dependency.srcSubpass = 0;
dependency.dstSubpass = VK_SUBPASS_EXTERNAL;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // we are writing to
the image in this stage
dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; // presentation reads
image in this stage (is it?)
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
All this is very confusing. As you can see multiple competent sources have a different vision. Which one to use? How to understand these dependencies?
Subpass dependencies are basically syntactic sugar for pipeline barriers targeting attachments during a render pass. Internal dependencies (those with non-external but distinct subpass indices) are used to ensure order between subpasses.
The use of a render pass in a command buffer is a render pass instance.” Page 4 4 7.0 Render Pass Vulkan® 1.1.74 - A Specification (with KHR extensions) “A subpass represents a phase of rendering that reads and writes a subset of the attachments in a render pass.
Framebuffers represent a collection of memory attachments that are used by a render pass instance. Examples of these memory attachments include the color image buffers and depth buffer that we created in previous samples.
Oh dear, this is a topic that is not that well understood by most people. A lot of misinformation was assumed and proliferated. The proper place for canonical examples is the wikipage in the github repo.
The relevant section about swapchain images acquire/present can be found here.
it's an old example from before a lot of the synchronization specifics were properly nailed down.
The purpose there is to synchronize the semaphore use to wait on the result of vkAcquireNextImage with rendering. This is a write-after-read hazard and the semaphore will include memory visibility synchronization. So srcAccessMask is no necessary.
Again the barrier is meant to sync against the semaphore. When submitting a command buffer to a queue you can set which stages wait on which semaphores. In this case they use the bottom of pipe stage instead of the VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
same answer as 2, the only difference is with the dstAccessMask
The wiki page I linked above uses the following:
/* 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};
/* Normally, we would need an external dependency at the end as well since we are changing layout in finalLayout,
but since we are signalling a semaphore, we can rely on Vulkan's default behavior,
which injects an external dependency here with
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
dstAccessMask = 0. */
You mixed up the subpass indices for the second barrier. Though as mentioned in the comments there is already an implicit barrier at the end of the renderpass which in turn syncs against the semaphore or fence you use to sync against the present. So there is no need fo an explicit dependency.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With