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 <iason.paraskevopoulos@arm.com>
Change-Id: I4fd2b937351fc484897dc7e9597aa6e81b37fbdf
This commit is contained in:
Iason Paraskevopoulos 2021-10-14 15:45:34 +00:00 committed by Matteo Franchin
parent e00a2d8e40
commit 65c5f1e4f1
5 changed files with 190 additions and 112 deletions

View file

@ -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;
};

View file

@ -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<swapchain_base *>(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<swapchain_base *>(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<swapchain_base *>(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<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;
}
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<swapchain_base *>(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<swapchain_base *>(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<std::recursive_mutex> 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;
}

View file

@ -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 */

View file

@ -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;
}

View file

@ -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.