[lavapipe] VK_FUCHSIA_external_semaphore implementation
Bug: 42086544
Change-Id: I4446777f0de0748a3cf58bfeed2d8715fa323f19
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/mesa/+/991013
Reviewed-by: Josh Gargus <jjosh@google.com>
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/mesa/+/1005916
Reviewed-by: John Rosasco <rosasco@google.com>
Commit-Queue: John Rosasco <rosasco@google.com>
diff --git a/src/gallium/frontends/lavapipe/lvp_device.c b/src/gallium/frontends/lavapipe/lvp_device.c
index e0191dc..9acd022 100644
--- a/src/gallium/frontends/lavapipe/lvp_device.c
+++ b/src/gallium/frontends/lavapipe/lvp_device.c
@@ -45,9 +45,10 @@
#include "vulkan/vulkan_core.h"
#if defined(VK_USE_PLATFORM_FUCHSIA)
-#include "lvp_fuchsia_memory.h"
#include <zircon/process.h>
#include <zircon/syscalls.h>
+#include "lvp_fuchsia_memory.h"
+#include "vulkan/runtime/vk_zircon_syncobj.h"
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR) || \
@@ -201,6 +202,7 @@
.GOOGLE_hlsl_functionality1 = true,
#if defined(PIPE_MEMORY_FUCHSIA)
.FUCHSIA_external_memory = true,
+ .FUCHSIA_external_semaphore = true,
#endif
};
@@ -270,9 +272,15 @@
device->drv_options[i] = device->pscreen->get_compiler_options(device->pscreen, PIPE_SHADER_IR_NIR, i);
device->sync_timeline_type = vk_sync_timeline_get_type(&lvp_pipe_sync_type);
- device->sync_types[0] = &lvp_pipe_sync_type;
- device->sync_types[1] = &device->sync_timeline_type.sync;
- device->sync_types[2] = NULL;
+ uint32_t st = 0;
+ device->sync_types[st++] = &lvp_pipe_sync_type;
+ device->sync_types[st++] = &device->sync_timeline_type.sync;
+#ifdef VK_USE_PLATFORM_FUCHSIA
+ device->zircon_sync_type = vk_zircon_syncobj_get_type();
+ device->sync_types[st++] = &device->zircon_sync_type;
+#endif
+ device->sync_types[st] = NULL;
+ assert(st < MAX_SYNC_TYPES);
device->vk.supported_sync_types = device->sync_types;
device->max_images = device->pscreen->get_shader_param(device->pscreen, PIPE_SHADER_FRAGMENT, PIPE_SHADER_CAP_MAX_SHADER_IMAGES);
@@ -2537,6 +2545,16 @@
pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
pExternalSemaphoreProperties->compatibleHandleTypes = 0;
pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+
+#if VK_USE_PLATFORM_FUCHSIA
+ pExternalSemaphoreProperties->compatibleHandleTypes |=
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
+ pExternalSemaphoreProperties->exportFromImportedHandleTypes |=
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
+ pExternalSemaphoreProperties->externalSemaphoreFeatures |=
+ VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
+ VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
+#endif
}
static const VkTimeDomainEXT lvp_time_domains[] = {
diff --git a/src/gallium/frontends/lavapipe/lvp_private.h b/src/gallium/frontends/lavapipe/lvp_private.h
index 45b058e..52cdbed 100644
--- a/src/gallium/frontends/lavapipe/lvp_private.h
+++ b/src/gallium/frontends/lavapipe/lvp_private.h
@@ -86,6 +86,7 @@
#define MAX_PUSH_DESCRIPTORS 32
#define MAX_DESCRIPTOR_UNIFORM_BLOCK_SIZE 4096
#define MAX_PER_STAGE_DESCRIPTOR_UNIFORM_BLOCKS 8
+#define MAX_SYNC_TYPES 4
#ifdef _WIN32
#define lvp_printflike(a, b)
@@ -130,7 +131,10 @@
uint32_t max_images;
struct vk_sync_timeline_type sync_timeline_type;
- const struct vk_sync_type *sync_types[3];
+#if VK_USE_PLATFORM_FUCHSIA
+ struct vk_sync_type zircon_sync_type;
+#endif
+ const struct vk_sync_type *sync_types[MAX_SYNC_TYPES];
VkPhysicalDeviceLimits device_limits;
diff --git a/src/vulkan/runtime/BUILD.gn b/src/vulkan/runtime/BUILD.gn
index 6f18dbe..691983c 100644
--- a/src/vulkan/runtime/BUILD.gn
+++ b/src/vulkan/runtime/BUILD.gn
@@ -160,6 +160,8 @@
"$target_gen_dir/vk_common_entrypoints.c",
"$target_gen_dir/vk_dispatch_trampolines.c",
"$target_gen_dir/vk_physical_device_features.c",
+ "vk_zircon_syncobj.c",
+ "vk_zircon_syncobj.h"
] + runtime_sources + runtime_headers
configs = [ "//build/config:Wno-strict-prototypes" ]
diff --git a/src/vulkan/runtime/vk_device.h b/src/vulkan/runtime/vk_device.h
index 0072858..30092e1 100644
--- a/src/vulkan/runtime/vk_device.h
+++ b/src/vulkan/runtime/vk_device.h
@@ -295,7 +295,7 @@
const VkDeviceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *alloc);
-#if !defined(USE_MAGMA)
+#if !defined(USE_MAGMA) && !defined(__Fuchsia__)
static inline void
vk_device_set_drm_fd(struct vk_device *device, int drm_fd)
{
diff --git a/src/vulkan/runtime/vk_semaphore.c b/src/vulkan/runtime/vk_semaphore.c
index 5e9ab93..afee3c1 100644
--- a/src/vulkan/runtime/vk_semaphore.c
+++ b/src/vulkan/runtime/vk_semaphore.c
@@ -50,6 +50,9 @@
#if defined(USE_MAGMA)
if (type->import_magma_handle)
handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
+#elif defined(__Fuchsia__)
+ if (type->import_zircon_handle)
+ handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
#endif
return handle_types;
@@ -70,6 +73,9 @@
#if defined(USE_MAGMA)
if (type->export_magma_handle)
handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
+#elif defined(__Fuchsia__)
+ if (type->export_zircon_handle)
+ handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
#endif
return handle_types;
@@ -104,7 +110,9 @@
if (req_features & ~(*t)->features)
continue;
- if (handle_types & ~vk_sync_semaphore_handle_types(*t, semaphore_type))
+ uint32_t semaphore_handle_types =
+ vk_sync_semaphore_handle_types(*t, semaphore_type);
+ if (handle_types & ~semaphore_handle_types)
continue;
return *t;
@@ -245,6 +253,7 @@
const struct vk_sync_type *sync_type =
get_semaphore_sync_type(pdevice, semaphore_type, handle_type);
+
if (sync_type == NULL) {
pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
pExternalSemaphoreProperties->compatibleHandleTypes = 0;
@@ -581,8 +590,6 @@
#endif /* !defined(_WIN32) */
#if defined(__Fuchsia__)
-#if defined(USE_MAGMA)
-
VkResult
vk_common_ImportSemaphoreZirconHandleFUCHSIA(VkDevice vk_device,
const VkImportSemaphoreZirconHandleInfoFUCHSIA* info)
@@ -597,7 +604,11 @@
if (info->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR) {
if (semaphore->temporary) {
+#if defined(USE_MAGMA)
result = vk_sync_import_magma_handle(device, semaphore->temporary, info->zirconHandle);
+#else
+ result = vk_sync_import_zircon_handle(device, semaphore->temporary, info->zirconHandle);
+#endif
} else {
const struct vk_sync_type *sync_type =
get_semaphore_sync_type(device->physical, semaphore->type, info->handleType);
@@ -610,7 +621,11 @@
semaphore->temporary->flags |= VK_SYNC_IS_SHAREABLE | VK_SYNC_IS_SHARED;
}
} else {
+#if defined(USE_MAGMA)
result = vk_sync_import_magma_handle(device, &semaphore->permanent, info->zirconHandle);
+#else
+ result = vk_sync_import_zircon_handle(device, &semaphore->permanent, info->zirconHandle);
+#endif
}
return result;
@@ -631,7 +646,11 @@
struct vk_sync *sync = vk_semaphore_get_active_sync(semaphore);
+#if defined(USE_MAGMA)
VkResult result = vk_sync_export_magma_handle(device, sync, pZirconHandle);
+#else
+ VkResult result = vk_sync_export_zircon_handle(device, sync, pZirconHandle);
+#endif
if (result != VK_SUCCESS)
return result;
@@ -647,6 +666,4 @@
return VK_SUCCESS;
}
-#else /* Zircon */
-#endif /* defined(USE_MAGMA) */
#endif /* defined(__Fuchsia__) */
diff --git a/src/vulkan/runtime/vk_sync.c b/src/vulkan/runtime/vk_sync.c
index dcd4e42..42f7cfc 100644
--- a/src/vulkan/runtime/vk_sync.c
+++ b/src/vulkan/runtime/vk_sync.c
@@ -407,6 +407,37 @@
return VK_SUCCESS;
}
+#elif defined(__Fuchsia__)
+VkResult
+vk_sync_import_zircon_handle(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t handle)
+{
+ VkResult result = sync->type->import_zircon_handle(device, sync, handle);
+ if (unlikely(result != VK_SUCCESS))
+ return result;
+
+ sync->flags |= VK_SYNC_IS_SHAREABLE |
+ VK_SYNC_IS_SHARED;
+
+ return VK_SUCCESS;
+}
+
+VkResult
+vk_sync_export_zircon_handle(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t* handle_out)
+{
+ assert(sync->flags & VK_SYNC_IS_SHAREABLE);
+
+ VkResult result = sync->type->export_zircon_handle(device, sync, handle_out);
+ if (unlikely(result != VK_SUCCESS))
+ return result;
+
+ sync->flags |= VK_SYNC_IS_SHARED;
+
+ return VK_SUCCESS;
+}
#endif
VkResult
diff --git a/src/vulkan/runtime/vk_sync.h b/src/vulkan/runtime/vk_sync.h
index 769109d..129ca8f 100644
--- a/src/vulkan/runtime/vk_sync.h
+++ b/src/vulkan/runtime/vk_sync.h
@@ -279,8 +279,16 @@
uint32_t handle);
VkResult (*export_magma_handle)(struct vk_device *device,
- struct vk_sync *sync,
- uint32_t* handle_out);
+ struct vk_sync *sync,
+ uint32_t* handle_out);
+#elif defined(__Fuchsia__)
+ VkResult (*import_zircon_handle)(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t handle);
+
+ VkResult (*export_zircon_handle)(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t* handle_out);
#endif
};
@@ -379,6 +387,14 @@
VkResult MUST_CHECK vk_sync_export_magma_handle(struct vk_device *device,
struct vk_sync *sync,
uint32_t *handle_out);
+#elif defined(__Fuchsia__)
+VkResult MUST_CHECK vk_sync_import_zircon_handle(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t handle);
+
+VkResult MUST_CHECK vk_sync_export_zircon_handle(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t *handle_out);
#endif
VkResult MUST_CHECK vk_sync_move(struct vk_device *device,
diff --git a/src/vulkan/runtime/vk_zircon_syncobj.c b/src/vulkan/runtime/vk_zircon_syncobj.c
new file mode 100644
index 0000000..475a8a6
--- /dev/null
+++ b/src/vulkan/runtime/vk_zircon_syncobj.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright © 2024 The Fuchsia Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "vk_zircon_syncobj.h"
+
+#include <string.h>
+#include <zircon/syscalls.h>
+#include <zircon/syscalls/port.h>
+
+#include "vk_device.h"
+#include "vk_log.h"
+#include "vk_util.h"
+
+static struct vk_zircon_syncobj *
+to_zircon_syncobj(struct vk_sync *sync)
+{
+ assert(vk_sync_type_is_zircon_syncobj(sync->type));
+ return container_of(sync, struct vk_zircon_syncobj, base);
+}
+
+static VkResult vk_zircon_syncobj_init(struct vk_device *device,
+ struct vk_sync *sync,
+ uint64_t initial_value) {
+ struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
+ zx_handle_t handle = (zx_handle_t) initial_value;
+
+ assert((sync->flags & VK_SYNC_IS_TIMELINE) == 0);
+
+ /* |initial_value| is overloaded to convey zx_handle_t handles. */
+ /* If it's set and not 1, it's a zx_handle_t. */
+ if (initial_value && initial_value != 1) {
+ assert((initial_value >> 32) == 0);
+ /* Close existing handle if it exists. */
+ if (sobj->semaphore != ZX_HANDLE_INVALID) {
+ if (zx_handle_close(sobj->semaphore) != ZX_OK) {
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to close handle: %m");
+ }
+ }
+
+ /* Duplicate and store |handle| into |sobj->semaphore|. */
+ zx_status_t status =
+ zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS,
+ (zx_handle_t *) &sobj->semaphore);
+ if(status != ZX_OK) {
+ if(status == ZX_ERR_BAD_HANDLE)
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to initialize, bad Zircon handle: %m");
+ else
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to initialize: %m");
+ }
+
+ /* Close original |handle| to invalidate it. */
+ if (zx_handle_close(handle) != ZX_OK) {
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to close handle: %m");
+ }
+ } else {
+ zx_status_t status = zx_event_create(0, (zx_handle_t *) &sobj->semaphore);
+ if (status != ZX_OK) {
+ assert(status == ZX_ERR_NO_MEMORY);
+ return vk_errorf(device, VK_ERROR_OUT_OF_HOST_MEMORY,
+ "Unable to create semaphore: %m");
+ }
+ }
+
+ if (initial_value == 1) {
+ zx_status_t status =
+ zx_object_signal((zx_handle_t) sobj->semaphore, 0u, ZX_EVENT_SIGNALED);
+ if(status != ZX_OK) {
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to signal semaphore: %m");
+ }
+ }
+
+ return VK_SUCCESS;
+}
+
+void
+vk_zircon_syncobj_finish(struct vk_device *device,
+ struct vk_sync *sync)
+{
+ struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
+ if (sobj->semaphore == ZX_HANDLE_INVALID) {
+ vk_logi(VK_LOG_OBJS(device), "Attempt to close invalid Zircon event handle.\n");
+ return;
+ }
+ zx_status_t status = zx_handle_close(sobj->semaphore);
+ if (status == ZX_ERR_BAD_HANDLE) {
+ vk_loge(VK_LOG_OBJS(device), "Attempt to close bad Zircon event handle.\n");
+ return;
+ }
+ assert(status == ZX_OK);
+ sobj->semaphore = ZX_HANDLE_INVALID;
+}
+
+static VkResult
+vk_zircon_syncobj_signal(struct vk_device *device,
+ struct vk_sync *sync,
+ uint64_t value)
+{
+ struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
+ if (sobj->semaphore == ZX_HANDLE_INVALID) {
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Attempt to signal invalid Zircon event handle: %m");
+ }
+
+ zx_status_t status = zx_object_signal(sobj->semaphore, 0u, ZX_EVENT_SIGNALED);
+ switch(status) {
+ case ZX_OK:
+ return VK_SUCCESS;
+ case ZX_ERR_BAD_HANDLE:
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to signal semaphore, bad Zircon handle: %m");
+ default:
+ return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to signal semaphore: %m");
+ }
+}
+
+static VkResult
+vk_zircon_syncobj_reset(struct vk_device *device,
+ struct vk_sync *sync)
+{
+ struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
+ if (sobj->semaphore == ZX_HANDLE_INVALID) {
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Attempt to reset invalid Zircon event handle: %m");
+ }
+
+ zx_status_t status = zx_object_signal(sobj->semaphore, ZX_EVENT_SIGNALED, 0u);
+ switch(status) {
+ case ZX_OK:
+ return VK_SUCCESS;
+ case ZX_ERR_BAD_HANDLE:
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to reset semaphore, bad Zircon handle: %m");
+ default:
+ return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to reset semaphore: %m");
+ }
+}
+
+static VkResult
+vk_zircon_syncobj_wait_many(struct vk_device *device,
+ uint32_t wait_count,
+ const struct vk_sync_wait *waits,
+ enum vk_sync_wait_flags wait_flags,
+ uint64_t abs_timeout_ns)
+{
+ assert((wait_flags & VK_SYNC_WAIT_PENDING) == 0);
+ if(!wait_count) return VK_SUCCESS;
+
+ /* Syncobj timeouts are signed. */
+ abs_timeout_ns = MIN2(abs_timeout_ns, (uint64_t)ZX_TIME_INFINITE);
+
+ /* Create port on which to monitor signaled semaphores. */
+ zx_handle_t port = ZX_HANDLE_INVALID;
+ zx_status_t status = zx_port_create(0, &port);
+ if(status != ZX_OK) {
+ return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to create port: %m");
+ }
+
+ /* Enqueue all semaphores (events) on the port. */
+ for (uint32_t i = 0; i < wait_count; i++) {
+ uint64_t semaphore = to_zircon_syncobj(waits[i].sync)->semaphore;
+ status = zx_object_wait_async(semaphore, port, i /* key */, ZX_EVENT_SIGNALED,
+ 0 /* options */);
+ if (status != ZX_OK) {
+ return vk_errorf(device, VK_ERROR_UNKNOWN, "Unable to queue semaphore on port: %m");
+ }
+ }
+
+ status = ZX_OK;
+ const bool kWaitAll = (wait_flags & VK_SYNC_WAIT_ANY) == 0;
+ zx_port_packet_t packet = {0};
+ if(kWaitAll) {
+ const size_t kMaxMasks = 16;
+ uint64_t masks[kMaxMasks];
+ uint64_t *masks_base = NULL;
+ uint64_t *masks_ptr = masks;
+ const uint32_t num_masks = ((wait_count - 1) / 64) + 1;
+ if(num_masks > kMaxMasks) {
+ masks_base = (uint64_t *) malloc(num_masks * sizeof(uint64_t));
+ if(!masks_base) {
+ return vk_errorf(device, VK_ERROR_OUT_OF_HOST_MEMORY, "Masks alloc failed: %m");
+ }
+ masks_ptr = masks_base;
+ }
+ memset(masks_ptr, 0xFF, num_masks * sizeof(uint64_t));
+
+ /* Clear all unused bits of the last mask. */
+ *(masks_ptr + num_masks - 1) >>= (64 - (wait_count % 64));
+ int cleared_masks = 0;
+ while(cleared_masks < num_masks) {
+ status = zx_port_wait(port, abs_timeout_ns, &packet);
+ const uint32_t mask_index = packet.key / 64;
+ switch(status) {
+ case ZX_OK:
+ /* Knock out the bit for the current signal. */
+ *(masks_ptr + mask_index) &= ~(1 << (packet.key % 64));
+ for(cleared_masks = 0; cleared_masks < num_masks; cleared_masks++) {
+ if(*(masks_ptr + cleared_masks)) {
+ break;
+ }
+ }
+ break;
+ default:
+ cleared_masks = num_masks;
+ break;
+ }
+ }
+ if(masks_base) free(masks_base);
+ } else {
+ /* Wait for any event signal. */
+ status = zx_port_wait(port, abs_timeout_ns, &packet);
+ }
+
+ switch(status) {
+ case ZX_OK:
+ return VK_SUCCESS;
+ case ZX_ERR_TIMED_OUT:
+ return vk_errorf(device, VK_TIMEOUT, "zx_port_wait timed out: %m");
+ default:
+ return vk_errorf(device, VK_ERROR_UNKNOWN, "zx_port_wait failed: %m");
+ }
+}
+
+static VkResult
+vk_zircon_syncobj_import_zircon_handle(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t handle)
+{
+ struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
+ zx_handle_t semaphore = ZX_HANDLE_INVALID;
+
+ zx_status_t status =
+ zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &semaphore);
+ if(status != ZX_OK) {
+ if(status == ZX_ERR_BAD_HANDLE)
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to import, bad Zircon handle: %m");
+ else
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to import handle: %m");
+ }
+
+ if (zx_handle_close(handle) != ZX_OK) {
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to import handle: %m");
+ }
+
+ status = zx_handle_close((zx_handle_t) sobj->semaphore);
+ switch(status) {
+ case ZX_OK:
+ sobj->semaphore = (uint64_t) semaphore;
+ return VK_SUCCESS;
+ case ZX_ERR_BAD_HANDLE:
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to import, bad Zircon handle: %m");
+ default:
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to import handle: %m");
+ }
+}
+
+static VkResult
+vk_zircon_syncobj_export_zircon_handle(struct vk_device *device,
+ struct vk_sync *sync,
+ uint32_t* handle_out)
+{
+ struct vk_zircon_syncobj *sobj = to_zircon_syncobj(sync);
+
+ if (sobj->semaphore == ZX_HANDLE_INVALID) {
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Attempt to export invalid Zircon event handle: %m");
+ }
+
+ zx_status_t status = zx_handle_duplicate(sobj->semaphore,
+ ZX_RIGHT_SAME_RIGHTS, handle_out);
+ switch(status) {
+ case ZX_OK:
+ return VK_SUCCESS;
+ case ZX_ERR_BAD_HANDLE:
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to export, bad Zircon handle: %m");
+ default:
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to export handle: %m");
+ }
+}
+
+static VkResult
+vk_zircon_syncobj_move(struct vk_device *device,
+ struct vk_sync *dst,
+ struct vk_sync *src)
+{
+ struct vk_zircon_syncobj *dst_sobj = to_zircon_syncobj(dst);
+ struct vk_zircon_syncobj *src_sobj = to_zircon_syncobj(src);
+
+ if(src_sobj->semaphore == ZX_HANDLE_INVALID) {
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to move invalid handle: %m");
+ }
+
+ zx_handle_t handle_out = ZX_HANDLE_INVALID;
+ zx_status_t status = zx_handle_duplicate(src_sobj->semaphore,
+ ZX_RIGHT_SAME_RIGHTS, &handle_out);
+ if(status != ZX_OK) {
+ if(status == ZX_ERR_BAD_HANDLE)
+ return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
+ "Unable to move, bad Zircon handle: %m");
+ else
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to move handle: %m");
+ }
+
+ if (zx_handle_close((zx_handle_t) src_sobj->semaphore) != ZX_OK) {
+ return vk_errorf(device, VK_ERROR_UNKNOWN,
+ "Unable to close (move) src semaphore handle: %m");
+ }
+
+ src_sobj->semaphore = ZX_HANDLE_INVALID;
+
+ *dst_sobj = *src_sobj;
+ dst_sobj->semaphore = (uint64_t) handle_out;
+
+ return VK_SUCCESS;
+}
+
+struct vk_sync_type
+vk_zircon_syncobj_get_type(void)
+{
+ struct vk_sync_type type = {
+ .size = sizeof(struct vk_zircon_syncobj),
+ .features = VK_SYNC_FEATURE_BINARY |
+ VK_SYNC_FEATURE_GPU_WAIT |
+ VK_SYNC_FEATURE_CPU_RESET |
+ VK_SYNC_FEATURE_CPU_SIGNAL |
+ VK_SYNC_FEATURE_CPU_WAIT |
+ VK_SYNC_FEATURE_WAIT_PENDING |
+ VK_SYNC_FEATURE_WAIT_ANY,
+ .init = vk_zircon_syncobj_init,
+ .finish = vk_zircon_syncobj_finish,
+ .move = vk_zircon_syncobj_move,
+ .signal = vk_zircon_syncobj_signal,
+ .reset = vk_zircon_syncobj_reset,
+ .wait_many = vk_zircon_syncobj_wait_many,
+ .import_zircon_handle = vk_zircon_syncobj_import_zircon_handle,
+ .export_zircon_handle = vk_zircon_syncobj_export_zircon_handle,
+ };
+
+ return type;
+}
diff --git a/src/vulkan/runtime/vk_zircon_syncobj.h b/src/vulkan/runtime/vk_zircon_syncobj.h
new file mode 100644
index 0000000..de600fb
--- /dev/null
+++ b/src/vulkan/runtime/vk_zircon_syncobj.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2024 The Fuchsia Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef VK_ZIRCON_SYNCOBJ_H
+#define VK_ZIRCON_SYNCOBJ_H
+
+#include "vk_sync.h"
+
+#include "util/macros.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct vk_zircon_syncobj {
+ struct vk_sync base;
+ uint64_t semaphore;
+};
+
+void vk_zircon_syncobj_finish(struct vk_device *device,
+ struct vk_sync *sync);
+
+static inline bool
+vk_sync_type_is_zircon_syncobj(const struct vk_sync_type *type)
+{
+ return type->finish == vk_zircon_syncobj_finish;
+}
+
+static inline struct vk_zircon_syncobj *
+vk_sync_as_zircon_syncobj(struct vk_sync *sync)
+{
+ if (!vk_sync_type_is_zircon_syncobj(sync->type))
+ return NULL;
+
+ return container_of(sync, struct vk_zircon_syncobj, base);
+}
+
+struct vk_sync_type vk_zircon_syncobj_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VK_ZIRCON_SYNCOBJ_H */