blob: d246841914d454bd8b5e24c017026fb74187ef6a [file] [log] [blame]
From 720c0358a67499f149b8506256aa5ab24fdbab39 Mon Sep 17 00:00:00 2001
From: Yiwei Zhang <zzyiwei@chromium.org>
Date: Sun, 12 Jun 2022 06:45:40 +0000
Subject: [PATCH] vkr: add support for globalFencing
Squashed commit of the following:
commit a482b8e292d4ad362757b27ba77615dd47652e43
Author: Yiwei Zhang <zzyiwei@chromium.org>
Date: Sat Jun 11 21:14:46 2022 +0000
vkr: disable KHR_external_fence_fd
KHR_external_fence_fd is not currently used by globalFencing and
disabling it is to workaround a mali driver bug
Signed-off-by: Yiwei Zhang <zzyiwei@chromium.org>
commit 55df60448c29fe5de707456e8d8a1e8c32518954
Author: Chia-I Wu <olvaffe@gmail.com>
Date: Fri Dec 10 14:10:48 2021 -0800
proxy: stub out export_fence
commit 8a49207848f3e07134043766e8037976804ed96b
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 4432b6348f9efd0cec122831a22fdb4adf34576c
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 2ed02eea12311cee43a06be6de9c07c47ed6b22e
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 64a9e21825cd3acfe32aef2e9a1fd0eddd7cfbbb
Author: Ryan Neph <ryanneph@google.com>
Date: Thu Mar 31 13:22:05 2022 -0700
virgl: fix fence submit/retire ordering with async_cb
Ensure timeline_point is added to the timeline before
fences are retired, otherwise it is missed.
Signed-off-by: Ryan Neph <ryanneph@google.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
commit 1317a6cd85c5ef0b7ff278e707c2c25ec9f5b905
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 ba5bb7e35df05e6010ef9c97edcad41a4308a277
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 1e49f8726758fb3cd1251f820068fa7c2f353f77
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 bd9f11890830f9f9b136266e8694ab30ce74c463
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 180014b016a8ce52f7f2fb9bc5ba2f433c0f0a88
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>
v2: do the same to proxy_context
commit 135351a425030aa39fc10515416c64c16f407b63
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/drm/drm_fence.c | 2 +-
src/drm/msm/msm_renderer.c | 2 +-
src/proxy/proxy_context.c | 14 +-
src/venus/vkr_context.c | 111 ++++++++++++-
src/venus/vkr_context.h | 2 +
src/venus/vkr_device.c | 27 ++++
src/venus/vkr_physical_device.c | 40 ++++-
src/venus/vkr_physical_device.h | 2 +
src/venus/vkr_queue.c | 15 +-
src/venus/vkr_transport.c | 2 +-
src/virgl_context.h | 6 +
src/virglrenderer.c | 276 +++++++++++++++++++++++++++++++-
src/vrend_decode.c | 6 +-
src/vrend_renderer.c | 4 +-
src/vrend_renderer.h | 3 +-
15 files changed, 481 insertions(+), 31 deletions(-)
diff --git a/src/drm/drm_fence.c b/src/drm/drm_fence.c
index 7215883..ba7158a 100644
--- a/src/drm/drm_fence.c
+++ b/src/drm/drm_fence.c
@@ -80,7 +80,7 @@ thread_sync(void *arg)
if (ret == 1) {
drm_dbg("fence signaled: %p (%" PRIu64 ")", fence, fence->fence_id);
- timeline->vctx->fence_retire(timeline->vctx, timeline->ring_idx,
+ timeline->vctx->fence_retire(timeline->vctx, fence->flags, timeline->ring_idx,
fence->fence_id);
write_eventfd(timeline->eventfd, 1);
drm_fence_destroy(fence);
diff --git a/src/drm/msm/msm_renderer.c b/src/drm/msm/msm_renderer.c
index 84684f4..f678f49 100644
--- a/src/drm/msm/msm_renderer.c
+++ b/src/drm/msm/msm_renderer.c
@@ -1199,7 +1199,7 @@ msm_renderer_submit_fence(struct virgl_context *vctx, uint32_t flags, uint64_t q
* already passed.. so just immediate signal:
*/
if (queue_id == 0) {
- vctx->fence_retire(vctx, queue_id, fence_id);
+ vctx->fence_retire(vctx, flags, queue_id, fence_id);
return 0;
}
diff --git a/src/proxy/proxy_context.c b/src/proxy/proxy_context.c
index f2a035b..ab133f2 100644
--- a/src/proxy/proxy_context.c
+++ b/src/proxy/proxy_context.c
@@ -128,7 +128,7 @@ proxy_context_retire_timeline_fences_locked(struct proxy_context *ctx,
if (!proxy_fence_is_signaled(fence, timeline->cur_seqno) && !force_retire_all)
return false;
- ctx->base.fence_retire(&ctx->base, ring_idx, fence->fence_id);
+ ctx->base.fence_retire(&ctx->base, fence->flags, ring_idx, fence->fence_id);
list_del(&fence->head);
proxy_context_free_fence(ctx, fence);
@@ -193,6 +193,14 @@ proxy_context_sync_thread(void *arg)
return 0;
}
+static int
+proxy_context_export_fence(UNUSED struct virgl_context *ctx,
+ UNUSED uint64_t fence_id,
+ UNUSED int *out_fd)
+{
+ return -ENODEV;
+}
+
static int
proxy_context_submit_fence(struct virgl_context *base,
uint32_t flags,
@@ -227,7 +235,8 @@ proxy_context_submit_fence(struct virgl_context *base,
const struct render_context_op_submit_fence_request req = {
.header.op = RENDER_CONTEXT_OP_SUBMIT_FENCE,
- .flags = flags,
+ /* filter out internal-only VIRGL_RENDERER_FENCE_FLAG_TIMELINE */
+ .flags = flags & VIRGL_RENDERER_FENCE_FLAG_MERGEABLE,
.ring_index = ring_idx,
.seqno = fence->seqno,
};
@@ -532,6 +541,7 @@ proxy_context_init_base(struct proxy_context *ctx)
ctx->base.get_fencing_fd = proxy_context_get_fencing_fd;
ctx->base.retire_fences = proxy_context_retire_fences;
ctx->base.submit_fence = proxy_context_submit_fence;
+ ctx->base.export_fence = proxy_context_export_fence;
}
static bool
diff --git a/src/venus/vkr_context.c b/src/venus/vkr_context.c
index 9ecb9cd..d9e4e00 100644
--- a/src/venus/vkr_context.c
+++ b/src/venus/vkr_context.c
@@ -131,7 +131,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_id);
+ 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_id = fence_id;
+ 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;
@@ -180,6 +203,82 @@ vkr_context_submit_fence(struct virgl_context *base,
return ret;
}
+static struct vkr_queue_sync *
+find_sync(const struct list_head *syncs, uint64_t fence_id)
+{
+ struct vkr_queue_sync *sync;
+ LIST_FOR_EACH_ENTRY (sync, syncs, head) {
+ if (sync->fence_id == fence_id)
+ return sync;
+ }
+ return NULL;
+}
+
+static int
+vkr_context_export_fence_locked(struct virgl_context *base,
+ uint64_t fence_id,
+ 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_id);
+ }
+
+ if (!sync) {
+ sync = find_sync(&queue->pending_syncs, fence_id);
+ 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_id);
+
+ 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;
+ struct vn_device_proc_table *vk = &dev->proc_table;
+
+ 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 = vk->GetFenceFdKHR(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, uint64_t fence_id, 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_id, out_fd);
+ mtx_unlock(&ctx->mutex);
+ return ret;
+}
+
static void
vkr_context_retire_fences_locked(struct virgl_context *base)
{
@@ -189,10 +288,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_id);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id, sync->fence_id);
free(sync);
}
list_inithead(&ctx->signaled_syncs);
@@ -211,7 +313,7 @@ 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_id);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id, sync->fence_id);
vkr_device_free_queue_sync(dev, sync);
}
@@ -561,6 +663,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 ccbf5d9..d43cd0b 100644
--- a/src/venus/vkr_context.h
+++ b/src/venus/vkr_context.h
@@ -63,6 +63,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 4d7aea7..24f677f 100644
--- a/src/venus/vkr_device.c
+++ b/src/venus/vkr_device.c
@@ -84,6 +84,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;
@@ -152,6 +173,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
@@ -281,6 +305,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 19dd9c3..bf443f1 100644
--- a/src/venus/vkr_physical_device.c
+++ b/src/venus/vkr_physical_device.c
@@ -61,6 +61,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) {
@@ -70,6 +73,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);
@@ -235,6 +240,12 @@ vkr_physical_device_init_extensions(struct vkr_physical_device *physical_dev,
if (!(fence_props.externalFenceFeatures & VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT))
physical_dev->KHR_external_fence_fd = false;
+
+ /* XXX Disable KHR_external_fence_fd to workaround a bug in mali sync_fd
+ * external fence. We are okay to do so since export_fence is not used by
+ * any clients and proxy doesn't support export_fence with globalFencing.
+ */
+ physical_dev->KHR_external_fence_fd = false;
}
physical_dev->extensions = exts;
@@ -389,20 +400,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);
@@ -471,6 +489,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
@@ -593,6 +617,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 8cafa8b..c832d32 100644
--- a/src/venus/vkr_physical_device.h
+++ b/src/venus/vkr_physical_device.h
@@ -35,6 +35,8 @@ struct vkr_physical_device {
struct gbm_device *gbm_device;
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 d6b4a19..e78e3cb 100644
--- a/src/venus/vkr_queue.c
+++ b/src/venus/vkr_queue.c
@@ -131,7 +131,7 @@ vkr_queue_sync_retire(struct vkr_context *ctx,
struct vn_device_proc_table *vk = &dev->proc_table;
if (vkr_renderer_flags & VKR_RENDERER_ASYNC_FENCE_CB) {
- ctx->base.fence_retire(&ctx->base, sync->queue_id, sync->fence_id);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id, sync->fence_id);
vkr_device_free_queue_sync(dev, sync);
} else {
vk->DestroyFence(dev->base.handle.device, sync->fence, NULL);
@@ -177,6 +177,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
@@ -225,7 +228,7 @@ 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_id);
+ ctx->base.fence_retire(&ctx->base, sync->flags, sync->queue_id, sync->fence_id);
vkr_device_free_queue_sync(queue->device, sync);
} else {
list_addtail(&sync->head, &queue->signaled_syncs);
@@ -295,6 +298,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)
{
@@ -308,6 +312,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);
}
@@ -344,7 +351,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
@@ -365,7 +372,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 745b124..b3ed04a 100644
--- a/src/venus/vkr_transport.c
+++ b/src/venus/vkr_transport.c
@@ -317,7 +317,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 4bf1370..88c8ee3 100644
--- a/src/virgl_context.h
+++ b/src/virgl_context.h
@@ -52,6 +52,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,
uint64_t fence_id);
@@ -122,6 +123,11 @@ struct virgl_context {
uint32_t flags,
uint64_t queue_id,
uint64_t fence_id);
+
+ /* export the fence identified by fence_cookie as a sync fd */
+ int (*export_fence)(struct virgl_context *ctx,
+ uint64_t fence_id,
+ int *out_fd);
};
struct virgl_context_foreach_args {
diff --git a/src/virglrenderer.c b/src/virglrenderer.c
index b70aa61..838ebf7 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_allocator.h"
@@ -50,6 +51,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;
@@ -63,6 +78,16 @@ struct global_state {
bool vkr_initialized;
bool proxy_initialized;
bool external_winsys_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;
@@ -184,10 +209,115 @@ 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,
uint64_t fence_id)
{
+ if (flags & VIRGL_RENDERER_FENCE_FLAG_TIMELINE) {
+ struct timeline_point *point = (struct timeline_point *)(uintptr_t)fence_id;
+ if (state.timeline_async_cb) {
+ uint32_t write_fence_id = 0;
+ timeline_lock();
+ timeline_point_set_signaled_locked(point);
+ 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(point);
+ }
+
+ return;
+ }
+
state.cbs->write_context_fence(state.cookie,
ctx->ctx_id,
queue_id,
@@ -268,6 +398,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);
}
@@ -394,13 +534,50 @@ 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;
+
+ timeline_lock();
+ timeline_point_add_locked(point);
+ timeline_unlock();
+
+ if (ctx) {
+ ret = ctx->submit_fence(ctx, VIRGL_RENDERER_FENCE_FLAG_TIMELINE, 0, (uintptr_t)point);
+ } else {
+ ret = state.vrend_initialized ?
+ vrend_renderer_create_ctx0_fence(fence_id) : EINVAL;
+ }
+
+ if (ret) {
+ timeline_lock();
+ list_del(&point->head);
+ timeline_point_free_locked(point);
+ timeline_unlock();
+ }
+
+ return ret;
}
int virgl_renderer_context_create_fence(uint32_t ctx_id,
@@ -524,12 +701,25 @@ void virgl_renderer_get_rect(int resource_id, struct iovec *iov, unsigned int nu
}
-static void ctx0_fence_retire(uint64_t fence_id, UNUSED void *retire_data)
+static void ctx0_fence_retire(UNUSED uint32_t flags, uint64_t fence_id, UNUSED void *retire_data)
{
// ctx0 fence_id is created from uint32_t but stored internally as uint64_t,
// so casting back to uint32_t doesn't result in data loss.
assert((fence_id >> 32) == 0);
- state.cbs->write_fence(state.cookie, (uint32_t)fence_id);
+
+ if (state.timeline_async_cb) {
+ uint32_t write_fence_id = 0;
+ timeline_lock();
+ state.ctx0_retired_fence_id = (uint32_t)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 = (uint32_t)fence_id;
+ }
}
static virgl_renderer_gl_context create_gl_context(int scanout_idx, struct virgl_gl_ctx_param *param)
@@ -596,11 +786,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.vrend_initialized)
vrend_renderer_poll();
+
+ if (state.timeline_async_cb)
+ return;
+
+ 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)
@@ -772,6 +984,13 @@ int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks
drm_renderer_init(drm_fd);
}
+#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:
@@ -1109,6 +1328,16 @@ 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_resource_import_blob(const struct virgl_renderer_resource_import_blob_args *args)
{
@@ -1170,5 +1399,38 @@ 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, (uintptr_t)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 3bd3f15..8b57cdb 100644
--- a/src/vrend_decode.c
+++ b/src/vrend_decode.c
@@ -1506,11 +1506,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(uint64_t fence_id,
+static void vrend_decode_ctx_fence_retire(uint32_t flags,
+ uint64_t fence_id,
void *retire_data)
{
struct vrend_decode_ctx *dctx = retire_data;
- dctx->base.fence_retire(&dctx->base, 0, fence_id);
+ dctx->base.fence_retire(&dctx->base, flags, 0, fence_id);
}
struct virgl_context *vrend_renderer_context_create(uint32_t handle,
@@ -1790,4 +1791,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 aad4ef1..546e28f 100644
--- a/src/vrend_renderer.c
+++ b/src/vrend_renderer.c
@@ -6397,7 +6397,7 @@ static void wait_sync(struct vrend_fence *fence)
* by setting fence->ctx to NULL
*/
if (ctx) {
- ctx->fence_retire(fence->fence_id, ctx->fence_retire_data);
+ ctx->fence_retire(fence->flags, fence->fence_id, ctx->fence_retire_data);
}
free_fence_locked(fence);
@@ -9978,7 +9978,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_id, ctx->fence_retire_data);
+ ctx->fence_retire(fence->flags, fence->fence_id, ctx->fence_retire_data);
free_fence_locked(fence);
}
diff --git a/src/vrend_renderer.h b/src/vrend_renderer.h
index a9090ff..9606b96 100644
--- a/src/vrend_renderer.h
+++ b/src/vrend_renderer.h
@@ -113,8 +113,7 @@ struct vrend_format_table {
uint32_t flags;
};
-typedef void (*vrend_context_fence_retire)(uint64_t fence_id,
- void *retire_data);
+typedef void (*vrend_context_fence_retire)(uint32_t flags, uint64_t fence_id, void *retire_data);
struct vrend_if_cbs {
vrend_context_fence_retire ctx0_fence_retire;
--
2.31.0