vulkan: Win32 sync import/export support
authorJesse Natalie <jenatali@microsoft.com>
Fri, 5 May 2023 04:06:41 +0000 (21:06 -0700)
committerMarge Bot <emma+marge@anholt.net>
Mon, 15 May 2023 17:14:20 +0000 (17:14 +0000)
Acked-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22879>

src/vulkan/runtime/vk_semaphore.c
src/vulkan/runtime/vk_sync.c
src/vulkan/runtime/vk_sync.h

index a008384..abae89c 100644 (file)
@@ -25,7 +25,9 @@
 
 #include "util/os_time.h"
 
-#ifndef _WIN32
+#ifdef _WIN32
+#include <windows.h>
+#else
 #include <unistd.h>
 #endif
 
@@ -47,6 +49,12 @@ vk_sync_semaphore_import_types(const struct vk_sync_type *type,
    if (type->export_sync_file && semaphore_type == VK_SEMAPHORE_TYPE_BINARY)
       handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
 
+   if (type->import_win32_handle) {
+      handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT;
+      if (type->features & VK_SYNC_FEATURE_TIMELINE)
+         handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT;
+   }
+
    return handle_types;
 }
 
@@ -62,6 +70,12 @@ vk_sync_semaphore_export_types(const struct vk_sync_type *type,
    if (type->export_sync_file && semaphore_type == VK_SEMAPHORE_TYPE_BINARY)
       handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
 
+   if (type->export_win32_handle) {
+      handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT;
+      if (type->features & VK_SYNC_FEATURE_TIMELINE)
+         handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT;
+   }
+
    return handle_types;
 }
 
@@ -184,6 +198,20 @@ vk_common_CreateSemaphore(VkDevice _device,
       return result;
    }
 
+#ifdef _WIN32
+   const VkExportSemaphoreWin32HandleInfoKHR *export_win32 =
+      vk_find_struct_const(pCreateInfo->pNext, EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR);
+   if (export_win32) {
+      result = vk_sync_set_win32_export_params(device, &semaphore->permanent, export_win32->pAttributes,
+                                               export_win32->dwAccess, export_win32->name);
+      if (result != VK_SUCCESS) {
+         vk_sync_finish(device, &semaphore->permanent);
+         vk_object_free(device, pAllocator, semaphore);
+         return result;
+      }
+   }
+#endif
+
    *pSemaphore = vk_semaphore_to_handle(semaphore);
 
    return VK_SUCCESS;
@@ -247,18 +275,23 @@ vk_common_GetPhysicalDeviceExternalSemaphoreProperties(
    VkExternalSemaphoreHandleTypeFlagBits export =
       vk_sync_semaphore_export_types(sync_type, semaphore_type);
 
-   if (handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
-      const struct vk_sync_type *opaque_sync_type =
-         get_semaphore_sync_type(pdevice, semaphore_type,
-                                 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);
-
-      /* If we're a different vk_sync_type than the one selected when only
-       * OPAQUE_FD is set, then we can't import/export OPAQUE_FD.  Put
-       * differently, there can only be one OPAQUE_FD sync type.
-       */
-      if (sync_type != opaque_sync_type) {
-         import &= ~VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
-         export &= ~VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
+   VkExternalSemaphoreHandleTypeFlagBits opaque_types[] = {
+      VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
+      VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT,
+   };
+   for (uint32_t i = 0; i < ARRAY_SIZE(opaque_types); ++i) {
+      if (handle_type != opaque_types[i]) {
+         const struct vk_sync_type *opaque_sync_type =
+            get_semaphore_sync_type(pdevice, semaphore_type, opaque_types[i]);
+
+         /* If we're a different vk_sync_type than the one selected when only
+          * an opaque type is set, then we can't import/export that opaque type. Put
+          * differently, there can only be one OPAQUE_FD/WIN32_HANDLE sync type.
+          */
+         if (sync_type != opaque_sync_type) {
+            import &= ~opaque_types[i];
+            export &= ~opaque_types[i];
+         }
       }
    }
 
@@ -380,7 +413,123 @@ vk_common_SignalSemaphore(VkDevice _device,
    return VK_SUCCESS;
 }
 
-#ifndef _WIN32
+#ifdef _WIN32
+
+VKAPI_ATTR VkResult VKAPI_CALL
+vk_common_ImportSemaphoreWin32HandleKHR(VkDevice _device,
+                                        const VkImportSemaphoreWin32HandleInfoKHR *pImportSemaphoreWin32HandleInfo)
+{
+   VK_FROM_HANDLE(vk_device, device, _device);
+   VK_FROM_HANDLE(vk_semaphore, semaphore, pImportSemaphoreWin32HandleInfo->semaphore);
+
+   assert(pImportSemaphoreWin32HandleInfo->sType ==
+          VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR);
+
+   const HANDLE handle = pImportSemaphoreWin32HandleInfo->handle;
+   const wchar_t *name = pImportSemaphoreWin32HandleInfo->name;
+   const VkExternalSemaphoreHandleTypeFlagBits handle_type =
+      pImportSemaphoreWin32HandleInfo->handleType;
+
+   struct vk_sync *temporary = NULL, *sync;
+   if (pImportSemaphoreWin32HandleInfo->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT) {
+      /* From the Vulkan 1.2.194 spec:
+       *
+       *    VUID-VkImportSemaphoreWin32HandleInfoKHR-flags-03322
+       *
+       *    "If flags contains VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, the
+       *    VkSemaphoreTypeCreateInfo::semaphoreType field of the semaphore
+       *    from which handle or name was exported must not be
+       *    VK_SEMAPHORE_TYPE_TIMELINE"
+       */
+      if (unlikely(semaphore->type == VK_SEMAPHORE_TYPE_TIMELINE)) {
+         return vk_errorf(device, VK_ERROR_UNKNOWN,
+                          "Cannot temporarily import into a timeline "
+                          "semaphore");
+      }
+
+      const struct vk_sync_type *sync_type =
+         get_semaphore_sync_type(device->physical, semaphore->type, handle_type);
+
+      VkResult result = vk_sync_create(device, sync_type, 0 /* flags */,
+                                       0 /* initial_value */, &temporary);
+      if (result != VK_SUCCESS)
+         return result;
+
+      sync = temporary;
+   } else {
+      sync = &semaphore->permanent;
+   }
+   assert(handle_type &
+          vk_sync_semaphore_handle_types(sync->type, semaphore->type));
+
+   VkResult result;
+   switch (pImportSemaphoreWin32HandleInfo->handleType) {
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT:
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT:
+      result = vk_sync_import_win32_handle(device, sync, handle, name);
+      break;
+
+   default:
+      result = vk_error(semaphore, VK_ERROR_INVALID_EXTERNAL_HANDLE);
+   }
+
+   if (result != VK_SUCCESS) {
+      if (temporary != NULL)
+         vk_sync_destroy(device, temporary);
+      return result;
+   }
+
+   /* From a spec correctness point of view, we could probably replace the
+    * semaphore's temporary payload with the new vk_sync at the top.  However,
+    * we choose to be nice to applications and only replace the semaphore if
+    * the import succeeded.
+    */
+   if (temporary) {
+      vk_semaphore_reset_temporary(device, semaphore);
+      semaphore->temporary = temporary;
+   }
+
+   return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+vk_common_GetSemaphoreWin32HandleKHR(VkDevice _device,
+                                     const VkSemaphoreGetWin32HandleInfoKHR *pGetWin32HandleInfo,
+                                     HANDLE *pHandle)
+{
+   VK_FROM_HANDLE(vk_device, device, _device);
+   VK_FROM_HANDLE(vk_semaphore, semaphore, pGetWin32HandleInfo->semaphore);
+
+   assert(pGetWin32HandleInfo->sType == VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR);
+
+   struct vk_sync *sync = vk_semaphore_get_active_sync(semaphore);
+
+   VkResult result;
+   switch (pGetWin32HandleInfo->handleType) {
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT:
+   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT:
+      result = vk_sync_export_win32_handle(device, sync, pHandle);
+      if (result != VK_SUCCESS)
+         return result;
+      break;
+
+   default:
+      unreachable("Invalid semaphore export handle type");
+   }
+
+   /* From the Vulkan 1.2.194 spec:
+    *
+    *    "Export operations have the same transference as the specified
+    *    handle type’s import operations. [...] If the semaphore was using
+    *    a temporarily imported payload, the semaphore’s prior permanent
+    *    payload will be restored."
+    */
+   vk_semaphore_reset_temporary(device, semaphore);
+
+   return VK_SUCCESS;
+}
+
+#else
 
 VKAPI_ATTR VkResult VKAPI_CALL
 vk_common_ImportSemaphoreFdKHR(VkDevice _device,
index 26a6178..da680ca 100644 (file)
@@ -400,3 +400,47 @@ vk_sync_export_sync_file(struct vk_device *device,
    assert(!(sync->flags & VK_SYNC_IS_TIMELINE));
    return sync->type->export_sync_file(device, sync, sync_file);
 }
+
+VkResult
+vk_sync_import_win32_handle(struct vk_device *device,
+                            struct vk_sync *sync,
+                            void *handle,
+                            const wchar_t *name)
+{
+   VkResult result = sync->type->import_win32_handle(device, sync, handle, name);
+   if (unlikely(result != VK_SUCCESS))
+      return result;
+
+   sync->flags |= VK_SYNC_IS_SHAREABLE |
+                  VK_SYNC_IS_SHARED;
+
+   return VK_SUCCESS;
+}
+
+VkResult
+vk_sync_export_win32_handle(struct vk_device *device,
+                            struct vk_sync *sync,
+                            void **handle)
+{
+   assert(sync->flags & VK_SYNC_IS_SHAREABLE);
+
+   VkResult result = sync->type->export_win32_handle(device, sync, handle);
+   if (unlikely(result != VK_SUCCESS))
+      return result;
+
+   sync->flags |= VK_SYNC_IS_SHARED;
+
+   return VK_SUCCESS;
+}
+
+VkResult
+vk_sync_set_win32_export_params(struct vk_device *device,
+                                struct vk_sync *sync,
+                                const void *security_attributes,
+                                uint32_t access,
+                                const wchar_t *name)
+{
+   assert(sync->flags & VK_SYNC_IS_SHARED);
+
+   return sync->type->set_win32_export_params(device, sync, security_attributes, access, name);
+}
index bb5b204..15d85dc 100644 (file)
@@ -272,6 +272,29 @@ struct vk_sync_type {
    VkResult (*export_sync_file)(struct vk_device *device,
                                 struct vk_sync *sync,
                                 int *sync_file);
+
+   /** Permanently imports the given handle or name into this vk_sync
+    *
+    * This replaces the guts of the given vk_sync with whatever is in the object.
+    * In a sense, this vk_sync now aliases whatever vk_sync the handle was
+    * exported from.
+    */
+   VkResult (*import_win32_handle)(struct vk_device *device,
+                                   struct vk_sync *sync,
+                                   void *handle,
+                                   const wchar_t *name);
+
+   /** Export the guts of this vk_sync to a handle and/or name */
+   VkResult (*export_win32_handle)(struct vk_device *device,
+                                   struct vk_sync *sync,
+                                   void **handle);
+
+   /** Vulkan puts these as creation params instead of export params */
+   VkResult (*set_win32_export_params)(struct vk_device *device,
+                                       struct vk_sync *sync,
+                                       const void *security_attributes,
+                                       uint32_t access,
+                                       const wchar_t *name);
 };
 
 enum vk_sync_flags {
@@ -361,6 +384,21 @@ VkResult MUST_CHECK vk_sync_export_sync_file(struct vk_device *device,
                                              struct vk_sync *sync,
                                              int *sync_file);
 
+VkResult MUST_CHECK vk_sync_import_win32_handle(struct vk_device *device,
+                                                struct vk_sync *sync,
+                                                void *handle,
+                                                const wchar_t *name);
+
+VkResult MUST_CHECK vk_sync_export_win32_handle(struct vk_device *device,
+                                                struct vk_sync *sync,
+                                                void **handle);
+
+VkResult MUST_CHECK vk_sync_set_win32_export_params(struct vk_device *device,
+                                                    struct vk_sync *sync,
+                                                    const void *security_attributes,
+                                                    uint32_t access,
+                                                    const wchar_t *name);
+
 VkResult MUST_CHECK vk_sync_move(struct vk_device *device,
                                  struct vk_sync *dst,
                                  struct vk_sync *src);