29.6. WSI Swapchain

A VkSwapchainKHR object (a.k.a. swapchain) provides the ability to present rendering results to a surface. A swapchain is an abstraction for an array of presentable images that are associated with a surface. The swapchain images are represented by VkImage objects created by the platform. One image (which can be an array image for multiview/stereoscopic-3D surfaces) is displayed at a time, but multiple images can be queued for presentation. An application renders to the image, and then queues the image for presentation to the surface. A native window cannot be associated with more than one swapchain at a time. Further, swapchains cannot be created for native windows that have a non-Vulkan graphics API surface associated with them.

The presentation engine is an abstraction for the platform’s compositor or hardware/software display engine.

[Note]Note

The presentation engine may be synchronous or asynchronous with respect to the application and/or logical device.

Some implementations may use the device’s graphics queue or dedicated presentation hardware to perform presentation.

The presentable images of a swapchain are owned by the presentation engine. An application can acquire use of a presentable image from the presentation engine. Use of a presentable image must occur only after the image is returned by vkAcquireNextImageKHR, and before it is presented by vkQueuePresentKHR. This includes transitioning the image layout and rendering commands.

An application can acquire use of a presentable image with vkAcquireNextImageKHR. After acquiring a presentable image and before modifying it, the application must use a synchronization primitive to ensure that the presentation engine has finished reading from the image. The application can then transition the image’s layout, queue rendering commands to it, etc. Finally, the application presents the image with vkQueuePresentKHR, which releases the acquisition of the image.

The presentation engine controls the order in which presentable images are acquired for use by the application.

[Note]Note

This allows the platform to handle situations which require out-of-order return of images after presentation. At the same time, it allows the application to generate command buffers referencing all of the images in the swapchain at initialization time, rather than in its main loop.

How this all works is described below.

To create a swapchain, call:

 

VkResult vkCreateSwapchainKHR(
    VkDevice                                    device,
    const VkSwapchainCreateInfoKHR*             pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkSwapchainKHR*                             pSwapchain);

The VkSwapchainCreateInfoKHR structure is defined as:

 

typedef struct VkSwapchainCreateInfoKHR {
    VkStructureType                  sType;
    const void*                      pNext;
    VkSwapchainCreateFlagsKHR        flags;
    VkSurfaceKHR                     surface;
    uint32_t                         minImageCount;
    VkFormat                         imageFormat;
    VkColorSpaceKHR                  imageColorSpace;
    VkExtent2D                       imageExtent;
    uint32_t                         imageArrayLayers;
    VkImageUsageFlags                imageUsage;
    VkSharingMode                    imageSharingMode;
    uint32_t                         queueFamilyIndexCount;
    const uint32_t*                  pQueueFamilyIndices;
    VkSurfaceTransformFlagBitsKHR    preTransform;
    VkCompositeAlphaFlagBitsKHR      compositeAlpha;
    VkPresentModeKHR                 presentMode;
    VkBool32                         clipped;
    VkSwapchainKHR                   oldSwapchain;
} VkSwapchainCreateInfoKHR;

[Note]Note

Applications should set this value to VK_TRUE if they do not expect to read back the content of presentable images before presenting them or after reacquiring them and if their pixel shaders do not have any side effects that require them to run for all pixels in the presentable image.

As mentioned above, if vkCreateSwapchainKHR succeeds, it will return a handle to a swapchain that contains an array of at least minImageCount presentable images.

The VkSurfaceKHR associated with a swapchain must not be destroyed until after the swapchain is destroyed.

Like core functions, several WSI fuctions, including vkCreateSwapchainKHR return VK_ERROR_DEVICE_LOST if the logical device was lost. See Lost Device. As with most core objects, VkSwapchainKHR is a child of the device and is affected by the lost state; it must be destroyed before destroying the VkDevice. However, VkSurfaceKHR is not a child of any VkDevice and is not otherwise affected by the lost device. After successfully recreating a VkDevice, the same VkSurfaceKHR can be used to create a new VkSwapchainKHR, provided the previous one was destroyed.

[Note]Note

As mentioned in Lost Device, after a lost device event, the VkPhysicalDevice may also be lost. If other VkPhysicalDevice are available, they can be used together with the same VkSurfaceKHR to create the new VkSwapchainKHR, however the application must query the surface capabilities again, because they may differ on a per-physical device basis.

To destroy a swapchain object call:

 

void vkDestroySwapchainKHR(
    VkDevice                                    device,
    VkSwapchainKHR                              swapchain,
    const VkAllocationCallbacks*                pAllocator);

swapchain and all associated VkImage handles are destroyed, and must not be acquired or used any more by the application. The memory of each VkImage will only be freed after that image is no longer used by the platform. For example, if one image of the swapchain is being displayed in a window, the memory for that image may not be freed until the window is destroyed, or another swapchain is created for the window. Destroying the swapchain does not invalidate the parent VkSurfaceKHR, and a new swapchain can be created with it.

When the VK_KHR_display_swapchain extension is enabled, multiple swapchains that share presentable images are created by calling:

 

VkResult vkCreateSharedSwapchainsKHR(
    VkDevice                                    device,
    uint32_t                                    swapchainCount,
    const VkSwapchainCreateInfoKHR*             pCreateInfos,
    const VkAllocationCallbacks*                pAllocator,
    VkSwapchainKHR*                             pSwapchains);

vkCreateSharedSwapchains is similar to vkCreateSwapchainKHR, except that it takes an array of VkSwapchainCreateInfoKHR structures, and returns an array of swapchain objects.

The swapchain creation parameters that affect the properties and number of presentable images must match between all the swapchains. If the displays used by any of the swapchains do not use the same presentable image layout or are incompatible in a way that prevents sharing images, swapchain creation will fail with the result code VK_ERROR_INCOMPATIBLE_DISPLAY_KHR. If any error occurs, no swapchains will be created. Images presented to multiple swapchains must be re-acquired from all of them before transitioning away from VK_IMAGE_LAYOUT_PRESENT_SRC_KHR. After destroying one or more of the swapchains, the remaining swapchains and the presentable images can continue to be used.

To obtain the array of presentable images associated with a swapchain, call:

 

VkResult vkGetSwapchainImagesKHR(
    VkDevice                                    device,
    VkSwapchainKHR                              swapchain,
    uint32_t*                                   pSwapchainImageCount,
    VkImage*                                    pSwapchainImages);

If pSwapchainImages is NULL, then the number of presentable images for swapchain is returned in pSwapchainImageCount. Otherwise, pSwapchainImageCount must point to a variable set by the user to the number of elements in the pSwapchainImages array, and on return the variable is overwritten with the number of structures actually written to pSwapchainImages. If the value of pSwapchainImageCount is less than the number of presentable images for swapchain, at most pSwapchainImageCount structures will be written. If pSwapchainImageCount is smaller than the number of presentable images for swapchain, VK_INCOMPLETE will be returned instead of VK_SUCCESS to indicate that not all the available values were returned.

[Note]Note

By knowing all presentable images used in the swapchain, the application can create command buffers that reference these images prior to entering its main rendering loop.

The implementation will have already allocated and bound the memory backing the VkImages returned by vkGetSwapchainImagesKHR. The memory for each image will not alias with the memory for other images or with any VkDeviceMemory object. As such, performing any operation affecting the binding of memory to a presentable image results in undefined behavior. All presentable images are initially in the VK_IMAGE_LAYOUT_UNDEFINED layout, thus before using presentable images, the application must transition them to a valid layout for the intended use.

Further, the lifetime of presentable images is controlled by the implementation so destroying a presentable image with vkDestroyImage results in undefined behavior. See vkDestroySwapchainKHR for further details on the lifetime of presentable images.

To acquire an available presentable image to use, and retrieve the index of that image, call:

 

VkResult vkAcquireNextImageKHR(
    VkDevice                                    device,
    VkSwapchainKHR                              swapchain,
    uint64_t                                    timeout,
    VkSemaphore                                 semaphore,
    VkFence                                     fence,
    uint32_t*                                   pImageIndex);

When successful, vkAcquireNextImageKHR acquires a presentable image that the application can use, and sets pImageIndex to the index of that image. The presentation engine may not have finished reading from the image at the time it is acquired, so the application must use semaphore and/or fence to ensure that the image layout and contents are not modified until the presentation engine reads have completed.

As mentioned above, the presentation engine controls the order in which presentable images are made available to the application. This allows the platform to handle special situations. The order in which images are acquired is implementation-dependent. Images may be acquired in a seemingly random order that is not a simple round-robin.

If a swapchain has enough presentable images, applications can acquire multiple images without an intervening vkQueuePresentKHR. Applications can present images in a different order than the order in which they were acquired.

If timeout is 0, vkAcquireNextImageKHR will not block, but will either succeed or return VK_NOT_READY. If timeout is UINT64_MAX, the function will not return until an image is acquired from the presentation engine. Other values for timeout will cause the function to return when an image becomes available, or when the specified number of nanoseconds have passed (in which case it will return VK_TIMEOUT). An error can also cause vkAcquireNextImageKHR to return early.

[Note]Note

As mentioned above, the presentation engine may be asynchronous with respect to the application and/or logical device. vkAcquireNextImageKHR may return as soon as it can identify which image will be acquired, and can guarantee that semaphore and fence will be signaled by the presentation engine; and may not successfully return sooner. The application uses timeout to specify how long vkAcquireNextImageKHR waits for an image to become acquired.

Applications cannot rely on vkAcquireNextImageKHR blocking in order to meter their rendering speed. Various factors can interrupt vkAcquireNextImageKHR from blocking.

[Note]Note

For example, if an error occurs, vkAcquireNextImageKHR may return even though no image is available. As another example, some presentation engines are able to enqueue an unbounded number of presentation and acquire next image operations such that vkAcquireNextImageKHR never needs to wait for completion of outstanding present operations before returning.

The availability of presentable images is influenced by factors such as the implementation of the presentation engine, the VkPresentModeKHR being used, the number of images in the swapchain, the number of images that the application has acquired at any given time, and the performance of the application. The value of VkSurfaceCapabilitiesKHR::minImageCount indicates how many images must be in the swapchain in order for vkAcquireNextImageKHR to acquire an image if the application currently has no acquired images.

Let n be the total number of images in the swapchain, m be the value of VkSurfaceCapabilitiesKHR::minImageCount, and a be the number of presentable images that the application has currently acquired (i.e. images acquired with vkAcquireNextImageKHR, but not yet presented with vkQueuePresentKHR). vkAcquireNextImageKHR can always succeed if $a \leq n - m$ at the time vkAcquireNextImageKHR is called. vkAcquireNextImageKHR should not be called if $a > n - m$ with a timeout of UINT64_MAX; in such a case, vkAcquireNextImageKHR may block indefinitely.

[Note]Note

For example, if the minImageCount member of VkSurfaceCapabilitiesKHR is 2, and the application creates a swapchain with 2 presentable images, the application can acquire one image, and must present it before trying to acquire another image.

If we modify this example so that the application wishes to acquire up to 3 presentable images simultaneously, it must request a minimum image count of 4 when creating the swapchain.

If semaphore is not VK_NULL_HANDLE, the semaphore must be unsignaled and not have any uncompleted signal or wait operations pending. It will become signaled when the application can use the image. Queue operations that access the image contents must wait until the semaphore signals; typically applications should include the semaphore in the pWaitSemaphores list for the queue submission that transitions the image away from the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout. Use of the semaphore allows rendering operations to be recorded and submitted before the presentation engine has completed its use of the image.

If fence is not equal to VK_NULL_HANDLE, the fence must be unsignaled and not have any uncompleted signal operations pending. It will become signaled when the application can use the image. Applications can use this to meter their frame generation work to match the presentation rate.

semaphore and fence must not both be equal to VK_NULL_HANDLE. An application must wait until either the semaphore or fence is signaled before using the presentable image.

semaphore and fence may already be signaled when vkAcquireNextImageKHR returns, if the image is being acquired for the first time, or if the presentable image is immediately ready for use.

A successful call to vkAcquireNextImageKHR counts as a signal operation on semaphore for the purposes of queue forward-progress requirements. The semaphore is guaranteed to signal, so a wait operation can be queued for the semaphore without risk of deadlock.

The vkCmdWaitEvents or vkCmdPipelineBarrier used to transition the image away from VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout must have dstStageMask and dstAccessMask parameters set based on the next use of the image. The srcAccessMask must include VK_ACCESS_MEMORY_READ_BIT to ensure that all prior reads by the presentation engine are complete before the image layout transition occurs. The application must use implicit ordering guarantees and execution dependencies to prevent the image transition from occurring before the semaphore passed to vkAcquireNextImageKHR has signaled.

[Note]Note

When the swapchain image will be written by some stage $S$ , the recommended idiom for ensuring the semaphore signals before the transition occurs is:

  • The batch that contains the transition includes the image-acquire semaphore in the list of semaphores to wait for, with a wait stage mask that includes $S$ .
  • The pipeline barrier that performs the transition includes $S$ in both the srcStageMask and dstStageMask.

This causes the pipeline barrer to wait at $S$ until the semaphore signals before performing the transition and memory barrier, while allowing earlier pipeline stages of subsequent commands to proceed.

After a successful return, the image indicated by pImageIndex will still be in the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout if it was previously presented, or in the VK_IMAGE_LAYOUT_UNDEFINED layout if this is the first time it has been acquired.

The possible return values for vkAcquireNextImageKHR() depend on the timeout provided:

[Note]Note

This may happen, for example, if the platform surface has been resized but the platform is able to scale the presented images to the new size to produce valid surface updates. It is up to applications to decide whether they prefer to continue using the current swapchain indefinitely or temporarily in this state, or to re-create the swapchain to better match the platform surface properties.

If the native surface and presented image sizes no longer match, presentation may not succeed. If presentation does succeed, parts of the native surface may be undefined, parts of the presented image may have been clipped before presentation, and/or the image may have been scaled (uniformly or not uniformly) before presentation. It is the application’s responsibility to detect surface size changes and react appropriately. If presentation does not succeed because of a mismatch in the surface and presented image sizes, a VK_ERROR_OUT_OF_DATE_KHR error will be returned.

Before an application can present an image, the image’s layout must be transitioned to the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout. The vkCmdWaitEvents or vkCmdPipelineBarrier that perform the transition must have srcStageMask and srcAccessMask parameters set based on the preceding use of the image. The dstAccessMask must include VK_ACCESS_MEMORY_READ_BIT indicating all prior accesses indicated in srcAccessMask from stages in srcStageMask are to be made available to reads by the presentation engine. Any value of dstStageMask is valid, but should be set to VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT to avoid delaying subsequent commands that don’t access the image.

After queueing all rendering commands and transitioning the image to the correct layout, to queue an image for presentation, call:

 

VkResult vkQueuePresentKHR(
    VkQueue                                     queue,
    const VkPresentInfoKHR*                     pPresentInfo);

The VkPresentInfoKHR structure is defined as:

 

typedef struct VkPresentInfoKHR {
    VkStructureType          sType;
    const void*              pNext;
    uint32_t                 waitSemaphoreCount;
    const VkSemaphore*       pWaitSemaphores;
    uint32_t                 swapchainCount;
    const VkSwapchainKHR*    pSwapchains;
    const uint32_t*          pImageIndices;
    VkResult*                pResults;
} VkPresentInfoKHR;

When the VK_KHR_display_swapchain extension is enabled additional fields can be specified when presenting an image to a swapchain by setting VkPresentInfoKHR::pNext to point to an instance of the VkDisplayPresentInfoKHR structure.

The VkDisplayPresentInfoKHR structure is defined as:

 

typedef struct VkDisplayPresentInfoKHR {
    VkStructureType    sType;
    const void*        pNext;
    VkRect2D           srcRect;
    VkRect2D           dstRect;
    VkBool32           persistent;
} VkDisplayPresentInfoKHR;

If the extent of the srcRect and dstRect are not equal, the presented pixels will be scaled accordingly.

vkQueuePresentKHR, releases the acquisition of the images referenced by imageIndices. A presented images must not be used again before it has been reacquired using vkAcquireNextImageKHR.

The processing of the presentation happens in issue order with other queue operations, but semaphores have to be used to ensure that prior rendering and other commands in the specified queue complete before the presentation begins. The presentation command itself does not delay processing of subsequent commands on the queue, however, presentation requests sent to a particular queue are always performed in order. Exact presentation timing is controled by the semantics of the presentation engine and native platform in use.

The result codes VK_ERROR_OUT_OF_DATE_KHR and VK_SUBOPTIMAL_KHR have the same meaning when returned by vkQueuePresentKHR as they do when returned by vkAcquireNextImageKHR(). If multiple swapchains are presented, the result code is determined applying the following rules in order:

Presentation is a read-only operation that will not affect the content of the presentable images. Upon reacquiring the image and transitioning it away from the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout, the contents will be the same as they were prior to transitioning the image to the present source layout and presenting it. However, if a mechanism other than Vulkan is used to modify the platform window associated with the swapchain, the content of all presentable images in the swapchain becomes undefined.