2019-05-24 15:57:50 +01:00
|
|
|
/*
|
2025-01-15 16:05:05 +00:00
|
|
|
* Copyright (c) 2017-2025 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>
|
2023-10-30 16:11:26 +08:00
|
|
|
#include <system_error>
|
2025-11-20 13:55:45 +00:00
|
|
|
#include <algorithm>
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <vulkan/vulkan.h>
|
|
|
|
|
|
2021-03-23 10:24:43 +00:00
|
|
|
#include "util/log.hpp"
|
2022-03-18 12:05:46 +00:00
|
|
|
#include "util/helpers.hpp"
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-03-23 10:24:43 +00:00
|
|
|
#include "swapchain_base.hpp"
|
2024-04-22 14:52:05 +01:00
|
|
|
#include "wsi_factory.hpp"
|
2024-11-28 06:36:17 +00:00
|
|
|
|
|
|
|
|
#include "extensions/present_timing.hpp"
|
|
|
|
|
#include "extensions/swapchain_maintenance.hpp"
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
namespace wsi
|
|
|
|
|
{
|
|
|
|
|
|
2025-12-10 11:22:49 +00:00
|
|
|
bool swapchain_base::is_using_shared_present_mode()
|
|
|
|
|
{
|
|
|
|
|
return (m_present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
|
|
|
|
|
m_present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
2024-08-22 16:45:28 +01:00
|
|
|
pending_present_request submit_info{};
|
2025-12-10 11:22:49 +00: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
|
|
|
{
|
2025-12-10 11:22:49 +00:00
|
|
|
VkResult wait_res = m_page_flip_semaphore.wait(SEMAPHORE_TIMEOUT);
|
|
|
|
|
if (wait_res == VK_TIMEOUT)
|
2024-04-12 14:52:39 +02:00
|
|
|
{
|
2025-12-10 11:22:49 +00:00
|
|
|
/* Image is not ready yet. */
|
|
|
|
|
continue;
|
2024-04-12 14:52:39 +02:00
|
|
|
}
|
2020-05-01 13:25:30 +01:00
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2025-12-10 11:22:49 +00: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. */
|
|
|
|
|
{
|
2025-09-05 15:06:38 +00:00
|
|
|
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
|
|
|
|
|
if (!image_status_lock)
|
|
|
|
|
{
|
|
|
|
|
WSI_LOG_ERROR("Failed to acquire image status lock in page flip thread.");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
2024-08-22 16:45:28 +01:00
|
|
|
|
|
|
|
|
auto pending_submission = m_pending_buffer_pool.pop_front();
|
|
|
|
|
assert(pending_submission.has_value());
|
|
|
|
|
submit_info = *pending_submission;
|
2024-04-12 14:52:39 +02:00
|
|
|
}
|
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. */
|
2025-11-20 13:55:45 +00:00
|
|
|
while ((vk_res = sc_images[submit_info.image_index].wait_present(timeout)) == VK_TIMEOUT)
|
2023-04-18 10:58:09 +01:00
|
|
|
{
|
|
|
|
|
WSI_LOG_WARNING("Timeout waiting for image's present fences, retrying..");
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
if (vk_res != VK_SUCCESS)
|
|
|
|
|
{
|
2021-09-22 11:15:26 +01:00
|
|
|
set_error_state(vk_res);
|
2020-05-01 13:25:30 +01:00
|
|
|
m_free_image_semaphore.post();
|
2019-05-24 15:57:50 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 16:45:28 +01:00
|
|
|
call_present(submit_info);
|
2021-10-14 15:45:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 16:45:28 +01:00
|
|
|
void swapchain_base::call_present(const pending_present_request &pending_present)
|
2021-10-14 15:45:34 +00:00
|
|
|
{
|
|
|
|
|
/* First present of the swapchain. If it has an ancestor, wait until all the
|
|
|
|
|
* pending buffers from the ancestor have been presented. */
|
|
|
|
|
if (m_first_present)
|
|
|
|
|
{
|
|
|
|
|
if (m_ancestor != VK_NULL_HANDLE)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-10-14 15:45:34 +00:00
|
|
|
auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);
|
|
|
|
|
ancestor->wait_for_pending_buffers();
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
sem_post(&m_start_present_semaphore);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2024-08-22 16:45:28 +01:00
|
|
|
present_image(pending_present);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
m_first_present = false;
|
|
|
|
|
}
|
|
|
|
|
/* The swapchain has already started presenting. */
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-08-22 16:45:28 +01:00
|
|
|
present_image(pending_present);
|
2021-10-14 15:45:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool swapchain_base::has_descendant_started_presenting()
|
|
|
|
|
{
|
|
|
|
|
if (m_descendant == VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
|
2021-11-25 15:30:46 +00:00
|
|
|
return desc->m_started_presenting;
|
2021-10-14 15:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkResult swapchain_base::init_page_flip_thread()
|
|
|
|
|
{
|
|
|
|
|
/* Setup semaphore for signaling pageflip thread */
|
2022-11-24 11:24:34 +00:00
|
|
|
TRY_LOG_CALL(m_page_flip_semaphore.init(0));
|
2021-10-14 15:45:34 +00:00
|
|
|
m_thread_sem_defined = true;
|
|
|
|
|
|
|
|
|
|
/* Launch page flipping thread */
|
|
|
|
|
m_page_flip_thread_run = true;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
m_page_flip_thread = std::thread(&swapchain_base::page_flip_thread, this);
|
|
|
|
|
}
|
|
|
|
|
catch (const std::system_error &)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
|
|
|
}
|
2022-11-11 14:27:03 +00:00
|
|
|
catch (const std::bad_alloc &)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_INITIALIZATION_FAILED;
|
|
|
|
|
}
|
2021-10-14 15:45:34 +00:00
|
|
|
return VK_SUCCESS;
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::unpresent_image(uint32_t presented_index)
|
|
|
|
|
{
|
2025-09-05 15:06:38 +00:00
|
|
|
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
|
|
|
|
|
if (!image_status_lock)
|
|
|
|
|
{
|
|
|
|
|
WSI_LOG_ERROR("Failed to acquire image status lock in unpresent_image.");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
2021-05-27 14:57:53 +01:00
|
|
|
|
2025-12-10 11:22:49 +00:00
|
|
|
const bool is_shared_present = is_using_shared_present_mode();
|
|
|
|
|
m_swapchain_images[presented_index].set_status(is_shared_present ? swapchain_image::ACQUIRED :
|
|
|
|
|
swapchain_image::FREE);
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-05-27 14:57:53 +01:00
|
|
|
image_status_lock.unlock();
|
2025-12-10 11:22:49 +00:00
|
|
|
if (!is_shared_present)
|
2024-04-12 14:52:39 +02:00
|
|
|
{
|
|
|
|
|
m_free_image_semaphore.post();
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
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)
|
2021-10-14 15:45:34 +00:00
|
|
|
, m_page_flip_thread_run(false)
|
2021-10-07 14:48:44 +01:00
|
|
|
, m_start_present_semaphore()
|
2019-05-24 15:57:50 +01:00
|
|
|
, m_thread_sem_defined(false)
|
|
|
|
|
, m_first_present(true)
|
2021-10-07 14:48:44 +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)
|
2024-05-09 09:32:42 +01:00
|
|
|
, m_present_modes(m_allocator)
|
2019-05-24 15:57:50 +01:00
|
|
|
, m_descendant(VK_NULL_HANDLE)
|
|
|
|
|
, m_ancestor(VK_NULL_HANDLE)
|
|
|
|
|
, m_device(VK_NULL_HANDLE)
|
|
|
|
|
, m_queue(VK_NULL_HANDLE)
|
2021-09-21 09:03:49 +01:00
|
|
|
, m_image_acquire_lock()
|
2021-09-22 11:15:26 +01:00
|
|
|
, m_error_state(VK_NOT_READY)
|
2021-11-25 15:30:46 +00:00
|
|
|
, m_started_presenting(false)
|
2024-11-28 06:36:17 +00:00
|
|
|
, m_extensions(m_allocator)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 08:20:07 +00:00
|
|
|
static bool external_sync_supported(const layer::device_private_data &device_data)
|
|
|
|
|
{
|
|
|
|
|
return device_data.disp.get_fn<PFN_vkImportFenceFdKHR>("vkImportFenceFdKHR").has_value() &&
|
|
|
|
|
device_data.disp.get_fn<PFN_vkImportSemaphoreFdKHR>("vkImportSemaphoreFdKHR").has_value();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
m_device = device;
|
|
|
|
|
m_surface = swapchain_create_info->surface;
|
2021-01-31 20:09:53 +00:00
|
|
|
m_present_mode = swapchain_create_info->presentMode;
|
|
|
|
|
|
2024-11-28 06:36:17 +00:00
|
|
|
/* Register required extensions by the swapchain */
|
|
|
|
|
TRY_LOG_CALL(add_required_extensions(device, swapchain_create_info));
|
2024-05-09 09:32:42 +01:00
|
|
|
|
2024-11-28 06:36:17 +00:00
|
|
|
auto *ext = get_swapchain_extension<wsi::wsi_ext_swapchain_maintenance1>();
|
|
|
|
|
if (ext)
|
2022-03-18 12:05:46 +00:00
|
|
|
{
|
2024-11-28 06:36:17 +00:00
|
|
|
TRY_LOG_CALL(ext->handle_swapchain_present_modes_create_info(device, swapchain_create_info, m_surface));
|
|
|
|
|
TRY_LOG_CALL(ext->handle_scaling_create_info(device, swapchain_create_info, m_surface));
|
2022-03-18 12:05:46 +00:00
|
|
|
}
|
|
|
|
|
|
2025-09-11 08:28:03 +00:00
|
|
|
const bool want_mutable_format = (swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) != 0;
|
|
|
|
|
|
|
|
|
|
/* Enable mutable-format early so backends can react in init_platform. */
|
|
|
|
|
if (want_mutable_format)
|
|
|
|
|
{
|
|
|
|
|
enable_mutable_format();
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
bool use_presentation_thread = true;
|
2022-11-24 11:24:34 +00:00
|
|
|
TRY_LOG_CALL(init_platform(device, swapchain_create_info, use_presentation_thread));
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
if (use_presentation_thread)
|
|
|
|
|
{
|
2022-11-24 11:24:34 +00:00
|
|
|
TRY_LOG_CALL(init_page_flip_thread());
|
2021-10-14 15:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-15 21:09:09 +01:00
|
|
|
const bool image_deferred_allocation =
|
2024-04-25 15:07:47 +01:00
|
|
|
swapchain_create_info->flags & VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT;
|
2025-11-20 13:55:45 +00:00
|
|
|
for (size_t i = 0; i < swapchain_create_info->minImageCount; i++)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
std::variant<VkResult, swapchain_image> swapchain_image_result = get_image_factory().create_swapchain_image();
|
|
|
|
|
if (auto error = std::get_if<VkResult>(&swapchain_image_result))
|
2024-04-15 21:09:09 +01:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
return *error;
|
2024-04-15 21:09:09 +01:00
|
|
|
}
|
2025-11-20 13:55:45 +00:00
|
|
|
|
|
|
|
|
swapchain_image image = std::move(std::get<swapchain_image>(swapchain_image_result));
|
|
|
|
|
if (!image_deferred_allocation)
|
2024-04-15 21:09:09 +01:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
TRY_LOG_CALL(allocate_and_bind_swapchain_image(image));
|
|
|
|
|
image.set_status(swapchain_image::FREE);
|
2024-04-15 21:09:09 +01:00
|
|
|
}
|
2021-10-20 15:27:47 +01:00
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
bool success = m_swapchain_images.try_push_back(std::move(image));
|
|
|
|
|
if (!success)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-24 11:24:34 +00:00
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
if (VkResult result = m_free_image_semaphore.init(m_swapchain_images.size()); result != VK_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
assert(result == VK_ERROR_OUT_OF_HOST_MEMORY);
|
|
|
|
|
return result;
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
2025-04-30 08:20:07 +00:00
|
|
|
if (!external_sync_supported(m_device_data))
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* This could be problematic when a queue has been created
|
|
|
|
|
* with any flag enabled. The reason is that according to the
|
|
|
|
|
* Vulkan spec, vkGetDeviceQueue2 should be used to get queues
|
|
|
|
|
* that were created with non zero flags parameters.
|
|
|
|
|
*/
|
|
|
|
|
m_device_data.disp.GetDeviceQueue(m_device, 0, 0, &m_queue);
|
|
|
|
|
TRY_LOG_CALL(m_device_data.SetDeviceLoaderData(m_device, m_queue));
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
int res = sem_init(&m_start_present_semaphore, 0, 0);
|
2019-05-24 15:57:50 +01:00
|
|
|
/* Only programming error can cause this to fail. */
|
|
|
|
|
assert(res == 0);
|
|
|
|
|
if (res != 0)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-22 11:15:26 +01:00
|
|
|
set_error_state(VK_SUCCESS);
|
2019-05-24 15:57:50 +01:00
|
|
|
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. */
|
|
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
if (has_descendant_started_presenting())
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-10-14 15:45:34 +00: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. */
|
2019-05-24 15:57:50 +01:00
|
|
|
auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
|
2021-10-14 15:45:34 +00:00
|
|
|
sem_wait(&desc->m_start_present_semaphore);
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
2021-09-22 11:15:26 +01:00
|
|
|
else if (!error_has_occured())
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-10-14 15:45:34 +00:00
|
|
|
/* If descendant hasn't started presenting, there are pending buffers in the swapchain. */
|
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
|
|
|
}
|
2021-10-14 15:45:34 +00:00
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
int res = sem_destroy(&m_start_present_semaphore);
|
|
|
|
|
if (res != 0)
|
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-11 14:27:03 +00:00
|
|
|
VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence,
|
|
|
|
|
uint32_t *image_index)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2025-09-05 15:06:38 +00:00
|
|
|
util::unique_lock<util::mutex> acquire_lock(m_image_acquire_lock);
|
|
|
|
|
if (!acquire_lock)
|
|
|
|
|
{
|
|
|
|
|
WSI_LOG_ERROR("Failed to acquire image acquire lock.");
|
|
|
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
|
}
|
2021-09-21 09:03:49 +01:00
|
|
|
|
2025-06-19 12:51:51 +01:00
|
|
|
TRY(wait_and_get_free_buffer(timeout));
|
2021-09-22 11:15:26 +01:00
|
|
|
if (error_has_occured())
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2021-09-22 11:15:26 +01:00
|
|
|
return get_error_state();
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 15:06:38 +00:00
|
|
|
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
|
|
|
|
|
if (!image_status_lock)
|
|
|
|
|
{
|
|
|
|
|
WSI_LOG_ERROR("Failed to acquire image status lock in acquire_next_image.");
|
|
|
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
|
}
|
2021-05-27 14:57:53 +01:00
|
|
|
|
2021-10-07 14:48:44 +01:00
|
|
|
size_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
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
if (m_swapchain_images[i].get_status() == swapchain_image::UNALLOCATED)
|
2024-04-15 21:09:09 +01:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
TRY_LOG_CALL(allocate_and_bind_swapchain_image(m_swapchain_images[i]));
|
|
|
|
|
m_swapchain_images[i].set_status(swapchain_image::FREE);
|
2024-04-15 21:09:09 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
if (m_swapchain_images[i].get_status() == swapchain_image::FREE)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
m_swapchain_images[i].set_status(swapchain_image::ACQUIRED);
|
2019-05-24 15:57:50 +01:00
|
|
|
*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();
|
|
|
|
|
|
2025-04-30 08:20:07 +00:00
|
|
|
if (!external_sync_supported(m_device_data))
|
|
|
|
|
{
|
|
|
|
|
/* Fallback for when importing fence/semaphore sync FDs is unsupported by the ICD. */
|
|
|
|
|
queue_submit_semaphores semaphores = {
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
(semaphore != VK_NULL_HANDLE) ? &semaphore : nullptr,
|
|
|
|
|
(semaphore != VK_NULL_HANDLE) ? 1u : 0,
|
|
|
|
|
};
|
|
|
|
|
return sync_queue_submit(m_device_data, m_queue, fence, semaphores);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-13 10:08:03 +00:00
|
|
|
/* Try to signal fences/semaphores with a sync FD for optimal performance. */
|
2025-04-30 08:20:07 +00:00
|
|
|
if (fence != VK_NULL_HANDLE)
|
2022-01-13 10:08:03 +00:00
|
|
|
{
|
2025-04-30 08:20:07 +00:00
|
|
|
int already_signalled_sentinel_fd = -1;
|
|
|
|
|
auto info = VkImportFenceFdInfoKHR{};
|
2022-01-13 10:08:03 +00:00
|
|
|
{
|
2025-04-30 08:20:07 +00:00
|
|
|
info.sType = VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR;
|
|
|
|
|
info.fence = fence;
|
|
|
|
|
info.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
|
|
|
|
|
info.fd = already_signalled_sentinel_fd;
|
|
|
|
|
info.flags = VK_FENCE_IMPORT_TEMPORARY_BIT;
|
2022-01-13 10:08:03 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-30 08:20:07 +00:00
|
|
|
TRY_LOG_CALL(m_device_data.disp.ImportFenceFdKHR(m_device, &info));
|
|
|
|
|
fence = VK_NULL_HANDLE;
|
|
|
|
|
}
|
2022-01-13 10:08:03 +00:00
|
|
|
|
2025-04-30 08:20:07 +00:00
|
|
|
if (semaphore != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
int already_signalled_sentinel_fd = -1;
|
|
|
|
|
auto info = VkImportSemaphoreFdInfoKHR{};
|
|
|
|
|
{
|
|
|
|
|
info.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
|
|
|
|
|
info.semaphore = semaphore;
|
|
|
|
|
info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
|
|
|
|
|
info.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
|
|
|
|
|
info.fd = already_signalled_sentinel_fd;
|
2022-01-13 10:08:03 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-30 08:20:07 +00:00
|
|
|
TRY_LOG_CALL(m_device_data.disp.ImportSemaphoreFdKHR(m_device, &info));
|
|
|
|
|
semaphore = VK_NULL_HANDLE;
|
|
|
|
|
}
|
2022-11-24 11:24:34 +00:00
|
|
|
|
2024-04-09 16:04:28 +01:00
|
|
|
return VK_SUCCESS;
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
swapchain_images[current_image] = m_swapchain_images[current_image].get_image();
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-04 10:15:14 +01:00
|
|
|
VkResult swapchain_base::create_aliased_image_handle(VkImage *image)
|
|
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
auto image_create_info = get_image_factory().get_image_handle_creator().get_image_create_info();
|
2025-09-11 08:28:03 +00:00
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
VkImageCreateInfo alias_info = image_create_info;
|
|
|
|
|
alias_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
2025-09-11 08:28:03 +00:00
|
|
|
return m_device_data.disp.CreateImage(m_device, &alias_info, get_allocation_callbacks(), image);
|
2022-07-04 10:15:14 +01:00
|
|
|
}
|
|
|
|
|
|
2024-04-12 14:52:39 +02:00
|
|
|
VkResult swapchain_base::get_swapchain_status()
|
|
|
|
|
{
|
|
|
|
|
return get_error_state();
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 16:45:28 +01:00
|
|
|
VkResult swapchain_base::notify_presentation_engine(const pending_present_request &pending_present)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2025-09-05 15:06:38 +00:00
|
|
|
const util::unique_lock<util::recursive_mutex> lock(m_image_status_mutex);
|
|
|
|
|
if (!lock)
|
|
|
|
|
{
|
|
|
|
|
return VK_ERROR_UNKNOWN;
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2021-10-14 15:45:34 +00:00
|
|
|
/* 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. */
|
|
|
|
|
const bool descendant_started_presenting = has_descendant_started_presenting();
|
|
|
|
|
if (descendant_started_presenting)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
m_swapchain_images[pending_present.image_index].set_status(swapchain_image::FREE);
|
2021-10-14 15:45:34 +00:00
|
|
|
m_free_image_semaphore.post();
|
|
|
|
|
return VK_ERROR_OUT_OF_DATE_KHR;
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
m_swapchain_images[pending_present.image_index].set_status(swapchain_image::PENDING);
|
2021-11-25 15:30:46 +00:00
|
|
|
m_started_presenting = true;
|
2021-10-14 15:45:34 +00:00
|
|
|
|
|
|
|
|
if (m_page_flip_thread_run)
|
|
|
|
|
{
|
2024-08-22 16:45:28 +01:00
|
|
|
bool buffer_pool_res = m_pending_buffer_pool.push_back(pending_present);
|
2021-10-14 15:45:34 +00:00
|
|
|
(void)buffer_pool_res;
|
|
|
|
|
assert(buffer_pool_res);
|
|
|
|
|
m_page_flip_semaphore.post();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-08-22 16:45:28 +01:00
|
|
|
call_present(pending_present);
|
2021-10-14 15:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VK_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 16:45:28 +01:00
|
|
|
VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info,
|
|
|
|
|
const swapchain_presentation_parameters &submit_info)
|
2021-10-14 15:45:34 +00:00
|
|
|
{
|
2025-11-25 10:12:33 +00:00
|
|
|
#if VULKAN_WSI_LAYER_EXPERIMENTAL
|
|
|
|
|
const VkPresentTimingInfoEXT *present_timing_info = nullptr;
|
|
|
|
|
const auto *present_timings_info =
|
|
|
|
|
util::find_extension<VkPresentTimingsInfoEXT>(VK_STRUCTURE_TYPE_PRESENT_TIMINGS_INFO_EXT, present_info->pNext);
|
|
|
|
|
if (present_timings_info != nullptr)
|
|
|
|
|
{
|
|
|
|
|
present_timing_info = present_timings_info->pTimingInfos;
|
|
|
|
|
assert(present_timing_info != nullptr);
|
|
|
|
|
auto *ext_present_timing = get_swapchain_extension<wsi::wsi_ext_present_timing>(true);
|
|
|
|
|
if (present_timing_info->presentStageQueries)
|
|
|
|
|
{
|
|
|
|
|
TRY(ext_present_timing->queue_has_space());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-08-22 16:45:28 +01:00
|
|
|
if (submit_info.switch_presentation_mode)
|
2024-05-09 09:32:42 +01:00
|
|
|
{
|
2024-11-28 06:36:17 +00:00
|
|
|
/* Assert when a presentation mode switch is requested and the swapchain_maintenance1 extension which implements this is not available */
|
|
|
|
|
auto *ext = get_swapchain_extension<wsi::wsi_ext_swapchain_maintenance1>(true);
|
|
|
|
|
if (ext)
|
|
|
|
|
{
|
2025-03-07 17:49:18 +00:00
|
|
|
TRY_LOG_CALL(ext->handle_switching_presentation_mode(submit_info.present_mode, m_present_mode));
|
2024-11-28 06:36:17 +00:00
|
|
|
}
|
2024-05-09 09:32:42 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
VkSemaphore present_semaphore = m_swapchain_images[submit_info.pending_present.image_index].get_present_semaphore();
|
|
|
|
|
const VkSemaphore *wait_semaphores = &present_semaphore;
|
2021-10-20 15:27:47 +01:00
|
|
|
uint32_t sem_count = 1;
|
2024-08-22 16:45:28 +01:00
|
|
|
if (!submit_info.use_image_present_semaphore)
|
2021-10-20 15:27:47 +01:00
|
|
|
{
|
|
|
|
|
wait_semaphores = present_info->pWaitSemaphores;
|
|
|
|
|
sem_count = present_info->waitSemaphoreCount;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-01 15:18:22 +00:00
|
|
|
void *submission_pnext = nullptr;
|
2025-05-28 15:59:16 +00:00
|
|
|
uint32_t count_signal_semaphores = 0;
|
2024-10-01 15:18:22 +00:00
|
|
|
std::optional<VkFrameBoundaryEXT> frame_boundary;
|
|
|
|
|
/* Do not handle the event if it was handled before reaching this point */
|
|
|
|
|
if (submit_info.handle_present_frame_boundary_event)
|
|
|
|
|
{
|
2024-11-28 06:36:17 +00:00
|
|
|
auto *ext = get_swapchain_extension<wsi::wsi_ext_frame_boundary>();
|
2025-11-20 13:55:45 +00:00
|
|
|
|
|
|
|
|
VkImage image = m_swapchain_images[submit_info.pending_present.image_index].get_image();
|
|
|
|
|
frame_boundary = handle_frame_boundary_event(present_info, &image, ext);
|
2024-11-28 06:36:17 +00:00
|
|
|
|
|
|
|
|
if (frame_boundary)
|
2024-10-01 15:18:22 +00:00
|
|
|
{
|
|
|
|
|
submission_pnext = &frame_boundary.value();
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-28 15:59:16 +00:00
|
|
|
if (submit_info.present_fence != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
signal_semaphores[count_signal_semaphores++] =
|
2025-11-20 13:55:45 +00:00
|
|
|
m_swapchain_images[submit_info.pending_present.image_index].get_present_fence_wait_semaphore();
|
2025-05-28 15:59:16 +00:00
|
|
|
}
|
|
|
|
|
#if VULKAN_WSI_LAYER_EXPERIMENTAL
|
2025-11-25 10:12:33 +00:00
|
|
|
if ((present_timing_info != nullptr) &&
|
|
|
|
|
(present_timing_info->presentStageQueries & VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT))
|
2025-05-28 15:59:16 +00:00
|
|
|
{
|
2025-11-25 10:12:33 +00:00
|
|
|
auto *ext_present_timing = get_swapchain_extension<wsi::wsi_ext_present_timing>(true);
|
2025-12-10 11:22:49 +00:00
|
|
|
if (ext_present_timing->is_present_stage_supported(VK_PRESENT_STAGE_QUEUE_OPERATIONS_END_BIT_EXT))
|
|
|
|
|
{
|
|
|
|
|
signal_semaphores[count_signal_semaphores++] =
|
|
|
|
|
ext_present_timing->get_image_present_semaphore(submit_info.pending_present.image_index);
|
|
|
|
|
}
|
2025-05-28 15:59:16 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
2024-04-09 16:14:27 +01:00
|
|
|
queue_submit_semaphores semaphores = {
|
|
|
|
|
wait_semaphores,
|
|
|
|
|
sem_count,
|
2025-05-28 15:59:16 +00:00
|
|
|
count_signal_semaphores > 0 ? signal_semaphores.data() : nullptr,
|
|
|
|
|
count_signal_semaphores,
|
2024-04-09 16:14:27 +01:00
|
|
|
};
|
2025-11-20 13:55:45 +00:00
|
|
|
|
|
|
|
|
TRY_LOG_CALL(m_swapchain_images[submit_info.pending_present.image_index].set_present_payload(queue, semaphores,
|
|
|
|
|
submission_pnext));
|
2024-04-09 16:04:28 +01:00
|
|
|
|
2024-08-22 16:45:28 +01:00
|
|
|
if (submit_info.present_fence != VK_NULL_HANDLE)
|
2024-04-09 16:14:27 +01:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
VkSemaphore present_fence_wait_sem =
|
|
|
|
|
m_swapchain_images[submit_info.pending_present.image_index].get_present_fence_wait_semaphore();
|
|
|
|
|
const queue_submit_semaphores wait_semaphores = { &present_fence_wait_sem, 1, nullptr, 0 };
|
2024-04-09 16:14:27 +01:00
|
|
|
/*
|
|
|
|
|
* Here we chain wait_semaphores with present_fence through present_fence_wait.
|
|
|
|
|
*/
|
2024-08-22 16:45:28 +01:00
|
|
|
TRY(sync_queue_submit(m_device_data, queue, submit_info.present_fence, wait_semaphores));
|
2024-04-09 16:14:27 +01:00
|
|
|
}
|
|
|
|
|
|
2025-05-28 15:59:16 +00:00
|
|
|
#if VULKAN_WSI_LAYER_EXPERIMENTAL
|
2025-06-06 09:56:17 +00:00
|
|
|
if (present_timing_info != nullptr)
|
2025-05-28 15:59:16 +00:00
|
|
|
{
|
|
|
|
|
auto *ext_present_timing = get_swapchain_extension<wsi::wsi_ext_present_timing>(true);
|
|
|
|
|
TRY_LOG_CALL(ext_present_timing->add_presentation_entry(
|
2025-07-30 12:27:09 +00:00
|
|
|
queue, submit_info.pending_present.present_id, submit_info.pending_present.image_index, *present_timing_info));
|
2025-05-28 15:59:16 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
2025-07-18 11:39:41 +00:00
|
|
|
TRY(notify_presentation_engine(submit_info.pending_present));
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
return VK_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::deprecate(VkSwapchainKHR descendant)
|
|
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
/* Remove any images that have not been acquried or presented */
|
|
|
|
|
m_swapchain_images.erase(std::remove_if(m_swapchain_images.begin(), m_swapchain_images.end(),
|
|
|
|
|
[](auto &image) { return image.get_status() == swapchain_image::FREE; }),
|
|
|
|
|
m_swapchain_images.end());
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
/* Set its descendant. */
|
|
|
|
|
m_descendant = descendant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::wait_for_pending_buffers()
|
|
|
|
|
{
|
2025-09-05 15:06:38 +00:00
|
|
|
util::unique_lock<util::mutex> acquire_lock(m_image_acquire_lock);
|
|
|
|
|
util::unique_lock<util::recursive_mutex> image_status_lock(m_image_status_mutex);
|
|
|
|
|
if (!acquire_lock || !image_status_lock)
|
|
|
|
|
{
|
|
|
|
|
WSI_LOG_ERROR("Failed to acquire mutex lock in wait_for_pending_buffers.\n");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
2019-05-24 15:57:50 +01:00
|
|
|
|
2025-06-19 12:51:51 +01:00
|
|
|
uint64_t non_pending_images = 0;
|
2022-11-11 14:27:03 +00:00
|
|
|
for (auto &img : m_swapchain_images)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2025-06-19 12:51:51 +01:00
|
|
|
/*
|
|
|
|
|
* We don't want wait_and_get_free_buffer to be called when
|
|
|
|
|
* there are free images, because it will consume the semaphore
|
|
|
|
|
* and then subsequent calls might block if the semaphore value
|
|
|
|
|
* has reached to zero.
|
|
|
|
|
*/
|
2025-11-20 13:55:45 +00:00
|
|
|
if ((img.get_status() == swapchain_image::ACQUIRED) || (img.get_status() == swapchain_image::FREE))
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
2025-06-19 12:51:51 +01:00
|
|
|
/* If the image is acquired or free, it is not pending. */
|
|
|
|
|
non_pending_images++;
|
2019-05-24 15:57:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 12:51:51 +01:00
|
|
|
/*
|
|
|
|
|
* One pending image may be presented and acquired by a
|
|
|
|
|
* compositor. The WSI backend may not necessarily know which
|
|
|
|
|
* pending image is presented to change its state. It may
|
|
|
|
|
* be impossible to wait for that one presented image.
|
|
|
|
|
*/
|
|
|
|
|
int wait = static_cast<int>(m_swapchain_images.size() - non_pending_images - 1);
|
2021-05-27 14:57:53 +01:00
|
|
|
image_status_lock.unlock();
|
2019-05-24 15:57:50 +01:00
|
|
|
|
|
|
|
|
while (wait > 0)
|
|
|
|
|
{
|
2025-06-19 12:51:51 +01:00
|
|
|
/*
|
|
|
|
|
* Take down one free image semaphore.
|
|
|
|
|
*
|
|
|
|
|
* In backends which unpresent an image only after a new one
|
|
|
|
|
* has been submitted for presentation wait_and_get_free_buffer
|
|
|
|
|
* could hang if the semaphore has a zero value and the swapchain has been deprecated.
|
|
|
|
|
*/
|
|
|
|
|
wait_and_get_free_buffer(UINT64_MAX);
|
2019-05-24 15:57:50 +01:00
|
|
|
--wait;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::clear_ancestor()
|
|
|
|
|
{
|
|
|
|
|
m_ancestor = VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void swapchain_base::clear_descendant()
|
|
|
|
|
{
|
|
|
|
|
m_descendant = VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 12:51:51 +01:00
|
|
|
VkResult swapchain_base::wait_and_get_free_buffer(uint64_t timeout)
|
2019-05-24 15:57:50 +01:00
|
|
|
{
|
|
|
|
|
/* first see if a buffer is already marked as free */
|
2025-06-19 12:51:51 +01:00
|
|
|
VkResult retval = m_free_image_semaphore.wait(0);
|
2019-05-24 15:57:50 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-23 18:19:27 +00:00
|
|
|
void swapchain_base::release_images(uint32_t image_count, const uint32_t *indices)
|
|
|
|
|
{
|
|
|
|
|
for (uint32_t i = 0; i < image_count; i++)
|
|
|
|
|
{
|
|
|
|
|
uint32_t index = indices[i];
|
|
|
|
|
assert(index < m_swapchain_images.size());
|
|
|
|
|
/* Applications can only pass acquired images that the device doesn't own */
|
2025-11-20 13:55:45 +00:00
|
|
|
assert(m_swapchain_images[index].get_status() == swapchain_image::ACQUIRED);
|
2024-01-23 18:19:27 +00:00
|
|
|
unpresent_image(index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-15 21:09:09 +01:00
|
|
|
VkResult swapchain_base::is_bind_allowed(uint32_t image_index) const
|
|
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
return m_swapchain_images[image_index].get_status() != swapchain_image::UNALLOCATED ? VK_SUCCESS :
|
|
|
|
|
VK_ERROR_OUT_OF_HOST_MEMORY;
|
2024-04-15 21:09:09 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
bool swapchain_base::add_swapchain_extension(util::unique_ptr<util::wsi_ext> extension)
|
2024-05-09 09:32:42 +01:00
|
|
|
{
|
2024-11-28 06:36:17 +00:00
|
|
|
return m_extensions.add_extension(std::move(extension));
|
2024-08-22 16:45:28 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
VkResult swapchain_base::bind_swapchain_image(const VkBindImageMemoryInfo *bind_image_mem_info,
|
|
|
|
|
const VkBindImageMemorySwapchainInfoKHR *bind_sc_info)
|
2025-03-26 13:18:02 +00:00
|
|
|
{
|
2025-11-20 13:55:45 +00:00
|
|
|
TRY_LOG(is_bind_allowed(bind_sc_info->imageIndex),
|
|
|
|
|
"Bind is not allowed on images that haven't been acquired first.");
|
2025-03-26 13:18:02 +00:00
|
|
|
|
2025-11-20 13:55:45 +00:00
|
|
|
wsi::swapchain_image &swapchain_image = m_swapchain_images[bind_sc_info->imageIndex];
|
|
|
|
|
return swapchain_image.bind(bind_image_mem_info);
|
2025-03-26 13:18:02 +00:00
|
|
|
}
|
|
|
|
|
|
2019-05-24 15:57:50 +01:00
|
|
|
} /* namespace wsi */
|