2019-05-24 15:57:50 +01:00
|
|
|
/*
|
2020-08-04 12:25:34 +01:00
|
|
|
* Copyright (c) 2017-2021 Arm Limited.
|
2019-05-24 15:57:50 +01:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
|
|
|
* copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
|
* SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @file swapchain_base.cpp
|
|
|
|
|
*
|
|
|
|
|
* @brief Contains the implementation for the swapchain.
|
|
|
|
|
*
|
|
|
|
|
* This file contains much of the swapchain implementation,
|
|
|
|
|
* that is not specific to how images are created or presented.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <cerrno>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <vulkan/vulkan.h>
|
|
|
|
|
|
2021-03-23 10:24:43 +00:00
|
|
|
#include "util/log.hpp"
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-03-23 10:24:43 +00:00
|
|
|
#include "swapchain_base.hpp"
|
2019-05-24 15:57:50 +01:00
|
|
|
namespace wsi
|
|
|
|
|
{
|
|
|
|
|
|
2020-05-01 13:25:30 +01:00
|
|
|
void swapchain_base::page_flip_thread()
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
auto &sc_images = m_swapchain_images;
|
2019-05-24 15:57:50 +01:00
|
|
|
VkResult vk_res = VK_SUCCESS;
|
|
|
|
|
uint64_t timeout = UINT64_MAX;
|
2020-05-01 13:25:30 +01:00
|
|
|
constexpr uint64_t SEMAPHORE_TIMEOUT = 250000000; /* 250 ms. */
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2020-05-01 13:25:30 +01:00
|
|
|
/* No mutex is needed for the accesses to m_page_flip_thread_run variable as after the variable is
|
|
|
|
|
* initialized it is only ever changed to false. The while loop will make the thread read the
|
|
|
|
|
* value repeatedly, and the combination of semaphores and thread joins will force any changes to
|
|
|
|
|
* the variable to be visible to this thread.
|
|
|
|
|
*/
|
|
|
|
|
while (m_page_flip_thread_run)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
/* Waiting for the page_flip_semaphore which will be signalled once there is an
|
|
|
|
|
* image to display.*/
|
2020-05-01 13:25:30 +01:00
|
|
|
if ((vk_res = m_page_flip_semaphore.wait(SEMAPHORE_TIMEOUT)) == VK_TIMEOUT)
|
|
|
|
|
{
|
|
|
|
|
/* Image is not ready yet. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
assert(vk_res == VK_SUCCESS);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
/* We want to present the oldest queued for present image from our present queue,
|
|
|
|
|
* which we can find at the sc->pending_buffer_pool.head index. */
|
2021-08-16 12:22:25 +01:00
|
|
|
auto pending_index = m_pending_buffer_pool.pop_front();
|
|
|
|
|
assert(pending_index.has_value());
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-09-01 22:30:31 +01:00
|
|
|
/* We may need to wait for the payload of the present sync of the oldest pending image to be finished. */
|
|
|
|
|
vk_res = image_wait_present(sc_images[*pending_index], timeout);
|
2019-05-24 15:57:50 +01:00
|
|
|
if (vk_res != VK_SUCCESS)
|
|
|
|
|
{
|
2020-05-01 13:25:30 +01:00
|
|
|
m_is_valid = false;
|
|
|
|
|
m_free_image_semaphore.post();
|
2019-05-24 15:57:50 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 14:57:53 +01:00
|
|
|
std::unique_lock<std::recursive_mutex> image_status_lock(m_image_status_mutex);
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
/* If the descendant has started presenting the queue_present operation has marked the image
|
|
|
|
|
* as FREE so we simply release it and continue. */
|
2021-08-16 12:22:25 +01:00
|
|
|
if (sc_images[*pending_index].status == swapchain_image::FREE)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-08-16 12:22:25 +01:00
|
|
|
destroy_image(sc_images[*pending_index]);
|
2020-05-01 13:25:30 +01:00
|
|
|
m_free_image_semaphore.post();
|
2019-05-24 15:57:50 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2021-05-27 14:57:53 +01:00
|
|
|
image_status_lock.unlock();
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
/* First present of the swapchain. If it has an ancestor, wait until all the pending buffers
|
|
|
|
|
* from the ancestor have finished page flipping before we set mode. */
|
2020-05-01 13:25:30 +01:00
|
|
|
if (m_first_present)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-05-01 13:25:30 +01:00
|
|
|
if (m_ancestor != VK_NULL_HANDLE)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-05-01 13:25:30 +01:00
|
|
|
auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);
|
2019-05-24 15:57:50 +01:00
|
|
|
ancestor->wait_for_pending_buffers();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-01 13:25:30 +01:00
|
|
|
sem_post(&m_start_present_semaphore);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-08-16 12:22:25 +01:00
|
|
|
present_image(*pending_index);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2020-05-01 13:25:30 +01:00
|
|
|
m_first_present = false;
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
/* The swapchain has already started presenting. */
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-08-16 12:22:25 +01:00
|
|
|
present_image(*pending_index);
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::unpresent_image(uint32_t presented_index)
|
|
|
|
|
{
|
2021-05-27 14:57:53 +01:00
|
|
|
std::unique_lock<std::recursive_mutex> image_status_lock(m_image_status_mutex);
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
m_swapchain_images[presented_index].status = swapchain_image::FREE;
|
|
|
|
|
|
|
|
|
|
if (m_descendant != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
destroy_image(m_swapchain_images[presented_index]);
|
|
|
|
|
}
|
2021-05-27 14:57:53 +01:00
|
|
|
image_status_lock.unlock();
|
2019-05-24 15:57:50 +01:00
|
|
|
m_free_image_semaphore.post();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-27 09:59:18 +00:00
|
|
|
swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *callbacks)
|
2019-05-24 15:57:50 +01:00
|
|
|
: m_device_data(dev_data)
|
2020-10-27 11:12:00 +00:00
|
|
|
, m_page_flip_thread_run(true)
|
2019-05-24 15:57:50 +01:00
|
|
|
, m_thread_sem_defined(false)
|
|
|
|
|
, m_first_present(true)
|
2021-08-16 12:22:25 +01:00
|
|
|
, m_pending_buffer_pool{}
|
2021-05-27 13:53:07 +01:00
|
|
|
, m_allocator(dev_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, callbacks)
|
2020-11-27 09:59:18 +00:00
|
|
|
, m_swapchain_images(m_allocator)
|
2019-05-24 15:57:50 +01:00
|
|
|
, m_surface(VK_NULL_HANDLE)
|
|
|
|
|
, m_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR)
|
|
|
|
|
, m_descendant(VK_NULL_HANDLE)
|
|
|
|
|
, m_ancestor(VK_NULL_HANDLE)
|
|
|
|
|
, m_device(VK_NULL_HANDLE)
|
|
|
|
|
, m_queue(VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info)
|
|
|
|
|
{
|
|
|
|
|
assert(device != VK_NULL_HANDLE);
|
|
|
|
|
assert(swapchain_create_info != nullptr);
|
|
|
|
|
assert(swapchain_create_info->surface != VK_NULL_HANDLE);
|
|
|
|
|
|
|
|
|
|
int res;
|
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
|
|
m_device = device;
|
|
|
|
|
m_surface = swapchain_create_info->surface;
|
|
|
|
|
|
|
|
|
|
/* Check presentMode has a compatible value with swapchain - everything else should be taken care at image creation.*/
|
|
|
|
|
static const std::array<VkPresentModeKHR, 2> present_modes = { VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR };
|
|
|
|
|
bool present_mode_found = false;
|
|
|
|
|
for (uint32_t i = 0; i < present_modes.size() && !present_mode_found; i++)
|
|
|
|
|
{
|
|
|
|
|
if (swapchain_create_info->presentMode == present_modes[i])
|
|
|
|
|
{
|
|
|
|
|
present_mode_found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!present_mode_found)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-31 20:09:53 +00:00
|
|
|
m_present_mode = swapchain_create_info->presentMode;
|
|
|
|
|
|
2020-07-10 10:28:51 +01:00
|
|
|
/* Init image to invalid values. */
|
|
|
|
|
if (!m_swapchain_images.try_resize(swapchain_create_info->minImageCount))
|
2019-05-24 15:57:50 +01:00
|
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
|
|
2020-07-10 10:28:51 +01:00
|
|
|
/* We have allocated images, we can call the platform init function if something needs to be done. */
|
|
|
|
|
result = init_platform(device, swapchain_create_info);
|
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
VkImageCreateInfo image_create_info = {};
|
|
|
|
|
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
|
|
|
image_create_info.pNext = nullptr;
|
|
|
|
|
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
|
|
|
|
image_create_info.format = swapchain_create_info->imageFormat;
|
|
|
|
|
image_create_info.extent = { swapchain_create_info->imageExtent.width, swapchain_create_info->imageExtent.height, 1 };
|
|
|
|
|
image_create_info.mipLevels = 1;
|
|
|
|
|
image_create_info.arrayLayers = swapchain_create_info->imageArrayLayers;
|
|
|
|
|
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
|
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
|
image_create_info.usage = swapchain_create_info->imageUsage;
|
|
|
|
|
image_create_info.flags = 0;
|
|
|
|
|
image_create_info.sharingMode = swapchain_create_info->imageSharingMode;
|
|
|
|
|
image_create_info.queueFamilyIndexCount = swapchain_create_info->queueFamilyIndexCount;
|
|
|
|
|
image_create_info.pQueueFamilyIndices = swapchain_create_info->pQueueFamilyIndices;
|
|
|
|
|
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
|
2020-07-10 10:28:51 +01:00
|
|
|
result = m_free_image_semaphore.init(m_swapchain_images.size());
|
2019-05-24 15:57:50 +01:00
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
assert(result == VK_ERROR_OUT_OF_HOST_MEMORY);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-10 10:28:51 +01:00
|
|
|
for (auto& img : m_swapchain_images)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
result = create_image(image_create_info, img);
|
2019-05-24 15:57:50 +01:00
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_device_data.disp.GetDeviceQueue(m_device, 0, 0, &m_queue);
|
|
|
|
|
result = m_device_data.SetDeviceLoaderData(m_device, m_queue);
|
|
|
|
|
if (VK_SUCCESS != result)
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Setup semaphore for signaling pageflip thread */
|
2020-05-01 13:25:30 +01:00
|
|
|
result = m_page_flip_semaphore.init(0);
|
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
res = sem_init(&m_start_present_semaphore, 0, 0);
|
|
|
|
|
/* Only programming error can cause this to fail. */
|
|
|
|
|
assert(res == 0);
|
|
|
|
|
if (res != 0)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_thread_sem_defined = true;
|
|
|
|
|
|
|
|
|
|
/* Launch page flipping thread */
|
2020-05-01 13:25:30 +01:00
|
|
|
m_page_flip_thread = std::thread(&swapchain_base::page_flip_thread, this);
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
/* Release the swapchain images of the old swapchain in order
|
|
|
|
|
* to free up memory for new swapchain. This is necessary especially
|
|
|
|
|
* on platform with limited display memory size.
|
|
|
|
|
*
|
|
|
|
|
* NB: This must be done last in initialization, when the rest of
|
|
|
|
|
* the swapchain is valid.
|
|
|
|
|
*/
|
|
|
|
|
if (swapchain_create_info->oldSwapchain != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
/* Set ancestor. */
|
|
|
|
|
m_ancestor = swapchain_create_info->oldSwapchain;
|
|
|
|
|
|
|
|
|
|
auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);
|
|
|
|
|
ancestor->deprecate(reinterpret_cast<VkSwapchainKHR>(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_is_valid = true;
|
|
|
|
|
|
|
|
|
|
return VK_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::teardown()
|
|
|
|
|
{
|
|
|
|
|
/* This method will block until all resources associated with this swapchain
|
|
|
|
|
* are released. Images in the ACQUIRED or FREE state can be freed
|
2021-05-27 14:57:53 +01:00
|
|
|
* immediately. For images in the PENDING state, we will block until the
|
2019-05-24 15:57:50 +01:00
|
|
|
* presentation engine is finished with them. */
|
|
|
|
|
|
|
|
|
|
int res;
|
|
|
|
|
bool descendent_started_presenting = false;
|
|
|
|
|
|
|
|
|
|
if (m_descendant != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
|
2020-07-10 10:28:51 +01:00
|
|
|
for (auto& img : desc->m_swapchain_images)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
if (img.status == swapchain_image::PRESENTED ||
|
|
|
|
|
img.status == swapchain_image::PENDING)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
/* Here we wait for the start_present_semaphore, once this semaphore is up,
|
|
|
|
|
* the descendant has finished waiting, we don't want to delete vkImages and vkFences
|
|
|
|
|
* and semaphores before the waiting is done. */
|
|
|
|
|
sem_wait(&desc->m_start_present_semaphore);
|
|
|
|
|
|
|
|
|
|
descendent_started_presenting = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If descendant started presenting, there is no pending buffer in the swapchain. */
|
2020-11-26 18:00:05 +00:00
|
|
|
if (m_is_valid && descendent_started_presenting == false)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
wait_for_pending_buffers();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-26 18:00:05 +00:00
|
|
|
if (m_queue != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
/* Make sure the vkFences are done signaling. */
|
2020-08-04 12:25:34 +01:00
|
|
|
m_device_data.disp.QueueWaitIdle(m_queue);
|
2020-11-26 18:00:05 +00:00
|
|
|
}
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
/* We are safe to destroy everything. */
|
|
|
|
|
if (m_thread_sem_defined)
|
|
|
|
|
{
|
2020-05-01 13:25:30 +01:00
|
|
|
/* Tell flip thread to end. */
|
|
|
|
|
m_page_flip_thread_run = false;
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2020-05-01 13:25:30 +01:00
|
|
|
if (m_page_flip_thread.joinable())
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-05-01 13:25:30 +01:00
|
|
|
m_page_flip_thread.join();
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
2020-05-01 13:25:30 +01:00
|
|
|
else
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-03-23 10:24:43 +00:00
|
|
|
WSI_LOG_ERROR("m_page_flip_thread is not joinable");
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res = sem_destroy(&m_start_present_semaphore);
|
|
|
|
|
if (res != 0)
|
|
|
|
|
{
|
2021-03-23 10:24:43 +00:00
|
|
|
WSI_LOG_ERROR("sem_destroy failed for start_present_semaphore with %d", errno);
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_descendant != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
auto *sc = reinterpret_cast<swapchain_base *>(m_descendant);
|
|
|
|
|
sc->clear_ancestor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_ancestor != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
auto *sc = reinterpret_cast<swapchain_base *>(m_ancestor);
|
|
|
|
|
sc->clear_descendant();
|
|
|
|
|
}
|
|
|
|
|
/* Release the images array. */
|
2020-07-10 10:28:51 +01:00
|
|
|
for (auto& img : m_swapchain_images)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
/* Call implementation specific release */
|
|
|
|
|
destroy_image(img);
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index)
|
|
|
|
|
{
|
|
|
|
|
VkResult retval = wait_for_free_buffer(timeout);
|
|
|
|
|
if (retval != VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_is_valid)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 14:57:53 +01:00
|
|
|
std::unique_lock<std::recursive_mutex> image_status_lock(m_image_status_mutex);
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
uint32_t i;
|
2020-07-10 10:28:51 +01:00
|
|
|
for (i = 0; i < m_swapchain_images.size(); ++i)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
if (m_swapchain_images[i].status == swapchain_image::FREE)
|
|
|
|
|
{
|
|
|
|
|
m_swapchain_images[i].status = swapchain_image::ACQUIRED;
|
|
|
|
|
*image_index = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-10 10:28:51 +01:00
|
|
|
assert(i < m_swapchain_images.size());
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-05-27 14:57:53 +01:00
|
|
|
image_status_lock.unlock();
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
if (VK_NULL_HANDLE != semaphore || VK_NULL_HANDLE != fence)
|
|
|
|
|
{
|
|
|
|
|
VkSubmitInfo submit = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
|
|
|
|
|
|
|
|
|
if (VK_NULL_HANDLE != semaphore)
|
|
|
|
|
{
|
|
|
|
|
submit.signalSemaphoreCount = 1;
|
|
|
|
|
submit.pSignalSemaphores = &semaphore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
submit.commandBufferCount = 0;
|
|
|
|
|
submit.pCommandBuffers = nullptr;
|
|
|
|
|
retval = m_device_data.disp.QueueSubmit(m_queue, 1, &submit, fence);
|
|
|
|
|
assert(retval == VK_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_images)
|
|
|
|
|
{
|
|
|
|
|
if (swapchain_images == nullptr)
|
|
|
|
|
{
|
|
|
|
|
/* Return the number of swapchain images. */
|
2020-07-10 10:28:51 +01:00
|
|
|
*swapchain_image_count = m_swapchain_images.size();
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
return VK_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
assert(m_swapchain_images.size() > 0);
|
2019-05-24 15:57:50 +01:00
|
|
|
assert(*swapchain_image_count > 0);
|
|
|
|
|
|
|
|
|
|
/* Populate array, write actual number of images returned. */
|
|
|
|
|
uint32_t current_image = 0;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
swapchain_images[current_image] = m_swapchain_images[current_image].image;
|
|
|
|
|
|
|
|
|
|
current_image++;
|
|
|
|
|
|
2020-07-10 10:28:51 +01:00
|
|
|
if (current_image == m_swapchain_images.size())
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
*swapchain_image_count = current_image;
|
|
|
|
|
|
|
|
|
|
return VK_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} while (current_image < *swapchain_image_count);
|
|
|
|
|
|
|
|
|
|
/* If swapchain_image_count is smaller than the number of presentable images
|
|
|
|
|
* in the swapchain, VK_INCOMPLETE must be returned instead of VK_SUCCESS. */
|
|
|
|
|
*swapchain_image_count = current_image;
|
|
|
|
|
|
|
|
|
|
return VK_INCOMPLETE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-16 12:22:25 +01:00
|
|
|
VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, uint32_t image_index)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
VkResult result;
|
|
|
|
|
bool descendent_started_presenting = false;
|
2021-05-27 14:57:53 +01:00
|
|
|
const std::lock_guard<std::recursive_mutex> lock(m_image_status_mutex);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
if (m_descendant != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
|
2020-07-10 10:28:51 +01:00
|
|
|
for (auto& img : desc->m_swapchain_images)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
if (img.status == swapchain_image::PRESENTED ||
|
|
|
|
|
img.status == swapchain_image::PENDING)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
descendent_started_presenting = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-01 22:30:31 +01:00
|
|
|
result = image_set_present_payload(m_swapchain_images[image_index], queue, present_info->pWaitSemaphores,
|
|
|
|
|
present_info->waitSemaphoreCount);
|
2019-05-24 15:57:50 +01:00
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the descendant has started presenting, we should release the image
|
|
|
|
|
* however we do not want to block inside the main thread so we mark it
|
|
|
|
|
* as free and let the page flip thread take care of it. */
|
|
|
|
|
if (descendent_started_presenting)
|
|
|
|
|
{
|
|
|
|
|
m_swapchain_images[image_index].status = swapchain_image::FREE;
|
2021-08-16 12:22:25 +01:00
|
|
|
m_pending_buffer_pool.push_back(image_index);
|
2020-05-01 13:25:30 +01:00
|
|
|
m_page_flip_semaphore.post();
|
2019-05-24 15:57:50 +01:00
|
|
|
return VK_ERROR_OUT_OF_DATE_KHR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_swapchain_images[image_index].status = swapchain_image::PENDING;
|
2021-08-16 12:22:25 +01:00
|
|
|
m_pending_buffer_pool.push_back(image_index);
|
2020-05-01 13:25:30 +01:00
|
|
|
m_page_flip_semaphore.post();
|
2019-05-24 15:57:50 +01:00
|
|
|
return VK_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::deprecate(VkSwapchainKHR descendant)
|
|
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
for (auto& img : m_swapchain_images)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
if (img.status == swapchain_image::FREE)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2020-07-10 10:28:51 +01:00
|
|
|
destroy_image(img);
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set its descendant. */
|
|
|
|
|
m_descendant = descendant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::wait_for_pending_buffers()
|
|
|
|
|
{
|
|
|
|
|
int wait;
|
2021-05-27 14:57:53 +01:00
|
|
|
int pending_images = 0;
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::recursive_mutex> image_status_lock(m_image_status_mutex);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2020-07-10 10:28:51 +01:00
|
|
|
for (auto& img : m_swapchain_images)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-05-27 14:57:53 +01:00
|
|
|
if (img.status == swapchain_image::PENDING)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-05-27 14:57:53 +01:00
|
|
|
++pending_images;
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 14:57:53 +01:00
|
|
|
wait = pending_images;
|
|
|
|
|
image_status_lock.unlock();
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
while (wait > 0)
|
|
|
|
|
{
|
|
|
|
|
/* Take down one free image semaphore. */
|
|
|
|
|
wait_for_free_buffer(UINT64_MAX);
|
|
|
|
|
--wait;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::clear_ancestor()
|
|
|
|
|
{
|
|
|
|
|
m_ancestor = VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::clear_descendant()
|
|
|
|
|
{
|
|
|
|
|
m_descendant = VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult swapchain_base::wait_for_free_buffer(uint64_t timeout)
|
|
|
|
|
{
|
|
|
|
|
VkResult retval;
|
|
|
|
|
/* first see if a buffer is already marked as free */
|
|
|
|
|
retval = m_free_image_semaphore.wait(0);
|
|
|
|
|
if (retval == VK_NOT_READY)
|
|
|
|
|
{
|
|
|
|
|
/* if not, we still have work to do even if timeout==0 -
|
|
|
|
|
* the swapchain implementation may be able to get a buffer without
|
|
|
|
|
* waiting */
|
|
|
|
|
|
|
|
|
|
retval = get_free_buffer(&timeout);
|
|
|
|
|
if (retval == VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
/* the sub-implementation has done it's thing, so re-check the
|
|
|
|
|
* semaphore */
|
|
|
|
|
retval = m_free_image_semaphore.wait(timeout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} /* namespace wsi */
|