Mesa (main): v3dv: implement external semaphore/fence extensions

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Jun 2 10:12:24 UTC 2021


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

Author: Iago Toral Quiroga <itoral at igalia.com>
Date:   Tue Jun  1 09:41:48 2021 +0200

v3dv: implement external semaphore/fence extensions

This provides most of the implementation, but there are some
things we cannot enable until we improve of kernel submit
interface, namely:

We don't expose capacity to export SYNC_FD, although we do
have the implementation in place. This requires that we
improve our kernel interface and event wait implementation
first so we can cover the corner case where the application
submits a command buffer that includes a VkCmdWaitForEvents
and tries to export a SYNC_FD from its signal semaphores or
fence before it the event is signaled and the command buffer
is sent to the kernel for execution in full.

Likewise, we can't currently import semaphores. This is because
our current kernel submit interface can only take one syncobj.
We have been working around this so far by waiting on the last
syncobj produced from the device whenever we had to wait on any
semaphores (which is obviously suboptimal already), but this
won't work as soon as we allow importing external semaphores,
as those could (and would typically) be produced from a
different device.

Once we address the kernel bits, we should come back and enable
SYNC_FD exports as well as semaphore imports.

Relevant CTS tests:
dEQP-VK.api.external.fence.*
dEQP-VK.api.external.semaphore.*
dEQP-VK.synchronization.cross_instance.*

Reviewed-by: Alejandro Piñeiro <apinheiro at igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/11105>

---

 docs/features.txt                  |  12 +-
 src/broadcom/vulkan/v3dv_device.c  |   6 +
 src/broadcom/vulkan/v3dv_private.h |   6 +
 src/broadcom/vulkan/v3dv_queue.c   | 354 ++++++++++++++++++++++++++++++++++++-
 4 files changed, 365 insertions(+), 13 deletions(-)

diff --git a/docs/features.txt b/docs/features.txt
index f55d5f4aa15..d81bda79a26 100644
--- a/docs/features.txt
+++ b/docs/features.txt
@@ -427,12 +427,12 @@ Vulkan 1.1 -- all DONE: anv, lvp, radv, tu, vn
   VK_KHR_descriptor_update_template                     DONE (anv, lvp, radv, tu, vn)
   VK_KHR_device_group                                   DONE (lvp, tu, v3dv, vn)
   VK_KHR_device_group_creation                          DONE (lvp, tu, v3dv, vn)
-  VK_KHR_external_fence                                 DONE (anv, lvp, radv, tu, vn)
-  VK_KHR_external_fence_capabilities                    DONE (anv, lvp, radv, tu, vn)
+  VK_KHR_external_fence                                 DONE (anv, lvp, radv, tu, v3dv, vn)
+  VK_KHR_external_fence_capabilities                    DONE (anv, lvp, radv, tu, v3dv, vn)
   VK_KHR_external_memory                                DONE (anv, lvp, radv, tu, v3dv, vn)
   VK_KHR_external_memory_capabilities                   DONE (anv, lvp, radv, tu, v3dv, vn)
-  VK_KHR_external_semaphore                             DONE (anv, lvp, radv, tu, vn)
-  VK_KHR_external_semaphore_capabilities                DONE (anv, lvp, radv, tu, vn)
+  VK_KHR_external_semaphore                             DONE (anv, lvp, radv, tu, v3dv, vn)
+  VK_KHR_external_semaphore_capabilities                DONE (anv, lvp, radv, tu, v3dv, vn)
   VK_KHR_get_memory_requirements2                       DONE (anv, lvp, radv, tu, v3dv, vn)
   VK_KHR_get_physical_device_properties2                DONE (anv, lvp, radv, tu, v3dv, vn)
   VK_KHR_maintenance1                                   DONE (anv, lvp, radv, tu, v3dv, vn)
@@ -479,11 +479,11 @@ Khronos extensions that are not part of any Vulkan version:
   VK_KHR_deferred_host_operations                       DONE (anv, radv)
   VK_KHR_display                                        DONE (anv, lvp, radv, tu, v3dv)
   VK_KHR_display_swapchain                              not started
-  VK_KHR_external_fence_fd                              DONE (anv, radv, tu)
+  VK_KHR_external_fence_fd                              DONE (anv, radv, tu, v3dv)
   VK_KHR_external_fence_win32                           not started
   VK_KHR_external_memory_fd                             DONE (anv, radv, tu, v3dv)
   VK_KHR_external_memory_win32                          not started
-  VK_KHR_external_semaphore_fd                          DONE (anv, radv, tu)
+  VK_KHR_external_semaphore_fd                          DONE (anv, radv, tu, v3dv)
   VK_KHR_external_semaphore_win32                       not started
   VK_KHR_fragment_shading_rate                          not started
   VK_KHR_get_display_properties2                        DONE (anv, lvp, radv, tu)
diff --git a/src/broadcom/vulkan/v3dv_device.c b/src/broadcom/vulkan/v3dv_device.c
index 60d5d22bb74..7e12c3315b6 100644
--- a/src/broadcom/vulkan/v3dv_device.c
+++ b/src/broadcom/vulkan/v3dv_device.c
@@ -108,7 +108,9 @@ static const struct vk_instance_extension_table instance_extensions = {
 #ifdef VK_USE_PLATFORM_DISPLAY_KHR
    .KHR_display                         = true,
 #endif
+   .KHR_external_fence_capabilities     = true,
    .KHR_external_memory_capabilities    = true,
+   .KHR_external_semaphore_capabilities = true,
    .KHR_get_physical_device_properties2 = true,
 #ifdef V3DV_HAS_SURFACE
    .KHR_get_surface_capabilities2       = true,
@@ -134,8 +136,12 @@ get_device_extensions(const struct v3dv_physical_device *device,
       .KHR_bind_memory2                    = true,
       .KHR_dedicated_allocation            = true,
       .KHR_device_group                    = true,
+      .KHR_external_fence                  = true,
+      .KHR_external_fence_fd               = true,
       .KHR_external_memory                 = true,
       .KHR_external_memory_fd              = true,
+      .KHR_external_semaphore              = true,
+      .KHR_external_semaphore_fd           = true,
       .KHR_get_memory_requirements2        = true,
       .KHR_maintenance1                    = true,
       .KHR_maintenance2                    = true,
diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h
index a69fcb19e1b..a01238ec1e4 100644
--- a/src/broadcom/vulkan/v3dv_private.h
+++ b/src/broadcom/vulkan/v3dv_private.h
@@ -1372,6 +1372,9 @@ struct v3dv_semaphore {
 
    /* A syncobject handle associated with this semaphore */
    uint32_t sync;
+
+   /* A temporary syncobject handle produced from a vkImportSemaphoreFd. */
+   uint32_t temp_sync;
 };
 
 struct v3dv_fence {
@@ -1379,6 +1382,9 @@ struct v3dv_fence {
 
    /* A syncobject handle associated with this fence */
    uint32_t sync;
+
+   /* A temporary syncobject handle produced from a vkImportFenceFd. */
+   uint32_t temp_sync;
 };
 
 struct v3dv_event {
diff --git a/src/broadcom/vulkan/v3dv_queue.c b/src/broadcom/vulkan/v3dv_queue.c
index fe899064878..25cc6c723e3 100644
--- a/src/broadcom/vulkan/v3dv_queue.c
+++ b/src/broadcom/vulkan/v3dv_queue.c
@@ -518,7 +518,12 @@ process_semaphores_to_signal(struct v3dv_device *device,
    for (uint32_t i = 0; i < count; i++) {
       struct v3dv_semaphore *sem = v3dv_semaphore_from_handle(sems[i]);
 
-      int ret = drmSyncobjImportSyncFile(render_fd, sem->sync, fd);
+      int ret;
+      if (!sem->temp_sync)
+         ret = drmSyncobjImportSyncFile(render_fd, sem->sync, fd);
+      else
+         ret = drmSyncobjImportSyncFile(render_fd, sem->temp_sync, fd);
+
       if (ret) {
          result = VK_ERROR_OUT_OF_HOST_MEMORY;
          break;
@@ -548,7 +553,11 @@ process_fence_to_signal(struct v3dv_device *device, VkFence _fence)
    if (fd == -1)
       return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
 
-   int ret = drmSyncobjImportSyncFile(render_fd, fence->sync, fd);
+   int ret;
+   if (!fence->temp_sync)
+      ret = drmSyncobjImportSyncFile(render_fd, fence->sync, fd);
+   else
+      ret = drmSyncobjImportSyncFile(render_fd, fence->temp_sync, fd);
 
    assert(fd >= 0);
    close(fd);
@@ -1110,6 +1119,14 @@ done:
    return result;
 }
 
+static void
+destroy_syncobj(uint32_t device_fd, uint32_t *sync)
+{
+   assert(sync);
+   drmSyncobjDestroy(device_fd, *sync);
+   *sync = 0;
+}
+
 VKAPI_ATTR VkResult VKAPI_CALL
 v3dv_CreateSemaphore(VkDevice _device,
                      const VkSemaphoreCreateInfo *pCreateInfo,
@@ -1137,6 +1154,157 @@ v3dv_CreateSemaphore(VkDevice _device,
    return VK_SUCCESS;
 }
 
+VKAPI_ATTR void VKAPI_CALL
+v3dv_GetPhysicalDeviceExternalSemaphoreProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,
+    VkExternalSemaphoreProperties *pExternalSemaphoreProperties)
+{
+   switch (pExternalSemaphoreInfo->handleType) {
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT:
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT:
+      pExternalSemaphoreProperties->exportFromImportedHandleTypes =
+         VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT |
+         VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+      pExternalSemaphoreProperties->compatibleHandleTypes =
+         VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT |
+         VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+      /* FIXME: we can't import external semaphores until we improve the kernel
+       * submit interface to handle multiple in syncobjs, because once we have
+       * an imported semaphore in our list of semaphores to wait on, we can no
+       * longer use the workaround of waiting on the last syncobj fence produced
+       * from the device, since the imported semaphore may not (and in fact, it
+       * would typically not) have been produced from same device.
+       *
+       * This behavior is exercised via dEQP-VK.synchronization.cross_instance.*.
+       * Particularly, this test:
+       * dEQP-VK.synchronization.cross_instance.dedicated.
+       * write_ssbo_compute_read_vertex_input.buffer_16384_binary_semaphore_fd
+       * fails consistently because of this, so it'll be a good reference to
+       * verify the implementation when the kernel bits are in place.
+       */
+      pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+
+      /* FIXME: See comment in GetPhysicalDeviceExternalFenceProperties
+       * for details on why we can't export to SYNC_FD.
+       */
+      if (pExternalSemaphoreInfo->handleType !=
+          VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) {
+         pExternalSemaphoreProperties->externalSemaphoreFeatures |=
+            VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT;
+      }
+      break;
+   default:
+      pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
+      pExternalSemaphoreProperties->compatibleHandleTypes = 0;
+      pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+      break;
+   }
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+v3dv_ImportSemaphoreFdKHR(
+   VkDevice _device,
+   const VkImportSemaphoreFdInfoKHR *pImportSemaphoreFdInfo)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_semaphore, sem, pImportSemaphoreFdInfo->semaphore);
+
+   assert(pImportSemaphoreFdInfo->sType ==
+          VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR);
+
+   int fd = pImportSemaphoreFdInfo->fd;
+   int render_fd = device->pdevice->render_fd;
+
+   bool is_temporary =
+      pImportSemaphoreFdInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT ||
+      (pImportSemaphoreFdInfo->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT);
+
+   uint32_t new_sync;
+   switch (pImportSemaphoreFdInfo->handleType) {
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT: {
+      /* "If handleType is VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, the
+       *  special value -1 for fd is treated like a valid sync file descriptor
+       *  referring to an object that has already signaled. The import
+       *  operation will succeed and the VkSemaphore will have a temporarily
+       *  imported payload as if a valid file descriptor had been provided."
+       */
+      unsigned flags = fd == -1 ? DRM_SYNCOBJ_CREATE_SIGNALED : 0;
+      if (drmSyncobjCreate(render_fd, flags, &new_sync))
+         return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+      if (fd != -1) {
+         if (drmSyncobjImportSyncFile(render_fd, new_sync, fd)) {
+            drmSyncobjDestroy(render_fd, new_sync);
+            return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+         }
+      }
+      break;
+   }
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT: {
+      if (drmSyncobjFDToHandle(render_fd, fd, &new_sync))
+         return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+      break;
+   }
+   default:
+      return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+   }
+
+   destroy_syncobj(render_fd, &sem->temp_sync);
+   if (is_temporary) {
+      sem->temp_sync = new_sync;
+   } else {
+      destroy_syncobj(render_fd, &sem->sync);
+      sem->sync = new_sync;
+   }
+
+   /* From the Vulkan 1.0.53 spec:
+    *
+    *    "Importing a semaphore payload 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.
+    */
+   if (fd != -1)
+      close(fd);
+
+   return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+v3dv_GetSemaphoreFdKHR(VkDevice _device,
+                       const VkSemaphoreGetFdInfoKHR *pGetFdInfo,
+                       int *pFd)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_semaphore, sem, pGetFdInfo->semaphore);
+
+   assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR);
+
+   *pFd = -1;
+   int render_fd = device->pdevice->render_fd;
+   switch (pGetFdInfo->handleType) {
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT: {
+      drmSyncobjExportSyncFile(render_fd, sem->sync, pFd);
+      if (*pFd == -1)
+         return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+      break;
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT:
+      drmSyncobjHandleToFD(render_fd, sem->sync, pFd);
+      if (*pFd == -1)
+         return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+      break;
+   }
+   default:
+      unreachable("Unsupported external semaphore handle type");
+   }
+
+   return VK_SUCCESS;
+}
+
 VKAPI_ATTR void VKAPI_CALL
 v3dv_DestroySemaphore(VkDevice _device,
                       VkSemaphore semaphore,
@@ -1148,7 +1316,8 @@ v3dv_DestroySemaphore(VkDevice _device,
    if (sem == NULL)
       return;
 
-   drmSyncobjDestroy(device->pdevice->render_fd, sem->sync);
+   destroy_syncobj(device->pdevice->render_fd, &sem->sync);
+   destroy_syncobj(device->pdevice->render_fd, &sem->temp_sync);
 
    vk_object_free(&device->vk, pAllocator, sem);
 }
@@ -1183,6 +1352,127 @@ v3dv_CreateFence(VkDevice _device,
    return VK_SUCCESS;
 }
 
+VKAPI_ATTR void VKAPI_CALL
+v3dv_GetPhysicalDeviceExternalFenceProperties(
+    VkPhysicalDevice physicalDevice,
+    const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo,
+    VkExternalFenceProperties *pExternalFenceProperties)
+
+{
+   switch (pExternalFenceInfo->handleType) {
+   case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT:
+   case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT:
+      pExternalFenceProperties->exportFromImportedHandleTypes =
+         VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT |
+         VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
+      pExternalFenceProperties->compatibleHandleTypes =
+         VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT |
+         VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
+      pExternalFenceProperties->externalFenceFeatures =
+         VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT;
+
+      /* FIXME: SYNC_FD exports the actual fence referenced by the syncobj, not
+       * the syncobj itself, and that fence is only created after we have
+       * submitted to the kernel and updated the syncobj for the fence to import
+       * the actual DRM fence created with the submission. Unfortunately, if the
+       * queue submission has a 'wait for events' we may hold any jobs after the
+       * wait in a user-space thread until the events are signaled, and in that
+       * case we don't update the out fence of the submit until the events are
+       * signaled and we can submit all the jobs involved with the vkQueueSubmit
+       * call. This means that if the applications submits with an out fence and
+       * a wait for events, trying to export the out fence to a SYNC_FD rigth
+       * after the submission and before the events are signaled will fail,
+       * because the actual DRM fence won't exist yet. This is not a problem
+       * with OPAQUE_FD because in this case we export the entire syncobj, not
+       * the underlying DRM fence. To fix this we need to rework our kernel
+       * interface to be more flexible and accept multiple in/out syncobjs so
+       * we can implement event waits as regular fence waits on the kernel side,
+       * until then, we can only reliably export OPAQUE_FD.
+       */
+      if (pExternalFenceInfo->handleType !=
+          VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT) {
+         pExternalFenceProperties->externalFenceFeatures |=
+            VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT;
+      }
+      break;
+   default:
+      pExternalFenceProperties->exportFromImportedHandleTypes = 0;
+      pExternalFenceProperties->compatibleHandleTypes = 0;
+      pExternalFenceProperties->externalFenceFeatures = 0;
+      break;
+   }
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+v3dv_ImportFenceFdKHR(VkDevice _device,
+                      const VkImportFenceFdInfoKHR *pImportFenceFdInfo)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_fence, fence, pImportFenceFdInfo->fence);
+
+   assert(pImportFenceFdInfo->sType ==
+          VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR);
+
+   int fd = pImportFenceFdInfo->fd;
+   int render_fd = device->pdevice->render_fd;
+
+   bool is_temporary =
+      pImportFenceFdInfo->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT ||
+      (pImportFenceFdInfo->flags & VK_FENCE_IMPORT_TEMPORARY_BIT);
+
+   uint32_t new_sync;
+   switch (pImportFenceFdInfo->handleType) {
+   case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT: {
+      /* "If handleType is VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, the
+       *  special value -1 for fd is treated like a valid sync file descriptor
+       *  referring to an object that has already signaled. The import
+       *  operation will succeed and the VkFence will have a temporarily
+       *  imported payload as if a valid file descriptor had been provided."
+       */
+      unsigned flags = fd == -1 ? DRM_SYNCOBJ_CREATE_SIGNALED : 0;
+      if (drmSyncobjCreate(render_fd, flags, &new_sync))
+         return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+      if (fd != -1) {
+         if (drmSyncobjImportSyncFile(render_fd, new_sync, fd)) {
+            drmSyncobjDestroy(render_fd, new_sync);
+            return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+         }
+      }
+      break;
+   }
+   case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT: {
+      if (drmSyncobjFDToHandle(render_fd, fd, &new_sync))
+         return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+      break;
+   }
+   default:
+      return vk_error(device->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+   }
+
+   destroy_syncobj(render_fd, &fence->temp_sync);
+   if (is_temporary) {
+      fence->temp_sync = new_sync;
+   } else {
+      destroy_syncobj(render_fd, &fence->sync);
+      fence->sync = new_sync;
+   }
+
+   /* From the Vulkan 1.0.53 spec:
+    *
+    *    "Importing a fence payload 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.
+    */
+   if (fd != -1)
+      close(fd);
+
+   return VK_SUCCESS;
+}
+
 VKAPI_ATTR void VKAPI_CALL
 v3dv_DestroyFence(VkDevice _device,
                   VkFence _fence,
@@ -1194,7 +1484,8 @@ v3dv_DestroyFence(VkDevice _device,
    if (fence == NULL)
       return;
 
-   drmSyncobjDestroy(device->pdevice->render_fd, fence->sync);
+   destroy_syncobj(device->pdevice->render_fd, &fence->sync);
+   destroy_syncobj(device->pdevice->render_fd, &fence->temp_sync);
 
    vk_object_free(&device->vk, pAllocator, fence);
 }
@@ -1214,6 +1505,37 @@ v3dv_GetFenceStatus(VkDevice _device, VkFence _fence)
    return VK_SUCCESS;
 }
 
+VKAPI_ATTR VkResult VKAPI_CALL
+v3dv_GetFenceFdKHR(VkDevice _device,
+                   const VkFenceGetFdInfoKHR *pGetFdInfo,
+                   int *pFd)
+{
+   V3DV_FROM_HANDLE(v3dv_device, device, _device);
+   V3DV_FROM_HANDLE(v3dv_fence, fence, pGetFdInfo->fence);
+
+   assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR);
+
+   *pFd = -1;
+   int render_fd = device->pdevice->render_fd;
+   switch (pGetFdInfo->handleType) {
+   case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT: {
+      drmSyncobjExportSyncFile(render_fd, fence->sync, pFd);
+      if (*pFd == -1)
+         return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+      break;
+   case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT:
+      drmSyncobjHandleToFD(render_fd, fence->sync, pFd);
+      if (*pFd == -1)
+         return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+      break;
+   }
+   default:
+      unreachable("Unsupported external fence handle type");
+   }
+
+   return VK_SUCCESS;
+}
+
 VKAPI_ATTR VkResult VKAPI_CALL
 v3dv_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)
 {
@@ -1225,12 +1547,30 @@ v3dv_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)
    if (!syncobjs)
       return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
 
+   int render_fd = device->pdevice->render_fd;
+   uint32_t reset_count = 0;
    for (uint32_t i = 0; i < fenceCount; i++) {
       struct v3dv_fence *fence = v3dv_fence_from_handle(pFences[i]);
-      syncobjs[i] = fence->sync;
+      /* From the Vulkan spec, section 'Importing Fence Payloads':
+       *
+       *    "If the import is temporary, the fence will be restored to its
+       *     permanent state the next time that fence is passed to
+       *     vkResetFences.
+       *
+       *     Note: Restoring a fence to its prior permanent payload is a
+       *     distinct operation from resetting a fence payload."
+       *
+       * To restore the previous state, we just need to destroy the temporary.
+       */
+      if (fence->temp_sync)
+         destroy_syncobj(render_fd, &fence->temp_sync);
+      else
+         syncobjs[reset_count++] = fence->sync;
    }
 
-   int ret = drmSyncobjReset(device->pdevice->render_fd, syncobjs, fenceCount);
+   int ret = 0;
+   if (reset_count > 0)
+      ret = drmSyncobjReset(render_fd, syncobjs, reset_count);
 
    vk_free(&device->vk.alloc, syncobjs);
 
@@ -1258,7 +1598,7 @@ v3dv_WaitForFences(VkDevice _device,
 
    for (uint32_t i = 0; i < fenceCount; i++) {
       struct v3dv_fence *fence = v3dv_fence_from_handle(pFences[i]);
-      syncobjs[i] = fence->sync;
+      syncobjs[i] = fence->temp_sync ? fence->temp_sync : fence->sync;
    }
 
    unsigned flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;



More information about the mesa-commit mailing list