[Mesa-dev] [RFC] anv: Use DRM sync objects for external semaphores when available

Jason Ekstrand jason at jlekstrand.net
Wed Apr 12 18:12:31 UTC 2017


This patch is based on the work that's already been on the list for some
time to implement VK_KHX_external_semaphore.  The difference here is that
this patch makes us able to use the new DRM syncobj API cooked up by Dave
Airlie.  This patch seems to work ok assuming a Kernel API I cooked up that
has yet to get any review and very hackish kernel patch on top of Dave's
stuff.  Mostly, I'm sending it to the list so that people can get an idea
of what the implementation looks like so that we can get the ball rolling
on getting the final kernel API in place.

Cc: Chad Versace <chadversary at chromium.org>
Cc: Dave Airlie <airlied at redhat.com>
Cc: Daniel Vetter <daniel.vetter at ffwll.ch>
Cc: Chris Wilson <chris at chris-wilson.co.uk>

---
 src/intel/vulkan/anv_batch_chain.c | 72 +++++++++++++++++++++++++++++++++++
 src/intel/vulkan/anv_device.c      |  3 ++
 src/intel/vulkan/anv_private.h     |  8 ++++
 src/intel/vulkan/anv_queue.c       | 77 ++++++++++++++++++++++++++++++--------
 4 files changed, 144 insertions(+), 16 deletions(-)

diff --git a/src/intel/vulkan/anv_batch_chain.c b/src/intel/vulkan/anv_batch_chain.c
index 0a86ae5..46c4078 100644
--- a/src/intel/vulkan/anv_batch_chain.c
+++ b/src/intel/vulkan/anv_batch_chain.c
@@ -953,6 +953,21 @@ anv_cmd_buffer_add_secondary(struct anv_cmd_buffer *primary,
                          &secondary->surface_relocs, 0);
 }
 
+struct drm_i915_gem_exec_fence {
+	/**
+	 * User's handle for a dma-fence to wait on or signal.
+	 */
+	__u32 handle;
+
+	__u32 rsvd1;
+
+#define I915_EXEC_FENCE_WAIT            (1<<0)
+#define I915_EXEC_FENCE_SIGNAL          (1<<1)
+	__u64 flags;
+};
+
+#define I915_EXEC_FENCE_ARRAY   (1<<18)
+
 struct anv_execbuf {
    struct drm_i915_gem_execbuffer2           execbuf;
 
@@ -962,6 +977,10 @@ struct anv_execbuf {
 
    /* Allocated length of the 'objects' and 'bos' arrays */
    uint32_t                                  array_length;
+
+   uint32_t                                  fence_count;
+   uint32_t                                  fence_array_length;
+   struct drm_i915_gem_exec_fence *          fences;
 };
 
 static void
@@ -976,6 +995,7 @@ anv_execbuf_finish(struct anv_execbuf *exec,
 {
    vk_free(alloc, exec->objects);
    vk_free(alloc, exec->bos);
+   vk_free(alloc, exec->fences);
 }
 
 static VkResult
@@ -1040,6 +1060,9 @@ anv_execbuf_add_bo(struct anv_execbuf *exec,
       obj->flags = flags | (bo->is_winsys_bo ? EXEC_OBJECT_WRITE : 0);
       obj->rsvd1 = 0;
       obj->rsvd2 = 0;
+
+      if (!(obj->flags & EXEC_OBJECT_WRITE))
+         obj->flags |= EXEC_OBJECT_ASYNC;
    }
 
    if (relocs != NULL && obj->relocation_count == 0) {
@@ -1060,6 +1083,33 @@ anv_execbuf_add_bo(struct anv_execbuf *exec,
    return VK_SUCCESS;
 }
 
+static VkResult
+anv_execbuf_add_syncobj(struct anv_execbuf *exec,
+                        uint32_t handle,
+                        uint32_t flags,
+                        const VkAllocationCallbacks *alloc)
+{
+   if (exec->fence_count >= exec->fence_array_length) {
+      uint32_t new_len = MAX2(exec->fence_array_length * 2, 64);
+
+      struct drm_i915_gem_exec_fence *new_fences =
+         vk_realloc(alloc, exec->fences, new_len * sizeof(*new_fences),
+                    8, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+      if (new_fences == NULL)
+         return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+
+      exec->fences = new_fences;
+      exec->fence_array_length = new_len;
+   }
+
+   exec->fences[exec->fence_count] = (struct drm_i915_gem_exec_fence) {
+      .handle = handle,
+      .flags = flags,
+   };
+
+   return VK_SUCCESS;
+}
+
 static void
 anv_cmd_buffer_process_relocs(struct anv_cmd_buffer *cmd_buffer,
                               struct anv_reloc_list *list)
@@ -1444,6 +1494,14 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
          impl->fd = -1;
          break;
 
+      case ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ:
+         result = anv_execbuf_add_syncobj(&execbuf, impl->syncobj,
+                                          I915_EXEC_FENCE_WAIT,
+                                          &device->alloc);
+         if (result != VK_SUCCESS)
+            return result;
+         break;
+
       default:
          break;
       }
@@ -1478,6 +1536,14 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
          need_out_fence = true;
          break;
 
+      case ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ:
+         result = anv_execbuf_add_syncobj(&execbuf, impl->syncobj,
+                                          I915_EXEC_FENCE_SIGNAL,
+                                          &device->alloc);
+         if (result != VK_SUCCESS)
+            return result;
+         break;
+
       default:
          break;
       }
@@ -1491,6 +1557,12 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
       setup_empty_execbuf(&execbuf, device);
    }
 
+   if (execbuf.fence_count > 0) {
+      execbuf.execbuf.flags |= I915_EXEC_FENCE_ARRAY;
+      execbuf.execbuf.num_cliprects = execbuf.fence_count;
+      execbuf.execbuf.cliprects_ptr = (uintptr_t) execbuf.fences;
+   }
+
    if (in_fence != -1) {
       execbuf.execbuf.flags |= I915_EXEC_FENCE_IN;
       execbuf.execbuf.rsvd2 |= (uint32_t)in_fence;
diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
index fc52698..fbff618 100644
--- a/src/intel/vulkan/anv_device.c
+++ b/src/intel/vulkan/anv_device.c
@@ -1039,6 +1039,9 @@ VkResult anv_CreateDevice(
    device->robust_buffer_access = pCreateInfo->pEnabledFeatures &&
       pCreateInfo->pEnabledFeatures->robustBufferAccess;
 
+   /* FINISHME */
+   device->has_syncobj = true;
+
    if (pthread_mutex_init(&device->mutex, NULL) != 0) {
       result = vk_error(VK_ERROR_INITIALIZATION_FAILED);
       goto fail_context_id;
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index dcf0406..3d2e8d5 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -616,6 +616,7 @@ struct anv_device {
     int                                         fd;
     bool                                        can_chain_batches;
     bool                                        robust_buffer_access;
+    bool                                        has_syncobj;
 
     struct anv_bo_pool                          batch_bo_pool;
 
@@ -1550,6 +1551,7 @@ enum anv_semaphore_type {
    ANV_SEMAPHORE_TYPE_DUMMY,
    ANV_SEMAPHORE_TYPE_BO,
    ANV_SEMAPHORE_TYPE_SYNC_FILE,
+   ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ,
 };
 
 struct anv_semaphore_impl {
@@ -1568,6 +1570,12 @@ struct anv_semaphore_impl {
        * created or because it has been used for a wait, fd will be -1.
        */
       int fd;
+
+      /* Sync object handle when type == AKV_SEMAPHORE_TYPE_DRM_SYNCOBJ.
+       * Unlike GEM BOs, DRM sync objects aren't deduplicated by the kernel on
+       * import so we don't need to bother with a userspace cache.
+       */
+      uint32_t syncobj;
    };
 };
 
diff --git a/src/intel/vulkan/anv_queue.c b/src/intel/vulkan/anv_queue.c
index f83fd17..9b3d0c7 100644
--- a/src/intel/vulkan/anv_queue.c
+++ b/src/intel/vulkan/anv_queue.c
@@ -553,13 +553,22 @@ VkResult anv_CreateSemaphore(
       semaphore->permanent.type = ANV_SEMAPHORE_TYPE_DUMMY;
    } else if (handleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX) {
       assert(handleTypes == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX);
-
-      semaphore->permanent.type = ANV_SEMAPHORE_TYPE_BO;
-      VkResult result = anv_bo_cache_alloc(device, &device->bo_cache,
-                                           4096, &semaphore->permanent.bo);
-      if (result != VK_SUCCESS) {
-         vk_free2(&device->alloc, pAllocator, semaphore);
-         return result;
+      if (device->has_syncobj) {
+         semaphore->permanent.type = ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ;
+         semaphore->permanent.syncobj = anv_gem_syncobj_create(device);
+         if (!semaphore->permanent.syncobj) {
+            vk_free2(&device->alloc, pAllocator, semaphore);
+            return vk_errorf(VK_ERROR_TOO_MANY_OBJECTS,
+                             "drm_syncobj_create failed: %m");
+         }
+      } else {
+         semaphore->permanent.type = ANV_SEMAPHORE_TYPE_BO;
+         VkResult result = anv_bo_cache_alloc(device, &device->bo_cache,
+                                              4096, &semaphore->permanent.bo);
+         if (result != VK_SUCCESS) {
+            vk_free2(&device->alloc, pAllocator, semaphore);
+            return result;
+         }
       }
    } else if (handleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX) {
       assert(handleTypes == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX);
@@ -597,6 +606,10 @@ anv_semaphore_impl_cleanup(struct anv_device *device,
       close(impl->fd);
       break;
 
+   case ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ:
+      anv_gem_syncobj_close(device, impl->syncobj);
+      break;
+
    default:
       unreachable("Invalid semaphore type");
    }
@@ -666,17 +679,40 @@ VkResult anv_ImportSemaphoreFdKHX(
 
    switch (pImportSemaphoreFdInfo->handleType) {
    case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX: {
-      struct anv_bo *bo;
-      VkResult result = anv_bo_cache_import(device, &device->bo_cache,
-                                            pImportSemaphoreFdInfo->fd, 4096,
-                                            &bo);
-      if (result != VK_SUCCESS)
-         return result;
+      if (device->has_syncobj) {
+         uint32_t handle =
+            anv_gem_syncobj_fd_to_handle(device, pImportSemaphoreFdInfo->fd);
+         if (!handle) {
+            return vk_errorf(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX,
+                             "drm_syncobj_fd_to_handle failed: %m");
+         }
 
-      anv_semaphore_impl_cleanup(device, &semaphore->permanent);
+         /* From the Vulkan spec:
+          *
+          *    "Importing semaphore state from a file descriptor transfers
+          *    ownership of the file descriptor from the application to the
+          *    Vulkan implementation. The application must not perform any
+          *    operations on the file descriptor after a successful import."
+          *
+          * If the import fails, we leave the file descriptor open.
+          */
+         close(pImportSemaphoreFdInfo->fd);
+
+         semaphore->permanent.type = ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ;
+         semaphore->permanent.syncobj = handle;
+      } else {
+         struct anv_bo *bo;
+         VkResult result = anv_bo_cache_import(device, &device->bo_cache,
+                                               pImportSemaphoreFdInfo->fd, 4096,
+                                               &bo);
+         if (result != VK_SUCCESS)
+            return result;
 
-      semaphore->permanent.type = ANV_SEMAPHORE_TYPE_BO;
-      semaphore->permanent.bo = bo;
+         anv_semaphore_impl_cleanup(device, &semaphore->permanent);
+
+         semaphore->permanent.type = ANV_SEMAPHORE_TYPE_BO;
+         semaphore->permanent.bo = bo;
+      }
 
       return VK_SUCCESS;
    }
@@ -702,6 +738,7 @@ VkResult anv_GetSemaphoreFdKHX(
 {
    ANV_FROM_HANDLE(anv_device, device, _device);
    ANV_FROM_HANDLE(anv_semaphore, semaphore, _semaphore);
+   int fd;
 
    switch (semaphore->permanent.type) {
    case ANV_SEMAPHORE_TYPE_BO:
@@ -713,6 +750,14 @@ VkResult anv_GetSemaphoreFdKHX(
       semaphore->permanent.fd = -1;
       return VK_SUCCESS;
 
+   case ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ:
+      fd = anv_gem_syncobj_handle_to_fd(device, semaphore->permanent.syncobj);
+      if (fd < 0)
+         return vk_errorf(VK_ERROR_TOO_MANY_OBJECTS,
+                          "drm_syncobj_handle_to_fd failed: %m");
+      *pFd = fd;
+      return VK_SUCCESS;
+
    default:
       return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX);
    }
-- 
2.5.0.400.gff86faf



More information about the mesa-dev mailing list