blob: 4ca522c4eda667a00f2b130485ccbf2e12b74e62 [file] [log] [blame]
From 813979d63cfb77b6b63a612845fa68ffa25fe1bc Mon Sep 17 00:00:00 2001
From: Chia-I Wu <olvaffe@gmail.com>
Date: Wed, 13 Oct 2021 12:36:18 -0700
Subject: [PATCH] vkr: add support for globalFencing
Squashed commit of the following:
commit 851e67ae91171a447f04ca3096642982573089fb
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Wed Oct 13 11:27:34 2021 -0700
vkr: fix subsetAllocation
Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Ryan Neph <ryanneph@google.com>
commit e994c11a4c7735ac49ab09cc28aaa2de6b72bcc3
Author: Ryan Neph <ryanneph@google.com>
Date: Mon Oct 4 16:24:35 2021 -0700
vkr: only report instantiated vkr_physical_devices in device groups
Since venus lies about how many physical devices are available when
global fencing is supported, it must also lie about how physical devices
are organized into groups. If not, we get a segfault when trying to replace
host VkPhysicalDevice handles with vkr_physical_device object ids for
ignored devices and the guest app may try to interact with these
devices if they are exposed through the call to
vkEnumeratePhysicalDeviceGroups.
Signed-off-by: Ryan Neph <ryanneph@google.com>
Reviewed-by: Chia-I Wu <olvaffe@gmail.com>
commit 8f0d06f2ec3811e289e9beabf549c8a392ea883d
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 b5698340b6c10873989eeab81659d5a6c0882d5b
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 20f18e1a508d65a4569779420084ff050657e17a
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 e993f38b930275671a3862a8f4d61f6250ea28dd
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 6d2479445654872484a17b813efed0cf7bb09080
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 a06e2e888b0ae9ddd5203a5d5235747ab64ce67e
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 342845fd63e86e56855a81f5a90755f84f919e74
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 | 34 +++-
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, 460 insertions(+), 26 deletions(-)
diff --git a/src/venus/vkr_context.c b/src/venus/vkr_context.c
index e9433827..3812a992 100644
--- a/src/venus/vkr_context.c
+++ b/src/venus/vkr_context.c
@@ -127,7 +127,30 @@ vkr_context_submit_fence_locked(struct virgl_context *base,
struct vkr_queue *queue;
VkResult result;
- queue = vkr_context_get_object(ctx, queue_id);
+ if (queue_id) {
+ queue = vkr_context_get_object(ctx, 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;
@@ -173,6 +196,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)
{
@@ -182,10 +281,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);
@@ -204,7 +306,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);
}
@@ -583,6 +686,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 uint32_t
diff --git a/src/venus/vkr_context.h b/src/venus/vkr_context.h
index 2524a0a7..e143108e 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 a15d431a..fd57d6a9 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 259f09e9..2c797313 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);
@@ -272,20 +277,27 @@ vkr_dispatch_vkEnumeratePhysicalDeviceGroups(
return;
/* XXX this assumes vkEnumeratePhysicalDevices is called first */
- /* replace VkPhysicalDevice handles by object ids */
+ /* replace VkPhysicalDevice handles by object ids and discard
+ * physical devices without a corresponding vkr_physical_device */
for (uint32_t i = 0; i < *args->pPhysicalDeviceGroupCount; i++) {
const VkPhysicalDeviceGroupProperties *props =
&args->pPhysicalDeviceGroupProperties[i];
VkPhysicalDeviceGroupProperties *out = &orig_props[i];
- out->physicalDeviceCount = props->physicalDeviceCount;
- out->subsetAllocation = props->subsetAllocation;
+ out->physicalDeviceCount = 0;
for (uint32_t j = 0; j < props->physicalDeviceCount; j++) {
const struct vkr_physical_device *physical_dev =
vkr_instance_lookup_physical_device(instance, props->physicalDevices[j]);
- vkr_cs_handle_store_id((void **)&out->physicalDevices[j], physical_dev->base.id,
- VK_OBJECT_TYPE_PHYSICAL_DEVICE);
+
+ if (!physical_dev)
+ continue;
+
+ vkr_cs_handle_store_id(
+ (void **)&out->physicalDevices[out->physicalDeviceCount++],
+ physical_dev->base.id, VK_OBJECT_TYPE_PHYSICAL_DEVICE);
}
+ out->subsetAllocation =
+ out->physicalDeviceCount > 1 ? props->subsetAllocation : VK_FALSE;
}
free(args->pPhysicalDeviceGroupProperties);
@@ -354,6 +366,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 +494,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 0e84b5fe..26f71d91 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 abb8f6ea..4b2b2f23 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 5bf957a7..3176c46f 100644
--- a/src/venus/vkr_transport.c
+++ b/src/venus/vkr_transport.c
@@ -310,7 +310,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 ea86b31e..871a1486 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 44982d71..2e88375c 100644
--- a/src/virglrenderer.c
+++ b/src/virglrenderer.c
@@ -34,6 +34,7 @@
#include <sys/mman.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"
@@ -47,6 +48,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;
@@ -58,6 +73,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;
@@ -176,10 +201,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,
@@ -254,6 +383,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);
}
@@ -380,13 +519,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,
@@ -506,11 +678,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)
@@ -564,11 +750,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)
@@ -692,6 +900,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:
@@ -998,9 +1213,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 934c8b06..df823f53 100644
--- a/src/vrend_decode.c
+++ b/src/vrend_decode.c
@@ -1472,11 +1472,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,
@@ -1752,4 +1753,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 303d3538..6cca134d 100644
--- a/src/vrend_renderer.c
+++ b/src/vrend_renderer.c
@@ -6167,7 +6167,7 @@ static void wait_sync(struct vrend_fence *fence)
mtx_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;
}
@@ -9616,7 +9616,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 ac4031bc..be2f2255 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.882.g93a45727a2-goog