blob: 317a51bafade9cb2265e38a0366a360cf652e683 [file] [log] [blame]
/*
* Copyright © 2024 Google, LLC
*
* based in part on lvp_pipe_sync.c which is
* Copyright © 2022 Collabora Ltd.
*
* 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 <zircon/errors.h>
#include <zircon/syscalls.h>
#include <zircon/time.h>
#include <zircon/types.h>
#include "lvp_private.h"
#include "util/log.h"
#include "vk_log.h"
#include "vulkan/vulkan_core.h"
#define ZX_STATUS_VK_RTN(status, device, message) \
switch (status) { \
case ZX_OK: \
return VK_SUCCESS; \
case ZX_ERR_BAD_HANDLE: \
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE, \
"%s - Bad Zircon handle: %m", message); \
case ZX_ERR_TIMED_OUT: \
return VK_TIMEOUT; \
default: \
return vk_errorf(device, VK_ERROR_UNKNOWN, \
"%s - Unknown Zircon error: %m", message); \
}
/* No-op when status == ZX_OK. */
#define ZX_STATUS_VK_RTN_ERR(status, device, message) \
switch (status) { \
case ZX_OK: \
break; \
case ZX_ERR_BAD_HANDLE: \
return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE, \
"%s - Bad Zircon handle: %m", message); \
case ZX_ERR_TIMED_OUT: \
return VK_TIMEOUT; \
default: \
return vk_errorf(device, VK_ERROR_UNKNOWN, \
"%s - Unknown Zircon error: %m", message); \
}
static bool is_signaled(zx_handle_t handle) {
zx_status_t status = zx_object_wait_one(handle, ZX_EVENT_SIGNALED, 0, NULL);
return (status == ZX_OK);
}
static void lvp_pipe_sync_fuchsia_validate(
ASSERTED struct lvp_pipe_sync_fuchsia *sync) {
if (is_signaled(sync->signaled)) assert(sync->fence == NULL);
}
static VkResult lvp_pipe_sync_fuchsia_init(UNUSED struct vk_device *vk_device,
struct vk_sync *vk_sync,
uint64_t initial_value) {
struct lvp_pipe_sync_fuchsia *sync =
vk_sync_as_lvp_pipe_sync_fuchsia(vk_sync);
zx_handle_t handle = (zx_handle_t)initial_value;
/* |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 (sync->signaled != ZX_HANDLE_INVALID) {
if (zx_handle_close(sync->signaled) != ZX_OK) {
return vk_errorf(vk_device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Init (close): %m");
}
}
/* Duplicate and store |handle| into |sync->signaled|. */
zx_status_t status =
zx_handle_replace(handle, ZX_RIGHT_SAME_RIGHTS, &sync->signaled);
ZX_STATUS_VK_RTN_ERR(status, vk_device, "Init (replace)");
} else {
zx_status_t status = zx_event_create(0, &sync->signaled);
if (status != ZX_OK) {
assert(status == ZX_ERR_NO_MEMORY);
return vk_errorf(vk_device, VK_ERROR_OUT_OF_HOST_MEMORY,
"Init (event create): %m");
}
if (initial_value == 1) {
zx_status_t status =
zx_object_signal(sync->signaled, 0u, ZX_EVENT_SIGNALED);
ZX_STATUS_VK_RTN(status, vk_device, "Init (signal)");
}
}
sync->fence = NULL;
return VK_SUCCESS;
}
static void lvp_pipe_sync_fuchsia_finish(struct vk_device *vk_device,
struct vk_sync *vk_sync) {
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
struct lvp_pipe_sync_fuchsia *sync =
vk_sync_as_lvp_pipe_sync_fuchsia(vk_sync);
lvp_pipe_sync_fuchsia_validate(sync);
if (sync->fence)
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
if (sync->signaled == ZX_HANDLE_INVALID) {
vk_logi(VK_LOG_OBJS(device), "Finish - invalid handle\n");
return;
}
zx_status_t status = zx_handle_close(sync->signaled);
switch (status) {
case ZX_OK:
break;
case ZX_ERR_BAD_HANDLE:
vk_loge(VK_LOG_OBJS(device),
"Finish - attempt to close bad Zircon event handle.\n");
return;
default:
vk_loge(VK_LOG_OBJS(device), "Finish - Unknown error.\n");
return;
}
sync->signaled = ZX_HANDLE_INVALID;
}
void lvp_pipe_sync_fuchsia_signal_with_fence(struct lvp_device *device,
struct lvp_pipe_sync_fuchsia *sync,
struct pipe_fence_handle *fence) {
lvp_pipe_sync_fuchsia_validate(sync);
if (fence == NULL) {
if (zx_object_signal(sync->signaled, 0u, ZX_EVENT_SIGNALED) != ZX_OK) {
mesa_loge("Signal with fence (== NULL) failed.");
}
} else {
if (zx_object_signal(sync->signaled, ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
mesa_loge("Signal with fence (!= NULL) failed.");
}
}
device->pscreen->fence_reference(device->pscreen, &sync->fence, fence);
}
static VkResult lvp_pipe_sync_fuchsia_signal(struct vk_device *vk_device,
struct vk_sync *vk_sync,
uint64_t value) {
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
struct lvp_pipe_sync_fuchsia *sync =
vk_sync_as_lvp_pipe_sync_fuchsia(vk_sync);
lvp_pipe_sync_fuchsia_validate(sync);
zx_status_t status = zx_object_signal(sync->signaled, 0u, ZX_EVENT_SIGNALED);
if (status == ZX_OK) {
if (sync->fence)
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
return VK_SUCCESS;
}
ZX_STATUS_VK_RTN(status, vk_device, "Signal (signal)");
}
static VkResult lvp_pipe_sync_fuchsia_reset(struct vk_device *vk_device,
struct vk_sync *vk_sync) {
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
struct lvp_pipe_sync_fuchsia *sync =
vk_sync_as_lvp_pipe_sync_fuchsia(vk_sync);
lvp_pipe_sync_fuchsia_validate(sync);
if (sync->signaled == ZX_HANDLE_INVALID) {
return vk_errorf(
device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Reset - Attempt to reset invalid Zircon event handle: %m");
}
zx_status_t status = zx_object_signal(sync->signaled, ZX_EVENT_SIGNALED, 0u);
if (status == ZX_OK) {
if (sync->fence)
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
}
ZX_STATUS_VK_RTN(status, vk_device, "Reset (signal)");
}
static VkResult lvp_pipe_sync_fuchsia_move(struct vk_device *vk_device,
struct vk_sync *vk_dst,
struct vk_sync *vk_src) {
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
struct lvp_pipe_sync_fuchsia *dst = vk_sync_as_lvp_pipe_sync_fuchsia(vk_dst);
struct lvp_pipe_sync_fuchsia *src = vk_sync_as_lvp_pipe_sync_fuchsia(vk_src);
if (src->signaled == ZX_HANDLE_INVALID) {
return vk_errorf(&device->vk, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Move - Unable to move invalid handle: %m");
}
zx_handle_t handle_out = ZX_HANDLE_INVALID;
zx_status_t status =
zx_handle_replace(src->signaled, ZX_RIGHT_SAME_RIGHTS, &handle_out);
ZX_STATUS_VK_RTN_ERR(status, &device->vk, "Move (replace)");
src->signaled = ZX_HANDLE_INVALID;
*dst = *src;
dst->signaled = handle_out;
return VK_SUCCESS;
}
static VkResult lvp_pipe_sync_fuchsia_wait(struct vk_device *vk_device,
struct vk_sync *vk_sync,
uint64_t wait_value,
enum vk_sync_wait_flags wait_flags,
uint64_t abs_timeout_ns) {
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
struct lvp_pipe_sync_fuchsia *sync =
vk_sync_as_lvp_pipe_sync_fuchsia(vk_sync);
assert(!(wait_flags & VK_SYNC_WAIT_ANY));
lvp_pipe_sync_fuchsia_validate(sync);
zx_status_t status = ZX_OK;
zx_time_t now_ns = zx_clock_get_monotonic();
abs_timeout_ns =
(abs_timeout_ns > ZX_TIME_INFINITE) ? ZX_TIME_INFINITE : abs_timeout_ns;
while (!is_signaled(sync->signaled) && !sync->fence) {
if (now_ns >= abs_timeout_ns) return VK_TIMEOUT;
status = zx_object_wait_one(sync->signaled, ZX_EVENT_SIGNALED,
(zx_time_t)abs_timeout_ns, NULL);
ZX_STATUS_VK_RTN_ERR(status, device, "Wait (wait one)");
lvp_pipe_sync_fuchsia_validate(sync);
now_ns = zx_clock_get_monotonic();
}
if (is_signaled(sync->signaled) || (wait_flags & VK_SYNC_WAIT_PENDING))
return VK_SUCCESS;
/* Grab a reference before we drop the lock */
struct pipe_fence_handle *fence = NULL;
device->pscreen->fence_reference(device->pscreen, &fence, sync->fence);
uint64_t rel_timeout_ns =
now_ns >= abs_timeout_ns ? 0 : abs_timeout_ns - now_ns;
bool complete = device->pscreen->fence_finish(device->pscreen, NULL, fence,
rel_timeout_ns);
device->pscreen->fence_reference(device->pscreen, &fence, NULL);
lvp_pipe_sync_fuchsia_validate(sync);
if (!complete) return VK_TIMEOUT;
if (sync->fence == fence) {
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
status = zx_object_signal(sync->signaled, 0u, ZX_EVENT_SIGNALED);
ZX_STATUS_VK_RTN_ERR(status, device, "Wait (signal)");
}
return VK_SUCCESS;
}
static VkResult lvp_pipe_sync_fuchsia_import_zircon_handle(
struct vk_device *device, struct vk_sync *sync, uint32_t handle) {
struct lvp_pipe_sync_fuchsia *sobj = vk_sync_as_lvp_pipe_sync_fuchsia(sync);
zx_handle_t semaphore = ZX_HANDLE_INVALID;
zx_status_t status =
zx_handle_replace(handle, ZX_RIGHT_SAME_RIGHTS, &semaphore);
ZX_STATUS_VK_RTN_ERR(status, device, "Import (replace)");
status = zx_handle_close(sobj->signaled);
if (status == ZX_OK) {
sobj->signaled = semaphore;
return VK_SUCCESS;
}
zx_handle_close(semaphore);
ZX_STATUS_VK_RTN(status, device, "Import (close - signaled)");
}
static VkResult lvp_pipe_sync_fuchsia_export_zircon_handle(
struct vk_device *device, struct vk_sync *sync, uint32_t *handle_out) {
struct lvp_pipe_sync_fuchsia *sobj = vk_sync_as_lvp_pipe_sync_fuchsia(sync);
if (sobj->signaled == ZX_HANDLE_INVALID) {
return vk_errorf(
device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
"Export - Attempt to export invalid Zircon event handle: %m");
}
zx_status_t status =
zx_handle_duplicate(sobj->signaled, ZX_RIGHT_SAME_RIGHTS, handle_out);
ZX_STATUS_VK_RTN(status, device, "Export (duplicate)");
}
const struct vk_sync_type lvp_pipe_sync_fuchsia_type = {
.size = sizeof(struct lvp_pipe_sync_fuchsia),
.features = VK_SYNC_FEATURE_BINARY | VK_SYNC_FEATURE_GPU_WAIT |
VK_SYNC_FEATURE_GPU_MULTI_WAIT | VK_SYNC_FEATURE_CPU_WAIT |
VK_SYNC_FEATURE_CPU_RESET | VK_SYNC_FEATURE_CPU_SIGNAL |
VK_SYNC_FEATURE_WAIT_PENDING,
.init = lvp_pipe_sync_fuchsia_init,
.finish = lvp_pipe_sync_fuchsia_finish,
.signal = lvp_pipe_sync_fuchsia_signal,
.reset = lvp_pipe_sync_fuchsia_reset,
.move = lvp_pipe_sync_fuchsia_move,
.wait = lvp_pipe_sync_fuchsia_wait,
.import_zircon_handle = lvp_pipe_sync_fuchsia_import_zircon_handle,
.export_zircon_handle = lvp_pipe_sync_fuchsia_export_zircon_handle,
};