From: Lynne <dev@lynne.ee> To: ffmpeg-devel@ffmpeg.org Cc: Lynne <dev@lynne.ee> Subject: [FFmpeg-devel] [PATCH v2 13/13] WIP vp9: add Vulkan VP9 hwaccel Date: Sun, 13 Jul 2025 03:51:22 +0900 Message-ID: <20250712185128.862167-13-dev@lynne.ee> (raw) In-Reply-To: <20250712185128.862167-1-dev@lynne.ee> --- configure | 2 + libavcodec/Makefile | 1 + libavcodec/hwaccels.h | 1 + libavcodec/vp9.c | 18 +- libavcodec/vulkan_decode.c | 24 +++ libavcodec/vulkan_decode.h | 1 + libavcodec/vulkan_vp9.c | 366 +++++++++++++++++++++++++++++++++++ libavutil/hwcontext_vulkan.c | 15 ++ libavutil/vulkan_functions.h | 3 +- libavutil/vulkan_loader.h | 3 + 10 files changed, 432 insertions(+), 2 deletions(-) create mode 100644 libavcodec/vulkan_vp9.c diff --git a/configure b/configure index 92ee54c7a6..d1c54c0b8e 100755 --- a/configure +++ b/configure @@ -3326,6 +3326,8 @@ vp9_vdpau_hwaccel_deps="vdpau VdpPictureInfoVP9" vp9_vdpau_hwaccel_select="vp9_decoder" vp9_videotoolbox_hwaccel_deps="videotoolbox" vp9_videotoolbox_hwaccel_select="vp9_decoder" +vp9_vulkan_hwaccel_deps="vulkan" +vp9_vulkan_hwaccel_select="vp9_decoder" vvc_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferVVC" vvc_vaapi_hwaccel_select="vvc_decoder" wmv3_d3d11va_hwaccel_select="vc1_d3d11va_hwaccel" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 19fdaa9ad3..fcd1ae2a9e 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1078,6 +1078,7 @@ OBJS-$(CONFIG_VP9_NVDEC_HWACCEL) += nvdec_vp9.o OBJS-$(CONFIG_VP9_VAAPI_HWACCEL) += vaapi_vp9.o OBJS-$(CONFIG_VP9_VDPAU_HWACCEL) += vdpau_vp9.o OBJS-$(CONFIG_VP9_VIDEOTOOLBOX_HWACCEL) += videotoolbox_vp9.o +OBJS-$(CONFIG_VP9_VULKAN_HWACCEL) += vulkan_decode.o vulkan_vp9.o OBJS-$(CONFIG_VP8_QSV_HWACCEL) += qsvdec.o OBJS-$(CONFIG_VVC_VAAPI_HWACCEL) += vaapi_vvc.o OBJS-$(CONFIG_PRORES_RAW_VULKAN_HWACCEL) += vulkan_decode.o vulkan_prores_raw.o diff --git a/libavcodec/hwaccels.h b/libavcodec/hwaccels.h index fb9b850233..4b205d386e 100644 --- a/libavcodec/hwaccels.h +++ b/libavcodec/hwaccels.h @@ -85,6 +85,7 @@ extern const struct FFHWAccel ff_vp9_nvdec_hwaccel; extern const struct FFHWAccel ff_vp9_vaapi_hwaccel; extern const struct FFHWAccel ff_vp9_vdpau_hwaccel; extern const struct FFHWAccel ff_vp9_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_vp9_vulkan_hwaccel; extern const struct FFHWAccel ff_vvc_vaapi_hwaccel; extern const struct FFHWAccel ff_wmv3_d3d11va_hwaccel; extern const struct FFHWAccel ff_wmv3_d3d11va2_hwaccel; diff --git a/libavcodec/vp9.c b/libavcodec/vp9.c index a385956f4f..31792962b4 100644 --- a/libavcodec/vp9.c +++ b/libavcodec/vp9.c @@ -169,7 +169,8 @@ static int update_size(AVCodecContext *avctx, int w, int h) CONFIG_VP9_NVDEC_HWACCEL + \ CONFIG_VP9_VAAPI_HWACCEL + \ CONFIG_VP9_VDPAU_HWACCEL + \ - CONFIG_VP9_VIDEOTOOLBOX_HWACCEL) + CONFIG_VP9_VIDEOTOOLBOX_HWACCEL + \ + CONFIG_VP9_VULKAN_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts; VP9Context *s = avctx->priv_data; uint8_t *p; @@ -206,6 +207,9 @@ static int update_size(AVCodecContext *avctx, int w, int h) #endif #if CONFIG_VP9_VIDEOTOOLBOX_HWACCEL *fmtp++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_YUV420P12: @@ -217,6 +221,9 @@ static int update_size(AVCodecContext *avctx, int w, int h) #endif #if CONFIG_VP9_VDPAU_HWACCEL *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_YUV444P: @@ -224,6 +231,9 @@ static int update_size(AVCodecContext *avctx, int w, int h) case AV_PIX_FMT_YUV444P12: #if CONFIG_VP9_VAAPI_HWACCEL *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_GBRP: @@ -231,6 +241,9 @@ static int update_size(AVCodecContext *avctx, int w, int h) case AV_PIX_FMT_GBRP12: #if CONFIG_VP9_VAAPI_HWACCEL *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; #endif break; } @@ -1919,6 +1932,9 @@ const FFCodec ff_vp9_decoder = { #endif #if CONFIG_VP9_VIDEOTOOLBOX_HWACCEL HWACCEL_VIDEOTOOLBOX(vp9), +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + HWACCEL_VULKAN(vp9), #endif NULL }, diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c index 857f16bc0a..dea25d93aa 100644 --- a/libavcodec/vulkan_decode.c +++ b/libavcodec/vulkan_decode.c @@ -34,6 +34,9 @@ extern const FFVulkanDecodeDescriptor ff_vk_dec_h264_desc; #if CONFIG_HEVC_VULKAN_HWACCEL extern const FFVulkanDecodeDescriptor ff_vk_dec_hevc_desc; #endif +#if CONFIG_VP9_VULKAN_HWACCEL +extern const FFVulkanDecodeDescriptor ff_vk_dec_vp9_desc; +#endif #if CONFIG_AV1_VULKAN_HWACCEL extern const FFVulkanDecodeDescriptor ff_vk_dec_av1_desc; #endif @@ -51,6 +54,9 @@ static const FFVulkanDecodeDescriptor *dec_descs[] = { #if CONFIG_HEVC_VULKAN_HWACCEL &ff_vk_dec_hevc_desc, #endif +#if CONFIG_VP9_VULKAN_HWACCEL + &ff_vk_dec_vp9_desc, +#endif #if CONFIG_AV1_VULKAN_HWACCEL &ff_vk_dec_av1_desc, #endif @@ -78,6 +84,7 @@ static const VkVideoProfileInfoKHR *get_video_profile(FFVulkanDecodeShared *ctx, VkStructureType profile_struct_type = codec_id == AV_CODEC_ID_H264 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR : codec_id == AV_CODEC_ID_HEVC ? VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR : + codec_id == AV_CODEC_ID_VP9 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_VP9_PROFILE_INFO_KHR : codec_id == AV_CODEC_ID_AV1 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_KHR : VK_STRUCTURE_TYPE_MAX_ENUM; if (profile_struct_type == VK_STRUCTURE_TYPE_MAX_ENUM) @@ -688,6 +695,7 @@ static VkResult vulkan_setup_profile(AVCodecContext *avctx, const FFVulkanDecodeDescriptor *vk_desc, VkVideoDecodeH264CapabilitiesKHR *h264_caps, VkVideoDecodeH265CapabilitiesKHR *h265_caps, + VkVideoDecodeVP9CapabilitiesKHR *vp9_caps, VkVideoDecodeAV1CapabilitiesKHR *av1_caps, VkVideoCapabilitiesKHR *caps, VkVideoDecodeCapabilitiesKHR *dec_caps, @@ -699,6 +707,7 @@ static VkResult vulkan_setup_profile(AVCodecContext *avctx, VkVideoDecodeH264ProfileInfoKHR *h264_profile = &prof->h264_profile; VkVideoDecodeH265ProfileInfoKHR *h265_profile = &prof->h265_profile; + VkVideoDecodeVP9ProfileInfoKHR *vp9_profile = &prof->vp9_profile; VkVideoDecodeAV1ProfileInfoKHR *av1_profile = &prof->av1_profile; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); @@ -724,6 +733,11 @@ static VkResult vulkan_setup_profile(AVCodecContext *avctx, usage->pNext = h265_profile; h265_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR; h265_profile->stdProfileIdc = cur_profile; + } else if (avctx->codec_id == AV_CODEC_ID_VP9) { + dec_caps->pNext = vp9_caps; + usage->pNext = vp9_profile; + vp9_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_VP9_PROFILE_INFO_KHR; + vp9_profile->stdProfile = cur_profile; } else if (avctx->codec_id == AV_CODEC_ID_AV1) { dec_caps->pNext = av1_caps; usage->pNext = av1_profile; @@ -784,6 +798,9 @@ static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_ VkVideoDecodeH265CapabilitiesKHR h265_caps = { .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_KHR, }; + VkVideoDecodeVP9CapabilitiesKHR vp9_caps = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_VP9_CAPABILITIES_KHR, + }; VkVideoDecodeAV1CapabilitiesKHR av1_caps = { .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_CAPABILITIES_KHR, }; @@ -804,12 +821,14 @@ static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_ cur_profile = avctx->profile; base_profile = avctx->codec_id == AV_CODEC_ID_H264 ? AV_PROFILE_H264_CONSTRAINED_BASELINE : avctx->codec_id == AV_CODEC_ID_H265 ? AV_PROFILE_HEVC_MAIN : + avctx->codec_id == AV_CODEC_ID_VP9 ? STD_VIDEO_VP9_PROFILE_0 : avctx->codec_id == AV_CODEC_ID_AV1 ? STD_VIDEO_AV1_PROFILE_MAIN : 0; ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_desc, &h264_caps, &h265_caps, + &vp9_caps, &av1_caps, caps, dec_caps, @@ -826,6 +845,7 @@ static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_ ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_desc, &h264_caps, &h265_caps, + &vp9_caps, &av1_caps, caps, dec_caps, @@ -852,6 +872,7 @@ static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_ max_level = avctx->codec_id == AV_CODEC_ID_H264 ? ff_vk_h264_level_to_av(h264_caps.maxLevelIdc) : avctx->codec_id == AV_CODEC_ID_H265 ? ff_vk_h265_level_to_av(h265_caps.maxLevelIdc) : + avctx->codec_id == AV_CODEC_ID_VP9 ? vp9_caps.maxLevel : avctx->codec_id == AV_CODEC_ID_AV1 ? av1_caps.maxLevel : 0; @@ -1175,6 +1196,9 @@ static int create_empty_session_parameters(AVCodecContext *avctx, .videoSession = ctx->common.session, }; + if (avctx->codec_id == AV_CODEC_ID_VP9) + return 0; + ret = vk->CreateVideoSessionParametersKHR(s->hwctx->act_dev, &session_params_create, s->hwctx->alloc, &ctx->empty_session_params); if (ret != VK_SUCCESS) { diff --git a/libavcodec/vulkan_decode.h b/libavcodec/vulkan_decode.h index bf6506f280..bf51d5a170 100644 --- a/libavcodec/vulkan_decode.h +++ b/libavcodec/vulkan_decode.h @@ -38,6 +38,7 @@ typedef struct FFVulkanDecodeDescriptor { typedef struct FFVulkanDecodeProfileData { VkVideoDecodeH264ProfileInfoKHR h264_profile; VkVideoDecodeH265ProfileInfoKHR h265_profile; + VkVideoDecodeVP9ProfileInfoKHR vp9_profile; VkVideoDecodeAV1ProfileInfoKHR av1_profile; VkVideoDecodeUsageInfoKHR usage; VkVideoProfileInfoKHR profile; diff --git a/libavcodec/vulkan_vp9.c b/libavcodec/vulkan_vp9.c new file mode 100644 index 0000000000..6713ab2218 --- /dev/null +++ b/libavcodec/vulkan_vp9.c @@ -0,0 +1,366 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "vp9shared.h" + +#include "vulkan_decode.h" + +const FFVulkanDecodeDescriptor ff_vk_dec_vp9_desc = { + .codec_id = AV_CODEC_ID_VP9, + .decode_extension = FF_VK_EXT_VIDEO_DECODE_VP9, + .queue_flags = VK_QUEUE_VIDEO_DECODE_BIT_KHR, + .decode_op = VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR, + .ext_props = { + .extensionName = VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_EXTENSION_NAME, + .specVersion = VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_SPEC_VERSION, + }, +}; + +typedef struct VP9VulkanDecodePicture { + FFVulkanDecodePicture vp; + + /* TODO: investigate if this can be removed to make decoding completely + * independent. */ + FFVulkanDecodeContext *dec; + + /* Current picture */ + StdVideoVP9ColorConfig color_config; + StdVideoVP9Segmentation segmentation; + StdVideoVP9LoopFilter loop_filter; + StdVideoDecodeVP9PictureInfo std_pic_info; + VkVideoDecodeVP9PictureInfoKHR vp9_pic_info; + + const VP9Frame *ref_src[8]; + + uint8_t frame_id_set; + uint8_t frame_id; + uint8_t ref_frame_sign_bias_mask; +} VP9VulkanDecodePicture; + +static int vk_vp9_fill_pict(AVCodecContext *avctx, const VP9Frame **ref_src, + VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ + VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ + const VP9Frame *pic, int is_current) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + VP9VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vkpic = &hp->vp; + + int err = ff_vk_decode_prepare_frame(dec, pic->tf.f, vkpic, is_current, + dec->dedicated_dpb); + if (err < 0) + return err; + + *ref = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->tf.f->width, pic->tf.f->height }, + .baseArrayLayer = (dec->dedicated_dpb && ctx->common.layered_dpb) ? + hp->frame_id : 0, + .imageViewBinding = vkpic->view.ref[0], + }; + + *ref_slot = (VkVideoReferenceSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR, + .slotIndex = hp->frame_id, + .pPictureResource = ref, + }; + + if (ref_src) + *ref_src = pic; + + return 0; +} + +static enum StdVideoVP9InterpolationFilter remap_interp(uint8_t is_filter_switchable, + uint8_t raw_interpolation_filter_type) +{ + static const enum StdVideoVP9InterpolationFilter remap[] = { + STD_VIDEO_VP9_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH, + STD_VIDEO_VP9_INTERPOLATION_FILTER_EIGHTTAP, + STD_VIDEO_VP9_INTERPOLATION_FILTER_EIGHTTAP_SHARP, + STD_VIDEO_VP9_INTERPOLATION_FILTER_BILINEAR, + }; + if (is_filter_switchable) + return STD_VIDEO_VP9_INTERPOLATION_FILTER_SWITCHABLE; + return remap[raw_interpolation_filter_type]; +} + +static int vk_vp9_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buffer_ref, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + int err; + int ref_count = 0; + const VP9SharedContext *s = avctx->priv_data; + + const VP9Frame *pic = &s->frames[CUR_FRAME]; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + uint8_t profile = (pic->frame_header->profile_high_bit << 1) | pic->frame_header->profile_low_bit; + + VP9VulkanDecodePicture *ap = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + + if (!ap->frame_id_set) { + unsigned slot_idx = 0; + for (unsigned i = 0; i < 32; i++) { + if (!(dec->frame_id_alloc_mask & (1 << i))) { + slot_idx = i; + break; + } + } + ap->frame_id = slot_idx; + ap->frame_id_set = 1; + dec->frame_id_alloc_mask |= (1 << slot_idx); + } + + for (int i = 0; i < STD_VIDEO_VP9_REFS_PER_FRAME; i++) { + const int idx = pic->frame_header->ref_frame_idx[i]; + const VP9Frame *ref_frame = &s->frames[idx]; + VP9VulkanDecodePicture *hp = ref_frame->hwaccel_picture_private; + int found = 0; + + if (!ref_frame->tf.f) + continue; + + for (int j = 0; j < ref_count; j++) { + if (vp->ref_slots[j].slotIndex == hp->frame_id) { + found = 1; + break; + } + } + if (found) + continue; + + err = vk_vp9_fill_pict(avctx, &ap->ref_src[ref_count], + &vp->ref_slots[ref_count], &vp->refs[ref_count], + ref_frame, 0); + if (err < 0) + return err; + + ref_count++; + } + + err = vk_vp9_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, + pic, 1); + if (err < 0) + return err; + + ap->loop_filter = (StdVideoVP9LoopFilter) { + .flags = (StdVideoVP9LoopFilterFlags) { + .loop_filter_delta_enabled = pic->frame_header->loop_filter_delta_enabled, + .loop_filter_delta_update = pic->frame_header->loop_filter_delta_update, + }, + .loop_filter_level = pic->frame_header->loop_filter_level, + .loop_filter_sharpness = pic->frame_header->loop_filter_sharpness, + .update_ref_delta = 0x0, + .update_mode_delta = 0x0, + }; + + for (int i = 0; i < 2; i++) + ap->loop_filter.update_mode_delta |= pic->frame_header->update_mode_delta[i]; + + for (int i = 0; i < STD_VIDEO_VP9_MAX_REF_FRAMES; i++) { + ap->loop_filter.loop_filter_ref_deltas[i] = pic->frame_header->loop_filter_ref_deltas[i]; + ap->loop_filter.update_ref_delta |= pic->frame_header->update_ref_delta[i]; + } + for (int i = 0; i < STD_VIDEO_VP9_LOOP_FILTER_ADJUSTMENTS; i++) + ap->loop_filter.loop_filter_mode_deltas[i] = pic->frame_header->loop_filter_mode_deltas[i]; + + ap->segmentation = (StdVideoVP9Segmentation) { + .flags = (StdVideoVP9SegmentationFlags) { + .segmentation_update_map = pic->frame_header->segmentation_update_map, + .segmentation_temporal_update = pic->frame_header->segmentation_temporal_update, + .segmentation_update_data = pic->frame_header->segmentation_update_data, + .segmentation_abs_or_delta_update = pic->frame_header->segmentation_abs_or_delta_update, + }, + }; + + for (int i = 0; i < STD_VIDEO_VP9_MAX_SEGMENTATION_TREE_PROBS; i++) + ap->segmentation.segmentation_tree_probs[i] = pic->frame_header->segmentation_tree_probs[i]; + for (int i = 0; i < STD_VIDEO_VP9_MAX_SEGMENTATION_PRED_PROB; i++) + ap->segmentation.segmentation_pred_prob[i] = pic->frame_header->segmentation_pred_prob[i]; + for (int i = 0; i < STD_VIDEO_VP9_MAX_SEGMENTS; i++) { + ap->segmentation.FeatureEnabled[i] = 0x0; + for (int j = 0; j < STD_VIDEO_VP9_SEG_LVL_MAX; j++) { + ap->segmentation.FeatureEnabled[i] |= pic->frame_header->feature_enabled[i][j]; + ap->segmentation.FeatureData[i][j] = pic->frame_header->feature_sign[i][j] ? + -pic->frame_header->feature_value[i][j] : + +pic->frame_header->feature_value[i][j]; + } + } + + ap->color_config = (StdVideoVP9ColorConfig) { + .flags = (StdVideoVP9ColorConfigFlags) { + .color_range = pic->frame_header->color_range, + }, + .BitDepth = profile < 2 ? 8 : + pic->frame_header->ten_or_twelve_bit ? 12 : 10, + .subsampling_x = pic->frame_header->subsampling_x, + .subsampling_y = pic->frame_header->subsampling_y, + .color_space = pic->frame_header->color_space, + }; + + ap->std_pic_info = (StdVideoDecodeVP9PictureInfo) { + .flags = (StdVideoDecodeVP9PictureInfoFlags) { + .error_resilient_mode = pic->frame_header->error_resilient_mode, + .intra_only = pic->frame_header->intra_only, + .allow_high_precision_mv = pic->frame_header->allow_high_precision_mv, + .refresh_frame_context = pic->frame_header->refresh_frame_context, + .frame_parallel_decoding_mode = pic->frame_header->frame_parallel_decoding_mode, + .segmentation_enabled = pic->frame_header->segmentation_enabled, + .show_frame = pic->frame_header->segmentation_enabled, + .UsePrevFrameMvs = s->h.use_last_frame_mvs, + }, + .profile = profile, + .frame_type = pic->frame_header->frame_type, + .frame_context_idx = pic->frame_header->frame_context_idx, + .reset_frame_context = pic->frame_header->reset_frame_context, + .refresh_frame_flags = pic->frame_header->refresh_frame_flags, + .ref_frame_sign_bias_mask = 0x0, + .interpolation_filter = remap_interp(pic->frame_header->is_filter_switchable, + pic->frame_header->raw_interpolation_filter_type), + .base_q_idx = pic->frame_header->base_q_idx, + .delta_q_y_dc = pic->frame_header->delta_q_y_dc, + .delta_q_uv_dc = pic->frame_header->delta_q_uv_dc, + .delta_q_uv_ac = pic->frame_header->delta_q_uv_ac, + .tile_cols_log2 = pic->frame_header->tile_cols_log2, + .tile_rows_log2 = pic->frame_header->tile_rows_log2, + /* Reserved */ + .pColorConfig = &ap->color_config, + .pLoopFilter = &ap->loop_filter, + .pSegmentation = &ap->segmentation, + }; + + for (int i = 0; i < 3; i++) + ap->std_pic_info.ref_frame_sign_bias_mask |= pic->frame_header->ref_frame_sign_bias[i] << i; + + ap->vp9_pic_info = (VkVideoDecodeVP9PictureInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_VP9_PICTURE_INFO_KHR, + .pStdPictureInfo = &ap->std_pic_info, + .uncompressedHeaderOffset = 0, + .compressedHeaderOffset = s->h.uncompressed_header_size, + .tilesOffset = s->h.uncompressed_header_size + + s->h.compressed_header_size, + }; + + for (int i = 0; i < STD_VIDEO_VP9_REFS_PER_FRAME; i++) { + const int idx = pic->frame_header->ref_frame_idx[i]; + const VP9Frame *ref_frame = &s->frames[idx]; + VP9VulkanDecodePicture *hp = ref_frame->hwaccel_picture_private; + + if (!ref_frame->tf.f) + ap->vp9_pic_info.referenceNameSlotIndices[i] = -1; + else + ap->vp9_pic_info.referenceNameSlotIndices[i] = hp->frame_id; + } + + vp->decode_info = (VkVideoDecodeInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, + .pNext = &ap->vp9_pic_info, + .flags = 0x0, + .pSetupReferenceSlot = &vp->ref_slot, + .referenceSlotCount = ref_count, + .pReferenceSlots = vp->ref_slots, + .dstPictureResource = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->tf.f->width, pic->tf.f->height }, + .baseArrayLayer = 0, + .imageViewBinding = vp->view.out[0], + }, + }; + + ap->dec = dec; + + return 0; +} + +static int vk_vp9_decode_slice(AVCodecContext *avctx, + const uint8_t *data, + uint32_t size) +{ + int err; + const VP9SharedContext *s = avctx->priv_data; + VP9VulkanDecodePicture *ap = s->frames[CUR_FRAME].hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + + err = ff_vk_decode_add_slice(avctx, vp, data, size, 0, NULL, NULL); + if (err < 0) + return err; + + return 0; +} + +static int vk_vp9_end_frame(AVCodecContext *avctx) +{ + const VP9SharedContext *s = avctx->priv_data; + + const VP9Frame *pic = &s->frames[CUR_FRAME]; + VP9VulkanDecodePicture *ap = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + FFVulkanDecodePicture *rvp[STD_VIDEO_VP9_REFS_PER_FRAME] = { 0 }; + AVFrame *rav[STD_VIDEO_VP9_REFS_PER_FRAME] = { 0 }; + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + const VP9Frame *rp = ap->ref_src[i]; + VP9VulkanDecodePicture *rhp = rp->hwaccel_picture_private; + + rvp[i] = &rhp->vp; + rav[i] = ap->ref_src[i]->tf.f; + } + + av_log(avctx, AV_LOG_VERBOSE, "Decoding frame, %"SIZE_SPECIFIER" bytes\n", + vp->slices_size); + + return ff_vk_decode_frame(avctx, pic->tf.f, vp, rav, rvp); +} + +static void vk_vp9_free_frame_priv(AVRefStructOpaque _hwctx, void *data) +{ + AVHWDeviceContext *hwctx = _hwctx.nc; + VP9VulkanDecodePicture *ap = data; + + /* Workaround for a spec issue. */ + if (ap->frame_id_set) + ap->dec->frame_id_alloc_mask &= ~(1 << ap->frame_id); + + /* Free frame resources, this also destroys the session parameters. */ + ff_vk_decode_free_frame(hwctx, &ap->vp); +} + +const FFHWAccel ff_vp9_vulkan_hwaccel = { + .p.name = "av1_vulkan", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_VULKAN, + .start_frame = &vk_vp9_start_frame, + .decode_slice = &vk_vp9_decode_slice, + .end_frame = &vk_vp9_end_frame, + .free_frame_priv = &vk_vp9_free_frame_priv, + .frame_priv_data_size = sizeof(VP9VulkanDecodePicture), + .init = &ff_vk_decode_init, + .update_thread_context = &ff_vk_update_thread_context, + .flush = &ff_vk_decode_flush, + .uninit = &ff_vk_decode_uninit, + .frame_params = &ff_vk_frame_params, + .priv_data_size = sizeof(FFVulkanDecodeContext), + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c index e818099fdb..2c1c38ba66 100644 --- a/libavutil/hwcontext_vulkan.c +++ b/libavutil/hwcontext_vulkan.c @@ -90,6 +90,9 @@ typedef struct VulkanDeviceFeatures { #ifdef VK_KHR_video_maintenance2 VkPhysicalDeviceVideoMaintenance2FeaturesKHR video_maintenance_2; #endif +#ifdef VK_KHR_video_decode_vp9 + VkPhysicalDeviceVideoDecodeVP9FeaturesKHR vp9_decode; +#endif VkPhysicalDeviceShaderObjectFeaturesEXT shader_object; VkPhysicalDeviceCooperativeMatrixFeaturesKHR cooperative_matrix; @@ -227,6 +230,10 @@ static void device_features_init(AVHWDeviceContext *ctx, VulkanDeviceFeatures *f FF_VK_STRUCT_EXT(s, &feats->device, &feats->video_maintenance_2, FF_VK_EXT_VIDEO_MAINTENANCE_2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_2_FEATURES_KHR); #endif +#ifdef VK_KHR_video_decode_vp9 + FF_VK_STRUCT_EXT(s, &feats->device, &feats->vp9_decode, FF_VK_EXT_VIDEO_DECODE_VP9, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_DECODE_VP9_FEATURES_KHR); +#endif FF_VK_STRUCT_EXT(s, &feats->device, &feats->shader_object, FF_VK_EXT_SHADER_OBJECT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT); @@ -299,6 +306,10 @@ static void device_features_copy_needed(VulkanDeviceFeatures *dst, VulkanDeviceF COPY_VAL(video_maintenance_2.videoMaintenance2); #endif +#ifdef VK_KHR_video_decode_vp9 + COPY_VAL(vp9_decode.videoDecodeVP9); +#endif + COPY_VAL(shader_object.shaderObject); COPY_VAL(cooperative_matrix.cooperativeMatrix); @@ -644,6 +655,9 @@ static const VulkanOptExtension optional_device_exts[] = { { VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_H264 }, { VK_KHR_VIDEO_ENCODE_H265_EXTENSION_NAME, FF_VK_EXT_VIDEO_ENCODE_H265 }, { VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_H265 }, +#ifdef VK_KHR_video_decode_vp9 + { VK_KHR_VIDEO_DECODE_VP9_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_VP9 }, +#endif { VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_AV1 }, }; @@ -1548,6 +1562,7 @@ static int setup_queue_families(AVHWDeviceContext *ctx, VkDeviceCreateInfo *cd) PICK_QF(VK_QUEUE_VIDEO_ENCODE_BIT_KHR, VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR); PICK_QF(VK_QUEUE_VIDEO_DECODE_BIT_KHR, VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR); + PICK_QF(VK_QUEUE_VIDEO_DECODE_BIT_KHR, VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR); PICK_QF(VK_QUEUE_VIDEO_DECODE_BIT_KHR, VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR); av_free(qf); diff --git a/libavutil/vulkan_functions.h b/libavutil/vulkan_functions.h index 68fa7b802d..9fd646fa4e 100644 --- a/libavutil/vulkan_functions.h +++ b/libavutil/vulkan_functions.h @@ -59,7 +59,8 @@ typedef uint64_t FFVulkanExtensions; #define FF_VK_EXT_VIDEO_DECODE_QUEUE (1ULL << 40) /* VK_KHR_video_decode_queue */ #define FF_VK_EXT_VIDEO_DECODE_H264 (1ULL << 41) /* VK_KHR_video_decode_h264 */ #define FF_VK_EXT_VIDEO_DECODE_H265 (1ULL << 42) /* VK_KHR_video_decode_h265 */ -#define FF_VK_EXT_VIDEO_DECODE_AV1 (1ULL << 43) /* VK_KHR_video_decode_av1 */ +#define FF_VK_EXT_VIDEO_DECODE_VP9 (1ULL << 43) /* VK_KHR_video_decode_av1 */ +#define FF_VK_EXT_VIDEO_DECODE_AV1 (1ULL << 44) /* VK_KHR_video_decode_av1 */ #define FF_VK_EXT_VIDEO_ENCODE_QUEUE (1ULL << 50) /* VK_KHR_video_encode_queue */ #define FF_VK_EXT_VIDEO_ENCODE_H264 (1ULL << 51) /* VK_KHR_video_encode_h264 */ diff --git a/libavutil/vulkan_loader.h b/libavutil/vulkan_loader.h index 7e805fdd4c..37a3731feb 100644 --- a/libavutil/vulkan_loader.h +++ b/libavutil/vulkan_loader.h @@ -76,6 +76,9 @@ static inline uint64_t ff_vk_extensions_to_mask(const char * const *extensions, { VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_H264 }, { VK_KHR_VIDEO_ENCODE_H265_EXTENSION_NAME, FF_VK_EXT_VIDEO_ENCODE_H265 }, { VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_H265 }, +#ifdef VK_KHR_video_decode_vp9 + { VK_KHR_VIDEO_DECODE_VP9_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_VP9 }, +#endif { VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME, FF_VK_EXT_VIDEO_DECODE_AV1 }, { VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, FF_VK_EXT_PUSH_DESCRIPTOR }, #ifdef VK_KHR_shader_expect_assume -- 2.50.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
prev parent reply other threads:[~2025-07-12 18:54 UTC|newest] Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top 2025-07-12 18:51 [FFmpeg-devel] [PATCH v2 01/13] vf_libplacebo: add support for specifying a LUT for the input Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 02/13] hwcontext_vulkan: temporarily disable host_image_copy Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 03/13] hwcontext_vulkan: enable uniformBufferStandardLayout Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 04/13] vulkan: add support for 16-bit RGGB Bayer pixfmt Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 05/13] lavc/vulkan/common: sign-ify lengths Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 06/13] lavc: add codec ID and profiles for ProRes RAW Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 07/13] lavc: add a ProRes RAW parser Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 08/13] lavc: add a ProRes RAW decoder Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 09/13] lavc: add a ProRes RAW Vulkan hwaccel Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 10/13] scale_vulkan: refactor shader initialization Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 11/13] scale_vulkan: add support for basic Debayering Lynne 2025-07-12 18:51 ` [FFmpeg-devel] [PATCH v2 12/13] lavc/vp9dec: use cbs_vp9 to parse the frame header Lynne 2025-07-12 18:51 ` Lynne [this message]
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20250712185128.862167-13-dev@lynne.ee \ --to=dev@lynne.ee \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git