| 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 |
| |