This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit a1c83c513f3cd3705f9001edcad57dd1b8fceb6a Author: Lynne <[email protected]> AuthorDate: Sat Aug 8 01:25:00 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Mon Aug 17 14:57:07 2026 +0900 vulkan: carry the execution context's busy state in a timeline semaphore An execution abandoned on error between start and submission left the ownership mutex locked forever, deadlocking the context's next reuse. Replace the submission fence with a timeline semaphore: ff_vk_exec_start() claims the context by incrementing the awaited value, the submission signals it, and the mutex only briefly guards the dependency lists. Abandonment becomes ff_vk_exec_discard(), called exactly once on a started, unsubmitted context: it frees the dependencies, drops the recording, and un-claims by signalling from the host - which a fence cannot do. ff_vk_exec_submit() cleans up after its own failures; error paths which leaked the claim now discard, and ff_vk_exec_start() failures are checked everywhere. --- libavcodec/apv_encode_vulkan.c | 11 +-- libavcodec/ffv1enc_vulkan.c | 22 +++--- libavcodec/proresenc_kostya_vulkan.c | 7 +- libavcodec/vulkan_apv.c | 11 ++- libavcodec/vulkan_decode.c | 12 +++- libavcodec/vulkan_dpx.c | 7 +- libavcodec/vulkan_encode.c | 8 ++- libavcodec/vulkan_ffv1.c | 7 +- libavcodec/vulkan_prores.c | 11 ++- libavcodec/vulkan_prores_raw.c | 7 +- libavfilter/vf_blackdetect_vulkan.c | 16 ++++- libavfilter/vf_nlmeans_vulkan.c | 67 ++++++++++-------- libavfilter/vf_scdet_vulkan.c | 16 ++++- libavfilter/vulkan_filter.c | 18 +++-- libavutil/hwcontext_vulkan.c | 35 ++++++---- libavutil/vulkan.c | 127 +++++++++++++++++++++++------------ libavutil/vulkan.h | 19 ++++-- libavutil/vulkan_functions.h | 10 +-- 18 files changed, 277 insertions(+), 134 deletions(-) diff --git a/libavcodec/apv_encode_vulkan.c b/libavcodec/apv_encode_vulkan.c index 918228309c..85e43a1339 100644 --- a/libavcodec/apv_encode_vulkan.c +++ b/libavcodec/apv_encode_vulkan.c @@ -409,6 +409,11 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, FFVkBuffer *compacted_buf; FFVkBuffer *sizes_buf; + /* Start recording */ + err = ff_vk_exec_start(&ev->s, exec); + if (err < 0) + return err; + /* Allocate per-frame buffers */ RET(ff_vk_get_pooled_buffer(&ev->s, &ev->coeffs_pool, &fd->coeffs_ref, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, @@ -458,8 +463,6 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); sizes_buf = fd->sizes_ref; - ff_vk_exec_start(&ev->s, exec); - ff_vk_exec_add_dep_refstruct(&ev->s, exec, fd->coeffs_ref); ff_vk_exec_add_dep_refstruct(&ev->s, exec, fd->bytestream_ref); ff_vk_exec_add_dep_refstruct(&ev->s, exec, fd->compacted_ref); @@ -620,13 +623,13 @@ static int submit_frame(AVCodecContext *avctx, FFVkExecContext *exec, ff_vk_exec_move_dep_refstruct(&ev->s, exec, &gathered_buf); err = ff_vk_exec_submit(&ev->s, exec); if (err < 0) - goto fail; + return err; return 0; fail: av_refstruct_unref(&gathered_buf); - ff_vk_exec_discard_deps(&ev->s, exec); + ff_vk_exec_discard(&ev->s, exec); return err; } diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index c91a5f84de..d0c168924f 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -312,6 +312,11 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, VkBufferMemoryBarrier2 buf_bar[8]; int nb_buf_bar = 0; + /* Start recording */ + err = ff_vk_exec_start(&fv->s, exec); + if (err < 0) + return err; + /* Frame state */ f->cur_enc_frame = pict; if (avctx->gop_size == 0 || f->picture_number % avctx->gop_size == 0) { @@ -408,8 +413,10 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, if (fv->is_rgb) { /* Create a temporaty frame */ tmp = av_frame_alloc(); - if (!(tmp)) - return AVERROR(ENOMEM); + if (!(tmp)) { + err = AVERROR(ENOMEM); + goto fail; + } RET(av_hwframe_get_buffer(fv->intermediate_frames_ref, tmp, 0)); @@ -451,9 +458,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, else ff_vk_set_perm(avctx->sw_pix_fmt, pd.fmt_lut, 1); - /* Start recording */ - ff_vk_exec_start(&fv->s, exec); - /* For float pixel formats we want the raw bit pattern, not a value * already passed through fp16/fp32 conversion (which can flush * denormals). Use a UINT view in that case. */ @@ -750,8 +754,10 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, if (remap_data_buf) ff_vk_exec_move_dep_refstruct(&fv->s, exec, &remap_data_buf); err = ff_vk_exec_submit(&fv->s, exec); - if (err < 0) - goto fail; + if (err < 0) { + av_frame_free(&tmp); + return err; + } f->picture_number++; @@ -759,11 +765,11 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx, return 0; fail: + ff_vk_exec_discard(&fv->s, exec); av_frame_free(&tmp); av_refstruct_unref(&slice_data_buf); av_refstruct_unref(&remap_data_buf); av_refstruct_unref(&gathered_buf); - ff_vk_exec_discard_deps(&fv->s, exec); return err; } diff --git a/libavcodec/proresenc_kostya_vulkan.c b/libavcodec/proresenc_kostya_vulkan.c index de4b34efc2..1f95bc90e2 100644 --- a/libavcodec/proresenc_kostya_vulkan.c +++ b/libavcodec/proresenc_kostya_vulkan.c @@ -434,7 +434,9 @@ static int vulkan_encode_prores_submit_frame(AVCodecContext *avctx, FFVkExecCont FFVulkanShader *shd; /* Start recording */ - ff_vk_exec_start(vkctx, exec); + err = ff_vk_exec_start(vkctx, exec); + if (err < 0) + return err; /* Sparse slice output: one fixed-stride, device-local slot per slice */ RET(ff_vk_get_pooled_buffer(vkctx, &pv->pkt_buf_pool, &pd->out_data_ref[picture_idx], @@ -697,7 +699,10 @@ static int vulkan_encode_prores_submit_frame(AVCodecContext *avctx, FFVkExecCont 0, sizeof(gather_pd), &gather_pd); vk->CmdDispatch(exec->buf, ctx->slices_per_picture, 1, 1); + return 0; + fail: + ff_vk_exec_discard(vkctx, exec); return err; } diff --git a/libavcodec/vulkan_apv.c b/libavcodec/vulkan_apv.c index cd1320d2d0..fe115428c6 100644 --- a/libavcodec/vulkan_apv.c +++ b/libavcodec/vulkan_apv.c @@ -188,7 +188,9 @@ static int vk_apv_end_frame(AVCodecContext *avctx) int nb_buf_bar = 0; FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool); - ff_vk_exec_start(&ctx->s, exec); + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; /* Make sure the buffer is flushed */ RET(ff_vk_flush_buffer(&ctx->s, frame_data_buf, 0, frame_data_buf->size, 1)); @@ -368,10 +370,15 @@ static int vk_apv_end_frame(AVCodecContext *avctx) ff_vk_exec_move_dep_refstruct(&ctx->s, exec, &coeff_buf); err = ff_vk_exec_submit(&ctx->s, exec); + if (err < 0) + return err; + + return 0; fail: + ff_vk_exec_discard(&ctx->s, exec); av_refstruct_unref(&coeff_buf); - return err < 0 ? err : 0; + return err; } static int init_decode_shader(AVCodecContext *avctx, FFVulkanContext *s, diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c index d5c069de53..1b1db635d4 100644 --- a/libavcodec/vulkan_decode.c +++ b/libavcodec/vulkan_decode.c @@ -414,7 +414,9 @@ static int decode_reset(AVCodecContext *avctx, FFVulkanDecodeShared *ctx) }; FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool); - ff_vk_exec_start(&ctx->s, exec); + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; vk->CmdBeginVideoCodingKHR(exec->buf, &decode_start); vk->CmdControlVideoCodingKHR(exec->buf, &decode_ctrl); @@ -524,8 +526,10 @@ int ff_vk_decode_frame(AVCodecContext *avctx, err = ff_vk_exec_add_dep_frame(&ctx->s, exec, pic, VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); - if (err < 0) + if (err < 0) { + ff_vk_exec_discard(&ctx->s, exec); return err; + } /* The output view is kept alive by the execution context; freeing the * picture then needs no host wait. */ @@ -575,8 +579,10 @@ int ff_vk_decode_frame(AVCodecContext *avctx, err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ref, VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); - if (err < 0) + if (err < 0) { + ff_vk_exec_discard(&ctx->s, exec); return err; + } /* The reference's image views are kept alive by the execution, * so freeing the picture needs no host wait. */ diff --git a/libavcodec/vulkan_dpx.c b/libavcodec/vulkan_dpx.c index a10ca42c0a..69e672979c 100644 --- a/libavcodec/vulkan_dpx.c +++ b/libavcodec/vulkan_dpx.c @@ -118,7 +118,9 @@ static int vk_dpx_end_frame(AVCodecContext *avctx) int nb_img_bar = 0; FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool); - ff_vk_exec_start(&ctx->s, exec); + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; /* Prepare deps */ RET(ff_vk_exec_add_dep_frame(&ctx->s, exec, dpx->frame, @@ -204,7 +206,10 @@ static int vk_dpx_end_frame(AVCodecContext *avctx) if (err < 0) return err; + return 0; + fail: + ff_vk_exec_discard(&ctx->s, exec); return 0; } diff --git a/libavcodec/vulkan_encode.c b/libavcodec/vulkan_encode.c index 7dd690ca68..e5a252c5ec 100644 --- a/libavcodec/vulkan_encode.c +++ b/libavcodec/vulkan_encode.c @@ -354,7 +354,9 @@ static int vulkan_encode_issue(AVCodecContext *avctx, /* Start command buffer recording */ exec = vp->exec = ff_vk_exec_get(&ctx->s, &ctx->enc_pool); - ff_vk_exec_start(&ctx->s, exec); + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + goto fail; cmd_buf = exec->buf; /* Output packet buffer */ @@ -364,8 +366,10 @@ static int vulkan_encode_issue(AVCodecContext *avctx, err = ff_vk_exec_add_dep_frame(&ctx->s, exec, src, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR); - if (err < 0) + if (err < 0) { + ff_vk_exec_discard(&ctx->s, exec); goto fail; + } /* Source image layout conversion. Writes to it happened on other * queues, and were made available by the semaphore wait: their access diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c index c03c507148..779f316c51 100644 --- a/libavcodec/vulkan_ffv1.c +++ b/libavcodec/vulkan_ffv1.c @@ -288,7 +288,9 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx) int nb_buf_bar = 0; FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool); - ff_vk_exec_start(&ctx->s, exec); + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; /* Prepare deps */ RET(ff_vk_exec_add_dep_frame(&ctx->s, exec, f->picture.f, @@ -571,7 +573,10 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx) /* We don't need the temporary frame after decoding */ av_frame_free(&vp->dpb_frame); + return 0; + fail: + ff_vk_exec_discard(&ctx->s, exec); return 0; } diff --git a/libavcodec/vulkan_prores.c b/libavcodec/vulkan_prores.c index 6040dcc5f1..e082aff0ce 100644 --- a/libavcodec/vulkan_prores.c +++ b/libavcodec/vulkan_prores.c @@ -200,7 +200,9 @@ static int vk_prores_end_frame(AVCodecContext *avctx) pr->qmat_chroma, sizeof(pr->qmat_chroma)); FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool); - RET(ff_vk_exec_start(&ctx->s, exec)); + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; /* Prepare deps */ RET(ff_vk_exec_add_dep_frame(&ctx->s, exec, f, @@ -345,9 +347,14 @@ static int vk_prores_end_frame(AVCodecContext *avctx) vk->CmdDispatch(exec->buf, AV_CEIL_RSHIFT(pr->mb_width, 1), pr->mb_height, 3); - RET(ff_vk_exec_submit(&ctx->s, exec)); + err = ff_vk_exec_submit(&ctx->s, exec); + if (err < 0) + return err; + + return 0; fail: + ff_vk_exec_discard(&ctx->s, exec); return err; } diff --git a/libavcodec/vulkan_prores_raw.c b/libavcodec/vulkan_prores_raw.c index 874e55506e..54567fd07a 100644 --- a/libavcodec/vulkan_prores_raw.c +++ b/libavcodec/vulkan_prores_raw.c @@ -149,7 +149,9 @@ static int vk_prores_raw_end_frame(AVCodecContext *avctx) int nb_img_bar = 0; FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool); - ff_vk_exec_start(&ctx->s, exec); + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; /* Prepare deps */ RET(ff_vk_exec_add_dep_frame(&ctx->s, exec, prr->frame, @@ -269,7 +271,10 @@ static int vk_prores_raw_end_frame(AVCodecContext *avctx) if (err < 0) return err; + return 0; + fail: + ff_vk_exec_discard(&ctx->s, exec); return 0; } diff --git a/libavfilter/vf_blackdetect_vulkan.c b/libavfilter/vf_blackdetect_vulkan.c index a1908e7019..c8ab4647ef 100644 --- a/libavfilter/vf_blackdetect_vulkan.c +++ b/libavfilter/vf_blackdetect_vulkan.c @@ -214,7 +214,12 @@ static int blackdetect_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) sum = (BlackDetectBuf *) sum_vk->mapped_mem; exec = ff_vk_exec_get(vkctx, &s->e); - ff_vk_exec_start(vkctx, exec); + err = ff_vk_exec_start(vkctx, exec); + if (err < 0) { + av_frame_free(&in); + av_refstruct_unref(&sum_vk); + return err; + } RET(ff_vk_exec_add_dep_frame(vkctx, exec, in, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, @@ -301,7 +306,12 @@ static int blackdetect_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) .bufferMemoryBarrierCount = 1, }); - RET(ff_vk_exec_submit(vkctx, exec)); + err = ff_vk_exec_submit(vkctx, exec); + if (err < 0) { + av_frame_free(&in); + av_refstruct_unref(&sum_vk); + return err; + } ff_vk_exec_wait(vkctx, exec); evaluate(link, in, sum); s->last_pts = in->pts; @@ -311,7 +321,7 @@ static int blackdetect_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) fail: if (exec) - ff_vk_exec_discard_deps(&s->vkctx, exec); + ff_vk_exec_discard(&s->vkctx, exec); av_frame_free(&in); av_refstruct_unref(&sum_vk); return err; diff --git a/libavfilter/vf_nlmeans_vulkan.c b/libavfilter/vf_nlmeans_vulkan.c index b8615316df..01f5c51ca4 100644 --- a/libavfilter/vf_nlmeans_vulkan.c +++ b/libavfilter/vf_nlmeans_vulkan.c @@ -461,12 +461,27 @@ static int nlmeans_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) VkBufferMemoryBarrier2 buf_bar[2]; int nb_buf_bar = 0; - if (!s->initialized) - RET(init_filter(ctx)); + if (!s->initialized) { + err = init_filter(ctx); + if (err < 0) { + av_frame_free(&in); + return err; + } + } + + /* Execution context */ + exec = ff_vk_exec_get(&s->vkctx, &s->e); + err = ff_vk_exec_start(vkctx, exec); + if (err < 0) { + av_frame_free(&in); + return err; + } desc = av_pix_fmt_desc_get(vkctx->output_format); - if (!desc) - return AVERROR(EINVAL); + if (!desc) { + err = AVERROR(EINVAL); + goto fail; + } /* Integral image */ int_stride = FFALIGN(vkctx->output_width, s->shd_vertical.lg_size[0]) * TYPE_SIZE; @@ -490,22 +505,19 @@ static int nlmeans_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) ws_size = ws_count * sizeof(float); /* Buffers */ - err = ff_vk_get_pooled_buffer(&s->vkctx, &s->integral_buf_pool, &integral_vk, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - NULL, - int_size * s->opts.t * desc->nb_components, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (err < 0) - return err; - err = ff_vk_get_pooled_buffer(&s->vkctx, &s->ws_buf_pool, &ws_vk, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT, - NULL, - ws_size * s-> opts.t * 2, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - if (err < 0) - return err; + RET(ff_vk_get_pooled_buffer(&s->vkctx, &s->integral_buf_pool, &integral_vk, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + NULL, + int_size * s->opts.t * desc->nb_components, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); + RET(ff_vk_get_pooled_buffer(&s->vkctx, &s->ws_buf_pool, &ws_vk, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + NULL, + ws_size * s-> opts.t * 2, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); + /* Output frame */ out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) { @@ -513,10 +525,6 @@ static int nlmeans_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) goto fail; } - /* Execution context */ - exec = ff_vk_exec_get(&s->vkctx, &s->e); - ff_vk_exec_start(vkctx, exec); - /* Dependencies */ RET(ff_vk_exec_add_dep_frame(vkctx, exec, in, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, @@ -714,19 +722,24 @@ static int nlmeans_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) ff_vk_exec_move_dep_refstruct(vkctx, exec, &integral_vk); ff_vk_exec_move_dep_refstruct(vkctx, exec, &ws_vk); - err = ff_vk_exec_submit(vkctx, exec); - if (err < 0) - goto fail; err = av_frame_copy_props(out, in); if (err < 0) goto fail; + err = ff_vk_exec_submit(vkctx, exec); + if (err < 0) { + av_frame_free(&in); + av_frame_free(&out); + return err; + } + av_frame_free(&in); return ff_filter_frame(outlink, out); fail: + ff_vk_exec_discard(vkctx, exec); av_refstruct_unref(&integral_vk); av_refstruct_unref(&ws_vk); av_frame_free(&in); diff --git a/libavfilter/vf_scdet_vulkan.c b/libavfilter/vf_scdet_vulkan.c index f87c780dfc..ee66176fac 100644 --- a/libavfilter/vf_scdet_vulkan.c +++ b/libavfilter/vf_scdet_vulkan.c @@ -170,7 +170,12 @@ static int scdet_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) sad = (SceneDetectBuf *) buf_vk->mapped_mem; exec = ff_vk_exec_get(vkctx, &s->e); - ff_vk_exec_start(vkctx, exec); + err = ff_vk_exec_start(vkctx, exec); + if (err < 0) { + av_frame_free(&in); + av_refstruct_unref(&buf_vk); + return err; + } RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->prev, VK_PIPELINE_STAGE_2_NONE, @@ -270,7 +275,12 @@ static int scdet_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) .bufferMemoryBarrierCount = 1, }); - RET(ff_vk_exec_submit(vkctx, exec)); + err = ff_vk_exec_submit(vkctx, exec); + if (err < 0) { + av_frame_free(&in); + av_refstruct_unref(&buf_vk); + return err; + } ff_vk_exec_wait(vkctx, exec); score = evaluate(ctx, sad); @@ -297,7 +307,7 @@ done: fail: if (exec) - ff_vk_exec_discard_deps(&s->vkctx, exec); + ff_vk_exec_discard(&s->vkctx, exec); av_frame_free(&in); av_refstruct_unref(&buf_vk); return err; diff --git a/libavfilter/vulkan_filter.c b/libavfilter/vulkan_filter.c index 2fc467c6d9..437b9d8c63 100644 --- a/libavfilter/vulkan_filter.c +++ b/libavfilter/vulkan_filter.c @@ -256,7 +256,9 @@ int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, /* Update descriptors and init the exec context */ FFVkExecContext *exec = ff_vk_exec_get(vkctx, e); - ff_vk_exec_start(vkctx, exec); + err = ff_vk_exec_start(vkctx, exec); + if (err < 0) + return err; RET(ff_vk_exec_add_dep_frame(vkctx, exec, out_f, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, @@ -309,7 +311,7 @@ int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, return ff_vk_exec_submit(vkctx, exec); fail: - ff_vk_exec_discard_deps(vkctx, exec); + ff_vk_exec_discard(vkctx, exec); return err; } @@ -332,7 +334,9 @@ int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, /* Update descriptors and init the exec context */ FFVkExecContext *exec = ff_vk_exec_get(vkctx, e); - ff_vk_exec_start(vkctx, exec); + err = ff_vk_exec_start(vkctx, exec); + if (err < 0) + return err; RET(ff_vk_exec_add_dep_frame(vkctx, exec, in, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, @@ -402,7 +406,7 @@ int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, return ff_vk_exec_submit(vkctx, exec); fail: - ff_vk_exec_discard_deps(vkctx, exec); + ff_vk_exec_discard(vkctx, exec); return err; } @@ -424,7 +428,9 @@ int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, /* Update descriptors and init the exec context */ FFVkExecContext *exec = ff_vk_exec_get(vkctx, e); - ff_vk_exec_start(vkctx, exec); + err = ff_vk_exec_start(vkctx, exec); + if (err < 0) + return err; /* Add deps and create temporary imageviews */ RET(ff_vk_exec_add_dep_frame(vkctx, exec, out, @@ -481,6 +487,6 @@ int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, return ff_vk_exec_submit(vkctx, exec); fail: - ff_vk_exec_discard_deps(vkctx, exec); + ff_vk_exec_discard(vkctx, exec); return err; } diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c index a9ebd96b7a..008d4b4bb6 100644 --- a/libavutil/hwcontext_vulkan.c +++ b/libavutil/hwcontext_vulkan.c @@ -2578,7 +2578,9 @@ static int switch_layout(AVHWFramesContext *hwfc, FFVkExecPool *ectx, VkCommandBuffer cmd_buf; FFVkExecContext *exec = ff_vk_exec_get(&p->vkctx, ectx); cmd_buf = exec->buf; - ff_vk_exec_start(&p->vkctx, exec); + err = ff_vk_exec_start(&p->vkctx, exec); + if (err < 0) + return err; err = ff_vk_exec_add_dep_frame(&p->vkctx, exec, &tmp_frame, VK_PIPELINE_STAGE_2_NONE, @@ -2601,8 +2603,8 @@ static int switch_layout(AVHWFramesContext *hwfc, FFVkExecPool *ectx, if (err < 0) return err; - /* We can do this because there are no real dependencies */ - ff_vk_exec_discard_deps(&p->vkctx, exec); + /* Drops the stack-based frame above from the dependency list */ + ff_vk_exec_wait(&p->vkctx, exec); return 0; } @@ -3651,7 +3653,12 @@ static int vulkan_map_from_drm_frame_sync(AVHWFramesContext *hwfc, AVFrame *dst, exec = ff_vk_exec_get(&p->vkctx, &fp->compute_exec); cmd_buf = exec->buf; - ff_vk_exec_start(&p->vkctx, exec); + err = ff_vk_exec_start(&p->vkctx, exec); + if (err < 0) { + for (int i = 0; i < desc->nb_objects; i++) + vk->DestroySemaphore(hwctx->act_dev, drm_sync_sem[i], hwctx->alloc); + return err; + } /* Ownership of semaphores is passed */ ff_vk_exec_add_dep_bool_sem(&p->vkctx, exec, @@ -3661,8 +3668,10 @@ static int vulkan_map_from_drm_frame_sync(AVHWFramesContext *hwfc, AVFrame *dst, err = ff_vk_exec_add_dep_frame(&p->vkctx, exec, dst, VK_PIPELINE_STAGE_2_NONE, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT); - if (err < 0) + if (err < 0) { + ff_vk_exec_discard(&p->vkctx, exec); return err; + } ff_vk_frame_barrier(&p->vkctx, exec, dst, img_bar, &nb_img_bar, VK_PIPELINE_STAGE_2_NONE, @@ -4223,8 +4232,6 @@ static int vulkan_drm_export_sync_fd(AVHWFramesContext *hwfc, AVVkFrame *f, ff_vk_ret2str(ret)); sync_fd = -1; } - } else { - ff_vk_exec_discard_deps(&p->vkctx, exec); } } @@ -4807,14 +4814,18 @@ static int vulkan_transfer_frame(AVHWFramesContext *hwfc, exec = ff_vk_exec_get(&p->vkctx, &fp->upload_exec); cmd_buf = exec->buf; - ff_vk_exec_start(&p->vkctx, exec); + err = ff_vk_exec_start(&p->vkctx, exec); + if (err < 0) + goto end; /* Prep destination Vulkan frame */ err = ff_vk_exec_add_dep_frame(&p->vkctx, exec, hwf, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_2_TRANSFER_BIT); - if (err < 0) + if (err < 0) { + ff_vk_exec_discard(&p->vkctx, exec); goto end; + } /* No need to declare buf deps for synchronous transfers (downloads) */ if (upload) { @@ -4822,7 +4833,7 @@ static int vulkan_transfer_frame(AVHWFramesContext *hwfc, if (host_mapped) { err = ff_vk_exec_add_dep_sw_frame(&p->vkctx, exec, swf); if (err < 0) { - ff_vk_exec_discard_deps(&p->vkctx, exec); + ff_vk_exec_discard(&p->vkctx, exec); goto end; } } @@ -4871,9 +4882,7 @@ static int vulkan_transfer_frame(AVHWFramesContext *hwfc, } err = ff_vk_exec_submit(&p->vkctx, exec); - if (err < 0) { - ff_vk_exec_discard_deps(&p->vkctx, exec); - } else if (!upload) { + if (err >= 0 && !upload) { ff_vk_exec_wait(&p->vkctx, exec); if (!host_mapped) err = copy_buffer_data(hwfc, bufs[0], swf, region, planes, 0); diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c index 7d20473271..1a1e54abf2 100644 --- a/libavutil/vulkan.c +++ b/libavutil/vulkan.c @@ -322,6 +322,8 @@ AVVulkanDeviceQueueFamily *ff_vk_qf_find(FFVulkanContext *s, return NULL; } +static void exec_discard_deps(FFVulkanContext *s, FFVkExecContext *e); + void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool) { FFVulkanFunctions *vk = &s->vkfn; @@ -329,13 +331,18 @@ void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool) for (int i = 0; i < pool->pool_size; i++) { FFVkExecContext *e = &pool->contexts[i]; - if (e->fence) { - if (e->had_submission) - vk->WaitForFences(s->hwctx->act_dev, 1, &e->fence, VK_TRUE, UINT64_MAX); - vk->DestroyFence(s->hwctx->act_dev, e->fence, s->hwctx->alloc); + if (e->sem) { + VkSemaphoreWaitInfo sem_wait_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + .semaphoreCount = 1, + .pSemaphores = &e->sem, + .pValues = &e->sem_value, + }; + vk->WaitSemaphores(s->hwctx->act_dev, &sem_wait_info, UINT64_MAX); + vk->DestroySemaphore(s->hwctx->act_dev, e->sem, s->hwctx->alloc); } - ff_vk_exec_discard_deps(s, e); + exec_discard_deps(s, e); pthread_mutex_destroy(&e->lock); } @@ -530,16 +537,20 @@ int ff_vk_exec_pool_init(FFVulkanContext *s, AVVulkanDeviceQueueFamily *qf, /* Init contexts */ for (int i = 0; i < pool->pool_size; i++) { FFVkExecContext *e = &pool->contexts[i]; - VkFenceCreateInfo fence_create = { - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - .flags = VK_FENCE_CREATE_SIGNALED_BIT, + VkSemaphoreTypeCreateInfo sem_type = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, + .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE, + }; + VkSemaphoreCreateInfo sem_create = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &sem_type, }; - /* Fence */ - ret = vk->CreateFence(s->hwctx->act_dev, &fence_create, s->hwctx->alloc, - &e->fence); + /* Timeline semaphore carrying the busy state */ + ret = vk->CreateSemaphore(s->hwctx->act_dev, &sem_create, s->hwctx->alloc, + &e->sem); if (ret != VK_SUCCESS) { - av_log(s, AV_LOG_ERROR, "Failed to create submission fence: %s\n", + av_log(s, AV_LOG_ERROR, "Failed to create submission semaphore: %s\n", ff_vk_ret2str(ret)); return AVERROR_EXTERNAL; } @@ -610,13 +621,17 @@ FFVkExecContext *ff_vk_exec_get(FFVulkanContext *s, FFVkExecPool *pool) /* Release the dependencies of every completed context, rather than leaving them held until reuse */ for (int i = 0; i < pool->pool_size; i++) { + uint64_t sem_val; FFVkExecContext *e = &pool->contexts[i]; if (pthread_mutex_trylock(&e->lock)) continue; /* In use by a recording or submitting thread */ - if ((e->nb_buf_deps || e->nb_refstruct_deps || e->nb_obj_deps || - e->nb_frame_deps || e->nb_sw_frame_deps) && - vk->GetFenceStatus(s->hwctx->act_dev, e->fence) == VK_SUCCESS) - ff_vk_exec_discard_deps(s, e); + /* Busy contexts (counter below sem_value) are claimed or executing: + * their dependency lists are off-limits */ + if (vk->GetSemaphoreCounterValue(s->hwctx->act_dev, e->sem, &sem_val) == VK_SUCCESS && + sem_val >= e->sem_value && + (e->nb_buf_deps || e->nb_refstruct_deps || e->nb_obj_deps || + e->nb_frame_deps || e->nb_sw_frame_deps)) + exec_discard_deps(s, e); pthread_mutex_unlock(&e->lock); } @@ -626,9 +641,15 @@ FFVkExecContext *ff_vk_exec_get(FFVulkanContext *s, FFVkExecPool *pool) void ff_vk_exec_wait(FFVulkanContext *s, FFVkExecContext *e) { FFVulkanFunctions *vk = &s->vkfn; + VkSemaphoreWaitInfo sem_wait_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + .semaphoreCount = 1, + .pSemaphores = &e->sem, + .pValues = &e->sem_value, + }; pthread_mutex_lock(&e->lock); - vk->WaitForFences(s->hwctx->act_dev, 1, &e->fence, VK_TRUE, UINT64_MAX); - ff_vk_exec_discard_deps(s, e); + vk->WaitSemaphores(s->hwctx->act_dev, &sem_wait_info, UINT64_MAX); + exec_discard_deps(s, e); pthread_mutex_unlock(&e->lock); } @@ -642,17 +663,20 @@ int ff_vk_exec_start(FFVulkanContext *s, FFVkExecContext *e) .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; + VkSemaphoreWaitInfo sem_wait_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + .semaphoreCount = 1, + .pSemaphores = &e->sem, + .pValues = &e->sem_value, + }; - /* Take ownership of the context; held until the end of submission */ pthread_mutex_lock(&e->lock); - /* Wait for the fence to be signalled. The fence is only reset once a - * fully recorded submission is handed to the queue, so a recording - * abandoned after an error leaves the context reusable. */ - vk->WaitForFences(s->hwctx->act_dev, 1, &e->fence, VK_TRUE, UINT64_MAX); + /* Wait out the context's previous execution */ + vk->WaitSemaphores(s->hwctx->act_dev, &sem_wait_info, UINT64_MAX); /* Discard queue dependencies */ - ff_vk_exec_discard_deps(s, e); + exec_discard_deps(s, e); ret = vk->BeginCommandBuffer(e->buf, &cmd_start); if (ret != VK_SUCCESS) { @@ -666,10 +690,22 @@ int ff_vk_exec_start(FFVulkanContext *s, FFVkExecContext *e) vk->CmdResetQueryPool(e->buf, pool->query_pool, e->query_idx, pool->nb_queries); + /* Claim: the semaphore stays below this value until submission completes + * or the recording is discarded, keeping every other user away */ + e->sem_value++; + e->sem_sig[e->sem_sig_cnt++] = (VkSemaphoreSubmitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = e->sem, + .value = e->sem_value, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + }; + + pthread_mutex_unlock(&e->lock); + return 0; } -void ff_vk_exec_discard_deps(FFVulkanContext *s, FFVkExecContext *e) +static void exec_discard_deps(FFVulkanContext *s, FFVkExecContext *e) { FFVulkanFunctions *vk = &s->vkfn; @@ -720,6 +756,22 @@ void ff_vk_exec_discard_deps(FFVulkanContext *s, FFVkExecContext *e) e->sem_sig_val_dst_cnt = 0; } +void ff_vk_exec_discard(FFVulkanContext *s, FFVkExecContext *e) +{ + FFVulkanFunctions *vk = &s->vkfn; + VkSemaphoreSignalInfo sig_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO, + .semaphore = e->sem, + .value = e->sem_value, + }; + + /* An abandoned recording is exclusively the claimer's: free everything + * without the mutex, and un-claim last by signalling the burnt generation */ + exec_discard_deps(s, e); + vk->ResetCommandBuffer(e->buf, 0); + vk->SignalSemaphore(s->hwctx->act_dev, &sig_info); +} + void ff_vk_exec_add_dep_refstruct(FFVulkanContext *s, FFVkExecContext *e, void *obj) { @@ -758,10 +810,8 @@ int ff_vk_exec_add_dep_sw_frame(FFVulkanContext *s, FFVkExecContext *e, av_assert1(e->nb_sw_frame_deps < FF_VK_EXEC_MAX_SW_FRAME_DEPS); e->sw_frame_deps[e->nb_sw_frame_deps] = av_frame_clone(f); - if (!e->sw_frame_deps[e->nb_sw_frame_deps]) { - ff_vk_exec_discard_deps(s, e); + if (!e->sw_frame_deps[e->nb_sw_frame_deps]) return AVERROR(ENOMEM); - } e->nb_sw_frame_deps++; @@ -834,10 +884,8 @@ int ff_vk_exec_add_dep_frame(FFVulkanContext *s, FFVkExecContext *e, AVFrame *f, if (f->buf[0]) { av_assert1(e->nb_buf_deps < FF_VK_EXEC_MAX_BUF_DEPS); e->buf_deps[e->nb_buf_deps] = av_buffer_ref(f->buf[0]); - if (!e->buf_deps[e->nb_buf_deps]) { - ff_vk_exec_discard_deps(s, e); + if (!e->buf_deps[e->nb_buf_deps]) return AVERROR(ENOMEM); - } e->nb_buf_deps++; } @@ -936,23 +984,16 @@ int ff_vk_exec_submit(FFVulkanContext *s, FFVkExecContext *e) if (ret != VK_SUCCESS) { av_log(s, AV_LOG_ERROR, "Unable to finish command buffer: %s\n", ff_vk_ret2str(ret)); - ff_vk_exec_discard_deps(s, e); - pthread_mutex_unlock(&e->lock); + ff_vk_exec_discard(s, e); return AVERROR_EXTERNAL; } - vk->ResetFences(s->hwctx->act_dev, 1, &e->fence); - #if FF_API_VULKAN_SYNC_QUEUES FF_DISABLE_DEPRECATION_WARNINGS s->hwctx->lock_queue(s->device, e->qf, e->qi); FF_ENABLE_DEPRECATION_WARNINGS #endif - ret = vk->QueueSubmit2(e->queue, 1, &submit_info, e->fence); - if (ret != VK_SUCCESS) { - /* Keep the context usable, signal with an empty submission */ - vk->QueueSubmit2(e->queue, 0, NULL, e->fence); - } + ret = vk->QueueSubmit2(e->queue, 1, &submit_info, VK_NULL_HANDLE); #if FF_API_VULKAN_SYNC_QUEUES FF_DISABLE_DEPRECATION_WARNINGS s->hwctx->unlock_queue(s->device, e->qf, e->qi); @@ -962,8 +1003,8 @@ FF_ENABLE_DEPRECATION_WARNINGS if (ret != VK_SUCCESS) { av_log(s, AV_LOG_ERROR, "Unable to submit command buffer: %s\n", ff_vk_ret2str(ret)); - ff_vk_exec_discard_deps(s, e); - pthread_mutex_unlock(&e->lock); + /* This also un-claims the context by signalling its semaphore */ + ff_vk_exec_discard(s, e); return AVERROR_EXTERNAL; } @@ -993,8 +1034,6 @@ FF_ENABLE_DEPRECATION_WARNINGS e->had_submission = 1; - pthread_mutex_unlock(&e->lock); - return 0; } diff --git a/libavutil/vulkan.h b/libavutil/vulkan.h index 64142e0575..16799b3232 100644 --- a/libavutil/vulkan.h +++ b/libavutil/vulkan.h @@ -128,6 +128,9 @@ typedef struct FFVkExecObjDep { typedef struct FFVkExecContext { uint32_t idx; const struct FFVkExecPool *parent; + + /* Set on submission; pool-rolling users check and clear it to tell + * contexts with uncollected results apart from fresh ones */ int had_submission; /* Queue for the execution context */ @@ -138,12 +141,13 @@ typedef struct FFVkExecContext { /* Command buffer for the context */ VkCommandBuffer buf; - /* Fence for the command buffer */ - VkFence fence; + /* Busy (claimed or executing) while the counter is below sem_value; + * signalled by the submission, or by ff_vk_exec_discard() */ + VkSemaphore sem; + uint64_t sem_value; - /* CPU-side ownership. Held from ff_vk_exec_start() until the end of - * submission, and briefly by the eager dependency release in - * ff_vk_exec_get(). */ + /* Briefly guards the dependency lists; the busy state is carried by the + * semaphore above */ pthread_mutex_t lock; /* Opaque data, untouched, free to use by users */ @@ -495,6 +499,9 @@ void ff_vk_exec_wait(FFVulkanContext *s, FFVkExecContext *e); * are discarded, the execution is submitted, or a failure happens. * update_frame will update the frame's properties before it is unlocked, * only if submission was successful. + * ff_vk_exec_discard() abandons a started, unsubmitted recording: call it + * exactly once, from the claiming thread. ff_vk_exec_submit() cleans up + * after its own failures. */ /* Takes a new reference to an AVRefStruct-managed object, held until the @@ -529,7 +536,7 @@ void ff_vk_exec_update_frame(FFVulkanContext *s, FFVkExecContext *e, AVFrame *f, int ff_vk_exec_mirror_sem_value(FFVulkanContext *s, FFVkExecContext *e, VkSemaphore *dst, uint64_t *dst_val, AVFrame *f); -void ff_vk_exec_discard_deps(FFVulkanContext *s, FFVkExecContext *e); +void ff_vk_exec_discard(FFVulkanContext *s, FFVkExecContext *e); /** * Create a single imageview for a given plane. diff --git a/libavutil/vulkan_functions.h b/libavutil/vulkan_functions.h index 5b6fc09e5d..4835038514 100644 --- a/libavutil/vulkan_functions.h +++ b/libavutil/vulkan_functions.h @@ -118,6 +118,7 @@ typedef uint64_t FFVulkanExtensions; MACRO(1, 1, FF_VK_EXT_NO_FLAG, AllocateCommandBuffers) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, BeginCommandBuffer) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, EndCommandBuffer) \ + MACRO(1, 1, FF_VK_EXT_NO_FLAG, ResetCommandBuffer) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, FreeCommandBuffers) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, CmdDispatch) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, CmdDispatchBase) \ @@ -127,18 +128,13 @@ typedef uint64_t FFVulkanExtensions; MACRO(1, 1, FF_VK_EXT_NO_FLAG, QueueSubmit) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, QueueSubmit2) \ \ - /* Fences */ \ - MACRO(1, 1, FF_VK_EXT_NO_FLAG, CreateFence) \ - MACRO(1, 1, FF_VK_EXT_NO_FLAG, WaitForFences) \ - MACRO(1, 1, FF_VK_EXT_NO_FLAG, GetFenceStatus) \ - MACRO(1, 1, FF_VK_EXT_NO_FLAG, ResetFences) \ - MACRO(1, 1, FF_VK_EXT_NO_FLAG, DestroyFence) \ - \ /* Semaphores */ \ MACRO(1, 1, FF_VK_EXT_EXTERNAL_FD_SEM, GetSemaphoreFdKHR) \ MACRO(1, 1, FF_VK_EXT_EXTERNAL_FD_SEM, ImportSemaphoreFdKHR) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, CreateSemaphore) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, WaitSemaphores) \ + MACRO(1, 1, FF_VK_EXT_NO_FLAG, SignalSemaphore) \ + MACRO(1, 1, FF_VK_EXT_NO_FLAG, GetSemaphoreCounterValue) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, DestroySemaphore) \ \ /* Memory */ \ -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
