monado/src/xrt/compositor/common/comp_vk_swapchain.c

494 lines
14 KiB
C
Raw Normal View History

2019-03-18 05:52:32 +00:00
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Vulkan swapchain code.
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup comp_client
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "util/u_misc.h"
2019-03-18 05:52:32 +00:00
#include "comp_vk_swapchain.h"
2019-04-03 16:24:06 +00:00
/*
*
* Types, defines and data.
*
*/
/*!
* These formats will be 'preferred' - in future we may wish to give preference
* to higher bit depths if they are available, but most display devices we are
* interested in should support one these.
*/
static VkFormat preferred_color_formats[] = {
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_A8B8G8R8_UNORM_PACK32,
};
2019-03-18 05:52:32 +00:00
/*
*
* Pre declare functions.
*
*/
static void
vk_swapchain_create_image_view(struct vk_bundle *vk,
VkImage image,
VkFormat format,
VkImageView *view);
static void
vk_swapchain_create_image_views(struct vk_swapchain *sc);
static void
vk_swapchain_destroy_old(struct vk_swapchain *sc, VkSwapchainKHR old);
static VkExtent2D
vk_swapchain_select_extent(struct vk_swapchain *sc,
VkSurfaceCapabilitiesKHR caps,
uint32_t width,
uint32_t height);
static bool
_find_surface_format(struct vk_swapchain *sc,
VkSurfaceKHR surface,
VkSurfaceFormatKHR *format);
static bool
_check_surface_present_mode(struct vk_bundle *vk,
VkSurfaceKHR surface,
VkPresentModeKHR present_mode);
/*
*
* Functions!
*
*/
void
vk_swapchain_init(struct vk_swapchain *sc,
struct vk_bundle *vk,
vk_swapchain_cb dimension_cb,
void *priv)
{
sc->vk = vk;
sc->cb_priv = priv;
sc->dimension_cb = dimension_cb;
}
void
vk_swapchain_create(struct vk_swapchain *sc,
uint32_t width,
uint32_t height,
VkFormat color_format,
VkColorSpaceKHR color_space,
VkPresentModeKHR present_mode)
{
VkBool32 supported;
VkResult ret;
sc->image_count = 0;
sc->swap_chain = VK_NULL_HANDLE;
sc->color_format = color_format;
sc->color_space = color_space;
sc->present_mode = present_mode;
// Sanity check.
sc->vk->vkGetPhysicalDeviceSurfaceSupportKHR(sc->vk->physical_device, 0,
sc->surface, &supported);
if (!supported) {
VK_ERROR(sc->vk,
"vkGetPhysicalDeviceSurfaceSupportKHR: "
"surface not supported!");
}
// More sanity checks.
if (!_check_surface_present_mode(sc->vk, sc->surface,
sc->present_mode)) {
return;
}
// Find the correct format.
if (!_find_surface_format(sc, sc->surface, &sc->surface_format)) {
return;
}
// Get the caps first.
VkSurfaceCapabilitiesKHR surface_caps;
ret = sc->vk->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
sc->vk->physical_device, sc->surface, &surface_caps);
if (ret != VK_SUCCESS) {
VK_ERROR(sc->vk,
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR: %s",
vk_result_string(ret));
return;
}
VkSwapchainKHR old_swap_chain = sc->swap_chain;
VkExtent2D extent =
vk_swapchain_select_extent(sc, surface_caps, width, height);
VkSwapchainCreateInfoKHR swap_chain_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = NULL,
.flags = 0,
2019-03-18 05:52:32 +00:00
.surface = sc->surface,
.minImageCount = surface_caps.minImageCount,
.imageFormat = sc->surface_format.format,
.imageColorSpace = sc->surface_format.colorSpace,
.imageExtent =
{
.width = extent.width,
.height = extent.height,
},
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = NULL,
2019-03-18 05:52:32 +00:00
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = sc->present_mode,
.clipped = VK_TRUE,
.oldSwapchain = old_swap_chain,
};
ret = sc->vk->vkCreateSwapchainKHR(sc->vk->device, &swap_chain_info,
NULL, &sc->swap_chain);
if (ret != VK_SUCCESS) {
VK_ERROR(sc->vk, "vkCreateSwapchainKHR: %s",
vk_result_string(ret));
return;
}
if (old_swap_chain != VK_NULL_HANDLE) {
vk_swapchain_destroy_old(sc, old_swap_chain);
}
vk_swapchain_create_image_views(sc);
}
static VkExtent2D
vk_swapchain_select_extent(struct vk_swapchain *sc,
VkSurfaceCapabilitiesKHR caps,
uint32_t width,
uint32_t height)
{
VkExtent2D extent;
2019-06-18 19:04:19 +00:00
U_ZERO(&extent);
2019-03-18 05:52:32 +00:00
// If width (and height) equals the special value 0xFFFFFFFF,
// the size of the surface will be set by the swapchain
if (caps.currentExtent.width == (uint32_t)-1) {
extent.width = width;
extent.height = height;
} else {
extent = caps.currentExtent;
if (caps.currentExtent.width != width ||
caps.currentExtent.height != height) {
VK_DEBUG(sc->vk,
"Using swap chain extent dimensions %dx%d "
"instead of requested %dx%d.",
caps.currentExtent.width,
caps.currentExtent.height, width, height);
sc->dimension_cb(caps.currentExtent.width,
caps.currentExtent.height,
sc->cb_priv);
}
}
return extent;
}
static void
vk_swapchain_destroy_old(struct vk_swapchain *sc, VkSwapchainKHR old)
{
for (uint32_t i = 0; i < sc->image_count; i++) {
sc->vk->vkDestroyImageView(sc->vk->device, sc->buffers[i].view,
NULL);
}
sc->vk->vkDestroySwapchainKHR(sc->vk->device, old, NULL);
}
VkResult
vk_swapchain_acquire_next_image(struct vk_swapchain *sc,
VkSemaphore semaphore,
uint32_t *index)
{
return sc->vk->vkAcquireNextImageKHR(sc->vk->device, sc->swap_chain,
UINT64_MAX, semaphore,
VK_NULL_HANDLE, index);
}
VkResult
vk_swapchain_present(struct vk_swapchain *sc,
VkQueue queue,
uint32_t index,
VkSemaphore semaphore)
{
VkPresentInfoKHR presentInfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = NULL,
2019-03-18 05:52:32 +00:00
.waitSemaphoreCount = 1,
.pWaitSemaphores = &semaphore,
.swapchainCount = 1,
.pSwapchains = &sc->swap_chain,
.pImageIndices = &index,
.pResults = NULL,
2019-03-18 05:52:32 +00:00
};
return sc->vk->vkQueuePresentKHR(queue, &presentInfo);
}
static bool
_find_surface_format(struct vk_swapchain *sc,
VkSurfaceKHR surface,
VkSurfaceFormatKHR *format)
{
uint32_t num_formats;
VkSurfaceFormatKHR *formats = NULL;
sc->vk->vkGetPhysicalDeviceSurfaceFormatsKHR(
sc->vk->physical_device, surface, &num_formats, NULL);
if (num_formats != 0) {
formats = U_TYPED_ARRAY_CALLOC(VkSurfaceFormatKHR, num_formats);
2019-03-18 05:52:32 +00:00
sc->vk->vkGetPhysicalDeviceSurfaceFormatsKHR(
sc->vk->physical_device, surface, &num_formats, formats);
} else {
VK_ERROR(sc->vk, "Could not enumerate surface formats.");
return false;
}
VkSurfaceFormatKHR *formats_for_colorspace = NULL;
formats_for_colorspace =
U_TYPED_ARRAY_CALLOC(VkSurfaceFormatKHR, num_formats);
uint32_t num_formats_cs = 0;
uint32_t num_pref_formats = sizeof(preferred_color_formats) /
sizeof(preferred_color_formats[0]);
// Gather formats that match our color space, we will select
// from these in preference to others.
2019-03-18 05:52:32 +00:00
for (uint32_t i = 0; i < num_formats; i++) {
if (formats[i].colorSpace == sc->color_space) {
formats_for_colorspace[num_formats_cs] = formats[i];
num_formats_cs++;
2019-03-18 05:52:32 +00:00
}
}
if (num_formats_cs > 0) {
// we have at least one format with our preferred colorspace
// if we have one that is on our preferred formats list, use it
for (uint32_t i = 0; i < num_formats_cs; i++) {
if (formats_for_colorspace[i].format ==
sc->color_format) {
// perfect match.
*format = formats_for_colorspace[i];
goto cleanup;
}
}
2019-06-18 16:17:57 +00:00
// we don't have our swapchain default format and colorspace,
// but we may have at least one preferred format with the
// correct colorspace.
for (uint32_t i = 0; i < num_formats_cs; i++) {
for (uint32_t j = 0; j < num_pref_formats; j++) {
if (formats_for_colorspace[i].format ==
preferred_color_formats[j]) {
*format = formats_for_colorspace[i];
goto cleanup;
}
}
}
// are we still here? this means we have a format with our
// preferred colorspace but we have no preferred color format -
// maybe we only have 10/12 bpc or 15/16bpp format. return the
// first one we have, at least its in the right color space.
*format = formats_for_colorspace[0];
VK_ERROR(sc->vk, "Returning unknown color format");
goto cleanup;
} else {
// we have nothing with the preferred colorspace? we can try to
// return a preferred format at least
for (uint32_t i = 0; i < num_formats; i++) {
for (uint32_t j = 0; j < num_pref_formats; j++) {
if (formats[i].format ==
preferred_color_formats[j]) {
*format = formats_for_colorspace[i];
VK_ERROR(
sc->vk,
"Returning known-wrong color "
"space! Color shift may occur.");
goto cleanup;
}
}
}
// if we are still here, we should just return the first format
// we have. we know its the wrong colorspace, and its not on our
// list of preferred formats, but its something.
*format = formats[0];
VK_ERROR(sc->vk,
"Returning fallback format! cue up some Kenny "
"Loggins, cos we're in the DANGER ZONE!");
goto cleanup;
}
VK_ERROR(sc->vk, "We should not be here");
goto error;
cleanup:
free(formats_for_colorspace);
free(formats);
return true;
error:
free(formats_for_colorspace);
2019-03-18 05:52:32 +00:00
free(formats);
return false;
}
static bool
_check_surface_present_mode(struct vk_bundle *vk,
VkSurfaceKHR surface,
VkPresentModeKHR present_mode)
{
uint32_t num_present_modes;
VkPresentModeKHR *present_modes;
vk->vkGetPhysicalDeviceSurfacePresentModesKHR(
vk->physical_device, surface, &num_present_modes, NULL);
if (num_present_modes != 0) {
present_modes =
U_TYPED_ARRAY_CALLOC(VkPresentModeKHR, num_present_modes);
2019-03-18 05:52:32 +00:00
vk->vkGetPhysicalDeviceSurfacePresentModesKHR(
vk->physical_device, surface, &num_present_modes,
present_modes);
} else {
VK_ERROR(vk, "Could not enumerate present modes.");
return false;
}
for (uint32_t i = 0; i < num_present_modes; i++) {
if (present_modes[i] == present_mode) {
free(present_modes);
return true;
}
}
free(present_modes);
VK_ERROR(vk, "Requested present mode not supported.\n");
return false;
}
static void
vk_swapchain_create_image_views(struct vk_swapchain *sc)
{
sc->vk->vkGetSwapchainImagesKHR(sc->vk->device, sc->swap_chain,
&sc->image_count, NULL);
assert(sc->image_count > 0);
VK_DEBUG(sc->vk, "Creating %d image views.", sc->image_count);
VkImage *images = U_TYPED_ARRAY_CALLOC(VkImage, sc->image_count);
2019-03-18 05:52:32 +00:00
sc->vk->vkGetSwapchainImagesKHR(sc->vk->device, sc->swap_chain,
&sc->image_count, images);
if (sc->buffers != NULL) {
free(sc->buffers);
}
sc->buffers =
U_TYPED_ARRAY_CALLOC(struct vk_swapchain_buffer, sc->image_count);
2019-03-18 05:52:32 +00:00
for (uint32_t i = 0; i < sc->image_count; i++) {
sc->buffers[i].image = images[i];
vk_swapchain_create_image_view(sc->vk, sc->buffers[i].image,
sc->surface_format.format,
&sc->buffers[i].view);
}
free(images);
}
void
vk_swapchain_cleanup(struct vk_swapchain *sc)
{
for (uint32_t i = 0; i < sc->image_count; i++) {
2019-10-07 22:27:09 +00:00
if (sc->buffers[i].view != VK_NULL_HANDLE) {
2019-03-18 05:52:32 +00:00
sc->vk->vkDestroyImageView(sc->vk->device,
sc->buffers[i].view, NULL);
sc->buffers[i].view = VK_NULL_HANDLE;
}
}
if (sc->swap_chain != VK_NULL_HANDLE) {
sc->vk->vkDestroySwapchainKHR(sc->vk->device, sc->swap_chain,
NULL);
sc->swap_chain = VK_NULL_HANDLE;
}
if (sc->surface != VK_NULL_HANDLE) {
sc->vk->vkDestroySurfaceKHR(sc->vk->instance, sc->surface,
NULL);
sc->swap_chain = VK_NULL_HANDLE;
}
}
static void
vk_swapchain_create_image_view(struct vk_bundle *vk,
VkImage image,
VkFormat format,
VkImageView *view)
{
VkResult ret;
VkImageViewCreateInfo view_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = NULL,
.flags = 0,
2019-03-18 05:52:32 +00:00
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.components =
{
.r = VK_COMPONENT_SWIZZLE_R,
.g = VK_COMPONENT_SWIZZLE_G,
.b = VK_COMPONENT_SWIZZLE_B,
.a = VK_COMPONENT_SWIZZLE_A,
},
.subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
ret = vk->vkCreateImageView(vk->device, &view_create_info, NULL, view);
if (ret != VK_SUCCESS) {
VK_ERROR(vk, "vkCreateImageView: %s", vk_result_string(ret));
}
}