From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/wined3d/context_vk.c | 174 +++++++++++++++++++++++++++++++++++--- dlls/wined3d/decoder.c | 95 +++++++++++++++++++++ dlls/wined3d/wined3d_vk.h | 45 ++++++++++ 3 files changed, 302 insertions(+), 12 deletions(-)
diff --git a/dlls/wined3d/context_vk.c b/dlls/wined3d/context_vk.c index 42c54faeb8b..cc04b1cc1cf 100644 --- a/dlls/wined3d/context_vk.c +++ b/dlls/wined3d/context_vk.c @@ -1200,6 +1200,116 @@ static void wined3d_context_vk_remove_command_buffer(struct wined3d_context_vk * *buffer = context_vk->submitted.buffers[--context_vk->submitted.buffer_count]; }
+bool wined3d_aux_command_pool_vk_get_buffer(struct wined3d_context_vk *context_vk, + struct wined3d_aux_command_pool_vk *pool, struct wined3d_aux_command_buffer_vk *buffer) +{ + VkCommandBufferBeginInfo begin_info = {.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkResult vr; + + if (pool->buffer_count) + { + *buffer = pool->buffers[--pool->buffer_count]; + } + else + { + VkCommandBufferAllocateInfo command_buffer_info = {.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; + VkSemaphoreCreateInfo semaphore_info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + + if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, &semaphore_info, NULL, &buffer->signal_semaphore))) < 0) + { + ERR("Failed to create signal_semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); + return false; + } + + if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device, &semaphore_info, NULL, &buffer->wait_semaphore))) < 0) + { + ERR("Failed to create wait_semaphore, vr %s.\n", wined3d_debug_vkresult(vr)); + VK_CALL(vkDestroySemaphore(device_vk->vk_device, buffer->signal_semaphore, NULL)); + return false; + } + + command_buffer_info.commandPool = pool->vk_pool; + command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_info.commandBufferCount = 1; + if ((vr = VK_CALL(vkAllocateCommandBuffers(device_vk->vk_device, + &command_buffer_info, &buffer->vk_command_buffer))) < 0) + { + WARN("Failed to allocate Vulkan command buffer, vr %s.\n", wined3d_debug_vkresult(vr)); + VK_CALL(vkDestroySemaphore(device_vk->vk_device, buffer->signal_semaphore, NULL)); + VK_CALL(vkDestroySemaphore(device_vk->vk_device, buffer->wait_semaphore, NULL)); + return false; + } + } + + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + if ((vr = VK_CALL(vkBeginCommandBuffer(buffer->vk_command_buffer, &begin_info))) < 0) + { + WARN("Failed to begin command buffer, vr %s.\n", wined3d_debug_vkresult(vr)); + VK_CALL(vkFreeCommandBuffers(device_vk->vk_device, pool->vk_pool, 1, &buffer->vk_command_buffer)); + return false; + } + + return true; +} + +void wined3d_aux_command_pool_vk_retire_buffer( + struct wined3d_context_vk *context_vk, struct wined3d_aux_command_pool_vk *pool, + const struct wined3d_aux_command_buffer_vk *buffer, uint64_t command_buffer_id) +{ + struct wined3d_retired_object_vk *o; + + /* No point checking if it's complete. Auxiliary command buffers are + * always submitted before the main command buffer that depends on them. */ + + if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk))) + { + ERR("Leaking auxiliary command buffer %p.\n", buffer->vk_command_buffer); + return; + } + + o->type = WINED3D_RETIRED_AUX_COMMAND_BUFFER_VK; + o->u.aux_command_buffer.buffer = *buffer; + o->u.aux_command_buffer.pool = pool; + o->command_buffer_id = command_buffer_id; +} + +static void wined3d_aux_command_pool_vk_complete_buffer(struct wined3d_context_vk *context_vk, + struct wined3d_aux_command_pool_vk *pool, const struct wined3d_aux_command_buffer_vk *buffer) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + + if (!wined3d_array_reserve((void **)&pool->buffers, &pool->buffers_size, + pool->buffer_count + 1, sizeof(*pool->buffers))) + { + VK_CALL(vkFreeCommandBuffers(device_vk->vk_device, pool->vk_pool, 1, &buffer->vk_command_buffer)); + return; + } + + pool->buffers[pool->buffer_count++] = *buffer; +} + +static void wined3d_aux_command_pool_vk_cleanup(struct wined3d_context_vk *context_vk, + struct wined3d_aux_command_pool_vk *pool) +{ + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + + for (unsigned int i = 0; i < pool->buffer_count; ++i) + { + struct wined3d_aux_command_buffer_vk *buffer = &pool->buffers[i]; + + VK_CALL(vkDestroySemaphore(device_vk->vk_device, buffer->wait_semaphore, NULL)); + VK_CALL(vkDestroySemaphore(device_vk->vk_device, buffer->signal_semaphore, NULL)); + VK_CALL(vkFreeCommandBuffers(device_vk->vk_device, + pool->vk_pool, 1, &buffer->vk_command_buffer)); + } + VK_CALL(vkDestroyCommandPool(device_vk->vk_device, pool->vk_pool, NULL)); + free(pool->buffers); +} + static void wined3d_context_vk_cleanup_resources(struct wined3d_context_vk *context_vk, VkFence vk_fence) { struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); @@ -1311,6 +1421,11 @@ static void wined3d_context_vk_cleanup_resources(struct wined3d_context_vk *cont TRACE("Destroyed video session 0x%s.\n", wine_dbgstr_longlong(o->u.vk_video_session)); break;
+ case WINED3D_RETIRED_AUX_COMMAND_BUFFER_VK: + wined3d_aux_command_pool_vk_complete_buffer(context_vk, + o->u.aux_command_buffer.pool, &o->u.aux_command_buffer.buffer); + break; + default: ERR("Unhandled object type %#x.\n", o->type); break; @@ -1788,6 +1903,7 @@ void wined3d_context_vk_cleanup(struct wined3d_context_vk *context_vk) * this needs to happen after all command buffers are freed, because * vkFreeCommandBuffers() requires a valid pool handle. */ VK_CALL(vkDestroyCommandPool(device_vk->vk_device, context_vk->vk_command_pool, NULL)); + wined3d_aux_command_pool_vk_cleanup(context_vk, &context_vk->decode_pool); wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_occlusion_query_pools); wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_timestamp_query_pools); wined3d_context_vk_destroy_query_pools(context_vk, &context_vk->free_pipeline_statistics_query_pools); @@ -1989,11 +2105,26 @@ void wined3d_context_vk_submit_command_buffer(struct wined3d_context_vk *context
VK_CALL(vkResetFences(device_vk->vk_device, 1, &buffer->vk_fence));
+ if (wait_semaphore_count) + { + wined3d_array_reserve((void **)&context_vk->wait_semaphores, &context_vk->wait_semaphores_size, + context_vk->wait_semaphore_count + wait_semaphore_count, sizeof(*context_vk->wait_semaphores)); + memcpy(context_vk->wait_semaphores + context_vk->wait_semaphore_count, + wait_semaphores, wait_semaphore_count * sizeof(VkSemaphore)); + + wined3d_array_reserve((void **)&context_vk->wait_stages, &context_vk->wait_stages_size, + context_vk->wait_semaphore_count + wait_semaphore_count, sizeof(*context_vk->wait_stages)); + memcpy(context_vk->wait_stages + context_vk->wait_semaphore_count, + wait_stages, wait_semaphore_count * sizeof(VkPipelineStageFlags)); + + context_vk->wait_semaphore_count += wait_semaphore_count; + } + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = NULL; - submit_info.waitSemaphoreCount = wait_semaphore_count; - submit_info.pWaitSemaphores = wait_semaphores; - submit_info.pWaitDstStageMask = wait_stages; + submit_info.waitSemaphoreCount = context_vk->wait_semaphore_count; + submit_info.pWaitSemaphores = context_vk->wait_semaphores; + submit_info.pWaitDstStageMask = context_vk->wait_stages; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffer->vk_command_buffer; submit_info.signalSemaphoreCount = signal_semaphore_count; @@ -2007,6 +2138,8 @@ void wined3d_context_vk_submit_command_buffer(struct wined3d_context_vk *context context_vk->submitted.buffer_count + 1, sizeof(*context_vk->submitted.buffers))) ERR("Failed to grow submitted command buffer array.\n");
+ context_vk->wait_semaphore_count = 0; + context_vk->submitted.buffers[context_vk->submitted.buffer_count++] = *buffer;
buffer->vk_command_buffer = VK_NULL_HANDLE; @@ -4182,13 +4315,26 @@ VkCommandBuffer wined3d_context_vk_apply_compute_state(struct wined3d_context_vk return vk_command_buffer; }
+static VkCommandPool create_command_pool(struct wined3d_device_vk *device_vk, + const struct wined3d_vk_info *vk_info, uint32_t queue_family_index) +{ + VkCommandPoolCreateInfo command_pool_info = {.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; + VkCommandPool pool; + VkResult vr; + + command_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + command_pool_info.queueFamilyIndex = queue_family_index; + if ((vr = VK_CALL(vkCreateCommandPool(device_vk->vk_device, &command_pool_info, NULL, &pool))) == VK_SUCCESS) + return pool; + ERR("Failed to create Vulkan command pool, vr %s.\n", wined3d_debug_vkresult(vr)); + return VK_NULL_HANDLE; +} + HRESULT wined3d_context_vk_init(struct wined3d_context_vk *context_vk, struct wined3d_swapchain *swapchain) { - VkCommandPoolCreateInfo command_pool_info; const struct wined3d_vk_info *vk_info; struct wined3d_adapter_vk *adapter_vk; struct wined3d_device_vk *device_vk; - VkResult vr;
TRACE("context_vk %p, swapchain %p.\n", context_vk, swapchain);
@@ -4198,19 +4344,23 @@ HRESULT wined3d_context_vk_init(struct wined3d_context_vk *context_vk, struct wi adapter_vk = wined3d_adapter_vk(device_vk->d.adapter); context_vk->vk_info = vk_info = &adapter_vk->vk_info;
- command_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - command_pool_info.pNext = NULL; - command_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - command_pool_info.queueFamilyIndex = device_vk->graphics_queue.vk_queue_family_index; - if ((vr = VK_CALL(vkCreateCommandPool(device_vk->vk_device, - &command_pool_info, NULL, &context_vk->vk_command_pool))) < 0) + if (!(context_vk->vk_command_pool = create_command_pool(device_vk, + vk_info, device_vk->graphics_queue.vk_queue_family_index))) { - ERR("Failed to create Vulkan command pool, vr %s.\n", wined3d_debug_vkresult(vr)); wined3d_context_cleanup(&context_vk->c); return E_FAIL; } context_vk->current_command_buffer.id = 1;
+ if (device_vk->decode_queue.vk_queue + && !(context_vk->decode_pool.vk_pool = create_command_pool(device_vk, + vk_info, device_vk->decode_queue.vk_queue_family_index))) + { + VK_CALL(vkDestroyCommandPool(device_vk->vk_device, context_vk->vk_command_pool, NULL)); + wined3d_context_cleanup(&context_vk->c); + return E_FAIL; + } + wined3d_context_vk_init_graphics_pipeline_key(context_vk);
list_init(&context_vk->render_pass_queries); diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index 578b00d1f6b..5a4fd33b65c 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -159,6 +159,9 @@ struct wined3d_decoder_vk uint64_t command_buffer_id; struct wined3d_allocator_block *session_memory; VkDeviceMemory vk_session_memory; + + bool needs_wait_semaphore; + struct wined3d_aux_command_buffer_vk command_buffer; };
static struct wined3d_decoder_vk *wined3d_decoder_vk(struct wined3d_decoder *decoder) @@ -440,11 +443,103 @@ static HRESULT wined3d_decoder_vk_create(struct wined3d_device *device, return WINED3D_OK; }
+static bool get_decode_command_buffer(struct wined3d_decoder_vk *decoder_vk, + struct wined3d_context_vk *context_vk, struct wined3d_decoder_output_view *view) +{ + const struct wined3d_texture_vk *texture_vk = wined3d_texture_vk(view->texture); + + if (!wined3d_aux_command_pool_vk_get_buffer(context_vk, &context_vk->decode_pool, &decoder_vk->command_buffer)) + return false; + + /* If the output texture in question is in use by the current main CB, + * we will need this ACB to wait for the main CB to complete. + * + * We check this by comparing IDs. + * Note that if view_vk->command_buffer_id == current_command_buffer.id + * then the current CB must be active, otherwise the view should not have + * been referenced to it. */ + if (texture_vk->image.command_buffer_id == context_vk->current_command_buffer.id) + { + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, + 1, &decoder_vk->command_buffer.wait_semaphore); + decoder_vk->needs_wait_semaphore = true; + } + else + { + /* Submit the main CB anyway. We don't strictly need to do this + * immediately, but we need to do it before the resource will be used. + * We also need to do this because resources we're tracking (session, + * session parameters, reference frames) need to be tied to the next + * main CB rather than the current one. + * Submitting now saves us the work of tracking that information, + * and the resource will probably be used almost immediately anyway. */ + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); + decoder_vk->needs_wait_semaphore = false; + } + + return true; +} + +static void submit_decode_command_buffer(struct wined3d_decoder_vk *decoder_vk, + struct wined3d_context_vk *context_vk) +{ + static const VkPipelineStageFlags stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device); + VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO}; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkResult vr; + + /* We don't strictly need to submit the ACB here. But ffmpeg and gstreamer + * do, so it's probably the right thing to do. + * + * We don't strictly need to submit the main CB here either; we could delay + * until we use the output resource. However that's a bit more complex to + * track, and I'm not sure that there's a performance reason *not* to + * submit early? */ + + VK_CALL(vkEndCommandBuffer(decoder_vk->command_buffer.vk_command_buffer)); + + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &decoder_vk->command_buffer.vk_command_buffer; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &decoder_vk->command_buffer.signal_semaphore; + if (decoder_vk->needs_wait_semaphore) + { + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &decoder_vk->command_buffer.wait_semaphore; + submit_info.pWaitDstStageMask = &stage_mask; + } + + if ((vr = VK_CALL(vkQueueSubmit(device_vk->decode_queue.vk_queue, 1, &submit_info, VK_NULL_HANDLE))) < 0) + ERR("Failed to submit, vr %d.\n", vr); + + /* Mark that the next CB needs to wait on our semaphore. */ + wined3d_array_reserve((void **)&context_vk->wait_semaphores, &context_vk->wait_semaphores_size, + context_vk->wait_semaphore_count + 1, sizeof(*context_vk->wait_semaphores)); + context_vk->wait_semaphores[context_vk->wait_semaphore_count] = decoder_vk->command_buffer.signal_semaphore; + wined3d_array_reserve((void **)&context_vk->wait_stages, &context_vk->wait_stages_size, + context_vk->wait_semaphore_count + 1, sizeof(*context_vk->wait_stages)); + context_vk->wait_stages[context_vk->wait_semaphore_count] = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + ++context_vk->wait_semaphore_count; + + /* Retire this buffer. */ + wined3d_aux_command_pool_vk_retire_buffer(context_vk, &context_vk->decode_pool, + &decoder_vk->command_buffer, context_vk->current_command_buffer.id); +} + static void wined3d_decoder_vk_decode(struct wined3d_context *context, struct wined3d_decoder *decoder, struct wined3d_decoder_output_view *output_view, unsigned int bitstream_size, unsigned int slice_control_size) { + struct wined3d_decoder_vk *decoder_vk = wined3d_decoder_vk(decoder); + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + + if (!get_decode_command_buffer(decoder_vk, context_vk, output_view)) + return; + FIXME("Not implemented.\n"); + + submit_decode_command_buffer(decoder_vk, context_vk); }
const struct wined3d_decoder_ops wined3d_decoder_vk_ops = diff --git a/dlls/wined3d/wined3d_vk.h b/dlls/wined3d/wined3d_vk.h index fcc275490c7..c3317ea6ce1 100644 --- a/dlls/wined3d/wined3d_vk.h +++ b/dlls/wined3d/wined3d_vk.h @@ -433,6 +433,17 @@ struct wined3d_command_buffer_vk VkFence vk_fence; };
+struct wined3d_aux_command_buffer_vk +{ + VkCommandBuffer vk_command_buffer; + /* Semaphore that the auxiliary CB signals and the main CB will wait on. */ + VkSemaphore signal_semaphore; + /* Semaphore that the main CB signals and the auxiliary CB will wait on. + * This is necessary for when the main CB uses resources that the auxiliary + * CB needs to use. */ + VkSemaphore wait_semaphore; +}; + enum wined3d_retired_object_type_vk { WINED3D_RETIRED_FREE_VK, @@ -450,6 +461,7 @@ enum wined3d_retired_object_type_vk WINED3D_RETIRED_EVENT_VK, WINED3D_RETIRED_PIPELINE_VK, WINED3D_RETIRED_VIDEO_SESSION_VK, + WINED3D_RETIRED_AUX_COMMAND_BUFFER_VK, };
struct wined3d_retired_object_vk @@ -481,6 +493,11 @@ struct wined3d_retired_object_vk uint32_t start; uint32_t count; } queries; + struct + { + struct wined3d_aux_command_pool_vk *pool; + struct wined3d_aux_command_buffer_vk buffer; + } aux_command_buffer; } u; uint64_t command_buffer_id; }; @@ -595,6 +612,28 @@ struct wined3d_shader_descriptor_writes_vk SIZE_T size, count; };
+/* In order to track whether resources can be destroyed or reused, we use + * the sequence ID of a command buffer submitted to the graphics queue. + * + * In order to extend this system to command buffers submitted to different + * queues, we use a semaphore to associate these "auxiliary" command buffers + * with the next graphics queue submission. These command buffers then get + * an associated command_buffer_id and are freed back to the auxiliary + * command pool via wined3d_retired_object_vk, just like any other resource. + */ +struct wined3d_aux_command_pool_vk +{ + VkCommandPool vk_pool; + struct wined3d_aux_command_buffer_vk *buffers; + SIZE_T buffers_size, buffer_count; +}; + +bool wined3d_aux_command_pool_vk_get_buffer(struct wined3d_context_vk *context_vk, + struct wined3d_aux_command_pool_vk *pool, struct wined3d_aux_command_buffer_vk *buffer); +void wined3d_aux_command_pool_vk_retire_buffer( + struct wined3d_context_vk *context_vk, struct wined3d_aux_command_pool_vk *pool, + const struct wined3d_aux_command_buffer_vk *buffer, uint64_t command_buffer_id); + struct wined3d_context_vk { struct wined3d_context c; @@ -632,6 +671,10 @@ struct wined3d_context_vk /* Number of draw or dispatch calls that have been recorded into the * current command buffer. */ unsigned int command_buffer_work_count; + /* Semaphores that the current command buffer must wait on. */ + VkSemaphore *wait_semaphores; + VkPipelineStageFlags *wait_stages; + SIZE_T wait_semaphore_count, wait_semaphores_size, wait_stages_size;
struct { @@ -640,6 +683,8 @@ struct wined3d_context_vk SIZE_T buffer_count; } submitted, completed;
+ struct wined3d_aux_command_pool_vk decode_pool; + struct wined3d_shader_descriptor_writes_vk descriptor_writes;
VkFramebuffer vk_framebuffer;