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