Mesa (main): vulkan/wsi: Signal semaphores and fences from the dma-buf

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Fri Jun 10 02:14:02 UTC 2022


Module: Mesa
Branch: main
Commit: 30b57f10b36d9824a3de8285c667cdd240e61465
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=30b57f10b36d9824a3de8285c667cdd240e61465

Author: Jason Ekstrand <jason at jlekstrand.net>
Date:   Mon Mar 15 12:06:53 2021 -0500

vulkan/wsi: Signal semaphores and fences from the dma-buf

Instead of attempting to signal based on the memory object, use the new
DMA_BUF_IOCTL_EXPORT_SYNC_FILE to get a sync_file for the dma-buf and
use that to signal the semaphore or fence.  Because this happens before
we transfer ownership back to the driver, the resulting sync_file should
only contain dma_fences from the compositor and/or display and shouldn't
be mixed up with the driver in any way.  This gives us a real semaphore
and fence (as opposed to the dummy objects we've used int the past)
without over-synchronization.

Reviewed-by: Bas Nieuwenhuizen <bas at basnieuwenhuizen.nl>
Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin at intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4037>

---

 src/vulkan/wsi/wsi_common.c         |  81 +++++++++++++-
 src/vulkan/wsi/wsi_common.h         |   3 +
 src/vulkan/wsi/wsi_common_drm.c     | 205 ++++++++++++++++++++++++++++++++++++
 src/vulkan/wsi/wsi_common_private.h |  15 +++
 4 files changed, 299 insertions(+), 5 deletions(-)

diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c
index b90c8e420e2..7094171db7d 100644
--- a/src/vulkan/wsi/wsi_common.c
+++ b/src/vulkan/wsi/wsi_common.c
@@ -64,6 +64,7 @@ wsi_device_init(struct wsi_device *wsi,
    wsi->sw = sw_device;
 #define WSI_GET_CB(func) \
    PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func)
+   WSI_GET_CB(GetPhysicalDeviceExternalSemaphoreProperties);
    WSI_GET_CB(GetPhysicalDeviceProperties2);
    WSI_GET_CB(GetPhysicalDeviceMemoryProperties);
    WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties);
@@ -83,6 +84,23 @@ wsi_device_init(struct wsi_device *wsi,
    GetPhysicalDeviceMemoryProperties(pdevice, &wsi->memory_props);
    GetPhysicalDeviceQueueFamilyProperties(pdevice, &wsi->queue_family_count, NULL);
 
+   for (VkExternalSemaphoreHandleTypeFlags handle_type = 1;
+        handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+        handle_type <<= 1) {
+      const VkPhysicalDeviceExternalSemaphoreInfo esi = {
+         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
+         .handleType = handle_type,
+      };
+      VkExternalSemaphoreProperties esp = {
+         .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
+      };
+      GetPhysicalDeviceExternalSemaphoreProperties(pdevice, &esi, &esp);
+
+      if (esp.externalSemaphoreFeatures &
+          VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT)
+         wsi->semaphore_export_handle_types |= handle_type;
+   }
+
    list_inithead(&wsi->hotplug_fences);
 
 #define WSI_GET_CB(func) \
@@ -116,6 +134,7 @@ wsi_device_init(struct wsi_device *wsi,
    WSI_GET_CB(GetPhysicalDeviceFormatProperties);
    WSI_GET_CB(GetPhysicalDeviceFormatProperties2KHR);
    WSI_GET_CB(GetPhysicalDeviceImageFormatProperties2);
+   WSI_GET_CB(GetSemaphoreFdKHR);
    WSI_GET_CB(ResetFences);
    WSI_GET_CB(QueueSubmit);
    WSI_GET_CB(WaitForFences);
@@ -349,6 +368,8 @@ wsi_swapchain_finish(struct wsi_swapchain *chain)
 
       vk_free(&chain->alloc, chain->buffer_blit_semaphores);
    }
+   chain->wsi->DestroySemaphore(chain->device, chain->dma_buf_semaphore,
+                                &chain->alloc);
 
    int cmd_pools_count = chain->buffer_blit_queue != VK_NULL_HANDLE ?
       1 : chain->wsi->queue_family_count;
@@ -843,6 +864,14 @@ wsi_signal_semaphore_for_image(struct vk_device *device,
 
    vk_semaphore_reset_temporary(device, semaphore);
 
+#ifndef _WIN32
+   VkResult result = wsi_create_sync_for_dma_buf_wait(chain, image,
+                                                      VK_SYNC_FEATURE_GPU_WAIT,
+                                                      &semaphore->temporary);
+   if (result != VK_ERROR_FEATURE_NOT_PRESENT)
+      return result;
+#endif
+
    if (chain->wsi->signal_semaphore_with_memory) {
       return device->create_sync_for_memory(device, image->memory,
                                             false /* signal_memory */,
@@ -867,6 +896,14 @@ wsi_signal_fence_for_image(struct vk_device *device,
 
    vk_fence_reset_temporary(device, fence);
 
+#ifndef _WIN32
+   VkResult result = wsi_create_sync_for_dma_buf_wait(chain, image,
+                                                      VK_SYNC_FEATURE_CPU_WAIT,
+                                                      &fence->temporary);
+   if (result != VK_ERROR_FEATURE_NOT_PRESENT)
+      return result;
+#endif
+
    if (chain->wsi->signal_fence_with_memory) {
       return device->create_sync_for_memory(device, image->memory,
                                             false /* signal_memory */,
@@ -1036,16 +1073,50 @@ wsi_common_queue_present(const struct wsi_device *wsi,
 
       VkFence fence = swapchain->fences[image_index];
 
-      struct wsi_memory_signal_submit_info mem_signal = {
-         .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA,
-         .memory = image->memory,
-      };
-      __vk_append_struct(&submit_info, &mem_signal);
+      bool has_signal_dma_buf = false;
+#ifndef _WIN32
+      result = wsi_prepare_signal_dma_buf_from_semaphore(swapchain, image);
+      if (result == VK_SUCCESS) {
+         assert(submit_info.signalSemaphoreCount == 0);
+         submit_info.signalSemaphoreCount = 1;
+         submit_info.pSignalSemaphores = &swapchain->dma_buf_semaphore;
+         has_signal_dma_buf = true;
+      } else if (result == VK_ERROR_FEATURE_NOT_PRESENT) {
+         result = VK_SUCCESS;
+         has_signal_dma_buf = false;
+      } else {
+         goto fail_present;
+      }
+#endif
+
+      struct wsi_memory_signal_submit_info mem_signal;
+      if (!has_signal_dma_buf) {
+         /* If we don't have dma-buf signaling, signal the memory object by
+          * chaining wsi_memory_signal_submit_info into VkSubmitInfo.
+          */
+         result = VK_SUCCESS;
+         has_signal_dma_buf = false;
+         mem_signal = (struct wsi_memory_signal_submit_info) {
+            .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA,
+            .memory = image->memory,
+         };
+         __vk_append_struct(&submit_info, &mem_signal);
+      }
 
       result = wsi->QueueSubmit(submit_queue, 1, &submit_info, fence);
       if (result != VK_SUCCESS)
          goto fail_present;
 
+#ifndef _WIN32
+      if (has_signal_dma_buf) {
+         result = wsi_signal_dma_buf_from_semaphore(swapchain, image);
+         if (result != VK_SUCCESS)
+            goto fail_present;
+      }
+#else
+      assert(!has_signal_dma_buf);
+#endif
+
       if (wsi->sw)
 	      wsi->WaitForFences(device, 1, &swapchain->fences[image_index],
 				 true, ~0ull);
diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
index f96632308df..7821e0b0200 100644
--- a/src/vulkan/wsi/wsi_common.h
+++ b/src/vulkan/wsi/wsi_common.h
@@ -102,6 +102,8 @@ struct wsi_device {
 
    VkPhysicalDevicePCIBusInfoPropertiesEXT pci_bus_info;
 
+   VkExternalSemaphoreHandleTypeFlags semaphore_export_handle_types;
+
    bool supports_modifiers;
    uint32_t maxImageDimension2D;
    VkPresentModeKHR override_present_mode;
@@ -209,6 +211,7 @@ struct wsi_device {
    WSI_CB(GetPhysicalDeviceFormatProperties);
    WSI_CB(GetPhysicalDeviceFormatProperties2KHR);
    WSI_CB(GetPhysicalDeviceImageFormatProperties2);
+   WSI_CB(GetSemaphoreFdKHR);
    WSI_CB(ResetFences);
    WSI_CB(QueueSubmit);
    WSI_CB(WaitForFences);
diff --git a/src/vulkan/wsi/wsi_common_drm.c b/src/vulkan/wsi/wsi_common_drm.c
index 2f82963841d..ed600606912 100644
--- a/src/vulkan/wsi/wsi_common_drm.c
+++ b/src/vulkan/wsi/wsi_common_drm.c
@@ -26,16 +26,221 @@
 #include "util/macros.h"
 #include "util/os_file.h"
 #include "util/xmlconfig.h"
+#include "vk_device.h"
 #include "vk_format.h"
+#include "vk_physical_device.h"
 #include "vk_util.h"
 #include "drm-uapi/drm_fourcc.h"
 
+#include <errno.h>
+#include <linux/dma-buf.h>
+#include <linux/sync_file.h>
 #include <time.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <xf86drm.h>
 
+struct dma_buf_export_sync_file_wsi {
+   __u32 flags;
+   __s32 fd;
+};
+
+struct dma_buf_import_sync_file_wsi {
+   __u32 flags;
+   __s32 fd;
+};
+
+#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE_WSI   _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file_wsi)
+#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE_WSI   _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file_wsi)
+
+static VkResult
+wsi_dma_buf_export_sync_file(int dma_buf_fd, int *sync_file_fd)
+{
+   /* Don't keep trying an IOCTL that doesn't exist. */
+   static bool no_dma_buf_sync_file = false;
+   if (no_dma_buf_sync_file)
+      return VK_ERROR_FEATURE_NOT_PRESENT;
+
+   struct dma_buf_export_sync_file_wsi export = {
+      .flags = DMA_BUF_SYNC_RW,
+      .fd = -1,
+   };
+   int ret = drmIoctl(dma_buf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE_WSI, &export);
+   if (ret) {
+      if (errno == ENOTTY) {
+         no_dma_buf_sync_file = true;
+         return VK_ERROR_FEATURE_NOT_PRESENT;
+      } else {
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+   }
+
+   *sync_file_fd = export.fd;
+
+   return VK_SUCCESS;
+}
+
+static VkResult
+wsi_dma_buf_import_sync_file(int dma_buf_fd, int sync_file_fd)
+{
+   /* Don't keep trying an IOCTL that doesn't exist. */
+   static bool no_dma_buf_sync_file = false;
+   if (no_dma_buf_sync_file)
+      return VK_ERROR_FEATURE_NOT_PRESENT;
+
+   struct dma_buf_import_sync_file_wsi import = {
+      .flags = DMA_BUF_SYNC_RW,
+      .fd = sync_file_fd,
+   };
+   int ret = drmIoctl(dma_buf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE_WSI, &import);
+   if (ret) {
+      if (errno == ENOTTY) {
+         no_dma_buf_sync_file = true;
+         return VK_ERROR_FEATURE_NOT_PRESENT;
+      } else {
+         return VK_ERROR_OUT_OF_HOST_MEMORY;
+      }
+   }
+
+   return VK_SUCCESS;
+}
+
+static VkResult
+prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain,
+                                      const struct wsi_image *image)
+{
+   VkResult result;
+
+   if (!(chain->wsi->semaphore_export_handle_types &
+         VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT))
+      return VK_ERROR_FEATURE_NOT_PRESENT;
+
+   int sync_file_fd = -1;
+   result = wsi_dma_buf_export_sync_file(image->dma_buf_fd, &sync_file_fd);
+   if (result != VK_SUCCESS)
+      return result;
+
+   result = wsi_dma_buf_import_sync_file(image->dma_buf_fd, sync_file_fd);
+   close(sync_file_fd);
+   if (result != VK_SUCCESS)
+      return result;
+
+   /* If we got here, all our checks pass.  Create the actual semaphore */
+   const VkExportSemaphoreCreateInfo export_info = {
+      .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
+      .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+   };
+   const VkSemaphoreCreateInfo semaphore_info = {
+      .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+      .pNext = &export_info,
+   };
+   result = chain->wsi->CreateSemaphore(chain->device, &semaphore_info,
+                                        &chain->alloc,
+                                        &chain->dma_buf_semaphore);
+   if (result != VK_SUCCESS)
+      return result;
+
+   return VK_SUCCESS;
+}
+
+VkResult
+wsi_prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain,
+                                          const struct wsi_image *image)
+{
+   VkResult result;
+
+   /* We cache result - 1 in the swapchain */
+   if (unlikely(chain->signal_dma_buf_from_semaphore == 0)) {
+      result = prepare_signal_dma_buf_from_semaphore(chain, image);
+      assert(result <= 0);
+      chain->signal_dma_buf_from_semaphore = (int)result - 1;
+   } else {
+      result = (VkResult)(chain->signal_dma_buf_from_semaphore + 1);
+   }
+
+   return result;
+}
+
+VkResult
+wsi_signal_dma_buf_from_semaphore(const struct wsi_swapchain *chain,
+                                  const struct wsi_image *image)
+{
+   VkResult result;
+
+   const VkSemaphoreGetFdInfoKHR get_fd_info = {
+      .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
+      .semaphore = chain->dma_buf_semaphore,
+      .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+   };
+   int sync_file_fd = -1;
+   result = chain->wsi->GetSemaphoreFdKHR(chain->device, &get_fd_info,
+                                          &sync_file_fd);
+   if (result != VK_SUCCESS)
+      return result;
+
+   result = wsi_dma_buf_import_sync_file(image->dma_buf_fd, sync_file_fd);
+   close(sync_file_fd);
+   return result;
+}
+
+static const struct vk_sync_type *
+get_sync_file_sync_type(struct vk_device *device,
+                        enum vk_sync_features req_features)
+{
+   for (const struct vk_sync_type *const *t =
+        device->physical->supported_sync_types; *t; t++) {
+      if (req_features & ~(*t)->features)
+         continue;
+
+      if ((*t)->import_sync_file != NULL)
+         return *t;
+   }
+
+   return NULL;
+}
+
+VkResult
+wsi_create_sync_for_dma_buf_wait(const struct wsi_swapchain *chain,
+                                 const struct wsi_image *image,
+                                 enum vk_sync_features req_features,
+                                 struct vk_sync **sync_out)
+{
+   VK_FROM_HANDLE(vk_device, device, chain->device);
+   VkResult result;
+
+   const struct vk_sync_type *sync_type =
+      get_sync_file_sync_type(device, req_features);
+   if (sync_type == NULL)
+      return VK_ERROR_FEATURE_NOT_PRESENT;
+
+   int sync_file_fd = -1;
+   result = wsi_dma_buf_export_sync_file(image->dma_buf_fd, &sync_file_fd);
+   if (result != VK_SUCCESS)
+      return result;
+
+   struct vk_sync *sync = NULL;
+   result = vk_sync_create(device, sync_type, VK_SYNC_IS_SHAREABLE, 0, &sync);
+   if (result != VK_SUCCESS)
+      goto fail_close_sync_file;
+
+   result = vk_sync_import_sync_file(device, sync, sync_file_fd);
+   if (result != VK_SUCCESS)
+      goto fail_destroy_sync;
+
+   close(sync_file_fd);
+   *sync_out = sync;
+
+   return VK_SUCCESS;
+
+fail_destroy_sync:
+   vk_sync_destroy(device, sync);
+fail_close_sync_file:
+   close(sync_file_fd);
+
+   return result;
+}
+
 bool
 wsi_common_drm_devices_equal(int fd_a, int fd_b)
 {
diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h
index b5198d45b0b..e5d6bb5204b 100644
--- a/src/vulkan/wsi/wsi_common_private.h
+++ b/src/vulkan/wsi/wsi_common_private.h
@@ -25,6 +25,7 @@
 
 #include "wsi_common.h"
 #include "vulkan/runtime/vk_object.h"
+#include "vulkan/runtime/vk_sync.h"
 
 struct wsi_image;
 struct wsi_swapchain;
@@ -97,6 +98,9 @@ struct wsi_swapchain {
    VkSemaphore* buffer_blit_semaphores;
    VkPresentModeKHR present_mode;
 
+   int signal_dma_buf_from_semaphore;
+   VkSemaphore dma_buf_semaphore;
+
    struct wsi_image_info image_info;
    uint32_t image_count;
 
@@ -193,6 +197,17 @@ void
 wsi_destroy_image(const struct wsi_swapchain *chain,
                   struct wsi_image *image);
 
+VkResult
+wsi_prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain,
+                                          const struct wsi_image *image);
+VkResult
+wsi_signal_dma_buf_from_semaphore(const struct wsi_swapchain *chain,
+                                  const struct wsi_image *image);
+VkResult
+wsi_create_sync_for_dma_buf_wait(const struct wsi_swapchain *chain,
+                                 const struct wsi_image *image,
+                                 enum vk_sync_features sync_features,
+                                 struct vk_sync **sync_out);
 
 struct wsi_interface {
    VkResult (*get_support)(VkIcdSurfaceBase *surface,



More information about the mesa-commit mailing list