Reputation:
I'm reading Vulkan Tutorial "Rendering and Presentation" chapter, the "Subpass dependencies" section confused me a lot.
It says: VK_SUBPASS_EXTERNAL is reference to the commands outside the render pass, but, the problem is, there is no commands outside the render pass in that Hello-Triangle example, and I didn't see spec or somewhere says it will automatically create a command block by semaphore, I'm confused now, what does the VK_SUBPASS_EXTERNAL
mean if there is no command before render pass?
Upvotes: 2
Views: 392
Reputation: 473302
The source operation defines the source command scope for the dependency, the set of commands that will have a "happens before" relationship to the commands in the destination scope.
Scopes are usually queue-wide (technically, they also include things in the intra-queue dependency chain based on semaphores and the like, but nevermind that now). So when a source scope talks about all of the commands before some point, that means all commands that the queue executed before that point in sequence of commands.
External as the source "subpass" in a subpass dependency means everything before the render pass. That's everything before the render pass. Every command processed by that queue up until the moment it processes the vkCmdBeginRenderPass
command.
If the very first command processed by a queue happens to be a begin render pass command, then the external source scope is an empty set. But there's nothing wrong with that; the synchronization is essentially a no-op. So everything proceeds as normal.
Upvotes: 1
Reputation: 13246
VK_SUBPASS_EXTERNAL
references commands recorded\submitted before vkCmdBeginRenderPass
or after vkCmdEndRenderpass
.
In a case of a 101 example, the adjacent use is just swapchain, which requires an semaphore on acquire and semaphore on present. There is actually concrete note about this use case in the specification, at the end of 7.4.2. Semaphore Waiting.
Now, semaphore signal (before you present) already covers all previous memory access on the device, so you are all set without any further synchronization. But for educational purposes and code readability, it cannot hurt to add that dependency explicitly anyway:
VkSubpassDependency presentDependency{
0, // srcSubpass
VK_SUBPASS_EXTERNAL, // dstSubpass
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // srcAccessMask
0, // dstAccessMask
0, // dependencyFlags
};
Note dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
, which practically means "synchronize with nothing". The srcStageMask
covers your store op, but it would have been covered implicitly even if you do not list any Dependency.
Now the semaphore wait (for your swapchain image acquire semaphore) is sneaky. There's a sneaky vkQueueSubmit
parameter, the pWaitDstStageMask
. That parameter says the semaphore will not be waited sooner than the pipeline stage in pWaitDstStageMask
. So you must actually put a dependency there that pushes the layout transition from your render pass after the semaphore wait:
pWaitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubpassDependency acquireDependency{
VK_SUBPASS_EXTERNAL, // srcSubpass
0, // dstSubpass
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // dstStageMask
0, // srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dstAccessMask
VK_DEPENDENCY_BY_REGION_BIT, // dependencyFlags
};
Note that pWaitDstStageMask = srcStageMask
, so an execution dependency chain is formed with the semaphore wait. This dependency also forces the layout transition away from initialLayout
to happen inbetween the semaphore wait and the renderpass load op.
Refer to this diagram about the synchronization, and what and why it is needed:
For a more complex app it actually gets simpler. Refer to official synch. examples. You simply add a dependency there that is appropriate for the previous\next usage of the image.
E.g. if you perform compute (vkCmdDispatch
) after the render pass, then you simply add a VK_SUBPASS_EXTERNAL
dependency with dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
.
Upvotes: 2