We want to avoid using the poll list, for occlusion queries especially, as in some games the number of occlusion queries can reach the hundreds. OpenGL backend already does this by using ARB_QUERY_BUFFER_OBJECT. We could mimic this in Vulkan too using compute shaders to accumulate pending queries, however, unlike with OpenGL, we can also just directly call vkGetQueryPoolResults() from the application's thread. To make it work, we need to keep the list of pending queries separately for each query, which also happens to be simpler than the current pending queries tracking. A second issue that is fixed here, incidentally, is that query results can be retrieved before the command buffer containing the queries ends. This makes a difference when a game wants the data from queries from the beginning of a frame, shortly after the frame ends. If the command buffer was not flushed during the frame, current code would stall polling until after the entire frame is rendered. A downside of the approach taken here is that Vulkan queries allocated to a wined3d_query are only released after the query is reused or destroyed. In principle, if this ever becomes a problem, it could be avoided by protecting the query pool with a mutex and releasing queries after reading the results.
Signed-off-by: Jan Sikorski jsikorski@codeweavers.com --- dlls/wined3d/context_vk.c | 65 -------------------- dlls/wined3d/query.c | 106 +++++++++++++++------------------ dlls/wined3d/wined3d_private.h | 27 ++------- 3 files changed, 54 insertions(+), 144 deletions(-)
diff --git a/dlls/wined3d/context_vk.c b/dlls/wined3d/context_vk.c index 56b2da53060..b3dd557c0ec 100644 --- a/dlls/wined3d/context_vk.c +++ b/dlls/wined3d/context_vk.c @@ -1512,7 +1512,6 @@ void wined3d_context_vk_cleanup(struct wined3d_context_vk *context_vk) wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_pipeline_statistics_query_pools); wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_stream_output_statistics_query_pools); wine_rb_destroy(&context_vk->bo_slab_available, wined3d_context_vk_destroy_bo_slab, context_vk); - heap_free(context_vk->pending_queries.queries); heap_free(context_vk->submitted.buffers); heap_free(context_vk->retired.objects);
@@ -1524,69 +1523,6 @@ void wined3d_context_vk_cleanup(struct wined3d_context_vk *context_vk) wined3d_context_cleanup(&context_vk->c); }
-void wined3d_context_vk_remove_pending_queries(struct wined3d_context_vk *context_vk, - struct wined3d_query_vk *query_vk) -{ - struct wined3d_pending_queries_vk *pending = &context_vk->pending_queries; - struct wined3d_pending_query_vk *p; - size_t i; - - pending->free_idx = ~(size_t)0; - for (i = pending->count; i; --i) - { - p = &pending->queries[i - 1]; - - if (p->query_vk) - { - if (p->query_vk != query_vk && !wined3d_query_vk_accumulate_data(p->query_vk, context_vk, &p->pool_idx)) - continue; - --p->query_vk->pending_count; - } - - if (i == pending->count) - { - --pending->count; - continue; - } - - p->query_vk = NULL; - p->pool_idx.pool_vk = NULL; - p->pool_idx.idx = pending->free_idx; - pending->free_idx = i - 1; - } -} - -void wined3d_context_vk_accumulate_pending_queries(struct wined3d_context_vk *context_vk) -{ - wined3d_context_vk_remove_pending_queries(context_vk, NULL); -} - -void wined3d_context_vk_add_pending_query(struct wined3d_context_vk *context_vk, struct wined3d_query_vk *query_vk) -{ - struct wined3d_pending_queries_vk *pending = &context_vk->pending_queries; - struct wined3d_pending_query_vk *p; - - if (pending->free_idx != ~(size_t)0) - { - p = &pending->queries[pending->free_idx]; - pending->free_idx = p->pool_idx.idx; - } - else - { - if (!wined3d_array_reserve((void **)&pending->queries, &pending->size, - pending->count + 1, sizeof(*pending->queries))) - { - ERR("Failed to allocate entry.\n"); - return; - } - p = &pending->queries[pending->count++]; - } - - p->query_vk = query_vk; - p->pool_idx = query_vk->pool_idx; - ++query_vk->pending_count; -} - VkCommandBuffer wined3d_context_vk_get_command_buffer(struct wined3d_context_vk *context_vk) { struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); @@ -1636,7 +1572,6 @@ VkCommandBuffer wined3d_context_vk_get_command_buffer(struct wined3d_context_vk return buffer->vk_command_buffer = VK_NULL_HANDLE; }
- wined3d_context_vk_accumulate_pending_queries(context_vk); LIST_FOR_EACH_ENTRY(query_vk, &context_vk->active_queries, struct wined3d_query_vk, entry) { if (!wined3d_context_vk_allocate_query(context_vk, query_vk->q.type, &query_vk->pool_idx)) diff --git a/dlls/wined3d/query.c b/dlls/wined3d/query.c index 77999c62ab1..b25ce0f1d7c 100644 --- a/dlls/wined3d/query.c +++ b/dlls/wined3d/query.c @@ -1352,26 +1352,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 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); }
bool wined3d_query_pool_vk_allocate_query(struct wined3d_query_pool_vk *pool_vk, size_t *idx) @@ -1485,11 +1471,10 @@ bool wined3d_query_pool_vk_init(struct wined3d_query_pool_vk *pool_vk, }
bool wined3d_query_vk_accumulate_data(struct wined3d_query_vk *query_vk, - struct wined3d_context_vk *context_vk, const struct wined3d_query_pool_idx_vk *pool_idx) + struct wined3d_device_vk *device_vk, const struct wined3d_query_pool_idx_vk *pool_idx) { - struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); const struct wined3d_query_data_pipeline_statistics *ps_tmp; - const struct wined3d_vk_info *vk_info = context_vk->vk_info; + const struct wined3d_vk_info *vk_info = &device_vk->vk_info; struct wined3d_query_data_pipeline_statistics *ps_result; VkResult vr; union @@ -1510,8 +1495,6 @@ bool wined3d_query_vk_accumulate_data(struct wined3d_query_vk *query_vk, if (vr == VK_NOT_READY) return false;
- wined3d_query_pool_vk_mark_complete(pool_idx->pool_vk, pool_idx->idx, context_vk); - result = (void *)query_vk->q.data; switch (query_vk->q.type) { @@ -1606,48 +1589,55 @@ void wined3d_query_vk_suspend(struct wined3d_query_vk *query_vk, struct wined3d_ VkCommandBuffer vk_command_buffer = context_vk->current_command_buffer.vk_command_buffer;
wined3d_query_vk_end(query_vk, context_vk, vk_command_buffer); - wined3d_context_vk_add_pending_query(context_vk, query_vk); + + if (!wined3d_array_reserve((void **)&query_vk->pending, &query_vk->pending_size, + query_vk->pending_count + 1, sizeof(*query_vk->pending))) + { + ERR("Failed to allocate entry.\n"); + return; + } + + query_vk->pending[query_vk->pending_count++] = query_vk->pool_idx; query_vk->pool_idx.pool_vk = NULL; query_vk->flags &= ~WINED3D_QUERY_VK_FLAG_ACTIVE; }
static BOOL wined3d_query_vk_poll(struct wined3d_query *query, uint32_t flags) { + struct wined3d_device_vk *device_vk = wined3d_device_vk(query->device); struct wined3d_query_vk *query_vk = wined3d_query_vk(query); - struct wined3d_context_vk *context_vk; + size_t i;
- context_vk = wined3d_context_vk(context_acquire(query->device, NULL, 0)); + memset((void *)query_vk->q.data, 0, query_vk->q.data_size);
- if (flags & WINED3DGETDATA_FLUSH) - wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); - if (query_vk->command_buffer_id == context_vk->current_command_buffer.id) + if (query_vk->pool_idx.pool_vk && !wined3d_query_vk_accumulate_data(query_vk, device_vk, &query_vk->pool_idx)) 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) - goto unavailable; - - /* If the query was suspended, and then ended before it was resumed, - * there's no data to accumulate here. */ - if (query_vk->pool_idx.pool_vk && !wined3d_query_vk_accumulate_data(query_vk, context_vk, &query_vk->pool_idx)) - goto unavailable; - - query_vk->pool_idx.pool_vk = NULL; - context_release(&context_vk->c); + for (i = 0; i < query_vk->pending_count; ++i) + { + if (!wined3d_query_vk_accumulate_data(query_vk, device_vk, &query_vk->pending[i])) + goto unavailable; + }
return TRUE;
unavailable: - context_release(&context_vk->c); + if ((flags & WINED3DGETDATA_FLUSH) && !query->device->cs->queries_flushed) + query->device->cs->c.ops->flush(&query->device->cs->c); return FALSE; }
+static void wined3d_query_vk_remove_pending_queries(struct wined3d_context_vk *context_vk, + struct wined3d_query_vk *query_vk) +{ + size_t i; + + for (i = 0; i < query_vk->pending_count; ++i) + wined3d_query_pool_vk_mark_complete(query_vk->pending[i].pool_vk, query_vk->pending[i].idx, context_vk); + + query_vk->pending_count = 0; +} + static BOOL wined3d_query_vk_issue(struct wined3d_query *query, uint32_t flags) { struct wined3d_device_vk *device_vk = wined3d_device_vk(query->device); @@ -1663,8 +1653,7 @@ static BOOL wined3d_query_vk_issue(struct wined3d_query *query, uint32_t flags) context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0));
if (query_vk->pending_count) - wined3d_context_vk_remove_pending_queries(context_vk, query_vk); - memset((void *)query->data, 0, query->data_size); + wined3d_query_vk_remove_pending_queries(context_vk, query_vk); vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk); if (query_vk->flags & WINED3D_QUERY_VK_FLAG_STARTED) { @@ -1744,12 +1733,12 @@ static void wined3d_query_vk_destroy(struct wined3d_query *query)
if (query_vk->flags & WINED3D_QUERY_VK_FLAG_STARTED) list_remove(&query_vk->entry); - if (query_vk->pending_count) - { - context_vk = wined3d_context_vk(context_acquire(query_vk->q.device, NULL, 0)); - wined3d_context_vk_remove_pending_queries(context_vk, query_vk); - context_release(&context_vk->c); - } + context_vk = wined3d_context_vk(context_acquire(query_vk->q.device, NULL, 0)); + wined3d_query_vk_remove_pending_queries(context_vk, query_vk); + if (query_vk->pool_idx.pool_vk) + wined3d_query_pool_vk_mark_complete(query_vk->pool_idx.pool_vk, query_vk->pool_idx.idx, context_vk); + context_release(&context_vk->c); + heap_free(query_vk->pending); heap_free(query_vk); }
@@ -1944,6 +1933,9 @@ HRESULT wined3d_query_vk_create(struct wined3d_device *device, enum wined3d_quer
wined3d_query_init(&query_vk->q, device, type, data, data_size, ops, parent, parent_ops);
+ if (type != WINED3D_QUERY_TYPE_EVENT) + query_vk->q.poll_in_cs = false; + switch (type) { case WINED3D_QUERY_TYPE_OCCLUSION: diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index d1136599319..c34fa62a53b 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -2102,7 +2102,8 @@ struct wined3d_query_vk uint8_t flags; uint64_t command_buffer_id; uint32_t control_flags; - size_t pending_count; + SIZE_T pending_count, pending_size; + struct wined3d_query_pool_idx_vk *pending; };
static inline struct wined3d_query_vk *wined3d_query_vk(struct wined3d_query *query) @@ -2110,7 +2111,9 @@ static inline struct wined3d_query_vk *wined3d_query_vk(struct wined3d_query *qu return CONTAINING_RECORD(query, struct wined3d_query_vk, q); }
-bool wined3d_query_vk_accumulate_data(struct wined3d_query_vk *query_vk, struct wined3d_context_vk *context_vk, +struct wined3d_device_vk; + +bool wined3d_query_vk_accumulate_data(struct wined3d_query_vk *query_vk, struct wined3d_device_vk *device_vk, const struct wined3d_query_pool_idx_vk *pool_idx) DECLSPEC_HIDDEN; HRESULT wined3d_query_vk_create(struct wined3d_device *device, enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) DECLSPEC_HIDDEN; @@ -2558,20 +2561,6 @@ struct wined3d_shader_descriptor_writes_vk SIZE_T size, count; };
-struct wined3d_pending_query_vk -{ - struct wined3d_query_vk *query_vk; - struct wined3d_query_pool_idx_vk pool_idx; -}; - -struct wined3d_pending_queries_vk -{ - struct wined3d_pending_query_vk *queries; - SIZE_T free_idx; - SIZE_T size; - SIZE_T count; -}; - struct wined3d_context_vk { struct wined3d_context c; @@ -2627,7 +2616,6 @@ struct wined3d_context_vk
struct list render_pass_queries; struct list active_queries; - struct wined3d_pending_queries_vk pending_queries; struct list completed_query_pools; struct list free_occlusion_query_pools; struct list free_timestamp_query_pools; @@ -2646,9 +2634,6 @@ static inline struct wined3d_context_vk *wined3d_context_vk(struct wined3d_conte return CONTAINING_RECORD(context, struct wined3d_context_vk, c); }
-void wined3d_context_vk_accumulate_pending_queries(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; -void wined3d_context_vk_add_pending_query(struct wined3d_context_vk *context_vk, - struct wined3d_query_vk *query_vk) DECLSPEC_HIDDEN; bool wined3d_context_vk_allocate_query(struct wined3d_context_vk *context_vk, enum wined3d_query_type type, struct wined3d_query_pool_idx_vk *pool_idx) DECLSPEC_HIDDEN; VkDeviceMemory wined3d_context_vk_allocate_vram_chunk_memory(struct wined3d_context_vk *context_vk, @@ -2696,8 +2681,6 @@ void wined3d_context_vk_image_barrier(struct wined3d_context_vk *context_vk, HRESULT wined3d_context_vk_init(struct wined3d_context_vk *context_vk, struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN; void wined3d_context_vk_poll_command_buffers(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN; -void wined3d_context_vk_remove_pending_queries(struct wined3d_context_vk *context_vk, - struct wined3d_query_vk *query_vk) DECLSPEC_HIDDEN; void wined3d_context_vk_submit_command_buffer(struct wined3d_context_vk *context_vk, unsigned int wait_semaphore_count, const VkSemaphore *wait_semaphores, const VkPipelineStageFlags *wait_stages, unsigned int signal_semaphore_count, const VkSemaphore *signal_semaphores) DECLSPEC_HIDDEN;