Contents

Synchronizing passes with consumer barriers

Block GPU stages in a pass, and all subsequent passes, from running until stages from earlier passes finish.

Overview

Consumer queue barriers are coarse synchronization primitives that resolve access conflicts between commands in different passes that you submit to the same command queue, including the passes from other command buffers you submit to the same queue. Consumer barriers are convenient for synchronizing passes that load from common resources that multiple, earlier passes modify in the same queue.

When your app encodes commands that access a resource from different passes — or different stages within a single pass — it creates an access conflict when at least one command modifies that resource. This conflict happens because the GPU can run multiple commands at the same time, including those from:

  • Multiple passes

  • Different stages of a pass, such as the blit and dispatch stages of a compute pass

  • Multiple instances of a stage, such as two or more dispatch commands within a compute pass

For more information about resource access conflicts and GPU stages, see Resource synchronization and MTLStages, respectively.

Start by identifying which memory operations from previous passes in the same queue introduce a conflict and resolve it with a consumer queue barrier in the consumer pass.

Identify access conflicts on the same queue

The following code example encodes three compute passes. The first pass runs a single copy command:

The second pass runs a copy command and a dispatch command:

The third pass runs a single dispatch command:

The example has at least one access conflict because passes 1 and 2 both access a common resource, bufferB:

  • The copy command from the first pass stores to bufferB.

  • The dispatch command from the second pass loads from bufferB.

[Image]

Without synchronization, the GPU can run all three passes and their stages in parallel, which can yield inconsistent results in resources with access conflicts.

[Image]

Resolve access conflicts with a consumer barrier

Resolve access conflicts between passes from the same command queue with a consumer barrier by calling the encoder’s barrier(afterQueueStages:beforeStages:visibilityOptions:) method.

Each consumer queue barrier temporarily blocks the GPU from running the specific stage types you pass to the beforeStages parameter in the current pass, and all subsequent passes in the same queue. The barrier unblocks those stages when all the stage types you pass to the afterQueueStages parameter finish running in all previous passes.

The following example modifies the code that encodes the second pass by adding a consumer queue barrier just before the dispatch command stage in the second pass:

In this example, the barrier prevents the GPU from running the dispatch stage in both the second and third passes until the blit stage in the first pass finishes storing its modifications.

[Image]

The barrier unblocks both dispatch stages when the blit stage from the first pass finishes running because it’s the only pass that applies to the afterQueueStages parameter.

For more information about other synchronization mechanisms, see these articles in the series:

See Also

Synchronizing with barriers and fences