This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 2f0c34b10e02df29a707cdc4d16a29044fbbe2bd Author: Lynne <[email protected]> AuthorDate: Sun Sep 20 13:58:46 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Sun Sep 20 14:45:52 2026 +0900 vulkan_h264: only bind reference fields that were decoded into their slot A Vulkan DPB slot only becomes active once a decode operation sets it up, and from then on holds exactly the picture resource that was decoded into it. The H.264 decoder however hands us references that never went through the hwaccel: the dummies it synthesizes to fill frame_num gaps and to stand in for missing references, and fields lost to a seek. Binding those as active slots fails validation (VUIDs 07239, 07265, 07266, 07151), and since the slot lookup for long-term references only searched the first 16 pictures and fell back to slot 0, a resource could end up bound twice (07238, 07264). Track, per picture, which fields were actually decoded into its slot, and bind only the references, and only the fields of them, that were: vk_h264_fill_pict returns 1 when it bound the picture and 0 when it cannot be bound, and the reference loops advance by that. Use the picture's index in the DPB as its slot directly instead of searching for it, and describe the setup slot by the structure of the picture being decoded rather than by its reference marking, which does not describe the slot's contents while decoding the second field of a pair. --- libavcodec/vulkan_h264.c | 96 +++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/libavcodec/vulkan_h264.c b/libavcodec/vulkan_h264.c index 0c50de4e84..6be0f957e9 100644 --- a/libavcodec/vulkan_h264.c +++ b/libavcodec/vulkan_h264.c @@ -37,6 +37,9 @@ const FFVulkanDecodeDescriptor ff_vk_dec_h264_desc = { typedef struct H264VulkanDecodePicture { FFVulkanDecodePicture vp; + /* PICT_* mask of what was actually decoded into the picture's DPB slot */ + int decoded; + /* Current picture */ StdVideoDecodeH264ReferenceInfo h264_ref; VkVideoDecodeH264DpbSlotInfoKHR vkh264_ref; @@ -53,22 +56,34 @@ typedef struct H264VulkanDecodePicture { const static int h264_scaling_list8_order[] = { 0, 3, 1, 4, 2, 5 }; +/* Returns 1 if the picture was bound, 0 if it cannot be */ static int vk_h264_fill_pict(AVCodecContext *avctx, H264Picture **ref_src, VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ VkVideoDecodeH264DpbSlotInfoKHR *vkh264_ref, /* Goes in ^ */ StdVideoDecodeH264ReferenceInfo *h264_ref, /* Goes in ^ */ H264Picture *pic, int is_current, - int is_field, int picture_structure, - int dpb_slot_index) + int is_field, int picture_structure) { + const H264Context *h = avctx->priv_data; FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; FFVulkanDecodeShared *ctx = dec->shared_ctx; H264VulkanDecodePicture *hp = pic->hwaccel_picture_private; FFVulkanDecodePicture *vkpic = &hp->vp; + const int dpb_slot_index = pic - h->DPB; + int err; + + /* A slot only holds what was decoded into it: not the pictures the + * decoder synthesizes to fill frame_num gaps or to stand in for missing + * references, nor fields lost to a seek. */ + if (!is_current) { + picture_structure &= hp->decoded; + if (!picture_structure) + return 0; + } - int err = ff_vk_decode_prepare_frame(dec, pic->f, vkpic, is_current, - dec->dedicated_dpb); + err = ff_vk_decode_prepare_frame(dec, pic->f, vkpic, is_current, + dec->dedicated_dpb); if (err < 0) return err; @@ -113,7 +128,7 @@ static int vk_h264_fill_pict(AVCodecContext *avctx, H264Picture **ref_src, if (ref_src) *ref_src = pic; - return 0; + return 1; } static StdVideoH264LevelIdc convert_to_vk_level_idc(int level_idc) @@ -375,7 +390,7 @@ static int vk_h264_start_frame(AVCodecContext *avctx, av_unused uint32_t size) { int err; - int dpb_slot_index = 0; + int nb_refs = 0; H264Context *h = avctx->priv_data; H264Picture *pic = h->cur_pic_ptr; @@ -383,62 +398,36 @@ static int vk_h264_start_frame(AVCodecContext *avctx, FFVulkanDecodePicture *vp = &hp->vp; /* Fill in main slot */ - dpb_slot_index = 0; - for (unsigned slot = 0; slot < H264_MAX_PICTURE_COUNT; slot++) { - if (pic == &h->DPB[slot]) { - dpb_slot_index = slot; - break; - } - } - err = vk_h264_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, &hp->vkh264_ref, &hp->h264_ref, pic, 1, - h->DPB[dpb_slot_index].field_picture, - h->DPB[dpb_slot_index].reference, - dpb_slot_index); + FIELD_PICTURE(h), h->picture_structure); if (err < 0) return err; /* Fill in short-term references */ for (int i = 0; i < h->short_ref_count; i++) { - dpb_slot_index = 0; - for (unsigned slot = 0; slot < H264_MAX_PICTURE_COUNT; slot++) { - if (h->short_ref[i] == &h->DPB[slot]) { - dpb_slot_index = slot; - break; - } - } - err = vk_h264_fill_pict(avctx, &hp->ref_src[i], &vp->ref_slots[i], - &vp->refs[i], &hp->vkh264_refs[i], - &hp->h264_refs[i], h->short_ref[i], 0, - h->DPB[dpb_slot_index].field_picture, - h->DPB[dpb_slot_index].reference, - dpb_slot_index); + H264Picture *ref = h->short_ref[i]; + err = vk_h264_fill_pict(avctx, &hp->ref_src[nb_refs], &vp->ref_slots[nb_refs], + &vp->refs[nb_refs], &hp->vkh264_refs[nb_refs], + &hp->h264_refs[nb_refs], ref, 0, + ref->field_picture, ref->reference); if (err < 0) return err; + nb_refs += err; } /* Fill in long-term refs */ - for (int r = 0, i = h->short_ref_count; r < H264_MAX_DPB_FRAMES && - i < h->short_ref_count + h->long_ref_count; r++) { - if (!h->long_ref[r]) + for (int r = 0, i = 0; r < H264_MAX_DPB_FRAMES && i < h->long_ref_count; r++) { + H264Picture *ref = h->long_ref[r]; + if (!ref) continue; - - dpb_slot_index = 0; - for (unsigned slot = 0; slot < 16; slot++) { - if (h->long_ref[r] == &h->DPB[slot]) { - dpb_slot_index = slot; - break; - } - } - err = vk_h264_fill_pict(avctx, &hp->ref_src[i], &vp->ref_slots[i], - &vp->refs[i], &hp->vkh264_refs[i], - &hp->h264_refs[i], h->long_ref[r], 0, - h->DPB[dpb_slot_index].field_picture, - h->DPB[dpb_slot_index].reference, - dpb_slot_index); + err = vk_h264_fill_pict(avctx, &hp->ref_src[nb_refs], &vp->ref_slots[nb_refs], + &vp->refs[nb_refs], &hp->vkh264_refs[nb_refs], + &hp->h264_refs[nb_refs], ref, 0, + ref->field_picture, ref->reference); if (err < 0) return err; + nb_refs += err; i++; } @@ -470,7 +459,7 @@ static int vk_h264_start_frame(AVCodecContext *avctx, .pNext = &hp->h264_pic_info, .flags = 0x0, .pSetupReferenceSlot = &vp->ref_slot, - .referenceSlotCount = h->short_ref_count + h->long_ref_count, + .referenceSlotCount = nb_refs, .pReferenceSlots = vp->ref_slots, .dstPictureResource = (VkVideoPictureResourceInfoKHR) { .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, @@ -520,6 +509,7 @@ static int vk_h264_end_frame(AVCodecContext *avctx) FFVulkanDecodePicture *vp = &hp->vp; FFVulkanDecodePicture *rvp[H264_MAX_PICTURE_COUNT] = { 0 }; AVFrame *rav[H264_MAX_PICTURE_COUNT] = { 0 }; + int err; #ifdef VK_KHR_video_maintenance2 StdVideoH264ScalingLists vksps_scaling; @@ -551,7 +541,7 @@ static int vk_h264_end_frame(AVCodecContext *avctx) if (!dec->session_params && !(ctx->s.extensions & FF_VK_EXT_VIDEO_MAINTENANCE_2)) { - int err = vk_h264_create_params(avctx, &dec->session_params); + err = vk_h264_create_params(avctx, &dec->session_params); if (err < 0) return err; @@ -570,7 +560,13 @@ static int vk_h264_end_frame(AVCodecContext *avctx) av_log(avctx, AV_LOG_DEBUG, "Decoding frame, %zu bytes, %i slices\n", vp->slices_size, hp->h264_pic_info.sliceCount); - return ff_vk_decode_frame(avctx, pic->f, vp, rav, rvp); + err = ff_vk_decode_frame(avctx, pic->f, vp, rav, rvp); + if (err < 0) + return err; + + hp->decoded |= h->picture_structure; + + return 0; } static void vk_h264_free_frame_priv(AVRefStructOpaque _hwctx, void *data) -- 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]
