blob: 5277ff0925facbc506b3483cd83ba669b3e194eb [file]
// 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.
#include "vk_swapchain.h"
#include <stdio.h>
#include <stdlib.h>
#include "vk_device_surface_info.h"
#include "vk_format_matcher.h"
#include "vk_strings.h"
#include "vk_swapchain_staging.h"
#include "vk_utils.h"
// Set to 1 to enable debug traces to stdout.
#define DEBUG_SWAPCHAIN 0
#if DEBUG_SWAPCHAIN
#include <unistd.h>
#define PRINT(...) (printf(__VA_ARGS__), fflush(stdout))
#define SLEEP(seconds) sleep(seconds)
#else
#define PRINT(...) ((void)0)
#define SLEEP(seconds) ((void)0)
#endif
#define PANIC(...) ASSERT_MSG(false, __VA_ARGS__)
//
//
//
// Maximum number of swapchain images
#define MAX_VK_SWAPCHAIN_IMAGES 8
struct vk_swapchain
{
VkInstance instance;
VkDevice device;
const VkAllocationCallbacks * allocator;
VkPhysicalDevice physical_device;
VkSurfaceKHR surface_khr;
VkSwapchainKHR swapchain_khr;
VkQueue present_queue;
VkSurfaceFormatKHR surface_format;
VkExtent2D surface_extent;
VkPresentModeKHR present_mode;
VkCommandPool present_command_pool;
// The following arrays are indexed by swapchain image index.
uint32_t image_count;
uint32_t image_index;
uint32_t image_counter;
bool has_framebuffers;
VkImage images[MAX_VK_SWAPCHAIN_IMAGES];
VkImageView image_views[MAX_VK_SWAPCHAIN_IMAGES];
VkCommandBuffer image_present_command_buffers[MAX_VK_SWAPCHAIN_IMAGES];
// The following arrays are indexed by frame index.
uint32_t frame_count;
uint32_t frame_index;
VkSemaphore frame_rendered_semaphores[MAX_VK_SWAPCHAIN_IMAGES];
VkSemaphore frame_available_semaphores[MAX_VK_SWAPCHAIN_IMAGES];
VkFence frame_inflight_fences[MAX_VK_SWAPCHAIN_IMAGES];
#if DEBUG_SWAPCHAIN
VkFence frame_acquired_fences[MAX_VK_SWAPCHAIN_IMAGES];
#endif
vk_swapchain_staging_t * staging; // NULL if not enabled.
};
VkSwapchainKHR
vk_swapchain_get_swapchain_khr(const vk_swapchain_t * swapchain)
{
return swapchain->swapchain_khr;
}
VkSurfaceFormatKHR
vk_swapchain_get_format(const vk_swapchain_t * swapchain)
{
if (swapchain->staging)
return vk_swapchain_staging_get_format(swapchain->staging);
return swapchain->surface_format;
}
VkExtent2D
vk_swapchain_get_extent(const vk_swapchain_t * swapchain)
{
return swapchain->surface_extent;
}
uint32_t
vk_swapchain_get_image_count(const vk_swapchain_t * swapchain)
{
return swapchain->image_count;
}
uint32_t
vk_swapchain_get_frame_count(const vk_swapchain_t * swapchain)
{
return swapchain->frame_count;
}
VkImage
vk_swapchain_get_image(const vk_swapchain_t * swapchain, uint32_t image_index)
{
ASSERT_MSG(image_index < swapchain->image_count, "Invalid image index: %u\n", image_index);
if (swapchain->staging)
return vk_swapchain_staging_get_image(swapchain->staging, image_index);
return swapchain->images[image_index];
}
VkImageView
vk_swapchain_get_image_view(const vk_swapchain_t * swapchain, uint32_t image_index)
{
ASSERT_MSG(image_index < swapchain->image_count, "Invalid image index: %u\n", image_index);
if (swapchain->staging)
return vk_swapchain_staging_get_image_view(swapchain->staging, image_index);
return swapchain->image_views[image_index];
}
// Create a new semaphore.
static VkSemaphore
vk_swapchain_create_semaphore(vk_swapchain_t * swapchain)
{
VkSemaphore semaphore;
vk(CreateSemaphore(swapchain->device,
&(const VkSemaphoreCreateInfo){
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
},
swapchain->allocator,
&semaphore));
return semaphore;
}
// Create a new fence.
static VkFence
vk_swapchain_create_fence(vk_swapchain_t * swapchain, bool signalled)
{
VkFence fence;
const VkFenceCreateInfo fence_info = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = signalled ? VK_FENCE_CREATE_SIGNALED_BIT : 0,
};
vk(CreateFence(swapchain->device, &fence_info, swapchain->allocator, &fence));
return fence;
}
// Synchronously transition all swapchain images to a new layout.
static void
vk_swapchain_transition_image_layouts(vk_swapchain_t * swapchain,
VkQueue queue,
VkCommandPool command_pool,
VkImageLayout old_layout,
VkImageLayout new_layout)
{
VkCommandBuffer cmd_buffer;
vk(AllocateCommandBuffers(swapchain->device,
&(const VkCommandBufferAllocateInfo){
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = command_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
},
&cmd_buffer));
const VkCommandBufferBeginInfo beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
};
vk(BeginCommandBuffer(cmd_buffer, &beginInfo));
VkImageMemoryBarrier image_barriers[MAX_VK_SWAPCHAIN_IMAGES];
for (uint32_t nn = 0; nn < swapchain->image_count; ++nn)
{
image_barriers[nn] = (const VkImageMemoryBarrier){
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
.dstAccessMask = 0,
.oldLayout = old_layout,
.newLayout = new_layout,
.image = swapchain->images[nn],
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1,
},
};
}
vkCmdPipelineBarrier(cmd_buffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0,
NULL,
0,
NULL,
swapchain->image_count,
image_barriers);
vkEndCommandBuffer(cmd_buffer);
VkFence fence = vk_swapchain_create_fence(swapchain, false);
vk(QueueSubmit(queue,
1,
&(const VkSubmitInfo){
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &cmd_buffer,
},
fence));
vk(WaitForFences(swapchain->device, 1, &fence, VK_TRUE, UINT64_MAX));
vkDestroyFence(swapchain->device, fence, swapchain->allocator);
vkFreeCommandBuffers(swapchain->device, command_pool, 1, &cmd_buffer);
}
vk_swapchain_t *
vk_swapchain_create(const vk_swapchain_config_t * config)
{
// Sanity check.
{
VkBool32 supported = 0;
VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(config->physical_device,
config->present_queue_family,
config->surface_khr,
&supported);
ASSERT_MSG(result == VK_SUCCESS && supported == VK_TRUE,
"This device does not support presenting to this surface!\n");
}
vk_swapchain_t * swapchain = malloc(sizeof(*swapchain));
*swapchain = (vk_swapchain_t){
.instance = config->instance,
.device = config->device,
.physical_device = config->physical_device,
.allocator = config->allocator,
.surface_khr = config->surface_khr,
.frame_count = config->max_frames,
};
// Grab surface info
vk_device_surface_info_t surface_info;
vk_device_surface_info_init(&surface_info,
config->physical_device,
config->surface_khr,
config->instance);
#if DEBUG_SWAPCHAIN
vk_device_surface_info_print(&surface_info);
#endif
// Format selection based on configuration.
VkImageUsageFlags image_usage = config->image_usage_flags;
if (!image_usage)
image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
VkFormat staging_format = VK_FORMAT_UNDEFINED;
VkImageUsageFlags staging_usage = (VkImageUsageFlags)0;
VkFormat format = vk_device_surface_info_find_presentation_format(&surface_info,
image_usage,
config->pixel_format);
if (format == VK_FORMAT_UNDEFINED)
{
PRINT("Could not find presentation format for: (format:%s,usage:%s)\n",
vk_format_to_string(config->pixel_format),
vk_image_usage_flags_to_string(image_usage));
}
bool try_swapchain_staging = false;
switch (config->staging_mode)
{
case VK_SWAPCHAIN_STAGING_MODE_NONE:
break;
case VK_SWAPCHAIN_STAGING_MODE_IF_NEEDED:
try_swapchain_staging =
format == VK_FORMAT_UNDEFINED && ((config->pixel_format == VK_FORMAT_B8G8R8A8_UNORM &&
(image_usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0) ||
(config->pixel_format == VK_FORMAT_R8G8B8A8_UNORM));
break;
case VK_SWAPCHAIN_STAGING_MODE_FORCED:
// Force swapchain staging, even if the swapchain doesn't require it.
// Useful to benchmark the impact of staging.
PRINT("Forcing swapchain staging\n");
if (format != VK_FORMAT_UNDEFINED)
{
staging_format = format;
staging_usage = image_usage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
else
{
try_swapchain_staging = true;
}
break;
}
if (try_swapchain_staging)
{
VkImageUsageFlags usage2 =
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
format = vk_device_surface_info_find_presentation_format(&surface_info,
usage2,
config->pixel_format);
PRINT("Trying to enable swapchain staging!\n"
"Found presentation format %s for (format:%s,usage %s)\n",
vk_format_to_string(format),
vk_format_to_string(config->pixel_format),
vk_image_usage_flags_to_string(usage2));
// Find a compatible format that matches the proper image usage.
vk_format_matcher_t matcher;
VkImageUsageFlags usage1 = image_usage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vk_format_matcher_init_for_image_usage(&matcher, usage1, config->physical_device);
if (format == VK_FORMAT_UNDEFINED)
format = config->pixel_format;
if (format != VK_FORMAT_UNDEFINED)
{
uint32_t compat_count = 0;
const VkFormat * compat_formats = NULL;
// Find compatible formats that have the same encoding (e.g. UNORM or SRGB, etc)
// as the desired pixel format. Otherwise, colors will be shifted unexpectedly.
// NOTE: Order in the lists below is important.
switch (format)
{
case VK_FORMAT_B8G8R8A8_UNORM: {
static const VkFormat formats[] = {
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_A8B8G8R8_UNORM_PACK32,
};
compat_count = ARRAY_SIZE(formats);
compat_formats = formats;
break;
}
case VK_FORMAT_B8G8R8A8_SRGB: {
static const VkFormat formats[] = {
VK_FORMAT_B8G8R8A8_SRGB,
VK_FORMAT_R8G8B8A8_SRGB,
};
compat_count = ARRAY_SIZE(formats);
compat_formats = formats;
break;
}
case VK_FORMAT_R8G8B8A8_UNORM: {
static const VkFormat formats[] = {
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_B8G8R8A8_UNORM,
};
compat_count = ARRAY_SIZE(formats);
compat_formats = formats;
break;
}
case VK_FORMAT_R8G8B8A8_SRGB: {
static const VkFormat formats[] = {
VK_FORMAT_B8G8R8A8_SRGB,
VK_FORMAT_R8G8B8A8_SRGB,
};
compat_count = ARRAY_SIZE(formats);
compat_formats = formats;
break;
}
default:;
}
for (uint32_t nn = 0; nn < compat_count; ++nn)
{
PRINT(" Probing %s for %s\n",
vk_format_to_string(compat_formats[nn]),
vk_image_usage_flags_to_string(usage1));
vk_format_matcher_probe(&matcher, compat_formats[nn]);
}
}
//vk_format_matcher_probe_compatible_formats_for(&matcher, config->pixel_format);
if (vk_format_matcher_done(&matcher, &staging_format, NULL))
{
// Fine, swapchain staging can be enabled.
staging_usage = usage1;
image_usage = usage2;
PRINT("Swapchain staging format=%s usage=%s, swapchain format=%s usage=%s\n",
vk_format_to_string(staging_format),
vk_image_usage_flags_to_string(staging_usage),
vk_format_to_string(format),
vk_image_usage_flags_to_string(image_usage));
}
else
{
PRINT("Unable to find format compatible with %s and usage %s\n",
vk_format_to_string(format),
vk_image_usage_flags_to_string(usage1));
format = VK_FORMAT_UNDEFINED;
}
}
if (format == VK_FORMAT_UNDEFINED)
{
if (config->pixel_format == VK_FORMAT_UNDEFINED)
{
ASSERT_MSG(false,
"This device has no presentation format compatible with %s\n",
vk_image_usage_flags_to_string(image_usage));
}
else
{
ASSERT_MSG(false,
"This device does not support %s for pixel format %s\n",
vk_image_usage_flags_to_string(image_usage),
vk_format_to_string(config->pixel_format));
}
}
if (staging_usage != 0)
{
// Staging requires transfering from the target to the swapchain image.
staging_usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
image_usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
swapchain->surface_format = (VkSurfaceFormatKHR){
.format = format,
.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR,
};
// Presentation mode selection.
// TODO(digit): Allow the client to specify an alternative presentation mode.
swapchain->present_mode =
config->disable_vsync ? VK_PRESENT_MODE_IMMEDIATE_KHR : VK_PRESENT_MODE_FIFO_KHR;
uint32_t surface_image_count = surface_info.capabilities.minImageCount;
// NOTE: A maxImageCount value of 0 means there is no limit in the number of swapchain images.
if (swapchain->frame_count > surface_info.capabilities.maxImageCount &&
surface_info.capabilities.maxImageCount != 0)
{
swapchain->frame_count = surface_info.capabilities.maxImageCount;
}
if (surface_info.capabilities.currentExtent.width == 0xffffffffu)
{
swapchain->surface_extent = surface_info.capabilities.minImageExtent;
}
else
{
swapchain->surface_extent = surface_info.capabilities.currentExtent;
}
vk_device_surface_info_destroy(&surface_info);
VkDevice device = swapchain->device;
const VkAllocationCallbacks * allocator = swapchain->allocator;
VkSwapchainCreateInfoKHR const swapchain_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.flags = 0,
.pNext = NULL,
.surface = swapchain->surface_khr,
.minImageCount = surface_image_count,
.imageFormat = swapchain->surface_format.format,
.imageColorSpace = swapchain->surface_format.colorSpace,
.imageExtent = swapchain->surface_extent,
.imageUsage = image_usage,
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.imageArrayLayers = 1,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = NULL,
.presentMode = swapchain->present_mode,
.oldSwapchain = VK_NULL_HANDLE,
.clipped = VK_FALSE,
};
vk(CreateSwapchainKHR(device, &swapchain_info, allocator, &swapchain->swapchain_khr));
swapchain->image_count = 0;
vk(GetSwapchainImagesKHR(device, swapchain->swapchain_khr, &swapchain->image_count, NULL));
ASSERT_MSG(swapchain->image_count > 0, "Could not create swapchain images!\n");
ASSERT_MSG(swapchain->image_count <= MAX_VK_SWAPCHAIN_IMAGES,
"Too many swapchain images (%d should be <= %d)\n",
swapchain->image_count,
MAX_VK_SWAPCHAIN_IMAGES);
vk(GetSwapchainImagesKHR(device,
swapchain->swapchain_khr,
&swapchain->image_count,
swapchain->images));
for (uint32_t nn = 0; nn < swapchain->image_count; ++nn)
{
const VkImageViewCreateInfo image_view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = NULL,
.image = swapchain->images[nn],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = swapchain->surface_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,
},
};
vk(CreateImageView(device, &image_view_info, allocator, &swapchain->image_views[nn]));
}
vkGetDeviceQueue(device,
config->present_queue_family,
config->present_queue_index,
&swapchain->present_queue);
ASSERT_MSG(swapchain->present_queue != VK_NULL_HANDLE,
"Could not get presentation queue handle!\n");
vk(CreateCommandPool(device,
&(const VkCommandPoolCreateInfo){
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.queueFamilyIndex = config->present_queue_family,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
},
allocator,
&swapchain->present_command_pool));
vk(AllocateCommandBuffers(device,
&(const VkCommandBufferAllocateInfo){
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = swapchain->present_command_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = swapchain->image_count,
},
swapchain->image_present_command_buffers));
for (uint32_t nn = 0; nn < swapchain->frame_count; ++nn)
{
swapchain->frame_available_semaphores[nn] = vk_swapchain_create_semaphore(swapchain);
swapchain->frame_rendered_semaphores[nn] = vk_swapchain_create_semaphore(swapchain);
swapchain->frame_inflight_fences[nn] = vk_swapchain_create_fence(swapchain, true);
#if DEBUG_SWAPCHAIN
swapchain->frame_acquired_fences[nn] = vk_swapchain_create_fence(swapchain, false);
#endif
}
// Transition each swapchain image to presentation layout to considerably
// simplify future usage.
vk_swapchain_transition_image_layouts(swapchain,
swapchain->present_queue,
swapchain->present_command_pool,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
if (staging_usage != (VkImageUsageFlags)0)
swapchain->staging = vk_swapchain_staging_create(swapchain->image_count,
swapchain->frame_count,
staging_usage,
staging_format,
swapchain->surface_extent,
swapchain->surface_format.format,
swapchain->images,
device,
config->physical_device,
config->present_queue_family,
config->present_queue_index,
allocator);
return swapchain;
}
VkSemaphore
vk_swapchain_get_image_acquired_semaphore(const vk_swapchain_t * swapchain)
{
uint32_t frame_index = swapchain->frame_index;
return swapchain->frame_available_semaphores[frame_index];
}
VkSemaphore
vk_swapchain_take_image_acquired_semaphore(vk_swapchain_t * swapchain)
{
uint32_t frame_index = swapchain->frame_index;
VkSemaphore result = swapchain->frame_available_semaphores[frame_index];
swapchain->frame_available_semaphores[frame_index] = VK_NULL_HANDLE;
return result;
}
VkSemaphore
vk_swapchain_get_image_rendered_semaphore(const vk_swapchain_t * swapchain)
{
uint32_t frame_index = swapchain->frame_index;
return swapchain->frame_rendered_semaphores[frame_index];
}
VkSemaphore
vk_swapchain_take_image_rendered_semaphore(vk_swapchain_t * swapchain)
{
uint32_t frame_index = swapchain->frame_index;
VkSemaphore result = swapchain->frame_rendered_semaphores[frame_index];
swapchain->frame_rendered_semaphores[frame_index] = VK_NULL_HANDLE;
return result;
}
uint32_t
vk_swapchain_get_image_index(const vk_swapchain_t * swapchain)
{
return swapchain->image_index;
}
bool
vk_swapchain_acquire_next_image(vk_swapchain_t * swapchain, uint32_t * image_index)
{
// Wait for the frame's fence to ensure we're not feeding too many of them
// to the presentation engine.
// BUG: The Fuchsia image pipe layer which provides vkAcquireNextImageKHR()
// does not support a non-null fence argument, and will crash at runtime!
uint32_t frame_index = swapchain->frame_index;
#if DEBUG_SWAPCHAIN && !defined(__Fuchsia__)
VkFence acquired_fence = swapchain->frame_acquired_fences[frame_index];
#else
const VkFence acquired_fence = VK_NULL_HANDLE;
#endif
VkSemaphore semaphore = swapchain->frame_available_semaphores[frame_index];
if (semaphore == VK_NULL_HANDLE)
{
semaphore = vk_swapchain_create_semaphore(swapchain);
swapchain->frame_available_semaphores[frame_index] = semaphore;
}
VkResult result = vkAcquireNextImageKHR(swapchain->device,
swapchain->swapchain_khr,
UINT64_MAX,
semaphore,
acquired_fence,
image_index);
switch (result)
{
case VK_SUCCESS:
case VK_SUBOPTIMAL_KHR:
break;
case VK_ERROR_OUT_OF_DATE_KHR:
*image_index = ~0U;
return false;
default:
VK_CHECK_MSG(result, "Could not acquire next swapchain image");
}
#if DEBUG_SWAPCHAIN && !defined(__Fuchsia__)
const uint64_t one_millisecond_ns = 1000000ULL; // 1ms in nanoseconds.
const uint64_t timeout_ns = 500 * one_millisecond_ns;
result = vkWaitForFences(swapchain->device, 1, &acquired_fence, VK_TRUE, timeout_ns);
if (result == VK_TIMEOUT)
{
ASSERT_MSG(result != VK_TIMEOUT, "Timeout while waiting for acquired fence!\n");
}
vkResetFences(swapchain->device, 1, &acquired_fence);
#endif // DEBUG_SWAPCHAIN
swapchain->image_index = *image_index;
PRINT("#%2u: ACQUIRED image_index=%u signal_sem=%p\n",
swapchain->image_counter,
*image_index,
semaphore);
return true;
}
bool
vk_swapchain_present_image(vk_swapchain_t * swapchain)
{
uint32_t frame_index = swapchain->frame_index;
VkSemaphore wait_semaphore = swapchain->frame_rendered_semaphores[frame_index];
if (swapchain->staging)
{
wait_semaphore = vk_swapchain_staging_present_image(swapchain->staging,
swapchain->image_index,
frame_index,
wait_semaphore);
}
const VkPresentInfoKHR presentInfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &wait_semaphore,
.swapchainCount = 1,
.pSwapchains = &swapchain->swapchain_khr,
.pImageIndices = &swapchain->image_index,
};
VkResult result = vkQueuePresentKHR(swapchain->present_queue, &presentInfo);
switch (result)
{
case VK_SUCCESS:
case VK_SUBOPTIMAL_KHR:
break;
case VK_ERROR_OUT_OF_DATE_KHR:
return false;
default:
VK_CHECK_MSG(result, "Problem during presentation!");
}
PRINT("#%2u: PRESENTED frame_index=%u image_index=%u wait_sem=%p\n",
swapchain->image_counter,
frame_index,
swapchain->image_index,
wait_semaphore);
swapchain->frame_index = (frame_index + 1) % swapchain->frame_count;
swapchain->image_counter += 1;
return true;
}
void
vk_swapchain_destroy(vk_swapchain_t * swapchain)
{
VkDevice device = swapchain->device;
const VkAllocationCallbacks * allocator = swapchain->allocator;
uint32_t image_count = swapchain->image_count;
if (swapchain->staging)
{
vk_swapchain_staging_destroy(swapchain->staging);
swapchain->staging = NULL;
}
for (uint32_t nn = 0; nn < swapchain->frame_count; ++nn)
{
#if DEBUG_SWAPCHAIN
vkDestroyFence(device, swapchain->frame_acquired_fences[nn], allocator);
#endif
vkDestroyFence(device, swapchain->frame_inflight_fences[nn], allocator);
vkDestroySemaphore(device, swapchain->frame_available_semaphores[nn], allocator);
vkDestroySemaphore(device, swapchain->frame_rendered_semaphores[nn], allocator);
}
swapchain->frame_count = 0;
swapchain->frame_index = 0;
if (swapchain->present_command_pool != VK_NULL_HANDLE)
{
vkFreeCommandBuffers(device,
swapchain->present_command_pool,
image_count,
swapchain->image_present_command_buffers);
vkDestroyCommandPool(device, swapchain->present_command_pool, allocator);
swapchain->present_command_pool = VK_NULL_HANDLE;
}
for (uint32_t nn = 0; nn < image_count; ++nn)
{
vkDestroyImageView(device, swapchain->image_views[nn], allocator);
swapchain->image_views[nn] = VK_NULL_HANDLE;
swapchain->images[nn] = VK_NULL_HANDLE;
}
swapchain->image_count = 0;
if (swapchain->swapchain_khr != VK_NULL_HANDLE)
{
vkDestroySwapchainKHR(device, swapchain->swapchain_khr, allocator);
swapchain->swapchain_khr = VK_NULL_HANDLE;
}
free(swapchain);
}
void
vk_swapchain_print(const vk_swapchain_t * swapchain)
{
printf(" Swapchain state:\n");
printf(" VkSurfaceKHR: %p\n", swapchain->surface_khr);
printf(" VkSwapchainKHR: %p\n", swapchain->swapchain_khr);
printf(" Present queue: %p\n", swapchain->present_queue);
printf(" Extent: %dx%d\n",
swapchain->surface_extent.width,
swapchain->surface_extent.height);
printf(" SurfaceFormat: %s\n",
vk_surface_format_khr_to_string(swapchain->surface_format));
printf(" Image count: %u\n", swapchain->image_count);
for (uint32_t nn = 0; nn < swapchain->image_count; ++nn)
{
printf(" image #%u\n", nn);
printf(" image: %p\n", swapchain->images[nn]);
printf(" image view: %p\n", swapchain->image_views[nn]);
}
printf(" Frame count: %u\n", swapchain->frame_count);
for (uint32_t nn = 0; nn < swapchain->frame_count; ++nn)
{
printf(" frame #%u\n", nn);
printf(" acquired_semaphore: %p\n", swapchain->frame_available_semaphores[nn]);
printf(" rendered_semaphore: %p\n", swapchain->frame_rendered_semaphores[nn]);
}
{
vk_device_surface_info_t surface_info;
vk_device_surface_info_init(&surface_info,
swapchain->physical_device,
swapchain->surface_khr,
swapchain->instance);
vk_device_surface_info_print(&surface_info);
vk_device_surface_info_destroy(&surface_info);
}
}