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]
