2
votes

Looking at the vulkan spec after being confused by previous revisions, I saw the following updated clarification of vkCmdPipelineBarrier:

If vkCmdPipelineBarrier was recorded outside a render pass instance, the first synchronization scope includes all commands that occur earlier in submission order. If vkCmdPipelineBarrier was recorded inside a render pass instance, the first synchronization scope includes only commands that occur earlier in submission order within the same subpass. In either case, the first synchronization scope is limited to operations on the pipeline stages determined by the source stage mask specified by srcStageMask.

If vkCmdPipelineBarrier was recorded outside a render pass instance, the second synchronization scope includes all commands that occur later in submission order. If vkCmdPipelineBarrier was recorded inside a render pass instance, the second synchronization scope includes only commands that occur later in submission order within the same subpass. In either case, the second synchronization scope is limited to operations on the pipeline stages determined by the destination stage mask specified by dstStageMask.

If I understand correctly, are these statements saying that:

  • first synchronization scope is the possible commands used for what can be considered for synchronization before (source)

  • second synchronization scope is the possible commands used for what can be considered for synchronization after (destination)

and that the only thing that changes is that commands in other subpasses are not considered for either synchronization scope when a pipeline barrier is used within a renderpass.

What I'm confused about is the way it is worded made me think that maybe even previous commands, before the render pass were not considered for the first synchronization scope. (same with after for the second)

Are these examples of synchronization correct?

if outside a render pass I do something like:

1. transfer;
2. computeDispatch;
3. beginRenderPass;
...
endRenderPass;

pipelineBarrier(...);

4. computeDispatch;
5. beginRenderPass;
...
endRenderPass;

commands 1, 2, and 3 will be considered in the pipelineBarrier for synchronization before, ie, be in the first synchronization scope, and commands 4, and 5 will be considered afterwards, ie the second synchronization scope.

if I have the following list of commands:

1. transfer;
2. computeDispatch;
3. beginRenderPass;
   3.1 next subpass;
       3.1.1 bindPipeline;
       3.1.2 bindDescriptor;
       3.1.3 bindVertexBuffer;

       pipelineBarrier(...);

       3.1.4 bindIndexBuffer;
       3.1.5 drawIndexed;
endRenderPass;

4. computeDispatch;
5. beginRenderPass;
...
endRenderPass;

the commands 1, 2, 3.1.1, 3.1.2 and 3.1.3 will be in the first synchronization scope, and 3.1.4 and 3.1.5, 4, 5 will be in the second.

and finally the bolded section of text is saying that if I do the following:

1. transfer;
2. computeDispatch;
3. beginRenderPass;
   3.1 first subpass;
       3.1.1 bindPipeline;
       3.1.2 bindDescriptors;
       3.1.3 bindVertexBuffer;
       3.1.4 draw;
   3.2 next subpass;
       3.2.1 bindPipeline;
       3.2.2 bindDescriptor;
       3.2.3 bindVertexBuffer;

       pipelineBarrier(...);

       3.2.4 bindIndexBuffer;
       3.2.5 drawIndexed;
   3.3 next subpass;
       3.3.1 bindPipeline;
       3.3.2 bindDescriptors;
       3.3.4 draw;
endRenderPass;

4. computeDispatch;
5. beginRenderPass;
...
endRenderPass;

commands 1,2, 3.2.1, 3.2.2, and 3.2.3 will be in the first synchronization scope, and commands 3.2.4, 3.2.5, 4, and 5 will be in the second synchronization scope correct? In other words, other subpasses are not considered for synchronization scopes for pipeline barriers used within a render pass? only the current subpass?

1

1 Answers

5
votes

What I'm confused about is the way it is worded made me think that maybe even previous commands, before the render pass were not considered for the first synchronization scope.

They aren't considered. Directly.

A pipeline barrier in a subpass will create a dependency between commands within a subpass. Subpass dependencies create dependencies between subpasses. External subpass dependencies create dependencies between a subpass and the commands before/after the render pass.

If subpass 1 cannot start its execution until subpass 0 has finished (and therefore, there is a dependency between them), then any commands in subpass 1 can assume that subpass 0 is done. This includes barriers. So this makes dependencies transitive; the stuff after the barrier in subpass 1 can assume that subpass 0 is finished because everything in subpass 1 can make that assumption. Similarly, commands in a subpass (like barriers) will depend on any external dependencies which the subpass directly or indirectly depends on.

Now, because dependencies are be based on particular stages, transitivity only applies when the chain of dependencies include stages that actually depend on each other.