+
+ av_freep(&cmd->bufs);
+ av_freep(&cmd->queues);
+}
+
+static VkCommandBuffer get_buf_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd)
+{
+ return cmd->bufs[cmd->cur_queue_idx];
+}
+
+static void unref_exec_ctx_deps(AVHWFramesContext *hwfc, VulkanExecCtx *cmd)
+{
+ VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx];
+
+ for (int j = 0; j < q->nb_buf_deps; j++)
+ av_buffer_unref(&q->buf_deps[j]);
+ q->nb_buf_deps = 0;
+}
+
+static int wait_start_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd)
+{
+ VkResult ret;
+ AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx];
+
+ VkCommandBufferBeginInfo cmd_start = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+
+ /* Create the fence and don't wait for it initially */
+ if (!q->fence) {
+ VkFenceCreateInfo fence_spawn = {
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ };
+ ret = vkCreateFence(hwctx->act_dev, &fence_spawn, hwctx->alloc,
+ &q->fence);
+ if (ret != VK_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to queue frame fence: %s\n",
+ vk_ret2str(ret));
+ return AVERROR_EXTERNAL;
+ }
+ } else if (!q->was_synchronous) {
+ vkWaitForFences(hwctx->act_dev, 1, &q->fence, VK_TRUE, UINT64_MAX);
+ vkResetFences(hwctx->act_dev, 1, &q->fence);
+ }
+
+ /* Discard queue dependencies */
+ unref_exec_ctx_deps(hwfc, cmd);
+
+ ret = vkBeginCommandBuffer(cmd->bufs[cmd->cur_queue_idx], &cmd_start);
+ if (ret != VK_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Unable to init command buffer: %s\n",
+ vk_ret2str(ret));
+ return AVERROR_EXTERNAL;
+ }
+
+ return 0;
+}
+
+static int add_buf_dep_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd,
+ AVBufferRef * const *deps, int nb_deps)
+{
+ AVBufferRef **dst;
+ VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx];
+
+ if (!deps || !nb_deps)
+ return 0;
+
+ dst = av_fast_realloc(q->buf_deps, &q->buf_deps_alloc_size,
+ (q->nb_buf_deps + nb_deps) * sizeof(*dst));
+ if (!dst)
+ goto err;
+
+ q->buf_deps = dst;
+
+ for (int i = 0; i < nb_deps; i++) {
+ q->buf_deps[q->nb_buf_deps] = av_buffer_ref(deps[i]);
+ if (!q->buf_deps[q->nb_buf_deps])
+ goto err;
+ q->nb_buf_deps++;
+ }
+
+ return 0;
+
+err:
+ unref_exec_ctx_deps(hwfc, cmd);
+ return AVERROR(ENOMEM);
+}
+
+static int submit_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd,
+ VkSubmitInfo *s_info, int synchronous)
+{
+ VkResult ret;
+ VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx];
+
+ ret = vkEndCommandBuffer(cmd->bufs[cmd->cur_queue_idx]);
+ if (ret != VK_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Unable to finish command buffer: %s\n",
+ vk_ret2str(ret));
+ unref_exec_ctx_deps(hwfc, cmd);
+ return AVERROR_EXTERNAL;
+ }
+
+ s_info->pCommandBuffers = &cmd->bufs[cmd->cur_queue_idx];
+ s_info->commandBufferCount = 1;
+
+ ret = vkQueueSubmit(q->queue, 1, s_info, q->fence);
+ if (ret != VK_SUCCESS) {
+ unref_exec_ctx_deps(hwfc, cmd);
+ return AVERROR_EXTERNAL;
+ }
+
+ q->was_synchronous = synchronous;
+
+ if (synchronous) {
+ AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ vkWaitForFences(hwctx->act_dev, 1, &q->fence, VK_TRUE, UINT64_MAX);
+ vkResetFences(hwctx->act_dev, 1, &q->fence);
+ unref_exec_ctx_deps(hwfc, cmd);
+ } else { /* Rotate queues */
+ cmd->cur_queue_idx = (cmd->cur_queue_idx + 1) % cmd->nb_queues;
+ }
+
+ return 0;