This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit b3ee8c8920b13160faf66a1bccaa2691477e7c6a
Author:     Lynne <[email protected]>
AuthorDate: Fri Aug 7 14:02:02 2026 +0900
Commit:     Lynne <[email protected]>
CommitDate: Mon Aug 17 14:57:07 2026 +0900

    vulkan_video: allocate DPB images internally, outside of any frame machinery
    
    DISTINCT-mode references and encoder reconstruction images are never
    user-visible and only ever accessed by a single queue, yet were
    allocated through a full AVHWFramesContext: CONCURRENT-shared images
    (disabling compression on several drivers), plus an unused frames
    context with its own execution pools, per codec instance.
    
    Pool them internally as plain {image, memory, view, layout} entries:
    one video-stage memory barrier per submission orders all prior work,
    so they need no semaphores or dependencies, views are created once,
    and freeing a DISTINCT-mode picture no longer waits on the host.
    COINCIDE-mode outputs remain ordinary frames.
---
 libavcodec/vulkan_decode.c | 218 +++++++++++++++++++++++++--------------------
 libavcodec/vulkan_decode.h |   3 +-
 libavcodec/vulkan_encode.c | 195 ++++++++++++++++++++++++----------------
 libavcodec/vulkan_video.c  | 115 ++++++++++++++++++++----
 libavcodec/vulkan_video.h  |  49 ++++++++--
 5 files changed, 382 insertions(+), 198 deletions(-)

diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c
index 1b1db635d4..5ea1bacd84 100644
--- a/libavcodec/vulkan_decode.c
+++ b/libavcodec/vulkan_decode.c
@@ -161,26 +161,13 @@ int ff_vk_params_invalidate(AVCodecContext *avctx, int t, 
const uint8_t *b, uint
     return 0;
 }
 
-static AVFrame *vk_get_dpb_pool(FFVulkanDecodeShared *ctx)
-{
-    int err;
-    AVFrame *avf = av_frame_alloc();
-    if (!avf)
-        return NULL;
-
-    err = av_hwframe_get_buffer(ctx->common.dpb_hwfc_ref, avf, 0x0);
-    if (err < 0)
-        av_frame_free(&avf);
-
-    return avf;
-}
-
 static void init_frame(FFVulkanDecodeContext *dec, FFVulkanDecodePicture 
*vkpic)
 {
     FFVulkanDecodeShared *ctx = dec->shared_ctx;
     FFVulkanFunctions *vk = &ctx->s.vkfn;
 
     vkpic->dpb_frame     = NULL;
+    vkpic->dpb_img       = NULL;
     vkpic->out_views     = NULL;
     vkpic->view.ref      = VK_NULL_HANDLE;
     vkpic->view.out      = VK_NULL_HANDLE;
@@ -204,9 +191,8 @@ int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, 
AVFrame *pic,
 
     init_frame(dec, vkpic);
 
-    /* Slot 0 holds the output view, slot 1 the DISTINCT-mode reference
-     * view; refcounted, so executions keep them alive past the picture */
-    vkpic->out_views = ff_vk_imageviews_alloc(&ctx->s, 2);
+    /* Refcounted, so executions keep it alive past the picture */
+    vkpic->out_views = ff_vk_imageviews_alloc(&ctx->s, 1);
     if (!vkpic->out_views)
         return AVERROR(ENOMEM);
 
@@ -214,33 +200,26 @@ int ff_vk_decode_prepare_frame(FFVulkanDecodeContext 
*dec, AVFrame *pic,
         vkpic->view.ref = ctx->common.layered_view;
         vkpic->view.aspect_ref = ctx->common.layered_aspect;
     } else if (alloc_dpb) {
-        AVHWFramesContext *dpb_frames = (AVHWFramesContext 
*)ctx->common.dpb_hwfc_ref->data;
-        AVVulkanFramesContext *dpb_hwfc = dpb_frames->hwctx;
-
-        vkpic->dpb_frame = vk_get_dpb_pool(ctx);
-        if (!vkpic->dpb_frame)
+        vkpic->dpb_img = av_refstruct_pool_get(ctx->common.dpb->img_pool);
+        if (!vkpic->dpb_img)
             return AVERROR(ENOMEM);
 
-        err = ff_vk_create_view(&ctx->s, &ctx->common,
-                                &vkpic->out_views->views[1], 
&vkpic->view.aspect_ref,
-                                (AVVkFrame *)vkpic->dpb_frame->data[0],
-                                dpb_hwfc->format[0], 
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR);
-        if (err < 0)
-            return err;
-
-        vkpic->view.ref = vkpic->out_views->views[1];
+        /* The view is owned by the pool entry */
+        vkpic->view.ref        = vkpic->dpb_img->view;
+        vkpic->view.aspect_ref = vkpic->dpb_img->aspect;
     }
 
     if (!alloc_dpb || is_current) {
         AVHWFramesContext *frames = (AVHWFramesContext 
*)pic->hw_frames_ctx->data;
         AVVulkanFramesContext *hwfc = frames->hwctx;
 
-        err = ff_vk_create_view(&ctx->s, &ctx->common,
+        err = ff_vk_create_view(&ctx->s,
                                 &vkpic->out_views->views[0], 
&vkpic->view.aspect,
-                                (AVVkFrame *)pic->data[0],
+                                ((AVVkFrame *)pic->data[0])->img[0],
                                 hwfc->format[0],
                                 VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR |
-                                (hwfc->usage & 
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR));
+                                (hwfc->usage & 
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR),
+                                0);
                                 // the above fixes 
VUID-VkVideoBeginCodingInfoKHR-slotIndex-07245
         if (err < 0)
             return err;
@@ -472,8 +451,9 @@ int ff_vk_decode_frame(AVCodecContext *avctx,
         .sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR,
     };
 
-    VkImageMemoryBarrier2 img_bar[37];
+    VkImageMemoryBarrier2 img_bar[38];
     int nb_img_bar = 0;
+
     size_t data_size = FFALIGN(vp->slices_size,
                                ctx->caps.minBitstreamBufferSizeAlignment);
 
@@ -544,7 +524,7 @@ int ff_vk_decode_frame(AVCodecContext *avctx,
         .srcAccessMask = VK_ACCESS_2_NONE,
         .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR,
         .oldLayout = vkf->layout[0],
-        .newLayout = (layered_dpb || vp->dpb_frame) ?
+        .newLayout = (layered_dpb || vp->dpb_img) ?
                      VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR :
                      VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, /* Spec, 07252 
utter madness */
         .srcQueueFamilyIndex = vkf->queue_family[0],
@@ -559,24 +539,42 @@ int ff_vk_decode_frame(AVCodecContext *avctx,
     ff_vk_exec_update_frame(&ctx->s, exec, pic,
                             &img_bar[nb_img_bar], &nb_img_bar);
 
-    /* Reference for the current image, if existing and not layered */
-    if (vp->dpb_frame) {
-        err = ff_vk_exec_add_dep_frame(&ctx->s, exec, vp->dpb_frame,
-                                       
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
-                                       
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR);
-        if (err < 0)
-            return err;
-    }
+    /* Current picture's DISTINCT-mode DPB image: transition on first use */
+    if (vp->dpb_img) {
+        FFVkVideoDPBImage *di = vp->dpb_img;
 
-    if (!layered_dpb) {
-        /* All references (apart from the current) for non-layered refs */
+        if (di->layout != VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR) {
+            img_bar[nb_img_bar] = (VkImageMemoryBarrier2) {
+                .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+                .srcStageMask = VK_PIPELINE_STAGE_2_NONE,
+                .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
+                .srcAccessMask = VK_ACCESS_2_NONE,
+                .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR |
+                                 VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR,
+                .oldLayout = di->layout,
+                .newLayout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR,
+                .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .image = di->img,
+                .subresourceRange = (VkImageSubresourceRange) {
+                    .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+                    .layerCount = 1,
+                    .levelCount = 1,
+                },
+            };
+            nb_img_bar++;
+            di->layout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR;
+        }
+    }
 
+    /* COINCIDE-mode references only: DISTINCT-mode references are covered
+     * by the memory barrier, with pool-owned views */
+    if (!dec->dedicated_dpb) {
         for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) {
             AVFrame *ref_frame = rpic[i];
             FFVulkanDecodePicture *rvp = rvkp[i];
-            AVFrame *ref = rvp->dpb_frame ? rvp->dpb_frame : ref_frame;
 
-            err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ref,
+            err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ref_frame,
                                            
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
                                            
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR);
             if (err < 0) {
@@ -589,8 +587,8 @@ int ff_vk_decode_frame(AVCodecContext *avctx,
             if (err == 0 && rvp->out_views)
                 ff_vk_exec_add_dep_refstruct(&ctx->s, exec, rvp->out_views);
 
-            if (!rvp->dpb_frame) {
-                AVVkFrame *rvkf = (AVVkFrame *)ref->data[0];
+            {
+                AVVkFrame *rvkf = (AVVkFrame *)ref_frame->data[0];
 
                 img_bar[nb_img_bar] = (VkImageMemoryBarrier2) {
                     .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
@@ -611,24 +609,31 @@ int ff_vk_decode_frame(AVCodecContext *avctx,
                         .levelCount = 1,
                     },
                 };
-                ff_vk_exec_update_frame(&ctx->s, exec, ref,
+                ff_vk_exec_update_frame(&ctx->s, exec, ref_frame,
                                         &img_bar[nb_img_bar], &nb_img_bar);
             }
         }
-    } else if (vp->decode_info.referenceSlotCount ||
-               vp->view.out != vp->view.ref) {
-        /* Single barrier for a single layered ref */
-        err = ff_vk_exec_add_dep_frame(&ctx->s, exec, 
ctx->common.layered_frame,
-                                       
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
-                                       
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR);
-        if (err < 0)
-            return err;
     }
 
-    /* Change image layout */
+    /* Internal DPB images are queue-exclusive: one decode-stage memory
+     * barrier per submission orders all prior work, references included, so
+     * they need no semaphores or dependencies, and image barriers only for
+     * initial layout transitions. */
+    VkMemoryBarrier2 mem_bar = {
+        .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
+        .srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
+        .srcAccessMask = VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR,
+        .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
+        .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR |
+                         VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR,
+    };
+
+    /* Change image layouts, and synchronize the internal DPB if in use */
     vk->CmdPipelineBarrier2(cmd_buf, &(VkDependencyInfo) {
             .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
             .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
+            .pMemoryBarriers = &mem_bar,
+            .memoryBarrierCount = ctx->common.dpb ? 1 : 0,
             .pImageMemoryBarriers = img_bar,
             .imageMemoryBarrierCount = nb_img_bar,
         });
@@ -652,6 +657,8 @@ void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, 
FFVulkanDecodePicture *
     av_refstruct_unref(&vp->out_views);
 
     av_frame_free(&vp->dpb_frame);
+    /* No wait: pool reuse is ordered by the DPB barrier */
+    av_refstruct_unref(&vp->dpb_img);
 }
 
 static void free_common(AVRefStructOpaque unused, void *obj)
@@ -662,9 +669,6 @@ static void free_common(AVRefStructOpaque unused, void *obj)
     /* Wait on and free execution pool */
     ff_vk_exec_pool_free(&ctx->s, &ctx->exec_pool);
 
-    /* This also frees all references from this pool */
-    av_frame_free(&ctx->common.layered_frame);
-
     av_refstruct_pool_uninit(&ctx->buf_pool);
 
     ff_vk_video_common_uninit(s, &ctx->common);
@@ -1309,25 +1313,10 @@ int ff_vk_decode_init(AVCodecContext *avctx)
             goto fail;
     }
 
-    /* If doing an out-of-place decoding, create a DPB pool */
+    /* Out-of-place decoding: create the internal, queue-exclusive DPB pool */
     if (dec->dedicated_dpb || avctx->codec_id == AV_CODEC_ID_AV1) {
-        AVHWFramesContext *dpb_frames;
-        AVVulkanFramesContext *dpb_hwfc;
-
-        ctx->common.dpb_hwfc_ref = av_hwframe_ctx_alloc(s->frames->device_ref);
-        if (!ctx->common.dpb_hwfc_ref) {
-            err = AVERROR(ENOMEM);
-            goto fail;
-        }
-
-        dpb_frames = (AVHWFramesContext *)ctx->common.dpb_hwfc_ref->data;
-        dpb_frames->format    = s->frames->format;
-        dpb_frames->sw_format = s->frames->sw_format;
-        dpb_frames->width     = s->frames->width;
-        dpb_frames->height    = s->frames->height;
-
-        dpb_hwfc = dpb_frames->hwctx;
-        void *profile_list = (void 
*)ff_vk_find_struct(ctx->s.hwfc->create_pnext,
+        VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
+        void *create_pnext = (void 
*)ff_vk_find_struct(ctx->s.hwfc->create_pnext,
                                                        
VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
         /* Reference (DPB) images use the same tiling and pNext chain as 
output.
          * If VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR is 0, 
the
@@ -1335,37 +1324,70 @@ int ff_vk_decode_init(AVCodecContext *avctx)
         void *drm_create_pnext = (void 
*)ff_vk_find_struct(ctx->s.hwfc->create_pnext,
                                                            
VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT);
         if (drm_create_pnext) {
-            dpb_hwfc->create_pnext = drm_create_pnext;
-            dpb_hwfc->tiling       = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
+            create_pnext = drm_create_pnext;
+            tiling       = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
             av_assert2(ff_vk_find_struct(drm_create_pnext, 
VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR));
-        } else {
-            dpb_hwfc->create_pnext = profile_list;
-            dpb_hwfc->tiling       = VK_IMAGE_TILING_OPTIMAL;
         }
-        dpb_hwfc->format[0]    = s->hwfc->format[0];
-        dpb_hwfc->usage        = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
-
-        if (ctx->common.layered_dpb)
-            dpb_hwfc->nb_layers = ctx->caps.maxDpbSlots;
 
-        err = av_hwframe_ctx_init(ctx->common.dpb_hwfc_ref);
+        err = ff_vk_video_dpb_init(s, &ctx->common, s->hwfc->format[0],
+                                   VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR,
+                                   tiling, create_pnext,
+                                   s->frames->width, s->frames->height,
+                                   ctx->common.layered_dpb ?
+                                   ctx->caps.maxDpbSlots : 1);
         if (err < 0)
             goto fail;
 
         if (ctx->common.layered_dpb) {
-            ctx->common.layered_frame = vk_get_dpb_pool(ctx);
-            if (!ctx->common.layered_frame) {
+            FFVulkanFunctions *vk = &ctx->s.vkfn;
+            FFVkExecContext *exec;
+            VkImageMemoryBarrier2 img_bar;
+
+            ctx->common.layered_img = 
av_refstruct_pool_get(ctx->common.dpb->img_pool);
+            if (!ctx->common.layered_img) {
                 err = AVERROR(ENOMEM);
                 goto fail;
             }
 
-            err = ff_vk_create_view(&ctx->s, &ctx->common,
-                                    &ctx->common.layered_view,
-                                    &ctx->common.layered_aspect,
-                                    (AVVkFrame 
*)ctx->common.layered_frame->data[0],
-                                    s->hwfc->format[0], 
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR);
+            ctx->common.layered_view   = ctx->common.layered_img->view;
+            ctx->common.layered_aspect = ctx->common.layered_img->aspect;
+
+            /* Created eagerly, so transitioned eagerly; per-picture images
+             * transition on first use instead */
+            exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool);
+            err = ff_vk_exec_start(&ctx->s, exec);
             if (err < 0)
                 goto fail;
+
+            img_bar = (VkImageMemoryBarrier2) {
+                .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+                .srcStageMask = VK_PIPELINE_STAGE_2_NONE,
+                .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
+                .srcAccessMask = VK_ACCESS_2_NONE,
+                .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR |
+                                 VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR,
+                .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+                .newLayout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR,
+                .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .image = ctx->common.layered_img->img,
+                .subresourceRange = (VkImageSubresourceRange) {
+                    .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+                    .layerCount = VK_REMAINING_ARRAY_LAYERS,
+                    .levelCount = 1,
+                },
+            };
+            vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+                    .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+                    .pImageMemoryBarriers = &img_bar,
+                    .imageMemoryBarrierCount = 1,
+                });
+
+            err = ff_vk_exec_submit(&ctx->s, exec);
+            if (err < 0)
+                goto fail;
+
+            ctx->common.layered_img->layout = 
VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR;
         }
     }
 
diff --git a/libavcodec/vulkan_decode.h b/libavcodec/vulkan_decode.h
index b2a5ab45b6..eb334ea1ae 100644
--- a/libavcodec/vulkan_decode.h
+++ b/libavcodec/vulkan_decode.h
@@ -71,7 +71,8 @@ typedef struct FFVulkanDecodeContext {
 } FFVulkanDecodeContext;
 
 typedef struct FFVulkanDecodePicture {
-    AVFrame                        *dpb_frame;      /* Only used for 
out-of-place decoding. */
+    AVFrame                        *dpb_frame;      /* Software-defined 
decoders only. */
+    FFVkVideoDPBImage              *dpb_img;        /* DISTINCT-mode reference 
image */
 
     struct {
         VkImageView                     ref;        /* Image representation 
view (reference) */
diff --git a/libavcodec/vulkan_encode.c b/libavcodec/vulkan_encode.c
index e5a252c5ec..74e1082353 100644
--- a/libavcodec/vulkan_encode.c
+++ b/libavcodec/vulkan_encode.c
@@ -39,12 +39,7 @@ static void vulkan_encode_free_pic(FFVulkanEncodeContext 
*ctx,
         vk->DestroyImageView(ctx->s.hwctx->act_dev, vp->in.view,
                              ctx->s.hwctx->alloc);
 
-    if (!ctx->common.layered_dpb && vp->dpb.view)
-        vk->DestroyImageView(ctx->s.hwctx->act_dev, vp->dpb.view,
-                             ctx->s.hwctx->alloc);
-
     vp->in.view = VK_NULL_HANDLE;
-    vp->dpb.view = VK_NULL_HANDLE;
 
     ctx->slots[vp->dpb_slot.slotIndex] = NULL;
 }
@@ -95,21 +90,18 @@ static int vulkan_encode_init(AVCodecContext *avctx, 
FFHWBaseEncodePicture *pic)
     }
 
     /* Input image view */
-    err = ff_vk_create_view(&ctx->s, &ctx->common,
+    err = ff_vk_create_view(&ctx->s,
                             &vp->in.view, &vp->in.aspect,
-                            vkf, vkfc->format[0], 
VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR);
+                            vkf->img[0], vkfc->format[0],
+                            VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR, 0);
     if (err < 0)
         return err;
 
-    /* Reference view */
+    /* Reference view: owned by the DPB pool entry */
     if (!ctx->common.layered_dpb) {
-        AVFrame *rf = pic->recon_image;
-        AVVkFrame *rvkf = (AVVkFrame *)rf->data[0];
-        err = ff_vk_create_view(&ctx->s, &ctx->common,
-                                &vp->dpb.view, &vp->dpb.aspect,
-                                rvkf, ctx->pic_format, 
VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
-        if (err < 0)
-            return err;
+        FFVkVideoDPBImage *di = (FFVkVideoDPBImage *)pic->recon_image->data[0];
+        vp->dpb.view   = di->view;
+        vp->dpb.aspect = di->aspect;
     } else {
         vp->dpb.view = ctx->common.layered_view;
         vp->dpb.aspect = ctx->common.layered_aspect;
@@ -396,38 +388,51 @@ static int vulkan_encode_issue(AVCodecContext *avctx,
     ff_vk_exec_update_frame(&ctx->s, exec, src,
                             &img_bar[nb_img_bar], &nb_img_bar);
 
+    /* DPB image barriers are only needed for initial transitions: the
+     * layered image at creation, per-picture recon images here */
     if (!ctx->common.layered_dpb) {
-        /* Source image's ref slot.
-         * No need to do a layout conversion, since the frames which are 
allocated
-         * with a DPB usage are automatically converted. */
-        err = ff_vk_exec_add_dep_frame(&ctx->s, exec, base_pic->recon_image,
-                                       VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
-                                       
VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR);
-        if (err < 0)
-            return err;
+        FFVkVideoDPBImage *di = (FFVkVideoDPBImage 
*)base_pic->recon_image->data[0];
 
-        /* All references */
-        for (int i = 0; i < MAX_REFERENCE_LIST_NUM; i++) {
-            for (int j = 0; j < base_pic->nb_refs[i]; j++) {
-                FFHWBaseEncodePicture *ref = base_pic->refs[i][j];
-                err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ref->recon_image,
-                                               
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
-                                               
VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR);
-                if (err < 0)
-                    return err;
-            }
+        if (di->layout != VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR) {
+            img_bar[nb_img_bar] = (VkImageMemoryBarrier2) {
+                .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+                .srcStageMask = VK_PIPELINE_STAGE_2_NONE,
+                .srcAccessMask = VK_ACCESS_2_NONE,
+                .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR,
+                .dstAccessMask = VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR |
+                                 VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR,
+                .oldLayout = di->layout,
+                .newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR,
+                .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+                .image = di->img,
+                .subresourceRange = (VkImageSubresourceRange) {
+                    .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+                    .layerCount = 1,
+                    .levelCount = 1,
+                },
+            };
+            nb_img_bar++;
+            di->layout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR;
         }
-    } else {
-        err = ff_vk_exec_add_dep_frame(&ctx->s, exec, 
ctx->common.layered_frame,
-                                       
VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR,
-                                       
VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR);
-        if (err < 0)
-            return err;
     }
 
-    /* Change image layout */
+    /* One encode-stage memory barrier per submission orders all prior
+     * encode work, references included */
+    VkMemoryBarrier2 mem_bar = {
+        .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
+        .srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR,
+        .srcAccessMask = VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR,
+        .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR,
+        .dstAccessMask = VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR |
+                         VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR,
+    };
+
+    /* Change image layouts, and synchronize the DPB */
     vk->CmdPipelineBarrier2(cmd_buf, &(VkDependencyInfo) {
             .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+            .pMemoryBarriers = &mem_bar,
+            .memoryBarrierCount = 1,
             .pImageMemoryBarriers = img_bar,
             .imageMemoryBarrierCount = nb_img_bar,
         });
@@ -611,63 +616,101 @@ int ff_vulkan_encode_receive_packet(AVCodecContext 
*avctx, AVPacket *pkt)
     return ff_hw_base_encode_receive_packet(&ctx->base, avctx, pkt);
 }
 
-static int vulkan_encode_create_dpb(AVCodecContext *avctx, 
FFVulkanEncodeContext *ctx)
+static void vulkan_encode_recon_free(void *opaque, uint8_t *data)
 {
-    int err;
-    FFHWBaseEncodeContext *base_ctx = &ctx->base;
-    AVVulkanFramesContext *hwfc;
+    FFVkVideoDPBImage *di = (FFVkVideoDPBImage *)data;
+    /* No wait: pool reuse is ordered by the DPB barrier */
+    av_refstruct_unref(&di);
+}
 
-    enum AVPixelFormat dpb_format;
-    err = ff_hw_base_get_recon_format(base_ctx, NULL, &dpb_format);
-    if (err < 0)
-        return err;
+static int vulkan_encode_get_recon_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+    FFVulkanEncodeContext *ctx = avctx->priv_data;
+    FFVkVideoDPBImage *di;
 
-    base_ctx->recon_frames_ref = av_hwframe_ctx_alloc(base_ctx->device_ref);
-    if (!base_ctx->recon_frames_ref)
+    di = av_refstruct_pool_get(ctx->common.dpb->img_pool);
+    if (!di)
         return AVERROR(ENOMEM);
 
-    base_ctx->recon_frames = (AVHWFramesContext 
*)base_ctx->recon_frames_ref->data;
-    hwfc = (AVVulkanFramesContext *)base_ctx->recon_frames->hwctx;
-
-    base_ctx->recon_frames->format    = AV_PIX_FMT_VULKAN;
-    base_ctx->recon_frames->sw_format = dpb_format;
-    base_ctx->recon_frames->width     = avctx->width;
-    base_ctx->recon_frames->height    = avctx->height;
+    /* The frame only carries the pool entry through the base layer */
+    frame->buf[0] = av_buffer_create((uint8_t *)di, sizeof(*di),
+                                     vulkan_encode_recon_free, NULL, 0);
+    if (!frame->buf[0]) {
+        av_refstruct_unref(&di);
+        return AVERROR(ENOMEM);
+    }
+    frame->data[0] = (uint8_t *)di;
 
-    hwfc->format[0]    = ctx->pic_format;
-    hwfc->create_pnext = &ctx->profile_list;
-    hwfc->tiling       = VK_IMAGE_TILING_OPTIMAL;
-    hwfc->usage        = VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR;
+    return 0;
+}
 
-    if (ctx->common.layered_dpb)
-        hwfc->nb_layers = ctx->caps.maxDpbSlots;
+static int vulkan_encode_create_dpb(AVCodecContext *avctx, 
FFVulkanEncodeContext *ctx)
+{
+    int err;
+    FFHWBaseEncodeContext *base_ctx = &ctx->base;
 
-    err = av_hwframe_ctx_init(base_ctx->recon_frames_ref);
+    /* Reconstruction images are queue-exclusive; allocate them internally */
+    err = ff_vk_video_dpb_init(&ctx->s, &ctx->common, ctx->pic_format,
+                               VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR,
+                               VK_IMAGE_TILING_OPTIMAL, &ctx->profile_list,
+                               avctx->width, avctx->height,
+                               ctx->common.layered_dpb ?
+                               ctx->caps.maxDpbSlots : 1);
     if (err < 0) {
-        av_log(avctx, AV_LOG_ERROR, "Failed to initialise DPB frame context: 
%s\n",
+        av_log(avctx, AV_LOG_ERROR, "Failed to initialise DPB image pool: 
%s\n",
                av_err2str(err));
         return err;
     }
 
     if (ctx->common.layered_dpb) {
-        ctx->common.layered_frame = av_frame_alloc();
-        if (!ctx->common.layered_frame)
+        FFVulkanFunctions *vk = &ctx->s.vkfn;
+        FFVkExecContext *exec;
+        VkImageMemoryBarrier2 img_bar;
+
+        ctx->common.layered_img = 
av_refstruct_pool_get(ctx->common.dpb->img_pool);
+        if (!ctx->common.layered_img)
             return AVERROR(ENOMEM);
 
-        err = av_hwframe_get_buffer(base_ctx->recon_frames_ref,
-                                    ctx->common.layered_frame, 0);
+        ctx->common.layered_view   = ctx->common.layered_img->view;
+        ctx->common.layered_aspect = ctx->common.layered_img->aspect;
+
+        /* Created eagerly, so transitioned eagerly, matching the decoder */
+        exec = ff_vk_exec_get(&ctx->s, &ctx->enc_pool);
+        err = ff_vk_exec_start(&ctx->s, exec);
         if (err < 0)
-            return AVERROR(ENOMEM);
+            return err;
+
+        img_bar = (VkImageMemoryBarrier2) {
+            .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+            .srcStageMask = VK_PIPELINE_STAGE_2_NONE,
+            .srcAccessMask = VK_ACCESS_2_NONE,
+            .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR,
+            .dstAccessMask = VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR |
+                             VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR,
+            .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+            .newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR,
+            .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+            .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+            .image = ctx->common.layered_img->img,
+            .subresourceRange = (VkImageSubresourceRange) {
+                .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+                .layerCount = VK_REMAINING_ARRAY_LAYERS,
+                .levelCount = 1,
+            },
+        };
+        vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+                .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+                .pImageMemoryBarriers = &img_bar,
+                .imageMemoryBarrierCount = 1,
+            });
 
-        err = ff_vk_create_view(&ctx->s, &ctx->common,
-                                &ctx->common.layered_view,
-                                &ctx->common.layered_aspect,
-                                (AVVkFrame 
*)ctx->common.layered_frame->data[0],
-                                hwfc->format[0], 
VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
+        err = ff_vk_exec_submit(&ctx->s, exec);
         if (err < 0)
             return err;
 
-        av_buffer_unref(&base_ctx->recon_frames_ref);
+        ctx->common.layered_img->layout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR;
+    } else {
+        base_ctx->get_recon_frame = vulkan_encode_get_recon_frame;
     }
 
     return 0;
diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c
index f85cb3d4e9..1ce432b47d 100644
--- a/libavcodec/vulkan_video.c
+++ b/libavcodec/vulkan_video.c
@@ -289,15 +289,13 @@ StdVideoAV1Profile ff_vk_av1_profile_to_vk(int profile)
     }
 }
 
-int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon *common,
-                      VkImageView *view, VkImageAspectFlags *aspect,
-                      AVVkFrame *src, VkFormat vkf, VkImageUsageFlags usage)
+int ff_vk_create_view(FFVulkanContext *s, VkImageView *view,
+                      VkImageAspectFlags *aspect, VkImage img,
+                      VkFormat vkf, VkImageUsageFlags usage, int layered)
 {
     VkResult ret;
     FFVulkanFunctions *vk = &s->vkfn;
     VkImageAspectFlags aspect_mask = ff_vk_aspect_bits_from_vkfmt(vkf);
-    int is_video_dpb = usage & (VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
-                                VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
 
     VkImageViewUsageCreateInfo usage_create_info = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
@@ -306,10 +304,10 @@ int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon 
*common,
     VkImageViewCreateInfo img_view_create_info = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
         .pNext = &usage_create_info,
-        .viewType = common->layered_dpb && is_video_dpb ?
-                    VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D,
+        .viewType = layered ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
+                              VK_IMAGE_VIEW_TYPE_2D,
         .format = vkf,
-        .image = src->img[0],
+        .image = img,
         .components = (VkComponentMapping) {
             .r = VK_COMPONENT_SWIZZLE_IDENTITY,
             .g = VK_COMPONENT_SWIZZLE_IDENTITY,
@@ -319,8 +317,7 @@ int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon 
*common,
         .subresourceRange = (VkImageSubresourceRange) {
             .aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT,
             .baseArrayLayer = 0,
-            .layerCount     = common->layered_dpb && is_video_dpb ?
-                              VK_REMAINING_ARRAY_LAYERS : 1,
+            .layerCount     = layered ? VK_REMAINING_ARRAY_LAYERS : 1,
             .levelCount     = 1,
         },
     };
@@ -335,6 +332,88 @@ int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon 
*common,
     return 0;
 }
 
+static void dpb_image_free(AVRefStructOpaque opaque, void *obj)
+{
+    FFVkVideoDPB *dpb = opaque.nc;
+    FFVkVideoDPBImage *di = obj;
+
+    if (di->view)
+        dpb->destroy_image_view(dpb->dev, di->view, dpb->alloc);
+    if (di->img)
+        dpb->destroy_image(dpb->dev, di->img, dpb->alloc);
+    if (di->mem)
+        dpb->free_memory(dpb->dev, di->mem, dpb->alloc);
+}
+
+static int dpb_image_init(AVRefStructOpaque opaque, void *obj)
+{
+    int err;
+    FFVkVideoDPB *dpb = opaque.nc;
+    FFVkVideoDPBImage *di = obj;
+
+    err = ff_vk_image_create(dpb->s, &di->img, &di->mem,
+                             dpb->width, dpb->height, dpb->format,
+                             dpb->nb_layers, dpb->tiling, dpb->usage,
+                             0x0, dpb->create_pnext);
+    if (err < 0)
+        return err;
+
+    err = ff_vk_create_view(dpb->s, &di->view, &di->aspect, di->img,
+                            dpb->format, dpb->usage, dpb->nb_layers > 1);
+    if (err < 0) {
+        ff_vk_image_free(dpb->s, &di->img, &di->mem);
+        return err;
+    }
+
+    di->layout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    return 0;
+}
+
+static void dpb_pool_free(AVRefStructOpaque opaque)
+{
+    av_free(opaque.nc);
+}
+
+av_cold int ff_vk_video_dpb_init(FFVulkanContext *s, FFVkVideoCommon *common,
+                                 VkFormat format, VkImageUsageFlags usage,
+                                 VkImageTiling tiling, void *create_pnext,
+                                 int width, int height, int nb_layers)
+{
+    FFVulkanFunctions *vk = &s->vkfn;
+    FFVkVideoDPB *dpb = av_mallocz(sizeof(*dpb));
+    if (!dpb)
+        return AVERROR(ENOMEM);
+
+    dpb->s            = s;
+    dpb->format       = format;
+    dpb->usage        = usage;
+    dpb->tiling       = tiling;
+    dpb->create_pnext = create_pnext;
+    dpb->width        = width;
+    dpb->height       = height;
+    dpb->nb_layers    = nb_layers;
+
+    dpb->dev                = s->hwctx->act_dev;
+    dpb->alloc              = s->hwctx->alloc;
+    dpb->destroy_image_view = vk->DestroyImageView;
+    dpb->destroy_image      = vk->DestroyImage;
+    dpb->free_memory        = vk->FreeMemory;
+
+    dpb->img_pool = av_refstruct_pool_alloc_ext(sizeof(FFVkVideoDPBImage), 0,
+                                                dpb, dpb_image_init, NULL,
+                                                dpb_image_free, dpb_pool_free);
+    if (!dpb->img_pool) {
+        av_free(dpb);
+        return AVERROR(ENOMEM);
+    }
+
+    common->dpb = dpb;
+
+    return 0;
+}
+
+
 av_cold void ff_vk_video_common_uninit(FFVulkanContext *s,
                                        FFVkVideoCommon *common)
 {
@@ -352,15 +431,15 @@ av_cold void ff_vk_video_common_uninit(FFVulkanContext *s,
 
     av_freep(&common->mem);
 
-    if (common->layered_view) {
-        vk->DestroyImageView(s->hwctx->act_dev, common->layered_view,
-                             s->hwctx->alloc);
-        common->layered_view = VK_NULL_HANDLE;
-    }
-
-    av_frame_free(&common->layered_frame);
+    /* The layered view is owned by the pool entry */
+    common->layered_view = VK_NULL_HANDLE;
+    av_refstruct_unref(&common->layered_img);
 
-    av_buffer_unref(&common->dpb_hwfc_ref);
+    if (common->dpb) {
+        /* The pool frees the FFVkVideoDPB once its last entry returns */
+        av_refstruct_pool_uninit(&common->dpb->img_pool);
+        common->dpb = NULL;
+    }
 }
 
 av_cold int ff_vk_video_common_init(AVCodecContext *avctx, FFVulkanContext *s,
diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h
index d63bfae3fc..15aefc7136 100644
--- a/libavcodec/vulkan_video.h
+++ b/libavcodec/vulkan_video.h
@@ -20,6 +20,7 @@
 #define AVCODEC_VULKAN_VIDEO_H
 
 #include "avcodec.h"
+#include "libavutil/refstruct.h"
 #include "libavutil/vulkan.h"
 
 #include <vk_video/vulkan_video_codecs_common.h>
@@ -29,14 +30,44 @@
 #define CODEC_VER_PAT(ver) (ver & ((1 << 12) - 1))
 #define CODEC_VER(ver) CODEC_VER_MAJ(ver), CODEC_VER_MIN(ver), 
CODEC_VER_PAT(ver)
 
+/* DEDICATED-mode queue-exclusive DPB image */
+typedef struct FFVkVideoDPBImage {
+    VkImage img;
+    VkDeviceMemory mem;
+    VkImageView view;
+    VkImageAspectFlags aspect;
+    VkImageLayout layout;
+} FFVkVideoDPBImage;
+
+/* Internal DPB image pool; av_refstruct_pool_get()/av_refstruct_unref() */
+typedef struct FFVkVideoDPB {
+    /* Creation only; destruction uses the stashed handles below, as the
+     * pool may outlive the context */
+    FFVulkanContext *s;
+
+    AVRefStructPool *img_pool; /* FFVkVideoDPBImage entries */
+
+    VkDevice dev;
+    const VkAllocationCallbacks *alloc;
+    PFN_vkDestroyImageView destroy_image_view;
+    PFN_vkDestroyImage destroy_image;
+    PFN_vkFreeMemory free_memory;
+
+    VkFormat format;
+    VkImageUsageFlags usage;
+    VkImageTiling tiling;
+    void *create_pnext;
+    int width, height, nb_layers;
+} FFVkVideoDPB;
+
 typedef struct FFVkVideoSession {
     VkVideoSessionKHR session;
     VkDeviceMemory *mem;
     uint32_t nb_mem;
 
-    AVBufferRef *dpb_hwfc_ref;
+    FFVkVideoDPB *dpb;
     int layered_dpb;
-    AVFrame *layered_frame;
+    FFVkVideoDPBImage *layered_img;
     VkImageView layered_view;
     VkImageAspectFlags layered_aspect;
 } FFVkVideoCommon;
@@ -81,9 +112,17 @@ StdVideoAV1Profile     ff_vk_av1_profile_to_vk(int profile);
 /**
  * Creates image views for video frames.
  */
-int ff_vk_create_view(FFVulkanContext *s, FFVkVideoCommon *common,
-                      VkImageView *view, VkImageAspectFlags *aspect,
-                      AVVkFrame *src, VkFormat vkf, VkImageUsageFlags flags);
+int ff_vk_create_view(FFVulkanContext *s, VkImageView *view,
+                      VkImageAspectFlags *aspect, VkImage img,
+                      VkFormat vkf, VkImageUsageFlags usage, int layered);
+
+/**
+ * Initialize the internal DPB image pool.
+ */
+int ff_vk_video_dpb_init(FFVulkanContext *s, FFVkVideoCommon *common,
+                         VkFormat format, VkImageUsageFlags usage,
+                         VkImageTiling tiling, void *create_pnext,
+                         int width, int height, int nb_layers);
 
 /**
  * Initialize video session, allocating and binding necessary 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]

Reply via email to