I'm struggling to understand when exactly execution dependency chains are formed.
The vulkan spec states:
An execution dependency chain is a sequence of execution dependencies that form a happens-before relation between the first dependency’s ScopedOps1 and the final dependency’s ScopedOps2. For each consecutive pair of execution dependencies, a chain exists if the intersection of Scope2nd in the first dependency and Scope1st in the second dependency is not an empty set.
and:
Including a particular pipeline stage in the first synchronization scope of a command implicitly includes logically earlier pipeline stages in the synchronization scope. Similarly, the second synchronization scope includes logically later pipeline stages.
Now consider slide 11 of this presentation.
The second synchronization scope of Barrier1
includes logically later stages as per spec. So if Barrier1.dstMask
is DRAW_INDIRECT
, it DOES include COMPUTE_SHADER
. Similarly, the first synchronization scope of Barrier2
includes logically earlier stages as per spec. So if Barrier2.srcMask
is COMPUTE_SHADER
, it DOES include DRAW_INDIRECT
. Then why is it, according to this slide, that this configuration DOES NOT constitute a execution dependency chain between the two barriers?
Also, if I set Barrier1.dstMask
to BOTTOM_OF_PIPE
, how does it suddenly form a chain with anything other than BOTTOM_OF_PIPE
in the srcMask of the next barrier? The spec clearly states BOTTOM_OF_PIPE
is equivalent to ALL_COMMANDS_BIT
with access flags set to 0 when specified in the first scope, not the second??
Is the linked presentation wrong?
I was wondering how to synchronize separate render passes, or synchronize something from inside a render pass with something outside a render pass. After scouring the internet I found you can specify BOTTOM_OF_PIPE
in the second scope and chain onto it with either BOTTOM_OF_PIPE
or ALL_COMMANDS
in the first scope of a next dependency (as suggested here, and slide 12 in the presentation linked above).
Is this correct? I can find nothing in the spec to support this.
Execution dependency chaining applies universally to all the synchronization primitives. Of which VK_SUBPASS_EXTERNAL
Subpass Dependencies are nearly the same thing as regular Pipeline Barriers.
Execution dependency chains are formed whenever it is satisfiable to match both the scopes at the same time. That is, if you can hypothetically imagine some queue operation that could match both the second sync scope of first sync command, and the first sync scope of the second sync command, then execution dependency chain is formed. The imaginary operation need not be real, actually recorded, nor executed.
For example if I have two Pipeline Barriers and barr1.dstStageMask
is STAGE_COMPUTE
, and barr2.srcStageMask
is also STAGE_COMPUTE
, then it is an execution dependency chain. I could imagine there is a vkCmdDispatch
recorded between the two. That command would match both the scopes, therefore it is a chain.
If barr1.dstStageMask
is VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
and barr2.srcStageMask
is VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
, then there is no chain. Both the scopes are not satisfiable at the same time. It is impossible to imagine any operation that would match both of them, therefore no execution dependency chain is formed.
(It is explained above in terms of pipeline stages at the risk of losing completeness, but keep in mind the pipeline stage is only a one part of the description of the synchronization scope as it is specified per each synchronization primitive in the specification. For example, a Fence would chain with nearly everything despite not really being specified in terms of pipeline stages)
ad nr. 1, seems like a trivial error in the presentation where the two columns are swapped there:
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
is not the equivalent of VK_PIPELINE_STAGE_ALL_COMMANDS_BIT
in the context of dstStageMask
. It would be interpreted as VK_PIPELINE_STAGE_NONE
in that context. So that's a tell.