Skip to content

Synchronization Examples

TobiasHector edited this page Jul 27, 2017 · 53 revisions

Synchronization in Vulkan can be confusing. It takes a lot of time to understand, and even then it's easy to trip up on small details. Most common use of Vulkan synchronization can be boiled down to a handful of use cases though, and this page lists a number of examples. May expand this in future to include other questions about synchronization.

Note that examples are usually expressed as a pipeline barrier, but events or subpass dependencies can be used similarly.

Compute to Compute Dependencies

First dispatch writes to a storage buffer, second dispatch reads from that storage buffer.
vkCmdDispatch(...);

VkMemoryBarrier memoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT };

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstStageMask
    1,                                    // memoryBarrierCount
    &memoryBarrier,                       // pMemoryBarriers
    ...);
    
vkCmdDispatch(...);
First dispatch reads from a storage buffer, second dispatch writes to that storage buffer.

WAR hazards don't need a memory barrier between them - execution barriers are sufficient.

vkCmdDispatch(...);

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstStageMask
    ...);
    
vkCmdDispatch(...);
First dispatch writes to a storage image, second dispatch reads from that storage image.
vkCmdDispatch(...);

// Storage image to storage image dependencies are always in GENERAL layout; no need for a layout transition
VkMemoryBarrier memoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstStageMask
    1,                                    // memoryBarrierCount
    &memoryBarrier,                       // pMemoryBarriers
    ...);
    
vkCmdDispatch(...);
Three dispatches. First dispatch writes to a storage buffer, second dispatch writes to non-overlapping region of same storage buffer, third dispatch reads both regions.
vkCmdDispatch(...);
vkCmdDispatch(...);

VkMemoryBarrier memoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT };

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstStageMask
    1,                                    // memoryBarrierCount
    &memoryBarrier,                       // pMemoryBarriers
    ...);
    
vkCmdDispatch(...);
Three dispatches. First dispatch writes to one storage buffer, second dispatch writes to a different storage buffer, third dispatch reads both.

Identical to previous example - global memory barrier covers all resources. Generally considered more efficient to do a global memory barrier than per-resource barriers, per-resource barriers should usually be used for queue ownership transfers and image layout transitions - otherwise use global barriers.

vkCmdDispatch(...);
vkCmdDispatch(...);

VkMemoryBarrier memoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT };

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstStageMask
    1,                                    // memoryBarrierCount
    &memoryBarrier,                       // pMemoryBarriers
    ...);
    
vkCmdDispatch(...);

Compute to Graphics Dependencies

Note that interactions with graphics should ideally be performed by using subpass dependencies (external or otherwise) rather than pipeline barriers, but most of the following examples are still described as pipeline barriers for brevity.

Dispatch writes into a storage buffer. Draw consumes that buffer as an index buffer.
vkCmdDispatch(...);

VkMemoryBarrier memoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_INDEX_READ_BIT };

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,   // dstStageMask
    1,                                    // memoryBarrierCount
    &memoryBarrier,                       // pMemoryBarriers
    ...);

... // Render pass setup etc.

vkCmdDraw(...);
Dispatch writes into a storage buffer. Draw consumes that buffer as an index buffer. A further compute shader reads from the buffer as a uniform buffer.
vkCmdDispatch(...);

// Batch barriers where possible if it doesn't change how synchronization takes place
VkMemoryBarrier memoryBarrier1 = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstStageMask
    1,                                    // memoryBarrierCount
    &memoryBarrier1,                      // pMemoryBarriers
    ...);

... // Render pass setup etc.

vkCmdDraw(...);

... // Render pass teardown etc.

vkCmdDispatch(...);
Dispatch writes into a storage buffer. Draw consumes that buffer as a draw indirect buffer.
vkCmdDispatch(...);

VkMemoryBarrier memoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT };

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // srcStageMask
    VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT,  // dstStageMask
    1,                                    // memoryBarrierCount
    &memoryBarrier,                       // pMemoryBarriers
    ...);

... // Render pass setup etc.

vkCmdDraw(...);
Dispatch writes into a storage image. Draw samples that image in a fragment shader.
vkCmdDispatch(...);

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
  .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
  .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL
  /* .image and .subresourceRange should identify image subresource accessed */};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,  // srcStageMask
    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask
    ...
    1,                                     // imageMemoryBarrierCount
    &imageMemoryBarrier,                   // pImageMemoryBarriers
    ...);
    

... // Render pass setup etc.

vkCmdDraw(...);
Dispatch writes into a storage texel buffer. Draw consumes that buffer as a draw indirect buffer, and then again as a uniform buffer in the fragment shader.
vkCmdDispatch(...);

VkMemoryBarrier memoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,    // srcStageMask
    VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | 
      VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask
    1,                                       // memoryBarrierCount
    &memoryBarrier,                          // pMemoryBarriers
    ...);

... // Render pass setup etc.

vkCmdDraw(...);

Graphics to Compute Dependencies

Draw writes to a color attachment. Dispatch samples from that image.

Note that color attachment write is NOT in the fragment shader, it has its own dedicated pipeline stage!

vkCmdDraw(...);

... // Render pass teardown etc.

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
  .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
  .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL
  /* .image and .subresourceRange should identify image subresource accessed */};
  
vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,          // dstStageMask
    ...
    1,                                             // imageMemoryBarrierCount
    &imageMemoryBarrier,                           // pImageMemoryBarriers
    ...);

vkCmdDispatch(...);
Draw writes to a depth attachment. Dispatch samples from that image.

Note that color attachment write is NOT in the fragment shader, it has its own dedicated pipeline stage!

vkCmdDraw(...);

... // Render pass teardown etc.

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
  .oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
  .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL
  /* .image and .subresourceRange should identify image subresource accessed */};
  
vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
    VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,   // srcStageMask
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,        // dstStageMask
    ...
    1,                                           // imageMemoryBarrierCount
    &imageMemoryBarrier,                         // pImageMemoryBarriers
    ...);

vkCmdDispatch(...);

Graphics to Graphics Dependencies

Many graphics to graphics dependencies can be expressed as a subpass dependency within a render pass, which is usually more efficient than a pipeline barrier or event. Where this is possible in the below, the example is expressed in terms of a subpass dependency.

First draw writes to a depth attachment. Second draw reads from is as an input attachment in the fragment shader.
// Set this to the index in VkRenderPassCreateInfo::pAttachments where the depth image is described.
uint32_t depthAttachmentIndex = ...;

VkSubpassDescription subpasses[2];

VkAttachmentReference depthAttachment = {
    .attachment = depthAttachmentIndex,
    .layout     = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};

// Subpass containing first draw
subpasses[0] = {
    ...
    .pDepthStencilAttachment = &depthAttachment,
    ...};
    
VkAttachmentReference depthAsInputAttachment = {
    .attachment = depthAttachmentIndex,
    .layout     = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL};

// Subpass containing second draw
subpasses[1] = {
    ...
    .inputAttachmentCount = 1,
    .pInputAttachments = &depthAsInputAttachment,
    ...};
    
VkSubpassDependency dependency = {
    .srcSubpass = 0,
    .dstSubpass = 1,
    .srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
                    VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
    .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
    .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
    .dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
    .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT};

VkRenderPassCreateInfo renderPassCreateInfo = {
    ...
    .subpassCount = 2,
    .pSubpasses = subpasses,
    .dependencyCount = 1,
    .pDependencies = &dependency};

vkCreateRenderPass(...);

...
First draw writes to a depth attachment. Second draw samples from that depth image in the fragment shader (e.g. shadow map rendering).
vkCmdDraw(...);

... // First render pass teardown etc.

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
  .oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
  .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL
  /* .image and .subresourceRange should identify image subresource accessed */};
  
vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
    VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,   // srcStageMask
    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,       // dstStageMask
    ...
    1,                                           // imageMemoryBarrierCount
    &imageMemoryBarrier,                         // pImageMemoryBarriers
    ...);

... // Second render pass setup etc.

vkCmdDraw(...);
First draw writes to a color attachment. Second draw reads from it as an input attachment in the fragment shader.
// Set this to the index in VkRenderPassCreateInfo::pAttachments where the color image is described.
uint32_t colorAttachmentIndex = ...;

VkSubpassDescription subpasses[2];

VkAttachmentReference colorAttachment = {
    .attachment = colorAttachmentIndex,
    .layout     = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

// Subpass containing first draw
subpasses[0] = {
    ...
    .colorAttachmentCount = 1,
    .pColorAttachments = &colorAttachment,
    ...};
    
VkAttachmentReference colorAsInputAttachment = {
    .attachment = colorAttachmentIndex,
    .layout     = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL};

// Subpass containing second draw
subpasses[1] = {
    ...
    .inputAttachmentCount = 1,
    .pInputAttachments = &colorAsInputAttachment,
    ...};
    
VkSubpassDependency dependency = {
    .srcSubpass = 0,
    .dstSubpass = 1,
    .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
    .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
    .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    .dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
    .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT};

VkRenderPassCreateInfo renderPassCreateInfo = {
    ...
    .subpassCount = 2,
    .pSubpasses = subpasses,
    .dependencyCount = 1,
    .pDependencies = &dependency};

vkCreateRenderPass(...);

...
First draw writes to a color attachment. Second draw samples from that color image in the vertex shader.
vkCmdDraw(...);

... // First render pass teardown etc.

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
  .oldLayout = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
  .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL
  /* .image and .subresourceRange should identify image subresource accessed */};
  
vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
    VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,         // dstStageMask
    ...
    1,                                             // imageMemoryBarrierCount
    &imageMemoryBarrier,                           // pImageMemoryBarriers
    ...);

... // Second render pass setup etc.

vkCmdDraw(...);
First draw samples a texture in the fragment shader. Second draw writes to that texture as a color attachment.

This is a WAR hazard, which you would usually only need an execution dependency for. In this case you still need a memory barrier to do a layout transition, but you don't need an access types in the src access mask. The layout transition itself is considered a write operation, so you do need the destination access mask to be correct - or there would be a WAW hazard between the layout transition and the color attachment write.

vkCmdDraw(...);

... // First render pass teardown etc.

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = 0,
  .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
  .oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_OPTIMAL,
  .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
  /* .image and .subresourceRange should identify image subresource accessed */};
  
vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,         // srcStageMask
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // dstStageMask
    ...
    1,                                             // imageMemoryBarrierCount
    &imageMemoryBarrier,                           // pImageMemoryBarriers
    ...);

... // Second render pass setup etc.

vkCmdDraw(...);

Interactions with semaphores

If you have a dependency where the two commands being synchronized have a semaphore signal/wait between them, the additional synchronization done by pipeline barriers/events/subpass dependencies can be reduced or removed. Only parameters affected by the presence of the semaphore dependency are listed

Any dependency where only buffers are affected, or images where the layout doesn't change
// Nothing to see here - semaphore alone is sufficient.
// No additional synchronization required - remove those barriers.
Dependency between images where a layout transition is required, expressed before the semaphore signal
vkCmdDispatch(...);

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .dstAccessMask = 0};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
    ...);

... // Semaphore signal/wait happens here

vkCmdDispatch(...);
Dependency between images where a layout transition is required, expressed after the semaphore signal

This example assumes that whatever stages are in dstStageMask are included in the VkSubmitInfo::pWaitDstStageMask defined for the relevant semaphore wait operation - otherwise this modification does not apply.

vkCmdDispatch(...);

... // Semaphore signal/wait happens here

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = 0};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
    ...);


vkCmdDispatch(...);

Swapchain Image Acquire and Present

Combined Graphics/Present Queue
VkAttachmentReference attachmentReference = {
    .attachment = 0,
    .layout     = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

// Subpass containing first draw
VkSubpassDescription subpass = {
    ...
    .colorAttachmentCount = 1,
    .pColorAttachments = &attachmentReference,
    ...};

/* Only need a dependency coming in to ensure that the first
   layout transition happens at the right time.
   Second external dependency is implied by having a different
   finalLayout and subpass layout. */
VkSubpassDependency dependency = {
    .srcSubpass = VK_SUBPASS_EXTERNAL,
    .dstSubpass = 0,
    .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
    .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
    .srcAccessMask = 0,
    .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    .dependencyFlags = 0};

VkAttachmentDescription attachmentDescription = {
    ...
    .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
    .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
    ...
    .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
    .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

VkRenderPassCreateInfo renderPassCreateInfo = {
    ...
    .attachmentCount = 1,
    .pAttachments    = &attachmentDescription,
    .subpassCount    = 1,
    .pSubpasses      = &subpass,
    .dependencyCount = 1,
    .pDependencies   = &dependency};

vkCreateRenderPass(...);

...

vkAcquireNextImageKHR(
    ...
    acquireSemaphore,   //semaphore
    ...
    &imageIndex);       //image index

VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;

VkSubmitInfo submitInfo = {
    ...
    .waitSemaphoreCount = 1,
    .pWaitSemaphores = &acquireSemaphore,
    .pWaitDstStageMask = &waitDstStageMask,
    ...
    .signalSemaphoreCount = 1,
    .pSignalSemaphores = &graphicsSemaphore};

vkQueueSubmit(..., &submitInfo, ...);

VkPresentInfoKHR presentInfo = {
    .waitSemaphoreCount = 1,
    .pWaitSemaphores = &graphicsSemaphore,
    ...};

vkQueuePresentKHR(..., &presentInfo);
Multiple Queues

If the present queue is a different queue to the queue where rendering is done, a queue ownership transfer must additionally be performed between the two queues at both acquire and present time, which requires additional synchronization.

Render pass setup:

VkAttachmentReference attachmentReference = {
    .attachment = 0,
    .layout     = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

// Subpass containing first draw
VkSubpassDescription subpass = {
    ...
    .colorAttachmentCount = 1,
    .pColorAttachments = &attachmentReference,
    ...};

VkAttachmentDescription attachmentDescription = {
    ...
    .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
    .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
    ...
    .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

/*  Due to these necessary extra synchronization points, it makes more sense
    to omit the sub pass external dependencies (which can't express a queue
    transfer), and batch the relevant operations with the new pipeline
    barriers we're introducing. */

VkRenderPassCreateInfo renderPassCreateInfo = {
    ...
    .attachmentCount = 1,
    .pAttachments    = &attachmentDescription,
    .subpassCount    = 1,
    .pSubpasses      = &subpass,
    .dependencyCount = 0,
    .pDependencies   = NULL};

vkCreateRenderPass(...);

Post-Acquire commands - presentation queue

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = 0,
  .dstAccessMask = 0,
  .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
  .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
  .srcQueueFamilyIndex = presentQueueFamilyIndex,   // index of the present queue family
  .dstQueueFamilyIndex = graphicsQueueFamilyIndex,  // index of the graphics queue family
  /* .image and .subresourceRange should identify image subresource accessed */};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,    // srcStageMask
    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
    ...
    1,                                    // imageMemoryBarrierCount
    &imageMemoryBarrier,                  // pImageMemoryBarriers
    ...);

Rendering command buffer - graphics queue

VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = 0,
  .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
  .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
  .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
  .srcQueueFamilyIndex = presentQueueFamilyIndex,   // index of the present queue family
  .dstQueueFamilyIndex = graphicsQueueFamilyIndex,  // index of the graphics queue family
  /* .image and .subresourceRange should identify image subresource accessed */};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // dstStageMask
    ...
    1,                                             // imageMemoryBarrierCount
    &imageMemoryBarrier,                           // pImageMemoryBarriers
    ...);


... // Render pass submission.


VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
  .dstAccessMask = 0,
  .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
  .newLayout = VK_IMAGE_LAYOUT_GENERAL,
  .srcQueueFamilyIndex = graphicsQueueFamilyIndex, // index of the graphics queue family
  .dstQueueFamilyIndex = presentQueueFamilyIndex,  // index of the present queue family
  /* .image and .subresourceRange should identify image subresource accessed */};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,          // dstStageMask
    ...
    1,                                             // imageMemoryBarrierCount
    &imageMemoryBarrier,                           // pImageMemoryBarriers
    ...);

Pre-present commands - presentation queue

// After submitting the render pass...
VkImageMemoryBarrier imageMemoryBarrier = {
  ...
  .srcAccessMask = 0,
  .dstAccessMask = 0,
  .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
  .newLayout = VK_IMAGE_LAYOUT_GENERAL,
  .srcQueueFamilyIndex = graphicsQueueFamilyIndex, // index of the graphics queue family
  .dstQueueFamilyIndex = presentQueueFamilyIndex,  // index of the present queue family
  /* .image and .subresourceRange should identify image subresource accessed */};

vkCmdPipelineBarrier(
    ...
    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,    // srcStageMask
    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
    ...
    1,                                    // imageMemoryBarrierCount
    &imageMemoryBarrier,                  // pImageMemoryBarriers
    ...);

Queue submission:

vkAcquireNextImageKHR(
    ...
    acquireSemaphore,   //semaphore
    ...
    &imageIndex);       //image index

VkPipelineStageFlags waitDstStageMask1 = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
VkSubmitInfo submitInfo1 = {
    ...
    .waitSemaphoreCount = 1,
    .pWaitSemaphores = &acquireSemaphore,
    .pWaitDstStageMask = &waitDstStageMask1,
    .commandBufferCount = 1,
    .pCommandBuffers = &postAcquireCommandBuffer,
    .signalSemaphoreCount = 1,
    .pSignalSemaphores = &ownershipAcquireSemaphore};

vkQueueSubmit(presentQueue, &submitInfo1, ...);

VkPipelineStageFlags waitDstStageMask2 = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submitInfo2 = {
    ...
    .waitSemaphoreCount = 1,
    .pWaitSemaphores = &ownershipAcquireSemaphore,
    .pWaitDstStageMask = &waitDstStageMask2,
    .commandBufferCount = 1,
    .pCommandBuffers = &renderingCommandBuffer,
    .signalSemaphoreCount = 1,
    .pSignalSemaphores = &graphicsSemaphore};

vkQueueSubmit(renderQueue, &submitInfo2, ...);

VkPipelineStageFlags waitDstStageMask3 = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
VkSubmitInfo submitInfo3 = {
    ...
    .waitSemaphoreCount = 1,
    .pWaitSemaphores = &graphicsSemaphore,
    .pWaitDstStageMask = &waitDstStageMask3,
    .commandBufferCount = 1,
    .pCommandBuffers = &prePresentCommandBuffer,
    .signalSemaphoreCount = 1,
    .pSignalSemaphores = &ownershipPresentSemaphore};

vkQueueSubmit(presentQueue, &submitInfo3, ...);

VkPresentInfoKHR presentInfo = {
    .waitSemaphoreCount = 1,
    .pWaitSemaphores = &ownershipPresentSemaphore,
    ...};

vkQueuePresentKHR(..., &presentInfo);

TODO

  • Transfer dependencies
  • More external subpass examples?
  • Event example?
  • External dependencies
  • Safely aliasing resources
Clone this wiki locally