blob: aeeb7c19c55e9a0f05b74b8a60c3f96c4a492709 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_VK_SWAPCHAIN_H_
#define SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_VK_SWAPCHAIN_H_
//
//
//
#include <stdbool.h>
#include <stdint.h>
#include <vulkan/vulkan_core.h>
//
//
//
#ifdef __cplusplus
extern "C" {
#endif
//
//
//
// Swapchain-related data. Usage is the following:
//
// 1) Create a VkSurfaceKHR then call vk_swapchain_create(...) with appropriate
// configuration arguments passed as a vk_swapchain_config_t pointer.
//
// 2) Retrieve swapchain surface information with one of the individual getter
// functions (e.g. vk_swapchain_get_image_count(), vk_swapchain_get_image_view(), etc).
//
// 3) To render to a swapchain, the hard way, do the following:
//
// - Call vk_swapchain_acquire_next_image() to acquire the index
// of the new swapchain image.
//
// - Perform one or more queue submits to render something to
// the image (using vk_swapchain_get_image() and
// vk_swapchain_get_image_view() to retrieve handles to the
// coresponding VkImage and VkImageView, respectively).
//
// NOTE: The first submit *must* wait on
// vk_swapchain_get_image_acquired_semaphore(), because the
// image might not be ready for access yet after
// vk_swapchain_acquire_next_image() returns.
//
// NOTE2: The last submit *must* signal
// vk_swapchain_get_image_rendered_semaphore(), because it
// is waited on to present the image.
//
// - Call vk_swapchain_present_image() to send the content of
// the current swapchain image for presentation. Note that this
// will always wait on vk_swapchain_get_image_rendered_semaphore()
//
// Usage example:
//
// // Acquire next swapchain image
// uint32_t image_index;
// if (!vk_swapchain_acquire_next_image(swapchain, &image_index)) {
// // exit rendering loop.
// }
//
// // Begin one or more command buffers, fill them with commands.
//
// // Submit the command buffer(s), waiting and signalling the
// // right semaphores.
//
// VkSemaphore waitSemaphore =
// vk_swapchain_get_image_acquired_semaphore(swapchain);
//
// VkSemaphore signalSemaphore =
// vk_swapchain_get_image_rendered_semaphore(swapchain);
//
// const VkSubmitInfo submitInfo = {
// .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
// ...
// .waitSemaphoreCount = 1,
// .pWaitSemaphores = &waitSemaphore,
// .pWaitDstStageMask = ...,
// ...
// .signalSemaphoreCount = 1,
// .pSignalSemaphores = &signalSemaphore,
// };
// vkQueueSubmit(queue, 1, &submitInfo, ...);
//
// // Send rendered image to presentation.
// vk_swapchain_present_image(swapchain);
//
//
// 4) Convenience functions are provided in "vk_swapchain_queue.h" to make
// this easier for simple applications. See documentation comments there.
//
// Also see the vk_triangle_test and vk_transfer_test examples for examples.
//
typedef struct vk_swapchain vk_swapchain_t;
// Swapchain staging means providing an intermediate target image to the client
// instead of the real swapchain image, and ensuring that its content is
// blitted or copied to the swapchain when vk_swapchain_present_image() is
// called.
//
// This is useful in the following cases:
//
// - The client wants a pixel format that is not supported by the
// swapchain implementation. E.g. the clients wants R8G8B8A but the
// swapchain only supports B8G8R8A8.
//
// - The client wants an image usage that is not supported by the swapchain
// implementation. E.g. Intel GPUs do not support the
// VK_IMAGE_USAGE_STORAGE_BIT usage flag, which means shaders cannot write
// directly to swapchain images.
//
// Staging requires a full blit/transfer per swapchain present event, and
// thus can be expensive, but it is a useful fallback when no other solution
// works.
//
// Valid values are:
//
// NONE: Never enable swapchain staging (the default). Asking for an unsupported
// (pixel_format, image_usage) combination will simply fail.
//
// IF_NEEDED: Enable swapchain staging if needed, as detected by
// vk_swapchain_create() based on configuration values and the surface's
// presentation formats and feature flags.
//
// FORCED: Force swapchain staging, even if it is not needed. This is only
// useful for debugging and unit-testing.
//
// NOTE: Not all (pixel_format, image_usage) combinations are supported. For
// now, the following are guaranteed to work:
//
// - FORCED staging, where the intermediate and swapchain images share
// the same format, and image usage.
//
// - IF_NEEDED, when the client asks for VK_FORMAT_B8G8R8A8_UNORM with a
// VK_IMAGE_USAGE_STORAGE_BIT flag that is not supported by the swapchain.
// In this case, the target image will use VK_FORMAT_R8G8B8A8_UNORM
// format internally, but its image view will swap the R and B channels,
// to its content can be directly transferred to the swapchain image
// without pixel format conversion.
//
// - IF_NEEDED, when the client asks for VK_FORMAT_A8R8G8B8_UNORM but the
// swapchain only supports VK_FORMAT_B8G8R8A8_UNORM. The same technique
// as above is used to swap the R and B channels of the target image in
// its image view.
//
typedef enum
{
VK_SWAPCHAIN_STAGING_MODE_NONE = 0,
VK_SWAPCHAIN_STAGING_MODE_IF_NEEDED,
VK_SWAPCHAIN_STAGING_MODE_FORCED,
} vk_swapchain_staging_mode_t;
typedef struct
{
// The Vulkan instance, device, physical device, device and allocator to use.
VkInstance instance;
VkDevice device;
VkPhysicalDevice physical_device;
const VkAllocationCallbacks * allocator;
// Queue family and index to be used for presentation.
uint32_t present_queue_family;
uint32_t present_queue_index;
// The target presentation surface to use and its extent.
VkSurfaceKHR surface_khr;
// Maximum number of inflight frames to send to the swapchain.
// This should be at least 1, and will be capped by the max number of
// swapchain images supported by the surface / presentation engine.
// Use 2 for double-buffering, and 3 for triple-buffering.
uint32_t max_frames;
// Favorite surface pixel format. If not 0, the swapchain will try to
// use this when creating the swapchain images. Check the results by
// looking at |surface_format.format| after swapchain creation.
VkFormat pixel_format;
// Set to true to disable synchronization to the vertical blanking period.
// Will result in tearing, but useful for benchmarking.
bool disable_vsync;
// If not 0, this is taken as the required image usage bits for the
// swapchain creation. Default will be VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.
VkImageUsageFlags image_usage_flags;
// Swapchain staging mode, default is to disable it.
vk_swapchain_staging_mode_t staging_mode;
// TODO(digit): Provide a way to suggest a favorite presentation mode.
// TODO(digit): Provide a way to provide an old swapchain to support resizes.
} vk_swapchain_config_t;
// Create a new vk_swapchain_t instance. On success, returns a pointer
// to the new instance. On failure, aborts with an error message on stderr
// explaining the issue.
extern vk_swapchain_t *
vk_swapchain_create(const vk_swapchain_config_t * config);
// Print swapchain details to stdout. Useful for debugging.
extern void
vk_swapchain_print(const vk_swapchain_t * swapchain);
// Destroy a given swapchain instance. This will also destroy any optional
// command buffers and framebuffers enabled with vk_swapchain_enable_image_xxx().
extern void
vk_swapchain_destroy(vk_swapchain_t * swapchain);
// Retrieve swapchain surface extent.
extern VkExtent2D
vk_swapchain_get_extent(const vk_swapchain_t * swapchain);
// Retrieve swapchain surface format and color space.
extern VkSurfaceFormatKHR
vk_swapchain_get_format(const vk_swapchain_t * swapchain);
// Retrieve number of swapchain images.
extern uint32_t
vk_swapchain_get_image_count(const vk_swapchain_t * swapchain);
// Retrieve number of sync frames (will be <= the image count).
extern uint32_t
vk_swapchain_get_frame_count(const vk_swapchain_t * swapchain);
// Retrieve the VkSwapchainKHR value used by this vk_swapchain_t instance.
// Should only be useful for debugging.
extern VkSwapchainKHR
vk_swapchain_get_swapchain_khr(const vk_swapchain_t * swapchain);
// Retrieve VkImage associated with swapchain image at |image_index|.
// Requires |image_index < image_count|.
extern VkImage
vk_swapchain_get_image(const vk_swapchain_t * swapchain, uint32_t image_index);
// Retrieve VkImageView associated with swapchain image at |image_index|.
// Requires |image_index < image_count|.
extern VkImageView
vk_swapchain_get_image_view(const vk_swapchain_t * swapchain, uint32_t image_index);
// NOTE: For simpler cases, consider using vk_swapchain_prepare_next_image() and
// vk_swapchain_submit_and_present_image() instead.
//
// Acquire the next swapchain image. On failure, i.e. if the display surface was
// resized or invalidated, return false. Otherwise, return true and sets
// |*p_image_index| to the swapchain image index. The latter can also be
// retrieved as vk_swapchain_get_image_index().
//
// IMPORTANT: The caller should then queue one or more submits, but the first
// one must wait on the vk_swapchain_get_image_acquired() semaphore, and the
// last one must signal the vk_swapchain_get_image_rendered() semaphore.
extern bool
vk_swapchain_acquire_next_image(vk_swapchain_t * swapchain, uint32_t * p_image_index);
// Return the current swapchain image index. The one returned when calling
// vk_swapchain_acquire_next_image().
extern uint32_t
vk_swapchain_get_image_index(const vk_swapchain_t * swapchain);
// Return the semaphore used to wait for the current swapchain image acquisition.
// This is the semaphore that vk_swapchain_submit_and_present_image() will use for
// waiting, or that any queue submit performed after
// vk_swapchain_acquire_next_image() should wait on.
extern VkSemaphore
vk_swapchain_get_image_acquired_semaphore(const vk_swapchain_t * swapchain);
// Return the semaphore used to signal rendering completion for the current
// swapchain image. This is the semaphore that is waited on by
// vk_swapchain_present_image(), and which is also signaled internally by
// vk_swapchain_submit_and_present_image().
extern VkSemaphore
vk_swapchain_get_image_rendered_semaphore(const vk_swapchain_t * swapchain);
// Return the semaphore used to wait for the current swapchain image acquisition
// and transfer ownership to the caller. The next call to
// vk_swapchain_acquire_next_image() will create a new semaphore on demand.
// This is necessary because certain libraries, like Skia, insist on owning
// the semaphores they wait on.
extern VkSemaphore
vk_swapchain_take_image_acquired_semaphore(vk_swapchain_t * swapchain);
// Same as above for the semaphore returned by vk_swapchain_get_image_rendered_semaphore().
extern VkSemaphore
vk_swapchain_take_image_rendered_semaphore(vk_swapchain_t * swapchain);
// Present the current swapchain image after waiting for
// vk_swapchain_get_image_rendered_semaphore(), which should have been
// signaled by a previous submit performed by the caller.
extern bool
vk_swapchain_present_image(vk_swapchain_t * swapchain);
//
//
//
#ifdef __cplusplus
}
#endif
//
//
//
#endif // SRC_GRAPHICS_LIB_COMPUTE_TESTS_COMMON_VK_SWAPCHAIN_H_