[Mesa-dev] [PATCH 08/10] anv: Use DRM sync objects to back fences whenever possible

Jason Ekstrand jason at jlekstrand.net
Tue Aug 8 22:45:33 UTC 2017


In order to implement VK_KHR_external_fence, we need to back our fences
with something that's shareable.  Since the kernel wait interface for
sync objects already supports waiting for multiple fences in one go, it
makes anv_WaitForFences much simpler if we only have one type of fence.
---
 src/intel/vulkan/anv_batch_chain.c |   8 +++
 src/intel/vulkan/anv_device.c      |   2 +
 src/intel/vulkan/anv_private.h     |   4 ++
 src/intel/vulkan/anv_queue.c       | 132 ++++++++++++++++++++++++++++++++++---
 4 files changed, 136 insertions(+), 10 deletions(-)

diff --git a/src/intel/vulkan/anv_batch_chain.c b/src/intel/vulkan/anv_batch_chain.c
index 5d876e4..15082b5 100644
--- a/src/intel/vulkan/anv_batch_chain.c
+++ b/src/intel/vulkan/anv_batch_chain.c
@@ -1556,6 +1556,14 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
             return result;
          break;
 
+      case ANV_FENCE_TYPE_SYNCOBJ:
+         result = anv_execbuf_add_syncobj(&execbuf, impl->syncobj,
+                                          I915_EXEC_FENCE_SIGNAL,
+                                          &device->alloc);
+         if (result != VK_SUCCESS)
+            return result;
+         break;
+
       default:
          unreachable("Invalid fence type");
       }
diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
index a6d5215..2e0fa19 100644
--- a/src/intel/vulkan/anv_device.c
+++ b/src/intel/vulkan/anv_device.c
@@ -339,6 +339,8 @@ anv_physical_device_init(struct anv_physical_device *device,
    device->has_exec_async = anv_gem_get_param(fd, I915_PARAM_HAS_EXEC_ASYNC);
    device->has_exec_fence = anv_gem_get_param(fd, I915_PARAM_HAS_EXEC_FENCE);
    device->has_syncobj = anv_gem_get_param(fd, I915_PARAM_HAS_EXEC_FENCE_ARRAY);
+   device->has_syncobj_wait = device->has_syncobj &&
+                              anv_gem_supports_syncobj_wait(fd);
 
    bool swizzled = anv_gem_get_bit6_swizzle(fd, I915_TILING_X);
 
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index 2f89d3f..430652d 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -654,6 +654,7 @@ struct anv_physical_device {
     bool                                        has_exec_async;
     bool                                        has_exec_fence;
     bool                                        has_syncobj;
+    bool                                        has_syncobj_wait;
 
     uint32_t                                    eu_total;
     uint32_t                                    subslice_total;
@@ -1755,6 +1756,9 @@ struct anv_fence_impl {
          struct anv_bo bo;
          enum anv_bo_fence_state state;
       } bo;
+
+      /** DRM syncobj handle for syncobj-based fences */
+      uint32_t syncobj;
    };
 };
 
diff --git a/src/intel/vulkan/anv_queue.c b/src/intel/vulkan/anv_queue.c
index 7348e15..8e45bb2 100644
--- a/src/intel/vulkan/anv_queue.c
+++ b/src/intel/vulkan/anv_queue.c
@@ -271,17 +271,25 @@ VkResult anv_CreateFence(
    if (fence == NULL)
       return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
 
-   fence->permanent.type = ANV_FENCE_TYPE_BO;
+   if (device->instance->physicalDevice.has_syncobj_wait) {
+      fence->permanent.type = ANV_FENCE_TYPE_SYNCOBJ;
 
-   VkResult result = anv_bo_pool_alloc(&device->batch_bo_pool,
-                                       &fence->permanent.bo.bo, 4096);
-   if (result != VK_SUCCESS)
-      return result;
-
-   if (pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT) {
-      fence->permanent.bo.state = ANV_BO_FENCE_STATE_SIGNALED;
+      fence->permanent.syncobj = anv_gem_syncobj_create(device);
+      if (!fence->permanent.syncobj)
+         return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
    } else {
-      fence->permanent.bo.state = ANV_BO_FENCE_STATE_RESET;
+      fence->permanent.type = ANV_FENCE_TYPE_BO;
+
+      VkResult result = anv_bo_pool_alloc(&device->batch_bo_pool,
+                                          &fence->permanent.bo.bo, 4096);
+      if (result != VK_SUCCESS)
+         return result;
+
+      if (pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT) {
+         fence->permanent.bo.state = ANV_BO_FENCE_STATE_SIGNALED;
+      } else {
+         fence->permanent.bo.state = ANV_BO_FENCE_STATE_RESET;
+      }
    }
 
    *pFence = anv_fence_to_handle(fence);
@@ -301,6 +309,10 @@ anv_fence_impl_cleanup(struct anv_device *device,
    case ANV_FENCE_TYPE_BO:
       anv_bo_pool_free(&device->batch_bo_pool, &impl->bo.bo);
       return;
+
+   case ANV_FENCE_TYPE_SYNCOBJ:
+      anv_gem_syncobj_destroy(device, impl->syncobj);
+      return;
    }
 
    unreachable("Invalid fence type");
@@ -328,6 +340,8 @@ VkResult anv_ResetFences(
     uint32_t                                    fenceCount,
     const VkFence*                              pFences)
 {
+   ANV_FROM_HANDLE(anv_device, device, _device);
+
    for (uint32_t i = 0; i < fenceCount; i++) {
       ANV_FROM_HANDLE(anv_fence, fence, pFences[i]);
 
@@ -339,6 +353,10 @@ VkResult anv_ResetFences(
          impl->bo.state = ANV_BO_FENCE_STATE_RESET;
          break;
 
+      case ANV_FENCE_TYPE_SYNCOBJ:
+         anv_gem_syncobj_reset(device, impl->syncobj);
+         break;
+
       default:
          unreachable("Invalid fence type");
       }
@@ -384,6 +402,22 @@ VkResult anv_GetFenceStatus(
          unreachable("Invalid fence status");
       }
 
+   case ANV_FENCE_TYPE_SYNCOBJ: {
+      int ret = anv_gem_syncobj_wait(device, &impl->syncobj, 1, 0, true);
+      if (ret == -1) {
+         if (errno == ETIME) {
+            return VK_NOT_READY;
+         } else {
+            /* We don't know the real error. */
+            device->lost = true;
+            return vk_errorf(VK_ERROR_DEVICE_LOST,
+                             "drm_syncobj_wait failed: %m");
+         }
+      } else {
+         return VK_SUCCESS;
+      }
+   }
+
    default:
       unreachable("Invalid fence type");
    }
@@ -392,6 +426,78 @@ VkResult anv_GetFenceStatus(
 #define NSEC_PER_SEC 1000000000
 #define INT_TYPE_MAX(type) ((1ull << (sizeof(type) * 8 - 1)) - 1)
 
+static uint64_t
+gettime_ns(void)
+{
+   struct timespec current;
+   clock_gettime(CLOCK_MONOTONIC, &current);
+   return (uint64_t)current.tv_sec * NSEC_PER_SEC + current.tv_nsec;
+}
+
+static VkResult
+anv_wait_for_syncobj_fences(struct anv_device *device,
+                            uint32_t fenceCount,
+                            const VkFence *pFences,
+                            bool waitAll,
+                            uint64_t _timeout)
+{
+   uint32_t *syncobjs = vk_zalloc(&device->alloc,
+                                  sizeof(*syncobjs) * fenceCount, 8,
+                                  VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+   if (!syncobjs)
+      return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   for (uint32_t i = 0; i < fenceCount; i++) {
+      ANV_FROM_HANDLE(anv_fence, fence, pFences[i]);
+      assert(fence->permanent.type == ANV_FENCE_TYPE_SYNCOBJ);
+
+      struct anv_fence_impl *impl =
+         fence->temporary.type != ANV_FENCE_TYPE_NONE ?
+         &fence->temporary : &fence->permanent;
+
+      assert(impl->type == ANV_FENCE_TYPE_SYNCOBJ);
+      syncobjs[i] = impl->syncobj;
+   }
+
+   int64_t abs_timeout_ns = 0;
+   if (_timeout > 0) {
+      uint64_t current_ns = gettime_ns();
+
+      /* Add but saturate to INT32_MAX */
+      if (current_ns + _timeout < current_ns)
+         abs_timeout_ns = INT64_MAX;
+      else if (current_ns + _timeout > INT64_MAX)
+         abs_timeout_ns = INT64_MAX;
+      else
+         abs_timeout_ns = current_ns + _timeout;
+   }
+
+   /* The gem_syncobj_wait ioctl may return early due to an inherent
+    * limitation in the way it computes timeouts.  Loop until we've actually
+    * passed the timeout.
+    */
+   int ret;
+   do {
+      ret = anv_gem_syncobj_wait(device, syncobjs, fenceCount,
+                                 abs_timeout_ns, waitAll);
+   } while (ret == -1 && errno == ETIME && gettime_ns() < abs_timeout_ns);
+
+   vk_free(&device->alloc, syncobjs);
+
+   if (ret == -1) {
+      if (errno == ETIME) {
+         return VK_TIMEOUT;
+      } else {
+         /* We don't know the real error. */
+         device->lost = true;
+         return vk_errorf(VK_ERROR_DEVICE_LOST,
+                          "drm_syncobj_wait failed: %m");
+      }
+   } else {
+      return VK_SUCCESS;
+   }
+}
+
 static VkResult
 anv_wait_for_bo_fences(struct anv_device *device,
                        uint32_t fenceCount,
@@ -546,7 +652,13 @@ VkResult anv_WaitForFences(
    if (unlikely(device->lost))
       return VK_ERROR_DEVICE_LOST;
 
-   return anv_wait_for_bo_fences(device, fenceCount, pFences, waitAll, timeout);
+   if (device->instance->physicalDevice.has_syncobj_wait) {
+      return anv_wait_for_syncobj_fences(device, fenceCount, pFences,
+                                         waitAll, timeout);
+   } else {
+      return anv_wait_for_bo_fences(device, fenceCount, pFences,
+                                    waitAll, timeout);
+   }
 }
 
 // Queue semaphore functions
-- 
2.5.0.400.gff86faf



More information about the mesa-dev mailing list