From 65c5f1e4f17be230028e81a26674ea8ecf01f9dd Mon Sep 17 00:00:00 2001 From: Iason Paraskevopoulos Date: Thu, 14 Oct 2021 15:45:34 +0000 Subject: [PATCH] Implement MAILBOX presentation for Wayland swapchains Adds support for image presentation without relying on the page flipping thread to communicate with the WSI backend. Signed-off-by: Iason Paraskevopoulos Change-Id: I4fd2b937351fc484897dc7e9597aa6e81b37fbdf --- wsi/headless/swapchain.hpp | 3 +- wsi/swapchain_base.cpp | 238 ++++++++++++++++++++----------------- wsi/swapchain_base.hpp | 45 ++++++- wsi/wayland/swapchain.cpp | 13 +- wsi/wayland/swapchain.hpp | 3 +- 5 files changed, 190 insertions(+), 112 deletions(-) diff --git a/wsi/headless/swapchain.hpp b/wsi/headless/swapchain.hpp index cadfb86..23bcd09 100644 --- a/wsi/headless/swapchain.hpp +++ b/wsi/headless/swapchain.hpp @@ -56,7 +56,8 @@ protected: /** * @brief Platform specific init */ - VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo) + VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info, + bool &use_presentation_thread) override { return VK_SUCCESS; }; diff --git a/wsi/swapchain_base.cpp b/wsi/swapchain_base.cpp index 7f93c14..fdc0c29 100644 --- a/wsi/swapchain_base.cpp +++ b/wsi/swapchain_base.cpp @@ -85,28 +85,77 @@ void swapchain_base::page_flip_thread() continue; } - /* 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. */ - if (m_first_present) + call_present(*pending_index); + } +} + +void swapchain_base::call_present(uint32_t image_index) +{ + /* 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) { - if (m_ancestor != VK_NULL_HANDLE) - { - auto *ancestor = reinterpret_cast(m_ancestor); - ancestor->wait_for_pending_buffers(); - } - - sem_post(&m_start_present_semaphore); - - present_image(*pending_index); - - m_first_present = false; + auto *ancestor = reinterpret_cast(m_ancestor); + ancestor->wait_for_pending_buffers(); } - /* The swapchain has already started presenting. */ - else + + sem_post(&m_start_present_semaphore); + + present_image(image_index); + + m_first_present = false; + } + /* The swapchain has already started presenting. */ + else + { + present_image(image_index); + } +} + +bool swapchain_base::has_descendant_started_presenting() +{ + if (m_descendant == VK_NULL_HANDLE) + { + return false; + } + + auto *desc = reinterpret_cast(m_descendant); + for (auto &image : desc->m_swapchain_images) + { + if (image.status == swapchain_image::PRESENTED || image.status == swapchain_image::PENDING) { - present_image(*pending_index); + return true; } } + + return false; +} + +VkResult swapchain_base::init_page_flip_thread() +{ + /* Setup semaphore for signaling pageflip thread */ + VkResult result = m_page_flip_semaphore.init(0); + if (result != VK_SUCCESS) + { + return result; + } + + 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; + } + + return VK_SUCCESS; } void swapchain_base::unpresent_image(uint32_t presented_index) @@ -121,7 +170,7 @@ void swapchain_base::unpresent_image(uint32_t presented_index) swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *callbacks) : m_device_data(dev_data) - , m_page_flip_thread_run(true) + , m_page_flip_thread_run(false) , m_thread_sem_defined(false) , m_first_present(true) , m_pending_buffer_pool{} @@ -143,28 +192,9 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s 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 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; - } - m_present_mode = swapchain_create_info->presentMode; /* Init image to invalid values. */ @@ -172,12 +202,22 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s return VK_ERROR_OUT_OF_HOST_MEMORY; /* We have allocated images, we can call the platform init function if something needs to be done. */ - result = init_platform(device, swapchain_create_info); + bool use_presentation_thread = true; + VkResult result = init_platform(device, swapchain_create_info, use_presentation_thread); if (result != VK_SUCCESS) { return result; } + if (use_presentation_thread) + { + result = init_page_flip_thread(); + if (result != VK_SUCCESS) + { + return result; + } + } + VkImageCreateInfo image_create_info = {}; image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_create_info.pNext = nullptr; @@ -218,14 +258,7 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s return result; } - /* Setup semaphore for signaling pageflip thread */ - result = m_page_flip_semaphore.init(0); - if (result != VK_SUCCESS) - { - return result; - } - - res = sem_init(&m_start_present_semaphore, 0, 0); + int res = sem_init(&m_start_present_semaphore, 0, 0); /* Only programming error can cause this to fail. */ assert(res == 0); if (res != 0) @@ -233,11 +266,6 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s return VK_ERROR_OUT_OF_HOST_MEMORY; } - m_thread_sem_defined = true; - - /* Launch page flipping thread */ - m_page_flip_thread = std::thread(&swapchain_base::page_flip_thread, this); - /* 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. @@ -266,31 +294,17 @@ void swapchain_base::teardown() * immediately. For images in the PENDING state, we will block until the * presentation engine is finished with them. */ - int res; - bool descendent_started_presenting = false; - - if (m_descendant != VK_NULL_HANDLE) + if (has_descendant_started_presenting()) { + /* 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. */ auto *desc = reinterpret_cast(m_descendant); - for (auto& img : desc->m_swapchain_images) - { - if (img.status == swapchain_image::PRESENTED || - img.status == swapchain_image::PENDING) - { - /* 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; - } - } + sem_wait(&desc->m_start_present_semaphore); } - - /* If descendant started presenting, there is no pending buffer in the swapchain. */ - if (m_is_valid && descendent_started_presenting == false) + else if (m_is_valid) { + /* If descendant hasn't started presenting, there are pending buffers in the swapchain. */ wait_for_pending_buffers(); } @@ -314,12 +328,12 @@ void swapchain_base::teardown() { WSI_LOG_ERROR("m_page_flip_thread is not joinable"); } + } - res = sem_destroy(&m_start_present_semaphore); - if (res != 0) - { - WSI_LOG_ERROR("sem_destroy failed for start_present_semaphore with %d", errno); - } + int res = sem_destroy(&m_start_present_semaphore); + if (res != 0) + { + WSI_LOG_ERROR("sem_destroy failed for start_present_semaphore with %d", errno); } if (m_descendant != VK_NULL_HANDLE) @@ -432,35 +446,15 @@ VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, V } } -VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, uint32_t image_index) +VkResult swapchain_base::notify_presentation_engine(uint32_t image_index) { - VkResult result; - bool descendent_started_presenting = false; - - if (m_descendant != VK_NULL_HANDLE) - { - auto *desc = reinterpret_cast(m_descendant); - for (auto& img : desc->m_swapchain_images) - { - if (img.status == swapchain_image::PRESENTED || - img.status == swapchain_image::PENDING) - { - descendent_started_presenting = true; - break; - } - } - } - - result = image_set_present_payload(m_swapchain_images[image_index], queue, present_info->pWaitSemaphores, - present_info->waitSemaphoreCount); - if (result != VK_SUCCESS) - { - return result; - } - const std::lock_guard lock(m_image_status_mutex); - /* If the descendant has started presenting, we should release the image. */ - if (descendent_started_presenting) + + /* 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) { m_swapchain_images[image_index].status = swapchain_image::FREE; m_free_image_semaphore.post(); @@ -468,10 +462,38 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr } m_swapchain_images[image_index].status = swapchain_image::PENDING; - bool buffer_pool_res = m_pending_buffer_pool.push_back(image_index); - (void)buffer_pool_res; - assert(buffer_pool_res); - m_page_flip_semaphore.post(); + + if (m_page_flip_thread_run) + { + bool buffer_pool_res = m_pending_buffer_pool.push_back(image_index); + (void)buffer_pool_res; + assert(buffer_pool_res); + m_page_flip_semaphore.post(); + } + else + { + call_present(image_index); + } + + return VK_SUCCESS; +} + +VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index) +{ + + VkResult result = image_set_present_payload(m_swapchain_images[image_index], queue, present_info->pWaitSemaphores, + present_info->waitSemaphoreCount); + if (result != VK_SUCCESS) + { + return result; + } + + result = notify_presentation_engine(image_index); + if (result != VK_SUCCESS) + { + return result; + } + return VK_SUCCESS; } diff --git a/wsi/swapchain_base.hpp b/wsi/swapchain_base.hpp index 7ffc92f..ae4668b 100644 --- a/wsi/swapchain_base.hpp +++ b/wsi/swapchain_base.hpp @@ -315,8 +315,16 @@ protected: /** * @brief Platform specific initialization + * + * @param device VkDevice object. + * @param swapchain_create_info Pointer to the swapchain create info struct. + * @param[out] use_presentation_thread Flag indicating if image presentation + * must happen in a separate thread. + * + * @return VK_SUCCESS on success or an error code otherwise. */ - virtual VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) = 0; + virtual VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info, + bool &use_presentation_thread) = 0; /** * @brief Base swapchain teardown. @@ -449,6 +457,41 @@ private: * semaphore of the swapchain will be posted. **/ void page_flip_thread(); + + /** + * @brief Call the swapchain implementation specific present_image function. + * + * In addition to calling the present_image function it also handles the + * communication with the ancestor before the first presentation. + * + * @param image_index Index of the image to be presented. + */ + void call_present(uint32_t image_index); + + /** + * @brief Return true if the descendant has an image with PENDING or PRESENTED status. + */ + bool has_descendant_started_presenting(); + + /** + * @brief Initialize the page flipping thread. + * + * @return VK_SUCCESS if the initialization was successful or an error code otherwise. + */ + VkResult init_page_flip_thread(); + + /** + * @brief Notify the presentation engine with the next image to be presented. + * + * Appends the next image to the ring buffer and notifies the page flipping + * thread if it is enabled or directly calls the WSI backend implementation to + * present the image. + * + * @param image_index Index of the image to be presented. + * + * @return VK_SUCCESS on success or an error code otherwise. + */ + VkResult notify_presentation_engine(uint32_t image_index); }; } /* namespace wsi */ diff --git a/wsi/wayland/swapchain.cpp b/wsi/wayland/swapchain.cpp index 59b73e6..6bf6185 100644 --- a/wsi/wayland/swapchain.cpp +++ b/wsi/wayland/swapchain.cpp @@ -102,7 +102,8 @@ swapchain::~swapchain() } } -VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo) +VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info, + bool &use_presentation_thread) { if ((m_display == nullptr) || (m_surface == nullptr) || (m_wsi_surface->get_dmabuf_interface() == nullptr)) { @@ -130,6 +131,16 @@ VkResult swapchain::init_platform(VkDevice device, const VkSwapchainCreateInfoKH return VK_ERROR_INITIALIZATION_FAILED; } + /* + * When VK_PRESENT_MODE_MAILBOX_KHR has been chosen by the application we don't + * initialize the page flip thread so the present_image function can be called + * during vkQueuePresent. + */ + if (m_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + { + use_presentation_thread = false; + } + return VK_SUCCESS; } diff --git a/wsi/wayland/swapchain.hpp b/wsi/wayland/swapchain.hpp index 4260866..d9dc3b1 100644 --- a/wsi/wayland/swapchain.hpp +++ b/wsi/wayland/swapchain.hpp @@ -78,7 +78,8 @@ protected: /** * @brief Initialize platform specifics. */ - VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo) override; + VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info, + bool &use_presentation_thread) override; /** * @brief Creates a VkImage handle.