vulkan/wsi: Add mechanism to wait for WSI semaphore unsignal.
authorHans-Kristian Arntzen <post@arntzen-software.no>
Mon, 24 Oct 2022 12:47:51 +0000 (14:47 +0200)
committerMarge Bot <emma+marge@anholt.net>
Wed, 23 Nov 2022 19:06:12 +0000 (19:06 +0000)
When vkWaitForPresentKHR succeeds, we are guaranteed
that any dependent semaphores have been unsignalled.

In an explicit sync world, we are guaranteed this automatically by
having a present complete, since that event must follow a semaphore wait
completion.

However, if the swapchain image is implicitly
synchronized, the semaphore might technically not have been unsignaled
before the present complete event triggers.

Present IDs must be signalled in monotonic order, same as timeline
semaphores.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
Reviewed-by: Joshua Ashton <joshua@froggi.es>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19279>

src/vulkan/wsi/wsi_common.c
src/vulkan/wsi/wsi_common.h
src/vulkan/wsi/wsi_common_private.h

index ae89f55..34512c9 100644 (file)
@@ -129,6 +129,12 @@ wsi_device_init(struct wsi_device *wsi,
       &vk_physical_device_from_handle(pdevice)->supported_extensions;
    wsi->has_import_memory_host =
       supported_extensions->EXT_external_memory_host;
+   wsi->khr_present_wait =
+      supported_extensions->KHR_present_id &&
+      supported_extensions->KHR_present_wait;
+
+   /* We cannot expose KHR_present_wait without timeline semaphores. */
+   assert(!wsi->khr_present_wait || supported_extensions->KHR_timeline_semaphore);
 
    list_inithead(&wsi->hotplug_fences);
 
@@ -169,6 +175,8 @@ wsi_device_init(struct wsi_device *wsi,
    WSI_GET_CB(WaitForFences);
    WSI_GET_CB(MapMemory);
    WSI_GET_CB(UnmapMemory);
+   if (wsi->khr_present_wait)
+      WSI_GET_CB(WaitSemaphoresKHR);
 #undef WSI_GET_CB
 
 #ifdef VK_USE_PLATFORM_XCB_KHR
@@ -487,6 +495,8 @@ wsi_swapchain_finish(struct wsi_swapchain *chain)
    }
    chain->wsi->DestroySemaphore(chain->device, chain->dma_buf_semaphore,
                                 &chain->alloc);
+   chain->wsi->DestroySemaphore(chain->device, chain->present_id_timeline,
+                                &chain->alloc);
 
    int cmd_pools_count = chain->buffer_blit_queue != VK_NULL_HANDLE ?
       1 : chain->wsi->queue_family_count;
@@ -879,12 +889,33 @@ wsi_CreateSwapchainKHR(VkDevice _device,
       return VK_ERROR_OUT_OF_HOST_MEMORY;
    }
 
+   if (wsi_device->khr_present_wait) {
+      const VkSemaphoreTypeCreateInfo type_info = {
+         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
+         .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
+      };
+
+      const VkSemaphoreCreateInfo sem_info = {
+         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+         .pNext = &type_info,
+         .flags = 0,
+      };
+
+      /* We assume here that a driver exposing present_wait also exposes VK_KHR_timeline_semaphore. */
+      result = wsi_device->CreateSemaphore(_device, &sem_info, alloc, &swapchain->present_id_timeline);
+      if (result != VK_SUCCESS) {
+         swapchain->destroy(swapchain, alloc);
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+   }
+
    if (swapchain->buffer_blit_queue != VK_NULL_HANDLE) {
       swapchain->buffer_blit_semaphores = vk_zalloc(alloc,
                                          sizeof (*swapchain->buffer_blit_semaphores) * swapchain->image_count,
                                          sizeof (*swapchain->buffer_blit_semaphores),
                                          VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
       if (!swapchain->buffer_blit_semaphores) {
+         wsi_device->DestroySemaphore(_device, swapchain->present_id_timeline, alloc);
          swapchain->destroy(swapchain, alloc);
          return VK_ERROR_OUT_OF_HOST_MEMORY;
       }
@@ -1091,6 +1122,27 @@ wsi_AcquireNextImage2KHR(VkDevice _device,
                                          _device, pAcquireInfo, pImageIndex);
 }
 
+static VkResult wsi_signal_present_id_timeline(struct wsi_swapchain *swapchain,
+                                               VkQueue queue, uint64_t present_id)
+{
+   assert(swapchain->present_id_timeline);
+
+   const VkTimelineSemaphoreSubmitInfo timeline_info = {
+      .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
+      .pSignalSemaphoreValues = &present_id,
+      .signalSemaphoreValueCount = 1,
+   };
+
+   const VkSubmitInfo submit_info = {
+      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+      .pNext = &timeline_info,
+      .signalSemaphoreCount = 1,
+      .pSignalSemaphores = &swapchain->present_id_timeline,
+   };
+
+   return swapchain->wsi->QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
+}
+
 VkResult
 wsi_common_queue_present(const struct wsi_device *wsi,
                          VkDevice device,
@@ -1260,6 +1312,12 @@ wsi_common_queue_present(const struct wsi_device *wsi,
       if (present_ids && present_ids->pPresentIds)
          present_id = present_ids->pPresentIds[i];
 
+      if (present_id) {
+         result = wsi_signal_present_id_timeline(swapchain, queue, present_id);
+         if (result != VK_SUCCESS)
+            goto fail_present;
+      }
+
       result = swapchain->queue_present(swapchain, image_index, present_id, region);
       if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
          goto fail_present;
@@ -1386,6 +1444,21 @@ wsi_common_bind_swapchain_image(const struct wsi_device *wsi,
    return wsi->BindImageMemory(chain->device, vk_image, image->memory, 0);
 }
 
+VkResult
+wsi_swapchain_wait_for_present_semaphore(const struct wsi_swapchain *chain,
+                                         uint64_t present_id, uint64_t timeout)
+{
+   assert(chain->present_id_timeline);
+   const VkSemaphoreWaitInfo wait_info = {
+      .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
+      .semaphoreCount = 1,
+      .pSemaphores = &chain->present_id_timeline,
+      .pValues = &present_id,
+   };
+
+   return chain->wsi->WaitSemaphoresKHR(chain->device, &wait_info, timeout);
+}
+
 uint32_t
 wsi_select_memory_type(const struct wsi_device *wsi,
                        VkMemoryPropertyFlags req_props,
index 886bac9..19b34ed 100644 (file)
@@ -166,6 +166,12 @@ struct wsi_device {
     */
    bool signal_fence_with_memory;
 
+   /* Whether present_wait functionality is enabled on the device.
+    * In this case, we have to create an extra timeline semaphore
+    * to be able to synchronize with the WSI present semaphore being unsignalled.
+    * This requires VK_KHR_timeline_semaphore. */
+   bool khr_present_wait;
+
    /*
     * This sets the ownership for a WSI memory object:
     *
@@ -229,6 +235,7 @@ struct wsi_device {
    WSI_CB(WaitForFences);
    WSI_CB(MapMemory);
    WSI_CB(UnmapMemory);
+   WSI_CB(WaitSemaphoresKHR);
 #undef WSI_CB
 
     struct wsi_interface *                  wsi[VK_ICD_WSI_PLATFORM_MAX];
index 1b1ed13..92efcdd 100644 (file)
@@ -134,6 +134,7 @@ struct wsi_swapchain {
    VkFence* fences;
    VkSemaphore* buffer_blit_semaphores;
    VkPresentModeKHR present_mode;
+   VkSemaphore present_id_timeline;
 
    int signal_dma_buf_from_semaphore;
    VkSemaphore dma_buf_semaphore;
@@ -256,6 +257,10 @@ void
 wsi_destroy_image(const struct wsi_swapchain *chain,
                   struct wsi_image *image);
 
+VkResult
+wsi_swapchain_wait_for_present_semaphore(const struct wsi_swapchain *chain,
+                                         uint64_t present_id, uint64_t timeout);
+
 #ifdef HAVE_LIBDRM
 VkResult
 wsi_prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain,