Handle present request for multiple swapchains
authorIason Paraskevopoulos <iason.paraskevopoulos@arm.com>
Wed, 20 Oct 2021 14:27:47 +0000 (15:27 +0100)
committerIason Paraskevopoulos <iason.paraskevopoulos@arm.com>
Mon, 1 Nov 2021 17:28:43 +0000 (17:28 +0000)
Adds an additional semaphore for every swapchain image to be waited for
before the present engine access an image. During a present request with
multiple swapchains, these semaphores are grouped together and are
signaled after the incoming pWaitSemaphores signal.

Signed-off-by: Iason Paraskevopoulos <iason.paraskevopoulos@arm.com>
Change-Id: I94cabbf5f7d9a45dceef60eec7773b48ccc32078

layer/private_data.hpp
layer/swapchain_api.cpp
wsi/swapchain_base.cpp
wsi/swapchain_base.hpp

index 7514f06..6092500 100644 (file)
@@ -120,6 +120,8 @@ struct instance_dispatch_table
    REQUIRED(FreeMemory)                             \
    REQUIRED(CreateFence)                            \
    REQUIRED(DestroyFence)                           \
+   REQUIRED(CreateSemaphore)                        \
+   REQUIRED(DestroySemaphore)                       \
    REQUIRED(ResetFences)                            \
    REQUIRED(WaitForFences)                          \
    REQUIRED(DestroyDevice)                          \
index 8ee59a8..b043d2b 100644 (file)
@@ -127,6 +127,54 @@ VKAPI_ATTR VkResult wsi_layer_vkAcquireNextImageKHR(VkDevice device, VkSwapchain
    return sc->acquire_next_image(timeout, semaphore, fence, pImageIndex);
 }
 
+static VkResult submit_wait_request(VkQueue queue, const VkPresentInfoKHR &present_info,
+                                    layer::device_private_data &device_data)
+{
+   util::vector<VkSemaphore> swapchain_semaphores{ util::allocator(device_data.get_allocator(),
+                                                                   VK_SYSTEM_ALLOCATION_SCOPE_COMMAND) };
+   if (!swapchain_semaphores.try_resize(present_info.swapchainCount))
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   for (uint32_t i = 0; i < present_info.swapchainCount; ++i)
+   {
+      auto swapchain = reinterpret_cast<wsi::swapchain_base *>(present_info.pSwapchains[i]);
+      swapchain_semaphores[i] = swapchain->get_image_present_semaphore(present_info.pImageIndices[i]);
+   }
+
+   util::vector<VkPipelineStageFlags> pipeline_stage_flags{ util::allocator::get_generic() };
+   if (!pipeline_stage_flags.try_resize(present_info.waitSemaphoreCount))
+   {
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
+
+   for (uint32_t i = 0; i < present_info.waitSemaphoreCount; ++i)
+   {
+      pipeline_stage_flags[i] = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+   }
+
+   VkSubmitInfo submit_info = {
+      VK_STRUCTURE_TYPE_SUBMIT_INFO,
+      NULL,
+      present_info.waitSemaphoreCount,
+      present_info.pWaitSemaphores,
+      pipeline_stage_flags.data(),
+      0,
+      NULL,
+      static_cast<uint32_t>(swapchain_semaphores.size()),
+      swapchain_semaphores.data(),
+   };
+
+   VkResult result = device_data.disp.QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
+   if (result != VK_SUCCESS)
+   {
+      return result;
+   }
+
+   return VK_SUCCESS;
+}
+
 VKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo)
 {
    assert(queue != VK_NULL_HANDLE);
@@ -139,6 +187,20 @@ VKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue, const VkPresentIn
       return device_data.disp.QueuePresentKHR(queue, pPresentInfo);
    }
 
+   /* Avoid allocating on the heap when there is only one swapchain. */
+   VkResult res = VK_SUCCESS;
+   const VkPresentInfoKHR *present_info = pPresentInfo;
+   if (pPresentInfo->swapchainCount > 1)
+   {
+      res = submit_wait_request(queue, *pPresentInfo, device_data);
+      if (res != VK_SUCCESS)
+      {
+         return res;
+      }
+
+      present_info = nullptr;
+   }
+
    VkResult ret = VK_SUCCESS;
    for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i)
    {
@@ -147,7 +209,7 @@ VKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue, const VkPresentIn
       wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);
       assert(sc != nullptr);
 
-      VkResult res = sc->queue_present(queue, pPresentInfo, pPresentInfo->pImageIndices[i]);
+      res = sc->queue_present(queue, present_info, pPresentInfo->pImageIndices[i]);
 
       if (pPresentInfo->pResults != nullptr)
       {
index fdc0c29..e139820 100644 (file)
@@ -199,7 +199,9 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s
 
    /* Init image to invalid values. */
    if (!m_swapchain_images.try_resize(swapchain_create_info->minImageCount))
+   {
       return VK_ERROR_OUT_OF_HOST_MEMORY;
+   }
 
    /* We have allocated images, we can call the platform init function if something needs to be done. */
    bool use_presentation_thread = true;
@@ -249,6 +251,15 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s
       {
          return result;
       }
+
+      VkSemaphoreCreateInfo semaphore_info = {};
+      semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+      result = m_device_data.disp.CreateSemaphore(m_device, &semaphore_info, get_allocation_callbacks(),
+                                                  &img.present_semaphore);
+      if (result != VK_SUCCESS)
+      {
+         return result;
+      }
    }
 
    m_device_data.disp.GetDeviceQueue(m_device, 0, 0, &m_queue);
@@ -352,6 +363,8 @@ void swapchain_base::teardown()
    {
       /* Call implementation specific release */
       destroy_image(img);
+
+      m_device_data.disp.DestroySemaphore(m_device, img.present_semaphore, get_allocation_callbacks());
    }
 }
 
@@ -481,8 +494,15 @@ VkResult swapchain_base::notify_presentation_engine(uint32_t image_index)
 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);
+   const VkSemaphore *wait_semaphores = &m_swapchain_images[image_index].present_semaphore;
+   uint32_t sem_count = 1;
+   if (present_info != nullptr)
+   {
+      wait_semaphores = present_info->pWaitSemaphores;
+      sem_count = present_info->waitSemaphoreCount;
+   }
+
+   VkResult result = image_set_present_payload(m_swapchain_images[image_index], queue, wait_semaphores, sem_count);
    if (result != VK_SUCCESS)
    {
       return result;
index ae4668b..382ee11 100644 (file)
@@ -60,6 +60,7 @@ struct swapchain_image
 
    VkImage image{VK_NULL_HANDLE};
    status status{swapchain_image::INVALID};
+   VkSemaphore present_semaphore{ VK_NULL_HANDLE };
 };
 
 /**
@@ -124,7 +125,10 @@ public:
     *
     * @param queue The queue to which the submission will be made to.
     *
-    * @param pPresentInfo Information about the swapchain and image to be presented.
+    * @param present_info Information about the swapchain and image to be presented.
+    * If it is nullptr it means that the presentation request will wait on the
+    * image's \p present_semaphore and not the semaphores that come with
+    * \p present_info.
     *
     * @param imageIndex The index of the image to be presented.
     *
@@ -169,6 +173,18 @@ public:
    virtual VkResult bind_swapchain_image(VkDevice &device, const VkBindImageMemoryInfo *bind_image_mem_info,
                                          const VkBindImageMemorySwapchainInfoKHR *bind_sc_info) = 0;
 
+   /**
+    * @brief Get image's present semaphore
+    *
+    * @param image_index Image's index
+    *
+    * @return the image's present_semaphore
+    */
+   VkSemaphore get_image_present_semaphore(uint32_t image_index)
+   {
+      return m_swapchain_images[image_index].present_semaphore;
+   }
+
 protected:
 
    layer::device_private_data &m_device_data;