v3dv: implement fences
authorIago Toral Quiroga <itoral@igalia.com>
Tue, 14 Jan 2020 08:48:19 +0000 (09:48 +0100)
committerMarge Bot <eric+marge@anholt.net>
Tue, 13 Oct 2020 21:21:26 +0000 (21:21 +0000)
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>

src/broadcom/vulkan/v3dv_private.h
src/broadcom/vulkan/v3dv_queue.c

index 1c3e64d..d183c12 100644 (file)
@@ -101,6 +101,8 @@ pack_emit_reloc(void *cl, const void *reloc) {}
                        memcpy((dest), (src), (count) * sizeof(*(src))); \
                })
 
+#define NSEC_PER_SEC 1000000000ull
+
 /* From vulkan spec "If the multiple viewports feature is not enabled,
  * scissorCount must be 1", ditto for viewportCount. For now we don't support
  * that feature.
@@ -527,6 +529,14 @@ struct v3dv_semaphore {
    int32_t fd;
 };
 
+struct v3dv_fence {
+   /* A syncobject handle associated with this fence */
+   uint32_t sync;
+
+   /* The file handle of a fence that we imported into our syncobject */
+   int32_t fd;
+};
+
 struct v3dv_shader_module {
    unsigned char sha1[20];
    uint32_t size;
@@ -731,6 +741,7 @@ V3DV_DEFINE_HANDLE_CASTS(v3dv_queue, VkQueue)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_cmd_pool, VkCommandPool)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_buffer, VkBuffer)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_device_memory, VkDeviceMemory)
+V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_fence, VkFence)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_framebuffer, VkFramebuffer)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_image, VkImage)
 V3DV_DEFINE_NONDISP_HANDLE_CASTS(v3dv_image_view, VkImageView)
index 8357b17..53f3dd1 100644 (file)
@@ -27,6 +27,7 @@
 #include "broadcom/clif/clif_dump.h"
 
 #include <errno.h>
+#include <time.h>
 
 static void
 v3dv_clif_dump(struct v3dv_device *device,
@@ -56,6 +57,25 @@ v3dv_clif_dump(struct v3dv_device *device,
    clif_dump_destroy(clif);
 }
 
+static uint64_t
+gettime_ns()
+{
+   struct timespec current;
+   clock_gettime(CLOCK_MONOTONIC, &current);
+   return (uint64_t)current.tv_sec * NSEC_PER_SEC + current.tv_nsec;
+}
+
+static uint64_t
+get_absolute_timeout(uint64_t timeout)
+{
+   uint64_t current_time = gettime_ns();
+   uint64_t max_timeout = (uint64_t) INT64_MAX - current_time;
+
+   timeout = MIN2(max_timeout, timeout);
+
+   return (current_time + timeout);
+}
+
 static VkResult
 process_semaphores_to_signal(struct v3dv_device *device,
                              uint32_t count, const VkSemaphore *sems)
@@ -86,6 +106,32 @@ process_semaphores_to_signal(struct v3dv_device *device,
 }
 
 static VkResult
+process_fence_to_signal(struct v3dv_device *device, VkFence _fence)
+{
+   if (_fence == VK_NULL_HANDLE)
+      return VK_SUCCESS;
+
+   struct v3dv_fence *fence = v3dv_fence_from_handle(_fence);
+
+   if (fence->fd >= 0)
+      close(fence->fd);
+   fence->fd = -1;
+
+   int fd;
+   drmSyncobjExportSyncFile(device->fd, device->last_job_sync, &fd);
+   if (fd == -1)
+      return VK_ERROR_DEVICE_LOST;
+
+   int ret = drmSyncobjImportSyncFile(device->fd, fence->sync, fd);
+   if (ret)
+      return VK_ERROR_DEVICE_LOST;
+
+   fence->fd = fd;
+
+   return VK_SUCCESS;
+}
+
+static VkResult
 job_submit(struct v3dv_job *job, bool do_wait)
 {
    assert(job);
@@ -160,7 +206,6 @@ queue_submit(struct v3dv_queue *queue,
              VkFence fence)
 {
    /* FIXME */
-   assert(fence == 0);
    assert(pSubmit->commandBufferCount == 1);
 
    V3DV_FROM_HANDLE(v3dv_cmd_buffer, cmd_buffer, pSubmit->pCommandBuffers[0]);
@@ -176,6 +221,10 @@ queue_submit(struct v3dv_queue *queue,
                                             pSubmit->pSignalSemaphores);
       if (result != VK_SUCCESS)
          return result;
+
+      result = process_fence_to_signal(cmd_buffer->device, fence);
+      if (result != VK_SUCCESS)
+         return result;
    }
 
    return VK_SUCCESS;
@@ -246,3 +295,133 @@ v3dv_DestroySemaphore(VkDevice _device,
 
    vk_free2(&device->alloc, pAllocator, sem);
 }
+
+VkResult
+v3dv_CreateFence(VkDevice _device,
+                 const VkFenceCreateInfo *pCreateInfo,
+                 const VkAllocationCallbacks *pAllocator,
+                 VkFence *pFence)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+
+   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_FENCE_CREATE_INFO);
+
+   struct v3dv_fence *fence =
+      vk_alloc2(&device->alloc, pAllocator, sizeof(struct v3dv_fence), 8,
+               VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (fence == NULL)
+      return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   unsigned flags = 0;
+   if (pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT)
+      flags |= DRM_SYNCOBJ_CREATE_SIGNALED;
+   int ret = drmSyncobjCreate(device->fd, flags, &fence->sync);
+   if (ret) {
+      vk_free2(&device->alloc, pAllocator, fence);
+      return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+   }
+
+   fence->fd = -1;
+
+   *pFence = v3dv_fence_to_handle(fence);
+
+   return VK_SUCCESS;
+}
+
+void
+v3dv_DestroyFence(VkDevice _device,
+                  VkFence _fence,
+                  const VkAllocationCallbacks *pAllocator)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_fence, fence, _fence);
+
+   if (fence == NULL)
+      return;
+
+   drmSyncobjDestroy(device->fd, fence->sync);
+
+   if (fence->fd != -1)
+      close(fence->fd);
+
+   vk_free2(&device->alloc, pAllocator, fence);
+}
+
+VkResult
+v3dv_GetFenceStatus(VkDevice _device, VkFence _fence)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_fence, fence, _fence);
+
+   int ret = drmSyncobjWait(device->fd, &fence->sync, 1,
+                            0, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, NULL);
+   if (ret == -ETIME)
+      return VK_NOT_READY;
+   else if (ret)
+      return vk_error(device->instance, VK_ERROR_DEVICE_LOST);
+   return VK_SUCCESS;
+}
+
+VkResult
+v3dv_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+
+   uint32_t *syncobjs = vk_alloc(&device->alloc,
+                                 sizeof(*syncobjs) * fenceCount, 8,
+                                 VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+   if (!syncobjs)
+      return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   for (uint32_t i = 0; i < fenceCount; i++) {
+      struct v3dv_fence *fence = v3dv_fence_from_handle(pFences[i]);
+      syncobjs[i] = fence->sync;
+   }
+
+   int ret = drmSyncobjReset(device->fd, syncobjs, fenceCount);
+
+   vk_free(&device->alloc, syncobjs);
+
+   return ret ? VK_ERROR_OUT_OF_HOST_MEMORY : VK_SUCCESS;
+}
+
+VkResult
+v3dv_WaitForFences(VkDevice _device,
+                   uint32_t fenceCount,
+                   const VkFence *pFences,
+                   VkBool32 waitAll,
+                   uint64_t timeout)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+
+   const uint64_t abs_timeout = get_absolute_timeout(timeout);
+
+   uint32_t *syncobjs = vk_alloc(&device->alloc,
+                                 sizeof(*syncobjs) * fenceCount, 8,
+                                 VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+   if (!syncobjs)
+      return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   for (uint32_t i = 0; i < fenceCount; i++) {
+      struct v3dv_fence *fence = v3dv_fence_from_handle(pFences[i]);
+      syncobjs[i] = fence->sync;
+   }
+
+   unsigned flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
+   if (waitAll)
+      flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
+
+   int ret;
+   do {
+      ret = drmSyncobjWait(device->fd, syncobjs, fenceCount,
+                           timeout, flags, NULL);
+   } while (ret == -ETIME && gettime_ns() < abs_timeout);
+
+   vk_free(&device->alloc, syncobjs);
+
+   if (ret == -ETIME)
+      return VK_TIMEOUT;
+   else if (ret)
+      return vk_error(device->instance, VK_ERROR_DEVICE_LOST);
+   return VK_SUCCESS;
+}