PR #24351 opened by t-boiko URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24351 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24351.patch
One VkVideoSessionKHR is round-robined across decode queues. DPB slot state is session-owned and checked at device execution time, so unordered queues can race. This adds one session timeline semaphore (wait N, signal N+1 on VIDEO_DECODE) around reset and decode. It is separate from the per-exec busy semaphore. Enabled only when the decode family has more than one queue; per-frame image deps are unchanged. >From cfce772d5eefccc3ec095d632723034e5a39366c Mon Sep 17 00:00:00 2001 From: Tymur Boiko <[email protected]> Date: Fri, 7 Aug 2026 14:00:27 +0200 Subject: [PATCH] vulkan_decode: serialize multi-queue session submissions Use one session timeline semaphore to prevent unordered decode queues from racing while retaining per-frame resource dependencies. --- libavcodec/vulkan_decode.c | 81 +++++++++++++++++++++++++++++++++++++- libavcodec/vulkan_decode.h | 6 +++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c index ab313423e5..61dfb6fc36 100644 --- a/libavcodec/vulkan_decode.c +++ b/libavcodec/vulkan_decode.c @@ -140,6 +140,60 @@ static const VkVideoProfileInfoKHR *get_video_profile(FFVulkanDecodeShared *ctx, return NULL; } +static int add_session_sem_dep(FFVulkanDecodeShared *ctx, FFVkExecContext *exec) +{ + uint64_t value; + + if (ctx->session_sem == VK_NULL_HANDLE) + return 0; + + value = atomic_load_explicit(&ctx->session_sem_val, memory_order_relaxed); + ff_vk_exec_add_dep_wait_sem(&ctx->s, exec, ctx->session_sem, value, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + ff_vk_exec_add_dep_signal_sem(&ctx->s, exec, ctx->session_sem, value + 1, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + + return 0; +} + +static int vulkan_decode_init_session_sem(AVCodecContext *avctx, + FFVulkanDecodeShared *ctx) +{ + VkResult ret; + FFVulkanContext *s = &ctx->s; + FFVulkanFunctions *vk = &s->vkfn; + + VkSemaphoreTypeCreateInfo sem_type_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, + .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE, + .initialValue = 0, + }; + VkSemaphoreCreateInfo sem_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &sem_type_info, + }; + + if (ctx->qf->num <= 1) + return 0; + + ret = vk->CreateSemaphore(s->hwctx->act_dev, &sem_info, s->hwctx->alloc, + &ctx->session_sem); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, + "Unable to create session timeline semaphore: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + atomic_init(&ctx->session_sem_val, 0); + + av_log(avctx, AV_LOG_VERBOSE, + "Serializing video session submissions over %i decode queues " + "with a session timeline semaphore\n", ctx->qf->num); + + return 0; +} + int ff_vk_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) { FFVulkanDecodeContext *src_ctx = src->internal->hwaccel_priv_data; @@ -402,7 +456,15 @@ static int decode_reset(AVCodecContext *avctx, FFVulkanDecodeShared *ctx) vk->CmdControlVideoCodingKHR(exec->buf, &decode_ctrl); vk->CmdEndVideoCodingKHR(exec->buf, &decode_end); + err = add_session_sem_dep(ctx, exec); + if (err < 0) { + ff_vk_exec_discard(&ctx->s, exec); + return err; + } + err = ff_vk_exec_submit(&ctx->s, exec); + if (err >= 0 && ctx->session_sem != VK_NULL_HANDLE) + atomic_fetch_add_explicit(&ctx->session_sem_val, 1, memory_order_relaxed); if (decode_start.videoSessionParameters) { /* Wait to complete to delete the temporary session parameters */ @@ -644,8 +706,17 @@ int ff_vk_decode_frame(AVCodecContext *avctx, vk->CmdDecodeVideoKHR(cmd_buf, &vp->decode_info); vk->CmdEndVideoCodingKHR(cmd_buf, &decode_end); + err = add_session_sem_dep(ctx, exec); + if (err < 0) { + ff_vk_exec_discard(&ctx->s, exec); + return err; + } + /* End recording and submit for execution */ - return ff_vk_exec_submit(&ctx->s, exec); + err = ff_vk_exec_submit(&ctx->s, exec); + if (err >= 0 && ctx->session_sem != VK_NULL_HANDLE) + atomic_fetch_add_explicit(&ctx->session_sem_val, 1, memory_order_relaxed); + return err; } void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, FFVulkanDecodePicture *vp) @@ -670,6 +741,10 @@ static void free_common(AVRefStructOpaque unused, void *obj) /* Wait on and free execution pool */ ff_vk_exec_pool_free(&ctx->s, &ctx->exec_pool); + if (ctx->session_sem != VK_NULL_HANDLE) + ctx->s.vkfn.DestroySemaphore(ctx->s.hwctx->act_dev, ctx->session_sem, + ctx->s.hwctx->alloc); + av_refstruct_pool_uninit(&ctx->buf_pool); ff_vk_video_common_uninit(s, &ctx->common); @@ -1323,6 +1398,10 @@ int ff_vk_decode_init(AVCodecContext *avctx) err = ff_vk_video_common_init(avctx, s, &ctx->common, &session_create); if (err < 0) goto fail; + + err = vulkan_decode_init_session_sem(avctx, ctx); + if (err < 0) + goto fail; } /* Out-of-place decoding: create the internal, queue-exclusive DPB pool */ diff --git a/libavcodec/vulkan_decode.h b/libavcodec/vulkan_decode.h index eb334ea1ae..bee007a1a7 100644 --- a/libavcodec/vulkan_decode.h +++ b/libavcodec/vulkan_decode.h @@ -19,6 +19,8 @@ #ifndef AVCODEC_VULKAN_DECODE_H #define AVCODEC_VULKAN_DECODE_H +#include <stdatomic.h> + #include "codec_id.h" #include "decode.h" #include "hwaccel_internal.h" @@ -46,6 +48,10 @@ typedef struct FFVulkanDecodeShared { VkVideoCapabilitiesKHR caps; VkVideoDecodeCapabilitiesKHR dec_caps; + /* Session-wide submission ordering across decode queues. */ + VkSemaphore session_sem; + atomic_uint_least64_t session_sem_val; + /* Software-defined decoder context */ void *sd_ctx; void (*sd_ctx_free)(struct FFVulkanDecodeShared *ctx); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
