[PATCH 2/2] wined3d: Avoid waiting for a command buffer to finish executing to read off its queries.

Jan Sikorski jsikorski at codeweavers.com
Thu Feb 10 08:21:56 CST 2022


Some games wait for query results on the frame executing the query,
expecting that enough time have passed for it to be available. By
requiring that the entire command buffer is done, we risk stalling the
whole frame if it wasn't flushed along the way. This patch drops this
requirement.

Mainly, we need to ensure that we don't call vkGetQueryPoolResults()
before the reset command is executed, and accidentally retrieve result
from previous usage. Using host query reset doesn't quite solve the
problem, as it might get called too early, if the application decides to
reuse a query without waiting for results. Besides, we want to support
devices where host query reset is not available.

To make it work, when a Vulkan query is no longer needed, we queue it
for resetting at command buffer submission time, and only mark it free
for reuse after this command buffer is finished executing.

Signed-off-by: Jan Sikorski <jsikorski at codeweavers.com>
---
 dlls/wined3d/context_vk.c      | 89 +++++++++++++++++++++++++++++-----
 dlls/wined3d/query.c           | 84 ++++++++++++++------------------
 dlls/wined3d/wined3d_private.h | 15 +++++-
 3 files changed, 126 insertions(+), 62 deletions(-)

diff --git a/dlls/wined3d/context_vk.c b/dlls/wined3d/context_vk.c
index 60132dc81b5..6628f0937ee 100644
--- a/dlls/wined3d/context_vk.c
+++ b/dlls/wined3d/context_vk.c
@@ -905,6 +905,41 @@ void wined3d_context_vk_destroy_vk_image_view(struct wined3d_context_vk *context
     o->command_buffer_id = command_buffer_id;
 }
 
+static void wined3d_context_vk_reset_completed_queries(struct wined3d_context_vk *context_vk,
+        struct wined3d_query_pool_vk *pool_vk, struct wined3d_command_buffer_vk *buffer)
+{
+    const struct wined3d_vk_info *vk_info = context_vk->vk_info;
+    struct wined3d_retired_object_vk *o;
+    struct wined3d_range range;
+    unsigned int start = 0;
+
+    for (;;)
+    {
+        if (!wined3d_bitmap_get_range(pool_vk->completed, WINED3D_QUERY_POOL_SIZE, start, &range))
+            break;
+
+        VK_CALL(vkCmdResetQueryPool(buffer->vk_command_buffer, pool_vk->vk_query_pool, range.offset, range.size));
+
+        if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk)))
+        {
+            ERR("Freeing query range %u+%u in pool %p.\n", range.offset, range.size, pool_vk);
+            wined3d_query_pool_vk_mark_free(context_vk, pool_vk, range.offset, range.size);
+        }
+        else
+        {
+            o->type = WINED3D_RETIRED_QUERY_POOL_VK;
+            o->u.queries.pool_vk = pool_vk;
+            o->u.queries.start = range.offset;
+            o->u.queries.count = range.size;
+            o->command_buffer_id = buffer->id;
+        }
+
+        start = range.offset + range.size;
+    }
+
+    memset(pool_vk->completed, 0, sizeof(pool_vk->completed));
+}
+
 void wined3d_context_vk_destroy_vk_sampler(struct wined3d_context_vk *context_vk,
         VkSampler vk_sampler, uint64_t command_buffer_id)
 {
@@ -1083,6 +1118,11 @@ static void wined3d_context_vk_cleanup_resources(struct wined3d_context_vk *cont
                 TRACE("Destroyed sampler 0x%s.\n", wine_dbgstr_longlong(o->u.vk_sampler));
                 break;
 
+            case WINED3D_RETIRED_QUERY_POOL_VK:
+                wined3d_query_pool_vk_mark_free(context_vk, o->u.queries.pool_vk, o->u.queries.start, o->u.queries.count);
+                TRACE("Freed query range %u+%u in pool %p.\n", o->u.queries.start, o->u.queries.count, o->u.queries.pool_vk);
+                break;
+
             default:
                 ERR("Unhandled object type %#x.\n", o->type);
                 break;
@@ -1338,7 +1378,6 @@ void wined3d_context_vk_end_current_render_pass(struct wined3d_context_vk *conte
 {
     VkCommandBuffer vk_command_buffer = context_vk->current_command_buffer.vk_command_buffer;
     const struct wined3d_vk_info *vk_info = context_vk->vk_info;
-    struct wined3d_query_pool_vk *pool_vk, *pool_vk_next;
     struct wined3d_query_vk *query_vk;
 
     if (context_vk->vk_render_pass)
@@ -1367,17 +1406,6 @@ void wined3d_context_vk_end_current_render_pass(struct wined3d_context_vk *conte
                 context_vk->vk_framebuffer, context_vk->current_command_buffer.id);
         context_vk->vk_framebuffer = VK_NULL_HANDLE;
     }
-
-    if (vk_command_buffer)
-    {
-        LIST_FOR_EACH_ENTRY_SAFE(pool_vk, pool_vk_next, &context_vk->completed_query_pools,
-                struct wined3d_query_pool_vk, completed_entry)
-        {
-            list_remove(&pool_vk->completed_entry);
-            list_init(&pool_vk->completed_entry);
-            wined3d_query_pool_vk_reset(pool_vk, context_vk, vk_command_buffer);
-        }
-    }
 }
 
 static void wined3d_context_vk_destroy_render_pass(struct wine_rb_entry *entry, void *ctx)
@@ -1410,7 +1438,9 @@ bool wined3d_context_vk_allocate_query(struct wined3d_context_vk *context_vk,
 {
     const struct wined3d_vk_info *vk_info = context_vk->vk_info;
     struct wined3d_query_pool_vk *pool_vk, *entry;
+    struct wined3d_device_vk *device_vk;
     struct list *free_pools;
+    VkResult vr;
     size_t idx;
 
     switch (type)
@@ -1456,16 +1486,39 @@ bool wined3d_context_vk_allocate_query(struct wined3d_context_vk *context_vk,
         return false;
     }
 
+    device_vk = wined3d_device_vk(context_vk->c.device);
+
     if (vk_info->supported[WINED3D_VK_EXT_HOST_QUERY_RESET])
     {
-        VK_CALL(vkResetQueryPoolEXT(wined3d_device_vk(context_vk->c.device)->vk_device,
+        VK_CALL(vkResetQueryPoolEXT(device_vk->vk_device,
                 pool_vk->vk_query_pool, 0, WINED3D_QUERY_POOL_SIZE));
     }
     else
     {
+        VkEventCreateInfo event_create_info;
+
         wined3d_context_vk_end_current_render_pass(context_vk);
         VK_CALL(vkCmdResetQueryPool(wined3d_context_vk_get_command_buffer(context_vk),
                 pool_vk->vk_query_pool, 0, WINED3D_QUERY_POOL_SIZE));
+
+        event_create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
+        event_create_info.pNext = NULL;
+        event_create_info.flags = 0;
+
+        /* We probably shouldn't call vkGetQueryPoolResults() without synchronizing with vkCmdResetQueryPool()
+         * even if the query pool is freshly allocated. wined3d_query_vk_accumulate_data() will check this event
+         * before returning results. */
+        vr = VK_CALL(vkCreateEvent(device_vk->vk_device, &event_create_info, NULL, &pool_vk->vk_event));
+        if (vr == VK_SUCCESS)
+        {
+            /* At which stage vkCmdResetQueryPool() executes? */
+            VK_CALL(vkCmdSetEvent(wined3d_context_vk_get_command_buffer(context_vk), pool_vk->vk_event,
+                    VK_PIPELINE_STAGE_ALL_COMMANDS_BIT));
+        }
+        else
+        {
+            ERR("Failed to create event, vr %s.\n", wined3d_debug_vkresult(vr));
+        }
     }
 
     if (!wined3d_query_pool_vk_allocate_query(pool_vk, &idx))
@@ -1661,6 +1714,7 @@ void wined3d_context_vk_submit_command_buffer(struct wined3d_context_vk *context
 {
     struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device);
     const struct wined3d_vk_info *vk_info = context_vk->vk_info;
+    struct wined3d_query_pool_vk *pool_vk, *pool_vk_next;
     struct wined3d_command_buffer_vk *buffer;
     struct wined3d_query_vk *query_vk;
     VkFenceCreateInfo fence_desc;
@@ -1681,6 +1735,15 @@ void wined3d_context_vk_submit_command_buffer(struct wined3d_context_vk *context
 
     wined3d_context_vk_end_current_render_pass(context_vk);
 
+    LIST_FOR_EACH_ENTRY_SAFE(pool_vk, pool_vk_next, &context_vk->completed_query_pools,
+            struct wined3d_query_pool_vk, completed_entry)
+    {
+        list_remove(&pool_vk->completed_entry);
+        list_init(&pool_vk->completed_entry);
+
+        wined3d_context_vk_reset_completed_queries(context_vk, pool_vk, buffer);
+    }
+
     LIST_FOR_EACH_ENTRY(query_vk, &context_vk->active_queries, struct wined3d_query_vk, entry)
         wined3d_query_vk_suspend(query_vk, context_vk);
 
diff --git a/dlls/wined3d/query.c b/dlls/wined3d/query.c
index 6c1ed1d6b5a..dd8c56820e5 100644
--- a/dlls/wined3d/query.c
+++ b/dlls/wined3d/query.c
@@ -1351,26 +1351,12 @@ HRESULT wined3d_query_gl_create(struct wined3d_device *device, enum wined3d_quer
 static void wined3d_query_pool_vk_mark_complete(struct wined3d_query_pool_vk *pool_vk, size_t idx,
         struct wined3d_context_vk *context_vk)
 {
-    const struct wined3d_vk_info *vk_info = context_vk->vk_info;
-
-    if (vk_info->supported[WINED3D_VK_EXT_HOST_QUERY_RESET])
-    {
-        VK_CALL(vkResetQueryPoolEXT(wined3d_device_vk(context_vk->c.device)->vk_device,
-                pool_vk->vk_query_pool, idx, 1));
-
-        wined3d_bitmap_clear(pool_vk->allocated, idx);
-        if (list_empty(&pool_vk->entry))
-            list_add_tail(pool_vk->free_list, &pool_vk->entry);
-    }
-    else
-    {
-        /* Don't reset completed queries right away, as vkCmdResetQueryPool() needs to happen
-         * outside of a render pass. Queue the query to be reset in wined3d_query_pool_vk_reset()
-         * instead, which is called when the render pass ends. */
-        wined3d_bitmap_set(pool_vk->completed, idx);
-        if (list_empty(&pool_vk->completed_entry))
-            list_add_tail(&context_vk->completed_query_pools, &pool_vk->completed_entry);
-    }
+    /* Don't reset completed queries right away, as vkCmdResetQueryPool() needs to happen
+     * outside of a render pass. Queue the query to be reset at the very end of the current
+     * command buffer instead. */
+    wined3d_bitmap_set(pool_vk->completed, idx);
+    if (list_empty(&pool_vk->completed_entry))
+        list_add_tail(&context_vk->completed_query_pools, &pool_vk->completed_entry);
 }
 
 bool wined3d_query_pool_vk_allocate_query(struct wined3d_query_pool_vk *pool_vk, size_t *idx)
@@ -1382,38 +1368,30 @@ bool wined3d_query_pool_vk_allocate_query(struct wined3d_query_pool_vk *pool_vk,
     return true;
 }
 
+void wined3d_query_pool_vk_mark_free(struct wined3d_context_vk *context_vk, struct wined3d_query_pool_vk *pool_vk,
+        uint32_t start, uint32_t count)
+{
+    unsigned int idx, end = start + count;
+
+    for (idx = start; idx < end; ++idx)
+        wined3d_bitmap_clear(pool_vk->allocated, idx);
+
+    if (list_empty(&pool_vk->entry))
+        list_add_tail(pool_vk->free_list, &pool_vk->entry);
+}
+
 void wined3d_query_pool_vk_cleanup(struct wined3d_query_pool_vk *pool_vk, struct wined3d_context_vk *context_vk)
 {
     struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device);
     const struct wined3d_vk_info *vk_info = context_vk->vk_info;
 
     VK_CALL(vkDestroyQueryPool(device_vk->vk_device, pool_vk->vk_query_pool, NULL));
+    if (pool_vk->vk_event)
+        VK_CALL(vkDestroyEvent(device_vk->vk_device, pool_vk->vk_event, NULL));
     list_remove(&pool_vk->entry);
     list_remove(&pool_vk->completed_entry);
 }
 
-void wined3d_query_pool_vk_reset(struct wined3d_query_pool_vk *pool_vk, struct wined3d_context_vk *context_vk,
-        VkCommandBuffer vk_command_buffer)
-{
-    const struct wined3d_vk_info *vk_info = context_vk->vk_info;
-    unsigned int start = 0, idx;
-    struct wined3d_range range;
-
-    for (;;)
-    {
-        if (!wined3d_bitmap_get_range(pool_vk->completed, WINED3D_QUERY_POOL_SIZE, start, &range))
-            break;
-
-        VK_CALL(vkCmdResetQueryPool(vk_command_buffer, pool_vk->vk_query_pool, range.offset, range.size));
-        start = range.offset + range.size;
-        for (idx = range.offset; idx < start; ++idx)
-            wined3d_bitmap_clear(pool_vk->allocated, idx);
-    }
-    memset(pool_vk->completed, 0, sizeof(pool_vk->completed));
-    if (list_empty(&pool_vk->entry))
-        list_add_tail(pool_vk->free_list, &pool_vk->entry);
-}
-
 bool wined3d_query_pool_vk_init(struct wined3d_query_pool_vk *pool_vk,
         struct wined3d_context_vk *context_vk, enum wined3d_query_type type, struct list *free_pools)
 {
@@ -1499,6 +1477,23 @@ bool wined3d_query_vk_accumulate_data(struct wined3d_query_vk *query_vk,
         struct wined3d_query_data_so_statistics so_statistics;
     } tmp, *result;
 
+    if (pool_idx->pool_vk->vk_event)
+    {
+        /* Check if the pool's initial reset command executed. */
+        vr = VK_CALL(vkGetEventStatus(device_vk->vk_device,
+                pool_idx->pool_vk->vk_event));
+        if (vr == VK_EVENT_RESET)
+            return false;
+        else if (vr != VK_EVENT_SET)
+        {
+            ERR("Failed to get event status, vr %s\n", wined3d_debug_vkresult(vr));
+            return false;
+        }
+
+        VK_CALL(vkDestroyEvent(device_vk->vk_device, pool_idx->pool_vk->vk_event, NULL));
+        pool_idx->pool_vk->vk_event = VK_NULL_HANDLE;
+    }
+
     if ((vr = VK_CALL(vkGetQueryPoolResults(device_vk->vk_device, pool_idx->pool_vk->vk_query_pool,
             pool_idx->idx, 1, sizeof(tmp), &tmp, sizeof(tmp), VK_QUERY_RESULT_64_BIT))) < 0)
     {
@@ -1622,11 +1617,6 @@ static BOOL wined3d_query_vk_poll(struct wined3d_query *query, uint32_t flags)
     if (query_vk->command_buffer_id == context_vk->current_command_buffer.id)
         goto unavailable;
 
-    if (query_vk->command_buffer_id > context_vk->completed_command_buffer_id)
-        wined3d_context_vk_poll_command_buffers(context_vk);
-    if (query_vk->command_buffer_id > context_vk->completed_command_buffer_id)
-        goto unavailable;
-
     if (query_vk->pending_count)
         wined3d_context_vk_accumulate_pending_queries(context_vk);
     if (query_vk->pending_count)
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index ef1062ec9b4..90966c8f076 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -2083,15 +2083,17 @@ struct wined3d_query_pool_vk
 
     struct list *free_list;
     VkQueryPool vk_query_pool;
+    VkEvent vk_event;
+
     uint32_t allocated[WINED3D_BITMAP_SIZE(WINED3D_QUERY_POOL_SIZE)];
     uint32_t completed[WINED3D_BITMAP_SIZE(WINED3D_QUERY_POOL_SIZE)];
 };
 
 bool wined3d_query_pool_vk_allocate_query(struct wined3d_query_pool_vk *pool_vk, size_t *idx) DECLSPEC_HIDDEN;
+void wined3d_query_pool_vk_mark_free(struct wined3d_context_vk *context_vk, struct wined3d_query_pool_vk *pool_vk,
+        uint32_t start, uint32_t count) DECLSPEC_HIDDEN;
 void wined3d_query_pool_vk_cleanup(struct wined3d_query_pool_vk *pool_vk,
         struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN;
-void wined3d_query_pool_vk_reset(struct wined3d_query_pool_vk *pool_vk, struct wined3d_context_vk *context_vk,
-        VkCommandBuffer vk_command_buffer) DECLSPEC_HIDDEN;
 bool wined3d_query_pool_vk_init(struct wined3d_query_pool_vk *pool_vk, struct wined3d_context_vk *context_vk,
         enum wined3d_query_type type, struct list *free_pools) DECLSPEC_HIDDEN;
 
@@ -2439,6 +2441,7 @@ enum wined3d_retired_object_type_vk
     WINED3D_RETIRED_BUFFER_VIEW_VK,
     WINED3D_RETIRED_IMAGE_VIEW_VK,
     WINED3D_RETIRED_SAMPLER_VK,
+    WINED3D_RETIRED_QUERY_POOL_VK,
 };
 
 struct wined3d_retired_object_vk
@@ -2461,6 +2464,12 @@ struct wined3d_retired_object_vk
         VkBufferView vk_buffer_view;
         VkImageView vk_image_view;
         VkSampler vk_sampler;
+        struct
+        {
+            struct wined3d_query_pool_vk *pool_vk;
+            uint32_t start;
+            uint32_t count;
+        } queries;
     } u;
     uint64_t command_buffer_id;
 };
@@ -2696,6 +2705,8 @@ void wined3d_context_vk_destroy_vk_memory(struct wined3d_context_vk *context_vk,
         VkDeviceMemory vk_memory, uint64_t command_buffer_id) DECLSPEC_HIDDEN;
 void wined3d_context_vk_destroy_vk_sampler(struct wined3d_context_vk *context_vk,
         VkSampler vk_sampler, uint64_t command_buffer_id) DECLSPEC_HIDDEN;
+void wined3d_context_vk_destroy_query_vk(struct wined3d_context_vk *context_vk,
+        struct wined3d_query_pool_vk *pool_vk, uint32_t pool_idx, uint64_t command_buffer_id) DECLSPEC_HIDDEN;
 void wined3d_context_vk_end_current_render_pass(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN;
 VkCommandBuffer wined3d_context_vk_get_command_buffer(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN;
 struct wined3d_pipeline_layout_vk *wined3d_context_vk_get_pipeline_layout(struct wined3d_context_vk *context_vk,
-- 
2.32.0




More information about the wine-devel mailing list