6.2. Semaphores

Semaphores are used to coordinate queue operations both within a queue and between different queues. A semaphore’s status is always either signaled or unsignaled.

To create a new semaphore object, use the command

 

VkResult vkCreateSemaphore(
    VkDevice                                    device,
    const VkSemaphoreCreateInfo*                pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkSemaphore*                                pSemaphore);

The VkSemaphoreCreateInfo structure is defined as:

 

typedef struct VkSemaphoreCreateInfo {
    VkStructureType           sType;
    const void*               pNext;
    VkSemaphoreCreateFlags    flags;
} VkSemaphoreCreateInfo;

To destroy a semaphore, call:

 

void vkDestroySemaphore(
    VkDevice                                    device,
    VkSemaphore                                 semaphore,
    const VkAllocationCallbacks*                pAllocator);

Semaphores can be signaled by including them in a batch as part of a queue submission command, defining a queue operation to signal that semaphore. This semaphore signal operation defines the first half of a memory dependency, guaranteeing that all memory accesses defined by the submitted queue operations in the batch are made available, and that those queue operations have completed execution.

Semaphore signal operations for vkQueueSubmit additionally include all queue operations previously submitted via vkQueueSubmit in their half of a memory dependency, and all batches that are stored at a lower index in the same pSubmits array.

Signaling of semaphores can be waited on by similarly including them in a batch, defining a queue operation to wait for a signal. A semaphore wait operation defines the second half of a memory dependency for the semaphores being waited on. This half of the memory dependency guarantees that the first half has completed execution, and also guarantees that all available memory accesses are made visible to the queue operations in the batch.

Semaphore wait operations for vkQueueSubmit additionally include all queue operations subsequently submitted via vkQueueSubmit in their half of a memory dependency, and all batches that are stored at a higher index in the same pSubmits array.

When queue execution reaches a semaphore wait operation, the queue will stall execution of queue operations in the batch until each semaphore becomes signaled. Once all semaphores are signaled, the semaphores will be reset to the unsignaled state, and subsequent queue operations will be permitted to execute.

Semaphore wait operations defined by vkQueueSubmit only wait at specific pipeline stages, rather than delaying all of each command buffer’s execution, with the pipeline stages determined by the corresponding element of the pWaitDstStageMask member of VkSubmitInfo. Execution of work by those stages in subsequent commands is stalled until the corresponding semaphore reaches the signaled state.

[Note]Note

A common scenario for using pWaitDstStageMask with values other than VK_PIPELINE_STAGE_ALL_COMMANDS_BIT is when synchronizing a window system presentation operation against subsequent command buffers which render the next frame. In this case, a presentation image must not be overwritten until the presentation operation completes, but other pipeline stages can execute without waiting. A mask of VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT prevents subsequent color attachment writes from executing until the semaphore signals. Some implementations may be able to execute transfer operations and/or vertex processing work before the semaphore is signaled.

If an image layout transition needs to be performed on a swapchain image before it is used in a framebuffer, that can be performed as the first operation submitted to the queue after acquiring the image, and should not prevent other work from overlapping with the presentation operation. For example, a VkImageMemoryBarrier could use:

  • srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
  • srcAccessMask = VK_ACCESS_MEMORY_READ_BIT
  • dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
  • dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT.
  • oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
  • newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL

Alternativeely, oldLayout can be VK_IMAGE_LAYOUT_UNDEFINED, if the image’s contents need not be preserved.

This barrier accomplishes a dependency chain between previous presentation operations and subsequent color attachment output operations, with the layout transition performed in between, and does not introduce a dependency between previous work and any vertex processing stages. More precisely, the semaphore signals after the presentation operation completes, then the semaphore wait stalls the VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT stage, then there is a dependency from that same stage to itself with the layout transition performed in between.

(The primary use case for this example is with the presentation extensions, thus the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR token is used even though it is not defined in the core Vulkan specification.)