blob: 6a0e7ad49673b5679b404ca98c59c4cbb77738a2 [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.
#include "vk_swapchain_queue.h"
#include <stdio.h>
#include <stdlib.h>
#include "tests/common/utils.h"
#include "tests/common/vk_swapchain.h"
#include "tests/common/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
// Maximum number of swapchain images
#define MAX_VK_SWAPCHAIN_IMAGES 8
// The vk_swapchain_queue_t instance struct.
// |size| is the number of images in the swapchain.
// |index| is the index of the current image.
// |images| is an array of |size| |vk_swapchain_queue_image_t| instances.
struct vk_swapchain_queue
{
uint32_t size;
uint32_t index;
uint32_t counter;
vk_swapchain_queue_image_t images[MAX_VK_SWAPCHAIN_IMAGES];
vk_swapchain_t * swapchain;
VkDevice device;
const VkAllocationCallbacks * allocator;
VkCommandPool command_pool;
VkQueue command_queue;
};
static void
vk_swapchain_queue_init(vk_swapchain_queue_t * queue, const vk_swapchain_queue_config_t * config)
{
vk_swapchain_t * swapchain = config->swapchain;
VkDevice device = config->device;
const VkAllocationCallbacks * allocator = config->allocator;
ASSERT_MSG(swapchain, "vk_swapchain_queue_config_t::swapchain required!\n");
ASSERT_MSG(device != VK_NULL_HANDLE, "vk_swapchain_queue_config_t::device required!\n");
queue->swapchain = config->swapchain;
queue->device = device;
queue->allocator = allocator;
queue->size = vk_swapchain_get_image_count(config->swapchain);
ASSERT_MSG(queue->size < MAX_VK_SWAPCHAIN_IMAGES,
"Too many swapchain images %u, only %u supported!\n",
queue->size,
MAX_VK_SWAPCHAIN_IMAGES);
ASSERT_MSG(config->sync_semaphores_count < MAX_VK_SYNC_SEMAPHORES,
"Too many sync semaphores %u, should be <= %u\n",
config->sync_semaphores_count,
MAX_VK_SYNC_SEMAPHORES);
const VkFenceCreateInfo fenceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
vkGetDeviceQueue(device, config->queue_family, config->queue_index, &queue->command_queue);
ASSERT_MSG(queue->command_queue != VK_NULL_HANDLE, "Could not get command queue handle!\n");
vk(CreateCommandPool(device,
&(const VkCommandPoolCreateInfo){
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.queueFamilyIndex = config->queue_family,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
},
allocator,
&queue->command_pool));
const VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = queue->command_pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
VkExtent2D extent = vk_swapchain_get_extent(queue->swapchain);
for (uint32_t nn = 0; nn < queue->size; ++nn)
{
vk_swapchain_queue_image_t * image = &queue->images[nn];
image->image = vk_swapchain_get_image(swapchain, nn);
image->image_view = vk_swapchain_get_image_view(swapchain, nn);
vk(AllocateCommandBuffers(device, &commandBufferAllocateInfo, &image->command_buffer));
vk(CreateFence(device, &fenceCreateInfo, allocator, &image->fence));
image->framebuffer = VK_NULL_HANDLE;
if (config->enable_framebuffers != VK_NULL_HANDLE)
{
const VkFramebufferCreateInfo framebufferInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = config->enable_framebuffers,
.attachmentCount = 1,
.pAttachments = &image->image_view,
.width = extent.width,
.height = extent.height,
.layers = 1,
};
vk(CreateFramebuffer(device, &framebufferInfo, allocator, &image->framebuffer));
}
for (uint32_t mm = 0; mm < MAX_VK_SYNC_SEMAPHORES; ++mm)
{
image->sync_semaphores[mm] = VK_NULL_HANDLE;
if (mm < config->sync_semaphores_count)
{
vk(CreateSemaphore(device,
&(const VkSemaphoreCreateInfo){
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
},
allocator,
&image->sync_semaphores[mm]));
}
}
}
}
vk_swapchain_queue_t *
vk_swapchain_queue_create(const vk_swapchain_queue_config_t * config)
{
vk_swapchain_queue_t * queue = calloc(1, sizeof(*queue));
vk_swapchain_queue_init(queue, config);
return queue;
}
uint32_t
vk_swapchain_queue_get_size(const vk_swapchain_queue_t * queue)
{
return queue->size;
}
uint32_t
vk_swapchain_queue_get_index(const vk_swapchain_queue_t * queue)
{
return queue->index;
}
const vk_swapchain_queue_image_t *
vk_swapchain_queue_get_image(const vk_swapchain_queue_t * queue, uint32_t image_index)
{
ASSERT_MSG(image_index < queue->size, "Invalid image index %u\n", image_index);
return &queue->images[image_index];
}
extern const vk_swapchain_queue_image_t *
vk_swapchain_queue_acquire_next_image(vk_swapchain_queue_t * queue)
{
uint32_t image_number = queue->counter + 1;
UNUSED(image_number);
PRINT("#%2u: ACQUIRING SWAPCHAIN IMAGE\n", image_number);
if (!vk_swapchain_acquire_next_image(queue->swapchain, &queue->index))
return NULL;
// Wait for the frame's fence to ensure we're not feeding too many of them
// to the presentation engine.
const vk_swapchain_queue_image_t * image = &queue->images[queue->index];
PRINT("#%2u: WAITING fence[%u]=%p\n", image_number, queue->index, image->fence);
const uint64_t one_millisecond_ns = 1000000ULL; // 1ms in nanoseconds.
uint64_t timeout_ns = 500 * one_millisecond_ns;
VkDevice device = queue->device;
VkResult result = vkWaitForFences(device, 1, &image->fence, VK_TRUE, timeout_ns);
if (result == VK_TIMEOUT)
{
ASSERT_MSG(result != VK_TIMEOUT, "Timeout while waiting for fence!\n");
}
vkResetFences(device, 1, &image->fence);
PRINT("#%2u: WAITED\n", image_number);
return image;
}
extern void
vk_swapchain_queue_submit_and_present_image(vk_swapchain_queue_t * queue)
{
vk_swapchain_queue_submit_and_present_image_wait_one(
queue,
vk_swapchain_get_image_acquired_semaphore(queue->swapchain),
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
}
extern void
vk_swapchain_queue_submit_and_present_image_wait_one(vk_swapchain_queue_t * queue,
VkSemaphore wait_semaphore,
VkPipelineStageFlags wait_stages)
{
uint32_t image_number = queue->counter + 1;
UNUSED(image_number);
const vk_swapchain_queue_image_t * image = &queue->images[queue->index];
VkSemaphore signal_semaphore = vk_swapchain_get_image_rendered_semaphore(queue->swapchain);
PRINT("#%2u: SUBMITTING image_index=%u wait_sem=%p signal_sem=%p fence=%p\n",
image_number,
queue->index,
wait_semaphore,
signal_semaphores,
image->fence);
//printf("SUBMIT(f%u,i%u)!", current_frame, image_index); fflush(stdout);
vk_submit_one(wait_semaphore,
wait_stages,
signal_semaphore,
queue->command_queue,
image->command_buffer,
image->fence);
PRINT("#%2u: SUBMITTED cmd_buffer=%p\n", image_number, image->command_buffer);
vk_swapchain_present_image(queue->swapchain);
queue->counter += 1;
}
extern void
vk_swapchain_queue_destroy(vk_swapchain_queue_t * queue)
{
VkDevice device = queue->device;
const VkAllocationCallbacks * allocator = queue->allocator;
for (uint32_t nn = 0; nn < queue->size; ++nn)
{
vk_swapchain_queue_image_t * image = &queue->images[nn];
for (uint32_t mm = 0; mm < MAX_VK_SYNC_SEMAPHORES; ++mm)
{
if (image->sync_semaphores[mm] != VK_NULL_HANDLE)
{
vkDestroySemaphore(device, image->sync_semaphores[mm], allocator);
image->sync_semaphores[mm] = VK_NULL_HANDLE;
}
}
if (image->framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(device, image->framebuffer, allocator);
image->framebuffer = VK_NULL_HANDLE;
}
vkDestroyFence(device, image->fence, allocator);
image->fence = VK_NULL_HANDLE;
vkFreeCommandBuffers(device, queue->command_pool, 1, &image->command_buffer);
image->command_buffer = VK_NULL_HANDLE;
}
vkDestroyCommandPool(device, queue->command_pool, allocator);
queue->command_pool = VK_NULL_HANDLE;
queue->command_queue = VK_NULL_HANDLE;
}