blob: 4dee622d01c2e1070b08ccc28300163a2acc12c6 [file] [log] [blame]
From e5fd1411a5d9714b363c960b56f80e14a9f6ed52 Mon Sep 17 00:00:00 2001
From: Chia-I Wu <olvaffe@gmail.com>
Date: Fri, 3 Sep 2021 22:48:18 +0000
Subject: [PATCH] vkr: add support for globalFencing
Squashed commit of the following:
commit 52bec3653108879fef7d78a0206702bfa398a03b
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Tue Jun 8 12:39:00 2021 -0700
vkr: advertise globalFencing
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
commit 65d8fd173207440b978a1c41e48e8437356169e7
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Tue Mar 16 16:22:18 2021 -0700
virgl: put virgl and venus on the same fence timeline
In other words, these functions
virgl_renderer_create_fence
virgl_renderer_poll
virgl_renderer_export_fence
(but not virgl_renderer_get_poll_fd)
now work with venus.
v2: clean up virgl_renderer_poll
v3: global fencing with VIRGL_RENDERER_ASYNC_FENCE_CB
v4: virgl_egl_export_signaled_fence returns a bool not int
(reported by Femi Adegunloye)
v5: fix another fence export bug
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
commit 165eb1a886f9571ee1b59702ac10795070a2b13e
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Tue Mar 16 16:50:02 2021 -0700
vkr: add support for queue_id 0
Becase we advertise only a single VkQueue per-context, we can treat
queue id 0 as the single VkQueue. When the queue hasn't been created,
all fences are treated as cpu fences added to ctx->signaled_syncs.
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
commit d64cf5b24a9bad8330fb3a2c35b659fab3cd8769
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Fri Jun 11 14:33:44 2021 -0700
vkr: implement virgl_context::export_fence
This assumes there is only a single VkQueue, which can be relaxed if we
choose to.
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
commit 979745b2ff42731a6fcb54c34a59f09e92472f5c
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Fri Jun 11 14:41:52 2021 -0700
vkr: restrict to a single VkQueue per-context
This simplifies things when venus joins global fencing.
v2: allow multiple logical devices in some cases
v3: allow more than one logical devices
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
commit 40b4ea810a16822a29bfb651569b9363782488a7
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Tue Mar 16 16:21:35 2021 -0700
virgl: pass fence flags in fence retire callbacks
This allows us to set internal flags and check for them in the
retire callbacks.
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
commit 5bfdb58e08ace4d4caeae9624ff7132a0b6d8de4
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Fri Jun 4 12:24:02 2021 -0700
virgl: add virgl_context::export_fence
This is needed when we get per-context version of
virgl_renderer_export_fence.
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
---
src/venus/vkr_context.c | 112 ++++++++++++-
src/venus/vkr_context.h | 2 +
src/venus/vkr_device.c | 27 ++++
src/venus/vkr_physical_device.c | 17 ++
src/venus/vkr_physical_device.h | 2 +
src/venus/vkr_queue.c | 16 +-
src/venus/vkr_transport.c | 2 +-
src/virgl_context.h | 6 +
src/virglrenderer.c | 272 +++++++++++++++++++++++++++++++-
src/vrend_decode.c | 6 +-
src/vrend_renderer.c | 4 +-
src/vrend_renderer.h | 3 +-
12 files changed, 448 insertions(+), 21 deletions(-)
diff --git a/src/venus/vkr_context.c b/src/venus/vkr_context.c
index 1e301d5..d790d35 100644
--- a/src/venus/vkr_context.c
+++ b/src/venus/vkr_context.c
@@ -124,7 +124,30 @@ vkr_context_submit_fence_locked(struct virgl_context *base,
struct vkr_queue *queue;
VkResult result;
- queue = util_hash_table_get_u64(ctx->object_table, queue_id);
+ if (queue_id) {
+ queue = util_hash_table_get_u64(ctx->object_table, queue_id);
+ } else if (ctx->queue_id_0_queue) {
+ queue = ctx->queue_id_0_queue;
+ } else if (vkr_renderer_flags & VKR_RENDERER_ASYNC_FENCE_CB) {
+ ctx->base.fence_retire(&ctx->base, flags, 0, fence_cookie);
+ return 0;
+ } else {
+ struct vkr_queue_sync *sync = malloc(sizeof(*sync));
+ if (!sync)
+ return -ENOMEM;
+
+ sync->fence = VK_NULL_HANDLE;
+ sync->flags = flags;
+ sync->queue_id = 0;
+ sync->fence_cookie = fence_cookie;
+ list_addtail(&sync->head, &ctx->signaled_syncs);
+
+ if (ctx->fence_eventfd >= 0)
+ write_eventfd(ctx->fence_eventfd, 1);
+
+ return 0;
+ }
+
if (!queue)
return -EINVAL;
struct vkr_device *dev = queue->device;
@@ -170,6 +193,82 @@ vkr_context_submit_fence(struct virgl_context *base,
return ret;
}
+static struct vkr_queue_sync *
+find_sync(const struct list_head *syncs, void *fence_cookie)
+{
+ struct vkr_queue_sync *sync;
+ LIST_FOR_EACH_ENTRY (sync, syncs, head) {
+ if (sync->fence_cookie == fence_cookie)
+ return sync;
+ }
+ return NULL;
+}
+
+static int
+vkr_context_export_fence_locked(struct virgl_context *base,
+ void *fence_cookie,
+ int *out_fd)
+{
+ struct vkr_context *ctx = (struct vkr_context *)base;
+
+ struct vkr_queue_sync *sync = NULL;
+ bool sync_pending = false;
+ if (ctx->queue_id_0_queue) {
+ struct vkr_queue *queue = ctx->queue_id_0_queue;
+
+ if (vkr_renderer_flags & VKR_RENDERER_THREAD_SYNC) {
+ mtx_lock(&queue->mutex);
+ sync = find_sync(&queue->signaled_syncs, fence_cookie);
+ }
+
+ if (!sync) {
+ sync = find_sync(&queue->pending_syncs, fence_cookie);
+ if (sync)
+ sync_pending = true;
+ }
+
+ if (vkr_renderer_flags & VKR_RENDERER_THREAD_SYNC)
+ mtx_unlock(&queue->mutex);
+ }
+
+ if (!sync)
+ sync = find_sync(&ctx->signaled_syncs, fence_cookie);
+
+ if (!sync)
+ return -EINVAL;
+
+ if (!sync_pending) {
+ *out_fd = -1;
+ return 0;
+ }
+
+ struct vkr_device *dev = ctx->queue_id_0_queue->device;
+ if (!dev->physical_device->KHR_external_fence_fd)
+ return -1;
+
+ const VkFenceGetFdInfoKHR get_fd_info = {
+ .sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR,
+ .fence = sync->fence,
+ .handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT,
+ };
+ VkResult result =
+ ctx->instance->get_fence_fd(dev->base.handle.device, &get_fd_info, out_fd);
+
+ return result == VK_SUCCESS ? 0 : -1;
+}
+
+static int
+vkr_context_export_fence(struct virgl_context *base, void *fence_cookie, int *out_fd)
+{
+ struct vkr_context *ctx = (struct vkr_context *)base;
+ int ret;
+
+ mtx_lock(&ctx->mutex);
+ ret = vkr_context_export_fence_locked(base, fence_cookie, out_fd);
+ mtx_unlock(&ctx->mutex);
+ return ret;
+}
+
static void
vkr_context_retire_fences_locked(struct virgl_context *base)
{
@@ -179,10 +278,13 @@ vkr_context_retire_fences_locked(struct virgl_context *base)
assert(!(vkr_renderer_flags & VKR_RENDERER_ASYNC_FENCE_CB));
- /* retire syncs from destroyed devices */
+ /* ctx->signaled_syncs consists of:
+ * 1. sw syncs created before the existance of queue_id_0 queue
+ * 2. not yet retired syncs from destroyed devices
+ */
LIST_FOR_EACH_ENTRY_SAFE (sync, sync_tmp, &ctx->signaled_syncs, head) {
/* queue_id might have already get reused but is opaque to the clients */
- ctx->base.fence_retire(&ctx->base, sync->queue_id, sync->fence_cookie);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id, sync->fence_cookie);
free(sync);
}
list_inithead(&ctx->signaled_syncs);
@@ -201,7 +303,8 @@ vkr_context_retire_fences_locked(struct virgl_context *base)
vkr_queue_get_signaled_syncs(queue, &retired_syncs, &queue_empty);
LIST_FOR_EACH_ENTRY_SAFE (sync, sync_tmp, &retired_syncs, head) {
- ctx->base.fence_retire(&ctx->base, sync->queue_id, sync->fence_cookie);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id,
+ sync->fence_cookie);
vkr_device_free_queue_sync(dev, sync);
}
@@ -580,6 +683,7 @@ vkr_context_init_base(struct vkr_context *ctx)
ctx->base.get_fencing_fd = vkr_context_get_fencing_fd;
ctx->base.retire_fences = vkr_context_retire_fences;
ctx->base.submit_fence = vkr_context_submit_fence;
+ ctx->base.export_fence = vkr_context_export_fence;
}
static void
diff --git a/src/venus/vkr_context.h b/src/venus/vkr_context.h
index 233205b..fd16c64 100644
--- a/src/venus/vkr_context.h
+++ b/src/venus/vkr_context.h
@@ -61,6 +61,8 @@ struct vkr_context {
struct list_head busy_queues;
struct list_head signaled_syncs;
+ struct vkr_queue *queue_id_0_queue;
+
struct vkr_instance *instance;
char *instance_name;
};
diff --git a/src/venus/vkr_device.c b/src/venus/vkr_device.c
index a15d431..fd57d6a 100644
--- a/src/venus/vkr_device.c
+++ b/src/venus/vkr_device.c
@@ -152,6 +152,27 @@ vkr_dispatch_vkCreateDevice(struct vn_dispatch_context *dispatch,
struct vkr_physical_device *physical_dev =
vkr_physical_device_from_handle(args->physicalDevice);
+ /* when external memory/fence/semaphore is enabled, the guest driver
+ * expects queue id 0 to be the queue of this device
+ */
+ bool use_queue_id_0 = false;
+ for (uint32_t i = 0; i < args->pCreateInfo->enabledExtensionCount; i++) {
+ if (!strcmp(args->pCreateInfo->ppEnabledExtensionNames[i],
+ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME) ||
+ !strcmp(args->pCreateInfo->ppEnabledExtensionNames[i],
+ VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME) ||
+ !strcmp(args->pCreateInfo->ppEnabledExtensionNames[i],
+ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME)) {
+ use_queue_id_0 = true;
+ break;
+ }
+ }
+
+ if (use_queue_id_0 && physical_dev->queue_id_0_device) {
+ vkr_log("external fencing might not work since more than one logical device were "
+ "created with external memory/fence/semaphore enabled");
+ }
+
/* append extensions for our own use */
const char **exts = NULL;
uint32_t ext_count = args->pCreateInfo->enabledExtensionCount;
@@ -218,6 +239,9 @@ vkr_dispatch_vkCreateDevice(struct vn_dispatch_context *dispatch,
list_add(&dev->base.track_head, &physical_dev->devices);
vkr_context_add_object(ctx, &dev->base);
+
+ if (use_queue_id_0 && !physical_dev->queue_id_0_device)
+ physical_dev->queue_id_0_device = dev;
}
static void
@@ -354,6 +378,9 @@ vkr_device_destroy(struct vkr_context *ctx, struct vkr_device *dev)
list_del(&dev->base.track_head);
+ if (dev->physical_device->queue_id_0_device == dev)
+ dev->physical_device->queue_id_0_device = NULL;
+
vkr_context_remove_object(ctx, &dev->base);
}
diff --git a/src/venus/vkr_physical_device.c b/src/venus/vkr_physical_device.c
index 259f09e..a0a5898 100644
--- a/src/venus/vkr_physical_device.c
+++ b/src/venus/vkr_physical_device.c
@@ -37,6 +37,9 @@ vkr_instance_enumerate_physical_devices(struct vkr_instance *instance)
if (result != VK_SUCCESS)
return result;
+ /* enumerate at most 1 physical device */
+ count = 1;
+
VkPhysicalDevice *handles = calloc(count, sizeof(*handles));
struct vkr_physical_device **physical_devs = calloc(count, sizeof(*physical_devs));
if (!handles || !physical_devs) {
@@ -46,6 +49,8 @@ vkr_instance_enumerate_physical_devices(struct vkr_instance *instance)
}
result = vkEnumeratePhysicalDevices(instance->base.handle.instance, &count, handles);
+ if (result == VK_INCOMPLETE)
+ result = VK_SUCCESS;
if (result != VK_SUCCESS) {
free(physical_devs);
free(handles);
@@ -354,6 +359,12 @@ vkr_dispatch_vkGetPhysicalDeviceQueueFamilyProperties(
vkGetPhysicalDeviceQueueFamilyProperties(args->physicalDevice,
args->pQueueFamilyPropertyCount,
args->pQueueFamilyProperties);
+
+ if (*args->pQueueFamilyPropertyCount) {
+ *args->pQueueFamilyPropertyCount = 1;
+ if (args->pQueueFamilyProperties)
+ args->pQueueFamilyProperties->queueCount = 1;
+ }
}
static void
@@ -476,6 +487,12 @@ vkr_dispatch_vkGetPhysicalDeviceQueueFamilyProperties2(
vkGetPhysicalDeviceQueueFamilyProperties2(args->physicalDevice,
args->pQueueFamilyPropertyCount,
args->pQueueFamilyProperties);
+
+ if (*args->pQueueFamilyPropertyCount) {
+ *args->pQueueFamilyPropertyCount = 1;
+ if (args->pQueueFamilyProperties)
+ args->pQueueFamilyProperties->queueFamilyProperties.queueCount = 1;
+ }
}
static void
diff --git a/src/venus/vkr_physical_device.h b/src/venus/vkr_physical_device.h
index 0e84b5f..26f71d9 100644
--- a/src/venus/vkr_physical_device.h
+++ b/src/venus/vkr_physical_device.h
@@ -25,6 +25,8 @@ struct vkr_physical_device {
VkPhysicalDeviceMemoryProperties memory_properties;
struct list_head devices;
+
+ struct vkr_device *queue_id_0_device;
};
VKR_DEFINE_OBJECT_CAST(physical_device, VK_OBJECT_TYPE_PHYSICAL_DEVICE, VkPhysicalDevice)
diff --git a/src/venus/vkr_queue.c b/src/venus/vkr_queue.c
index 3298e95..904c435 100644
--- a/src/venus/vkr_queue.c
+++ b/src/venus/vkr_queue.c
@@ -124,7 +124,7 @@ vkr_queue_sync_retire(struct vkr_context *ctx,
struct vkr_queue_sync *sync)
{
if (vkr_renderer_flags & VKR_RENDERER_ASYNC_FENCE_CB) {
- ctx->base.fence_retire(&ctx->base, sync->queue_id, sync->fence_cookie);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id, sync->fence_cookie);
vkr_device_free_queue_sync(dev, sync);
} else {
vkDestroyFence(dev->base.handle.device, sync->fence, NULL);
@@ -170,6 +170,9 @@ vkr_queue_destroy(struct vkr_context *ctx, struct vkr_queue *queue)
list_del(&queue->busy_head);
list_del(&queue->base.track_head);
+ if (ctx->queue_id_0_queue == queue)
+ ctx->queue_id_0_queue = NULL;
+
if (queue->base.id)
vkr_context_remove_object(ctx, &queue->base);
else
@@ -212,7 +215,8 @@ vkr_queue_thread(void *arg)
list_del(&sync->head);
if (vkr_renderer_flags & VKR_RENDERER_ASYNC_FENCE_CB) {
- ctx->base.fence_retire(&ctx->base, sync->queue_id, sync->fence_cookie);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id,
+ sync->fence_cookie);
vkr_device_free_queue_sync(queue->device, sync);
} else {
list_addtail(&sync->head, &queue->signaled_syncs);
@@ -282,6 +286,7 @@ vkr_queue_create(struct vkr_context *ctx,
static void
vkr_queue_assign_object_id(struct vkr_context *ctx,
+ struct vkr_device *dev,
struct vkr_queue *queue,
vkr_object_id id)
{
@@ -295,6 +300,9 @@ vkr_queue_assign_object_id(struct vkr_context *ctx,
queue->base.id = id;
+ if (dev->physical_device->queue_id_0_device == dev)
+ ctx->queue_id_0_queue = queue;
+
vkr_context_add_object(ctx, &queue->base);
}
@@ -331,7 +339,7 @@ vkr_dispatch_vkGetDeviceQueue(struct vn_dispatch_context *dispatch,
const vkr_object_id id =
vkr_cs_handle_load_id((const void **)args->pQueue, VK_OBJECT_TYPE_QUEUE);
- vkr_queue_assign_object_id(ctx, queue, id);
+ vkr_queue_assign_object_id(ctx, dev, queue, id);
}
static void
@@ -352,7 +360,7 @@ vkr_dispatch_vkGetDeviceQueue2(struct vn_dispatch_context *dispatch,
const vkr_object_id id =
vkr_cs_handle_load_id((const void **)args->pQueue, VK_OBJECT_TYPE_QUEUE);
- vkr_queue_assign_object_id(ctx, queue, id);
+ vkr_queue_assign_object_id(ctx, dev, queue, id);
}
static void
diff --git a/src/venus/vkr_transport.c b/src/venus/vkr_transport.c
index c0f3e3e..ff99fde 100644
--- a/src/venus/vkr_transport.c
+++ b/src/venus/vkr_transport.c
@@ -312,7 +312,7 @@ vkr_dispatch_vkGetVenusExperimentalFeatureData100000MESA(
{
const VkVenusExperimentalFeatures100000MESA features = {
.memoryResourceAllocationSize = VK_TRUE,
- .globalFencing = VK_FALSE,
+ .globalFencing = VK_TRUE,
.largeRing = VK_TRUE,
};
diff --git a/src/virgl_context.h b/src/virgl_context.h
index ea86b31..871a148 100644
--- a/src/virgl_context.h
+++ b/src/virgl_context.h
@@ -51,6 +51,7 @@ struct virgl_context_blob {
struct virgl_context;
typedef void (*virgl_context_fence_retire)(struct virgl_context *ctx,
+ uint32_t flags,
uint64_t queue_id,
void *fence_cookie);
@@ -120,6 +121,11 @@ struct virgl_context {
uint32_t flags,
uint64_t queue_id,
void *fence_cookie);
+
+ /* export the fence identified by fence_cookie as a sync fd */
+ int (*export_fence)(struct virgl_context *ctx,
+ void *fence_cookie,
+ int *out_fd);
};
struct virgl_context_foreach_args {
diff --git a/src/virglrenderer.c b/src/virglrenderer.c
index 8d69ce7..b8a0a79 100644
--- a/src/virglrenderer.c
+++ b/src/virglrenderer.c
@@ -33,6 +33,7 @@
#include <unistd.h>
#include "pipe/p_state.h"
+#include "util/u_double_list.h"
#include "util/u_format.h"
#include "util/u_math.h"
#include "vkr_renderer.h"
@@ -46,6 +47,20 @@
#include "virgl_resource.h"
#include "virgl_util.h"
+#include "c11/threads.h"
+
+#define VIRGL_RENDERER_FENCE_FLAG_TIMELINE (1u << 31)
+
+struct timeline_point {
+ uint32_t fence_id;
+
+ bool signaled;
+ /* can be NULL if has signaled or is using ctx0 fencing */
+ struct virgl_context *context;
+
+ struct list_head head;
+};
+
struct global_state {
bool client_initialized;
void *cookie;
@@ -57,6 +72,16 @@ struct global_state {
bool winsys_initialized;
bool vrend_initialized;
bool vkr_initialized;
+
+ bool timeline_async_cb;
+ mtx_t timeline_mutex;
+ /* when timeline_async_cb is true, these can be accessed by the main thread
+ * and the sync threads simultaneously and are protected by timeline_mutex
+ */
+ struct list_head timeline;
+ uint32_t timeline_retired_fence_id;
+ uint32_t ctx0_retired_fence_id;
+ struct list_head free_points;
};
static struct global_state state;
@@ -175,10 +200,114 @@ void virgl_renderer_fill_caps(uint32_t set, uint32_t version,
}
}
+static void timeline_lock(void)
+{
+ /* no check for state.timeline_async_cb because this should be cheap
+ * (cheaper than the if-check?) in the non-contended case
+ */
+ mtx_lock(&state.timeline_mutex);
+}
+
+static void timeline_unlock(void)
+{
+ mtx_unlock(&state.timeline_mutex);
+}
+
+static struct timeline_point *timeline_point_alloc(uint32_t fence_id,
+ struct virgl_context *ctx)
+{
+ struct timeline_point *point;
+
+ timeline_lock();
+ if (LIST_IS_EMPTY(&state.free_points)) {
+ timeline_unlock();
+
+ point = malloc(sizeof(*point));
+ if (!point)
+ return NULL;
+ } else {
+ point = LIST_ENTRY(struct timeline_point, state.free_points.next, head);
+ list_del(&point->head);
+
+ timeline_unlock();
+ }
+
+ point->fence_id = fence_id;
+ point->signaled = false;
+ point->context = ctx;
+
+ return point;
+}
+
+static void timeline_point_add_locked(struct timeline_point *point)
+{
+ list_addtail(&point->head, &state.timeline);
+}
+
+static void timeline_point_free_locked(struct timeline_point *point)
+{
+ list_add(&point->head, &state.free_points);
+}
+
+static bool timeline_point_match_context_locked(const struct timeline_point *point,
+ uint32_t ctx_id)
+{
+ return point->context && point->context->ctx_id == ctx_id;
+}
+
+static void timeline_point_set_signaled_locked(struct timeline_point *point)
+{
+ point->signaled = true;
+ point->context = NULL;
+}
+
+static bool timeline_point_is_signaled_locked(const struct timeline_point *point)
+{
+ return point->signaled ||
+ (!point->context && point->fence_id <= state.ctx0_retired_fence_id);
+}
+
+static uint32_t timeline_poll_retired_fences_locked(void)
+{
+ uint32_t write_fence_id = 0;
+ struct timeline_point *point, *tmp;
+ LIST_FOR_EACH_ENTRY_SAFE(point, tmp, &state.timeline, head) {
+ if (!timeline_point_is_signaled_locked(point))
+ break;
+
+ write_fence_id = point->fence_id;
+ list_del(&point->head);
+ timeline_point_free_locked(point);
+ }
+
+ if (write_fence_id)
+ state.timeline_retired_fence_id = write_fence_id;
+
+ return write_fence_id;
+}
+
static void per_context_fence_retire(struct virgl_context *ctx,
+ uint32_t flags,
uint64_t queue_id,
void *fence_cookie)
{
+ if (flags & VIRGL_RENDERER_FENCE_FLAG_TIMELINE) {
+ if (state.timeline_async_cb) {
+ uint32_t write_fence_id = 0;
+ timeline_lock();
+ timeline_point_set_signaled_locked(fence_cookie);
+ write_fence_id = timeline_poll_retired_fences_locked();
+ timeline_unlock();
+
+ if (write_fence_id)
+ state.cbs->write_fence(state.cookie, write_fence_id);
+ } else {
+ timeline_point_set_signaled_locked(fence_cookie);
+ }
+
+ return;
+ }
+
state.cbs->write_context_fence(state.cookie,
ctx->ctx_id,
queue_id,
@@ -253,6 +382,16 @@ int virgl_renderer_context_create(uint32_t handle, uint32_t nlen, const char *na
void virgl_renderer_context_destroy(uint32_t handle)
{
TRACE_FUNC();
+
+ struct timeline_point *point;
+
+ timeline_lock();
+ LIST_FOR_EACH_ENTRY(point, &state.timeline, head) {
+ if (timeline_point_match_context_locked(point, handle))
+ timeline_point_set_signaled_locked(point);
+ }
+ timeline_unlock();
+
virgl_context_remove(handle);
}
@@ -379,13 +518,46 @@ void virgl_renderer_resource_detach_iov(int res_handle, struct iovec **iov_p, in
virgl_resource_detach_iov(res);
}
-int virgl_renderer_create_fence(int client_fence_id, UNUSED uint32_t ctx_id)
+int virgl_renderer_create_fence(int client_fence_id, uint32_t ctx_id)
{
TRACE_FUNC();
const uint32_t fence_id = (uint32_t)client_fence_id;
- if (state.vrend_initialized)
- return vrend_renderer_create_ctx0_fence(fence_id);
- return EINVAL;
+
+ struct virgl_context *ctx;
+ struct timeline_point *point;
+ int ret;
+
+ /* this only works with crosvm because qemu passes garbage for ctx_id */
+ if (ctx_id) {
+ ctx = virgl_context_lookup(ctx_id);
+ if (!ctx)
+ return -EINVAL;
+ /* use per-context fencing only for venus */
+ if (ctx->capset_id != VIRGL_RENDERER_CAPSET_VENUS)
+ ctx = NULL;
+ } else {
+ ctx = NULL;
+ }
+
+ point = timeline_point_alloc(fence_id, ctx);
+ if (!point)
+ return -ENOMEM;
+
+ if (ctx) {
+ ret = ctx->submit_fence(ctx, VIRGL_RENDERER_FENCE_FLAG_TIMELINE, 0, point);
+ } else {
+ ret = state.vrend_initialized ?
+ vrend_renderer_create_ctx0_fence(fence_id) : EINVAL;
+ }
+
+ timeline_lock();
+ if (ret)
+ timeline_point_free_locked(point);
+ else
+ timeline_point_add_locked(point);
+ timeline_unlock();
+
+ return ret;
}
int virgl_renderer_context_create_fence(uint32_t ctx_id,
@@ -505,11 +677,25 @@ void virgl_renderer_get_rect(int resource_id, struct iovec *iov, unsigned int nu
}
-static void ctx0_fence_retire(void *fence_cookie,
+static void ctx0_fence_retire(UNUSED uint32_t flags,
+ void *fence_cookie,
UNUSED void *retire_data)
{
const uint32_t fence_id = (uint32_t)(uintptr_t)fence_cookie;
- state.cbs->write_fence(state.cookie, fence_id);
+
+ if (state.timeline_async_cb) {
+ uint32_t write_fence_id = 0;
+ timeline_lock();
+ state.ctx0_retired_fence_id = fence_id;
+ write_fence_id = timeline_poll_retired_fences_locked();
+ timeline_unlock();
+
+ if (write_fence_id)
+ state.cbs->write_fence(state.cookie, write_fence_id);
+ } else {
+ /* defer marking timeline_point signaled */
+ state.ctx0_retired_fence_id = fence_id;
+ }
}
static virgl_renderer_gl_context create_gl_context(int scanout_idx, struct virgl_gl_ctx_param *param)
@@ -563,11 +749,33 @@ void *virgl_renderer_get_cursor_data(uint32_t resource_id, uint32_t *width, uint
height);
}
+static bool timeline_poll(struct virgl_context *ctx, UNUSED void *data)
+{
+ /* we use per-context fencing only for venus */
+ if (ctx->capset_id == VIRGL_RENDERER_CAPSET_VENUS)
+ ctx->retire_fences(ctx);
+ return true;
+}
+
void virgl_renderer_poll(void)
{
TRACE_FUNC();
+
+ if (state.timeline_async_cb)
+ return;
+
if (state.vrend_initialized)
vrend_renderer_check_fences();
+
+ struct virgl_context_foreach_args args;
+ args.callback = timeline_poll;
+ args.data = NULL;
+ virgl_context_foreach(&args);
+
+ /* no locking needed because state.timeline_async_cb is false */
+ const uint32_t write_fence_id = timeline_poll_retired_fences_locked();
+ if (write_fence_id)
+ state.cbs->write_fence(state.cookie, write_fence_id);
}
void virgl_renderer_cleanup(UNUSED void *cookie)
@@ -691,6 +899,13 @@ int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks
state.vkr_initialized = true;
}
+#ifdef VIRGL_RENDERER_ASYNC_FENCE_CB
+ state.timeline_async_cb = flags & VIRGL_RENDERER_ASYNC_FENCE_CB;
+#endif
+ mtx_init(&state.timeline_mutex, mtx_plain);
+ list_inithead(&state.timeline);
+ list_inithead(&state.free_points);
+
return 0;
fail:
@@ -965,9 +1180,52 @@ virgl_renderer_resource_export_blob(uint32_t res_id, uint32_t *fd_type, int *fd)
return 0;
}
+static int
+export_signaled_fence(int *fd)
+{
+#ifdef HAVE_EPOXY_EGL_H
+ if (virgl_egl_supports_fences(egl))
+ return virgl_egl_export_signaled_fence(egl, fd) ? 0 : -EINVAL;
+#endif
+ return -1;
+}
+
int
virgl_renderer_export_fence(uint32_t client_fence_id, int *fd)
{
TRACE_FUNC();
- return vrend_renderer_export_ctx0_fence(client_fence_id, fd);
+
+ int ret;
+
+ timeline_lock();
+ if (state.timeline_retired_fence_id >= client_fence_id ||
+ LIST_IS_EMPTY(&state.timeline)) {
+ ret = 0;
+ *fd = -1;
+ } else {
+ struct timeline_point *point;
+
+ ret = -EINVAL;
+ LIST_FOR_EACH_ENTRY(point, &state.timeline, head) {
+ if (point->fence_id != client_fence_id)
+ continue;
+
+ if (timeline_point_is_signaled_locked(point)) {
+ ret = 0;
+ *fd = -1;
+ } else if (point->context) {
+ ret = point->context->export_fence(point->context, point, fd);
+ } else {
+ ret = vrend_renderer_export_ctx0_fence(client_fence_id, fd);
+ }
+ break;
+ }
+ }
+ timeline_unlock();
+
+ /* required by crosvm */
+ if (!ret && *fd == -1)
+ ret = export_signaled_fence(fd);
+
+ return ret;
}
diff --git a/src/vrend_decode.c b/src/vrend_decode.c
index 25a9204..b62470b 100644
--- a/src/vrend_decode.c
+++ b/src/vrend_decode.c
@@ -1479,11 +1479,12 @@ static int vrend_decode_pipe_resource_set_type(struct vrend_context *ctx, const
static void vrend_decode_ctx_init_base(struct vrend_decode_ctx *dctx,
uint32_t ctx_id);
-static void vrend_decode_ctx_fence_retire(void *fence_cookie,
+static void vrend_decode_ctx_fence_retire(uint32_t flags,
+ void *fence_cookie,
void *retire_data)
{
struct vrend_decode_ctx *dctx = retire_data;
- dctx->base.fence_retire(&dctx->base, 0, fence_cookie);
+ dctx->base.fence_retire(&dctx->base, flags, 0, fence_cookie);
}
struct virgl_context *vrend_renderer_context_create(uint32_t handle,
@@ -1759,4 +1760,5 @@ static void vrend_decode_ctx_init_base(struct vrend_decode_ctx *dctx,
ctx->get_fencing_fd = vrend_decode_ctx_get_fencing_fd;
ctx->retire_fences = vrend_decode_ctx_retire_fences;
ctx->submit_fence = vrend_decode_ctx_submit_fence;
+ ctx->export_fence = NULL;
}
diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c
index a0892b1..7d5b71f 100644
--- a/src/vrend_renderer.c
+++ b/src/vrend_renderer.c
@@ -6139,7 +6139,7 @@ static void wait_sync(struct vrend_fence *fence)
pipe_mutex_unlock(vrend_state.fence_mutex);
if (vrend_state.use_async_fence_cb) {
- ctx->fence_retire(fence->fence_cookie, ctx->fence_retire_data);
+ ctx->fence_retire(fence->flags, fence->fence_cookie, ctx->fence_retire_data);
free_fence_locked(fence);
return;
}
@@ -9583,7 +9583,7 @@ void vrend_renderer_check_fences(void)
LIST_FOR_EACH_ENTRY_SAFE(fence, stor, &retired_fences, fences) {
struct vrend_context *ctx = fence->ctx;
- ctx->fence_retire(fence->fence_cookie, ctx->fence_retire_data);
+ ctx->fence_retire(fence->flags, fence->fence_cookie, ctx->fence_retire_data);
free_fence_locked(fence);
}
diff --git a/src/vrend_renderer.h b/src/vrend_renderer.h
index ac4031b..be2f225 100644
--- a/src/vrend_renderer.h
+++ b/src/vrend_renderer.h
@@ -111,7 +111,8 @@ struct vrend_format_table {
uint32_t flags;
};
-typedef void (*vrend_context_fence_retire)(void *fence_cookie,
+typedef void (*vrend_context_fence_retire)(uint32_t flags,
+ void *fence_cookie,
void *retire_data);
struct vrend_if_cbs {
--
2.33.0.153.gba50c8fa24-goog