2023-01-16 07:23:27 +01:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
2024-10-15 22:37:56 +02:00
|
|
|
#include "libavutil/refstruct.h"
|
2023-01-16 07:23:27 +01:00
|
|
|
#include "vulkan_video.h"
|
|
|
|
|
#include "vulkan_decode.h"
|
|
|
|
|
#include "config_components.h"
|
2024-03-07 01:29:18 +01:00
|
|
|
#include "libavutil/avassert.h"
|
2024-03-25 01:30:37 +01:00
|
|
|
#include "libavutil/mem.h"
|
2024-03-03 13:04:29 +01:00
|
|
|
#include "libavutil/vulkan_loader.h"
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
#define DECODER_IS_SDR(codec_id) \
|
2025-07-05 03:12:45 +09:00
|
|
|
(((codec_id) == AV_CODEC_ID_FFV1) || \
|
2025-06-02 21:31:59 +02:00
|
|
|
((codec_id) == AV_CODEC_ID_PRORES_RAW) || \
|
|
|
|
|
((codec_id) == AV_CODEC_ID_PRORES))
|
2024-12-03 18:21:57 +09:00
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
#if CONFIG_H264_VULKAN_HWACCEL
|
2024-03-07 01:08:16 +01:00
|
|
|
extern const FFVulkanDecodeDescriptor ff_vk_dec_h264_desc;
|
2023-01-16 07:23:27 +01:00
|
|
|
#endif
|
|
|
|
|
#if CONFIG_HEVC_VULKAN_HWACCEL
|
2024-03-07 01:08:16 +01:00
|
|
|
extern const FFVulkanDecodeDescriptor ff_vk_dec_hevc_desc;
|
2023-01-16 07:23:27 +01:00
|
|
|
#endif
|
2025-03-27 12:50:30 +00:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
|
|
|
|
extern const FFVulkanDecodeDescriptor ff_vk_dec_vp9_desc;
|
|
|
|
|
#endif
|
2023-02-17 04:09:16 +01:00
|
|
|
#if CONFIG_AV1_VULKAN_HWACCEL
|
2024-03-07 01:08:16 +01:00
|
|
|
extern const FFVulkanDecodeDescriptor ff_vk_dec_av1_desc;
|
2023-02-17 04:09:16 +01:00
|
|
|
#endif
|
2025-03-10 03:04:39 +00:00
|
|
|
#if CONFIG_FFV1_VULKAN_HWACCEL
|
|
|
|
|
extern const FFVulkanDecodeDescriptor ff_vk_dec_ffv1_desc;
|
|
|
|
|
#endif
|
2025-07-05 03:12:45 +09:00
|
|
|
#if CONFIG_PRORES_RAW_VULKAN_HWACCEL
|
|
|
|
|
extern const FFVulkanDecodeDescriptor ff_vk_dec_prores_raw_desc;
|
|
|
|
|
#endif
|
2025-06-02 21:31:59 +02:00
|
|
|
#if CONFIG_PRORES_VULKAN_HWACCEL
|
|
|
|
|
extern const FFVulkanDecodeDescriptor ff_vk_dec_prores_desc;
|
|
|
|
|
#endif
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-03-07 01:08:16 +01:00
|
|
|
static const FFVulkanDecodeDescriptor *dec_descs[] = {
|
2023-01-16 07:23:27 +01:00
|
|
|
#if CONFIG_H264_VULKAN_HWACCEL
|
2024-03-07 01:29:18 +01:00
|
|
|
&ff_vk_dec_h264_desc,
|
2023-01-16 07:23:27 +01:00
|
|
|
#endif
|
|
|
|
|
#if CONFIG_HEVC_VULKAN_HWACCEL
|
2024-03-07 01:29:18 +01:00
|
|
|
&ff_vk_dec_hevc_desc,
|
2023-01-16 07:23:27 +01:00
|
|
|
#endif
|
2025-03-27 12:50:30 +00:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
|
|
|
|
&ff_vk_dec_vp9_desc,
|
|
|
|
|
#endif
|
2023-02-17 04:09:16 +01:00
|
|
|
#if CONFIG_AV1_VULKAN_HWACCEL
|
2024-03-07 01:29:18 +01:00
|
|
|
&ff_vk_dec_av1_desc,
|
2023-02-17 04:09:16 +01:00
|
|
|
#endif
|
2025-03-10 03:04:39 +00:00
|
|
|
#if CONFIG_FFV1_VULKAN_HWACCEL
|
|
|
|
|
&ff_vk_dec_ffv1_desc,
|
|
|
|
|
#endif
|
2025-07-05 03:12:45 +09:00
|
|
|
#if CONFIG_PRORES_RAW_VULKAN_HWACCEL
|
|
|
|
|
&ff_vk_dec_prores_raw_desc,
|
|
|
|
|
#endif
|
2025-06-02 21:31:59 +02:00
|
|
|
#if CONFIG_PRORES_VULKAN_HWACCEL
|
|
|
|
|
&ff_vk_dec_prores_desc,
|
|
|
|
|
#endif
|
2023-01-16 07:23:27 +01:00
|
|
|
};
|
|
|
|
|
|
2025-08-08 21:15:56 +09:00
|
|
|
typedef struct FFVulkanDecodeProfileData {
|
|
|
|
|
VkVideoDecodeH264ProfileInfoKHR h264_profile;
|
|
|
|
|
VkVideoDecodeH265ProfileInfoKHR h265_profile;
|
|
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
|
|
|
|
VkVideoDecodeVP9ProfileInfoKHR vp9_profile;
|
|
|
|
|
#endif
|
|
|
|
|
VkVideoDecodeAV1ProfileInfoKHR av1_profile;
|
|
|
|
|
|
|
|
|
|
VkVideoDecodeUsageInfoKHR usage;
|
|
|
|
|
VkVideoProfileInfoKHR profile;
|
|
|
|
|
VkVideoProfileListInfoKHR profile_list;
|
|
|
|
|
} FFVulkanDecodeProfileData;
|
|
|
|
|
|
2024-03-07 01:08:16 +01:00
|
|
|
static const FFVulkanDecodeDescriptor *get_codecdesc(enum AVCodecID codec_id)
|
2024-03-03 10:51:27 +01:00
|
|
|
{
|
2024-03-07 01:29:18 +01:00
|
|
|
for (size_t i = 0; i < FF_ARRAY_ELEMS(dec_descs); i++)
|
|
|
|
|
if (dec_descs[i]->codec_id == codec_id)
|
|
|
|
|
return dec_descs[i];
|
|
|
|
|
av_assert1(!"no codec descriptor");
|
|
|
|
|
return NULL;
|
2024-03-03 10:51:27 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-19 05:39:07 +02:00
|
|
|
static const VkVideoProfileInfoKHR *get_video_profile(FFVulkanDecodeShared *ctx, enum AVCodecID codec_id)
|
|
|
|
|
{
|
|
|
|
|
const VkVideoProfileListInfoKHR *profile_list;
|
|
|
|
|
|
|
|
|
|
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 :
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
codec_id == AV_CODEC_ID_VP9 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_VP9_PROFILE_INFO_KHR :
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2024-01-19 10:49:02 +10:00
|
|
|
codec_id == AV_CODEC_ID_AV1 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_KHR :
|
2024-12-03 18:21:57 +09:00
|
|
|
VK_STRUCTURE_TYPE_MAX_ENUM;
|
|
|
|
|
if (profile_struct_type == VK_STRUCTURE_TYPE_MAX_ENUM)
|
|
|
|
|
return NULL;
|
2023-07-19 05:39:07 +02:00
|
|
|
|
|
|
|
|
profile_list = ff_vk_find_struct(ctx->s.hwfc->create_pnext,
|
|
|
|
|
VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
|
|
|
|
|
if (!profile_list)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < profile_list->profileCount; i++)
|
|
|
|
|
if (ff_vk_find_struct(profile_list->pProfiles[i].pNext, profile_struct_type))
|
|
|
|
|
return &profile_list->pProfiles[i];
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
int ff_vk_update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
FFVulkanDecodeContext *src_ctx = src->internal->hwaccel_priv_data;
|
|
|
|
|
FFVulkanDecodeContext *dst_ctx = dst->internal->hwaccel_priv_data;
|
|
|
|
|
|
2024-10-15 22:37:56 +02:00
|
|
|
av_refstruct_replace(&dst_ctx->shared_ctx, src_ctx->shared_ctx);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2025-03-27 12:47:11 +00:00
|
|
|
err = av_buffer_replace(&dst_ctx->session_params, src_ctx->session_params);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
dst_ctx->dedicated_dpb = src_ctx->dedicated_dpb;
|
2023-02-17 04:09:16 +01:00
|
|
|
dst_ctx->external_fg = src_ctx->external_fg;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 18:24:55 +02:00
|
|
|
int ff_vk_params_invalidate(AVCodecContext *avctx, int t, const uint8_t *b, uint32_t s)
|
2023-01-16 07:23:27 +01:00
|
|
|
{
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2023-06-21 18:24:55 +02:00
|
|
|
av_buffer_unref(&dec->session_params);
|
2023-01-16 07:23:27 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static AVFrame *vk_get_dpb_pool(FFVulkanDecodeShared *ctx)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
AVFrame *avf = av_frame_alloc();
|
|
|
|
|
if (!avf)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
err = av_hwframe_get_buffer(ctx->common.dpb_hwfc_ref, avf, 0x0);
|
2023-01-16 07:23:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
av_frame_free(&avf);
|
|
|
|
|
|
|
|
|
|
return avf;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
static void init_frame(FFVulkanDecodeContext *dec, FFVulkanDecodePicture *vkpic)
|
|
|
|
|
{
|
|
|
|
|
FFVulkanDecodeShared *ctx = dec->shared_ctx;
|
|
|
|
|
FFVulkanFunctions *vk = &ctx->s.vkfn;
|
|
|
|
|
|
|
|
|
|
vkpic->dpb_frame = NULL;
|
2025-02-21 01:33:54 +00:00
|
|
|
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++) {
|
|
|
|
|
vkpic->view.ref[i] = VK_NULL_HANDLE;
|
|
|
|
|
vkpic->view.out[i] = VK_NULL_HANDLE;
|
|
|
|
|
vkpic->view.dst[i] = VK_NULL_HANDLE;
|
|
|
|
|
}
|
2024-12-03 18:21:57 +09:00
|
|
|
|
|
|
|
|
vkpic->destroy_image_view = vk->DestroyImageView;
|
|
|
|
|
vkpic->wait_semaphores = vk->WaitSemaphores;
|
2025-05-06 11:53:12 +02:00
|
|
|
vkpic->invalidate_memory_ranges = vk->InvalidateMappedMemoryRanges;
|
2024-12-03 18:21:57 +09:00
|
|
|
}
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic,
|
|
|
|
|
FFVulkanDecodePicture *vkpic, int is_current,
|
|
|
|
|
int alloc_dpb)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
2023-09-13 02:04:09 +02:00
|
|
|
FFVulkanDecodeShared *ctx = dec->shared_ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
vkpic->slices_size = 0;
|
|
|
|
|
|
|
|
|
|
/* If the decoder made a blank frame to make up for a missing ref, or the
|
|
|
|
|
* frame is the current frame so it's missing one, create a re-representation */
|
2025-02-21 01:33:54 +00:00
|
|
|
if (vkpic->view.ref[0])
|
2023-01-16 07:23:27 +01:00
|
|
|
return 0;
|
|
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
init_frame(dec, vkpic);
|
2023-09-15 02:22:00 +02:00
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
if (ctx->common.layered_dpb && alloc_dpb) {
|
2025-02-21 01:33:54 +00:00
|
|
|
vkpic->view.ref[0] = ctx->common.layered_view;
|
|
|
|
|
vkpic->view.aspect_ref[0] = ctx->common.layered_aspect;
|
2023-01-16 07:23:27 +01:00
|
|
|
} else if (alloc_dpb) {
|
2024-08-31 01:43:48 +00:00
|
|
|
AVHWFramesContext *dpb_frames = (AVHWFramesContext *)ctx->common.dpb_hwfc_ref->data;
|
2023-01-16 07:23:27 +01:00
|
|
|
AVVulkanFramesContext *dpb_hwfc = dpb_frames->hwctx;
|
|
|
|
|
|
|
|
|
|
vkpic->dpb_frame = vk_get_dpb_pool(ctx);
|
|
|
|
|
if (!vkpic->dpb_frame)
|
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
err = ff_vk_create_view(&ctx->s, &ctx->common,
|
2025-02-21 01:33:54 +00:00
|
|
|
&vkpic->view.ref[0], &vkpic->view.aspect_ref[0],
|
2024-08-31 01:43:48 +00:00
|
|
|
(AVVkFrame *)vkpic->dpb_frame->data[0],
|
|
|
|
|
dpb_hwfc->format[0], !is_current);
|
2023-01-16 07:23:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
vkpic->view.dst[0] = vkpic->view.ref[0];
|
2023-01-16 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!alloc_dpb || is_current) {
|
|
|
|
|
AVHWFramesContext *frames = (AVHWFramesContext *)pic->hw_frames_ctx->data;
|
|
|
|
|
AVVulkanFramesContext *hwfc = frames->hwctx;
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
err = ff_vk_create_view(&ctx->s, &ctx->common,
|
2025-02-21 01:33:54 +00:00
|
|
|
&vkpic->view.out[0], &vkpic->view.aspect[0],
|
2024-08-31 01:43:48 +00:00
|
|
|
(AVVkFrame *)pic->data[0],
|
|
|
|
|
hwfc->format[0], !is_current);
|
2023-01-16 07:23:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
if (!alloc_dpb) {
|
2025-02-21 01:33:54 +00:00
|
|
|
vkpic->view.ref[0] = vkpic->view.out[0];
|
|
|
|
|
vkpic->view.aspect_ref[0] = vkpic->view.aspect[0];
|
2023-01-16 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
int ff_vk_decode_prepare_frame_sdr(FFVulkanDecodeContext *dec, AVFrame *pic,
|
|
|
|
|
FFVulkanDecodePicture *vkpic, int is_current,
|
|
|
|
|
enum FFVkShaderRepFormat rep_fmt, int alloc_dpb)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
FFVulkanDecodeShared *ctx = dec->shared_ctx;
|
2025-02-21 01:33:54 +00:00
|
|
|
AVHWFramesContext *frames = (AVHWFramesContext *)pic->hw_frames_ctx->data;
|
2024-12-03 18:21:57 +09:00
|
|
|
|
|
|
|
|
vkpic->slices_size = 0;
|
|
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
if (vkpic->view.ref[0])
|
2024-12-03 18:21:57 +09:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
init_frame(dec, vkpic);
|
|
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
for (int i = 0; i < av_pix_fmt_count_planes(frames->sw_format); i++) {
|
|
|
|
|
if (alloc_dpb) {
|
|
|
|
|
vkpic->dpb_frame = vk_get_dpb_pool(ctx);
|
|
|
|
|
if (!vkpic->dpb_frame)
|
|
|
|
|
return AVERROR(ENOMEM);
|
2024-12-03 18:21:57 +09:00
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
err = ff_vk_create_imageview(&ctx->s,
|
|
|
|
|
&vkpic->view.ref[i], &vkpic->view.aspect_ref[i],
|
|
|
|
|
vkpic->dpb_frame, i, rep_fmt);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2024-12-03 18:21:57 +09:00
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
vkpic->view.dst[i] = vkpic->view.ref[i];
|
|
|
|
|
}
|
2024-12-03 18:21:57 +09:00
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
if (!alloc_dpb || is_current) {
|
|
|
|
|
err = ff_vk_create_imageview(&ctx->s,
|
|
|
|
|
&vkpic->view.out[i], &vkpic->view.aspect[i],
|
|
|
|
|
pic, i, rep_fmt);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2024-12-03 18:21:57 +09:00
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
if (!alloc_dpb) {
|
|
|
|
|
vkpic->view.ref[i] = vkpic->view.out[i];
|
|
|
|
|
vkpic->view.aspect_ref[i] = vkpic->view.aspect[i];
|
|
|
|
|
}
|
2024-12-03 18:21:57 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp,
|
|
|
|
|
const uint8_t *data, size_t size, int add_startcode,
|
|
|
|
|
uint32_t *nb_slices, const uint32_t **offsets)
|
|
|
|
|
{
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2023-09-13 02:04:09 +02:00
|
|
|
FFVulkanDecodeShared *ctx = dec->shared_ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
static const uint8_t startcode_prefix[3] = { 0x0, 0x0, 0x1 };
|
|
|
|
|
const size_t startcode_len = add_startcode ? sizeof(startcode_prefix) : 0;
|
2025-03-27 12:49:06 +00:00
|
|
|
const int nb = nb_slices ? *nb_slices : 0;
|
2023-01-16 07:23:27 +01:00
|
|
|
uint8_t *slices;
|
|
|
|
|
uint32_t *slice_off;
|
2024-07-18 10:12:09 +02:00
|
|
|
FFVkBuffer *vkbuf;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
size_t new_size = vp->slices_size + startcode_len + size +
|
2023-06-13 06:10:20 +02:00
|
|
|
ctx->caps.minBitstreamBufferSizeAlignment;
|
|
|
|
|
new_size = FFALIGN(new_size, ctx->caps.minBitstreamBufferSizeAlignment);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2025-03-27 12:49:06 +00:00
|
|
|
if (offsets) {
|
|
|
|
|
slice_off = av_fast_realloc(dec->slice_off, &dec->slice_off_max,
|
|
|
|
|
(nb + 1)*sizeof(slice_off));
|
|
|
|
|
if (!slice_off)
|
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
|
|
|
|
|
*offsets = dec->slice_off = slice_off;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2025-03-27 12:49:06 +00:00
|
|
|
slice_off[nb] = vp->slices_size;
|
|
|
|
|
}
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-07-18 10:12:09 +02:00
|
|
|
vkbuf = vp->slices_buf ? (FFVkBuffer *)vp->slices_buf->data : NULL;
|
|
|
|
|
if (!vkbuf || vkbuf->size < new_size) {
|
2023-01-16 07:23:27 +01:00
|
|
|
int err;
|
|
|
|
|
AVBufferRef *new_ref;
|
2024-07-18 10:12:09 +02:00
|
|
|
FFVkBuffer *new_buf;
|
|
|
|
|
|
|
|
|
|
/* No point in requesting anything smaller. */
|
|
|
|
|
size_t buf_size = FFMAX(new_size, 1024*1024);
|
|
|
|
|
|
|
|
|
|
/* Align buffer to nearest power of two. Makes fragmentation management
|
|
|
|
|
* easier, and gives us ample headroom. */
|
|
|
|
|
buf_size = 2 << av_log2(buf_size);
|
|
|
|
|
|
|
|
|
|
err = ff_vk_get_pooled_buffer(&ctx->s, &ctx->buf_pool, &new_ref,
|
2024-12-03 18:21:57 +09:00
|
|
|
DECODER_IS_SDR(avctx->codec_id) ?
|
|
|
|
|
(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
|
|
|
|
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) :
|
2024-07-18 10:12:09 +02:00
|
|
|
VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR,
|
|
|
|
|
ctx->s.hwfc->create_pnext, buf_size,
|
2024-12-03 18:21:57 +09:00
|
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
|
|
|
(DECODER_IS_SDR(avctx->codec_id) ?
|
|
|
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0x0));
|
2023-01-16 07:23:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
2024-07-18 10:12:09 +02:00
|
|
|
new_buf = (FFVkBuffer *)new_ref->data;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
/* Copy data from the old buffer */
|
|
|
|
|
if (vkbuf) {
|
2024-07-18 10:12:09 +02:00
|
|
|
memcpy(new_buf->mapped_mem, vkbuf->mapped_mem, vp->slices_size);
|
2023-01-16 07:23:27 +01:00
|
|
|
av_buffer_unref(&vp->slices_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vp->slices_buf = new_ref;
|
|
|
|
|
vkbuf = new_buf;
|
|
|
|
|
}
|
2024-07-18 10:12:09 +02:00
|
|
|
slices = vkbuf->mapped_mem;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
/* Startcode */
|
|
|
|
|
memcpy(slices + vp->slices_size, startcode_prefix, startcode_len);
|
|
|
|
|
|
|
|
|
|
/* Slice data */
|
|
|
|
|
memcpy(slices + vp->slices_size + startcode_len, data, size);
|
|
|
|
|
|
2025-03-27 12:49:06 +00:00
|
|
|
if (nb_slices)
|
|
|
|
|
*nb_slices = nb + 1;
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
vp->slices_size += startcode_len + size;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ff_vk_decode_flush(AVCodecContext *avctx)
|
|
|
|
|
{
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2023-09-13 02:04:09 +02:00
|
|
|
FFVulkanDecodeShared *ctx = dec->shared_ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
FFVulkanFunctions *vk = &ctx->s.vkfn;
|
|
|
|
|
VkVideoBeginCodingInfoKHR decode_start = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR,
|
|
|
|
|
.videoSession = ctx->common.session,
|
|
|
|
|
.videoSessionParameters = ctx->empty_session_params,
|
|
|
|
|
};
|
|
|
|
|
VkVideoCodingControlInfoKHR decode_ctrl = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR,
|
|
|
|
|
.flags = VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR,
|
|
|
|
|
};
|
|
|
|
|
VkVideoEndCodingInfoKHR decode_end = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
VkCommandBuffer cmd_buf;
|
vulkan_decode: use a single execution pool
Originally, the decoder had a single execution pool, with one
execution context per thread. Execution pools were always intended
to be thread-safe, as long as there were enough execution contexts
in the pool to satisfy all threads.
Due to synchronization issues, the threading part was removed at some
point, and, for decoding, each thread had its own execution pool.
Having a single execution pool per context is hacky, not to mention
wasteful.
Most importantly, we *cannot* associate single shaders across multiple
execution pools for a single application. This means that we cannot
use shaders to either apply film grain, or use this framework for
software-defined decoders.
The recent commits added threading capabilities back to the execution
pool, and the number of contexts in each pool was increased. This was
done with the assumption that the execution pool was singular, which
it was not. This led to increased parallelism and number of frames
in flight, which is taxing on memory.
This commit finally restores proper threading behaviour.
The validation layer has isses that are reported and addressed in the
earlier commit.
2024-12-03 18:11:16 +09:00
|
|
|
FFVkExecContext *exec;
|
|
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
/* Non-video queues do not need to be reset */
|
|
|
|
|
if (!(get_codecdesc(avctx->codec_id)->decode_op))
|
|
|
|
|
return;
|
|
|
|
|
|
vulkan_decode: use a single execution pool
Originally, the decoder had a single execution pool, with one
execution context per thread. Execution pools were always intended
to be thread-safe, as long as there were enough execution contexts
in the pool to satisfy all threads.
Due to synchronization issues, the threading part was removed at some
point, and, for decoding, each thread had its own execution pool.
Having a single execution pool per context is hacky, not to mention
wasteful.
Most importantly, we *cannot* associate single shaders across multiple
execution pools for a single application. This means that we cannot
use shaders to either apply film grain, or use this framework for
software-defined decoders.
The recent commits added threading capabilities back to the execution
pool, and the number of contexts in each pool was increased. This was
done with the assumption that the execution pool was singular, which
it was not. This led to increased parallelism and number of frames
in flight, which is taxing on memory.
This commit finally restores proper threading behaviour.
The validation layer has isses that are reported and addressed in the
earlier commit.
2024-12-03 18:11:16 +09:00
|
|
|
exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool);
|
2023-01-16 07:23:27 +01:00
|
|
|
ff_vk_exec_start(&ctx->s, exec);
|
|
|
|
|
cmd_buf = exec->buf;
|
|
|
|
|
|
|
|
|
|
vk->CmdBeginVideoCodingKHR(cmd_buf, &decode_start);
|
|
|
|
|
vk->CmdControlVideoCodingKHR(cmd_buf, &decode_ctrl);
|
|
|
|
|
vk->CmdEndVideoCodingKHR(cmd_buf, &decode_end);
|
|
|
|
|
ff_vk_exec_submit(&ctx->s, exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ff_vk_decode_frame(AVCodecContext *avctx,
|
|
|
|
|
AVFrame *pic, FFVulkanDecodePicture *vp,
|
|
|
|
|
AVFrame *rpic[], FFVulkanDecodePicture *rvkp[])
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
VkResult ret;
|
|
|
|
|
VkCommandBuffer cmd_buf;
|
2024-07-18 10:12:09 +02:00
|
|
|
FFVkBuffer *sd_buf;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2023-09-13 02:04:09 +02:00
|
|
|
FFVulkanDecodeShared *ctx = dec->shared_ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
FFVulkanFunctions *vk = &ctx->s.vkfn;
|
|
|
|
|
|
|
|
|
|
/* Output */
|
|
|
|
|
AVVkFrame *vkf = (AVVkFrame *)pic->buf[0]->data;
|
|
|
|
|
|
|
|
|
|
/* Quirks */
|
2024-08-31 01:43:48 +00:00
|
|
|
const int layered_dpb = ctx->common.layered_dpb;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
VkVideoBeginCodingInfoKHR decode_start = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR,
|
|
|
|
|
.videoSession = ctx->common.session,
|
2025-03-27 12:47:11 +00:00
|
|
|
.videoSessionParameters = dec->session_params ?
|
|
|
|
|
*((VkVideoSessionParametersKHR *)dec->session_params->data) :
|
|
|
|
|
VK_NULL_HANDLE,
|
2023-01-16 07:23:27 +01:00
|
|
|
.referenceSlotCount = vp->decode_info.referenceSlotCount,
|
|
|
|
|
.pReferenceSlots = vp->decode_info.pReferenceSlots,
|
|
|
|
|
};
|
|
|
|
|
VkVideoEndCodingInfoKHR decode_end = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
VkImageMemoryBarrier2 img_bar[37];
|
|
|
|
|
int nb_img_bar = 0;
|
|
|
|
|
size_t data_size = FFALIGN(vp->slices_size,
|
2023-06-13 06:10:20 +02:00
|
|
|
ctx->caps.minBitstreamBufferSizeAlignment);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
vulkan_decode: use a single execution pool
Originally, the decoder had a single execution pool, with one
execution context per thread. Execution pools were always intended
to be thread-safe, as long as there were enough execution contexts
in the pool to satisfy all threads.
Due to synchronization issues, the threading part was removed at some
point, and, for decoding, each thread had its own execution pool.
Having a single execution pool per context is hacky, not to mention
wasteful.
Most importantly, we *cannot* associate single shaders across multiple
execution pools for a single application. This means that we cannot
use shaders to either apply film grain, or use this framework for
software-defined decoders.
The recent commits added threading capabilities back to the execution
pool, and the number of contexts in each pool was increased. This was
done with the assumption that the execution pool was singular, which
it was not. This led to increased parallelism and number of frames
in flight, which is taxing on memory.
This commit finally restores proper threading behaviour.
The validation layer has isses that are reported and addressed in the
earlier commit.
2024-12-03 18:11:16 +09:00
|
|
|
FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
/* The current decoding reference has to be bound as an inactive reference */
|
|
|
|
|
VkVideoReferenceSlotInfoKHR *cur_vk_ref;
|
|
|
|
|
cur_vk_ref = (void *)&decode_start.pReferenceSlots[decode_start.referenceSlotCount];
|
|
|
|
|
cur_vk_ref[0] = vp->ref_slot;
|
|
|
|
|
cur_vk_ref[0].slotIndex = -1;
|
|
|
|
|
decode_start.referenceSlotCount++;
|
|
|
|
|
|
2024-07-18 10:12:09 +02:00
|
|
|
sd_buf = (FFVkBuffer *)vp->slices_buf->data;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
/* Flush if needed */
|
2024-07-18 10:12:09 +02:00
|
|
|
if (!(sd_buf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
|
2023-01-16 07:23:27 +01:00
|
|
|
VkMappedMemoryRange flush_buf = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
|
2024-07-18 10:12:09 +02:00
|
|
|
.memory = sd_buf->mem,
|
2023-01-16 07:23:27 +01:00
|
|
|
.offset = 0,
|
|
|
|
|
.size = FFALIGN(vp->slices_size,
|
|
|
|
|
ctx->s.props.properties.limits.nonCoherentAtomSize),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ret = vk->FlushMappedMemoryRanges(ctx->s.hwctx->act_dev, 1, &flush_buf);
|
|
|
|
|
if (ret != VK_SUCCESS) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Failed to flush memory: %s\n",
|
|
|
|
|
ff_vk_ret2str(ret));
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-18 10:12:09 +02:00
|
|
|
vp->decode_info.srcBuffer = sd_buf->buf;
|
2023-01-16 07:23:27 +01:00
|
|
|
vp->decode_info.srcBufferOffset = 0;
|
|
|
|
|
vp->decode_info.srcBufferRange = data_size;
|
|
|
|
|
|
|
|
|
|
/* Start command buffer recording */
|
|
|
|
|
err = ff_vk_exec_start(&ctx->s, exec);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
cmd_buf = exec->buf;
|
|
|
|
|
|
|
|
|
|
/* Slices */
|
|
|
|
|
err = ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
vp->slices_buf = NULL; /* Owned by the exec buffer from now on */
|
|
|
|
|
|
|
|
|
|
/* Parameters */
|
|
|
|
|
err = ff_vk_exec_add_dep_buf(&ctx->s, exec, &dec->session_params, 1, 1);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
err = ff_vk_exec_add_dep_frame(&ctx->s, exec, pic,
|
|
|
|
|
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
|
|
|
|
|
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value,
|
|
|
|
|
pic);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/* Output image - change layout, as it comes from a pool */
|
|
|
|
|
img_bar[nb_img_bar] = (VkImageMemoryBarrier2) {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
|
|
|
|
.pNext = NULL,
|
|
|
|
|
.srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
|
|
|
|
|
.dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
|
|
|
|
|
.srcAccessMask = VK_ACCESS_2_NONE,
|
|
|
|
|
.dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR,
|
|
|
|
|
.oldLayout = vkf->layout[0],
|
2024-08-31 01:43:48 +00:00
|
|
|
.newLayout = (layered_dpb || vp->dpb_frame) ?
|
2023-10-25 01:32:20 +02:00
|
|
|
VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR :
|
2023-01-16 07:23:27 +01:00
|
|
|
VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, /* Spec, 07252 utter madness */
|
|
|
|
|
.srcQueueFamilyIndex = vkf->queue_family[0],
|
|
|
|
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
|
|
|
.image = vkf->img[0],
|
|
|
|
|
.subresourceRange = (VkImageSubresourceRange) {
|
2025-02-21 01:33:54 +00:00
|
|
|
.aspectMask = vp->view.aspect[0],
|
2023-01-16 07:23:27 +01:00
|
|
|
.layerCount = 1,
|
|
|
|
|
.levelCount = 1,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!layered_dpb) {
|
|
|
|
|
/* All references (apart from the current) for non-layered refs */
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
|
|
|
|
|
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
|
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec,
|
|
|
|
|
&rvp->sem, &rvp->sem_value,
|
|
|
|
|
ref);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rvp->dpb_frame) {
|
|
|
|
|
AVVkFrame *rvkf = (AVVkFrame *)ref->data[0];
|
|
|
|
|
|
|
|
|
|
img_bar[nb_img_bar] = (VkImageMemoryBarrier2) {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
|
|
|
|
.pNext = NULL,
|
|
|
|
|
.srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
|
|
|
|
|
.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 = rvkf->layout[0],
|
|
|
|
|
.newLayout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR,
|
|
|
|
|
.srcQueueFamilyIndex = rvkf->queue_family[0],
|
|
|
|
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
|
|
|
.image = rvkf->img[0],
|
|
|
|
|
.subresourceRange = (VkImageSubresourceRange) {
|
2025-02-21 01:33:54 +00:00
|
|
|
.aspectMask = rvp->view.aspect_ref[0],
|
2023-01-16 07:23:27 +01:00
|
|
|
.layerCount = 1,
|
|
|
|
|
.levelCount = 1,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
ff_vk_exec_update_frame(&ctx->s, exec, ref,
|
|
|
|
|
&img_bar[nb_img_bar], &nb_img_bar);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (vp->decode_info.referenceSlotCount ||
|
2025-02-21 01:33:54 +00:00
|
|
|
vp->view.out[0] != vp->view.ref[0]) {
|
2023-01-16 07:23:27 +01:00
|
|
|
/* Single barrier for a single layered ref */
|
2024-08-31 01:43:48 +00:00
|
|
|
err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ctx->common.layered_frame,
|
2023-01-16 07:23:27 +01:00
|
|
|
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
|
|
|
|
|
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Change image layout */
|
|
|
|
|
vk->CmdPipelineBarrier2(cmd_buf, &(VkDependencyInfo) {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
|
|
|
|
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT,
|
|
|
|
|
.pImageMemoryBarriers = img_bar,
|
|
|
|
|
.imageMemoryBarrierCount = nb_img_bar,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Start, use parameters, decode and end decoding */
|
|
|
|
|
vk->CmdBeginVideoCodingKHR(cmd_buf, &decode_start);
|
|
|
|
|
vk->CmdDecodeVideoKHR(cmd_buf, &vp->decode_info);
|
|
|
|
|
vk->CmdEndVideoCodingKHR(cmd_buf, &decode_end);
|
|
|
|
|
|
|
|
|
|
/* End recording and submit for execution */
|
|
|
|
|
return ff_vk_exec_submit(&ctx->s, exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, FFVulkanDecodePicture *vp)
|
|
|
|
|
{
|
|
|
|
|
AVVulkanDeviceContext *hwctx = dev_ctx->hwctx;
|
|
|
|
|
|
|
|
|
|
VkSemaphoreWaitInfo sem_wait = (VkSemaphoreWaitInfo) {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
|
|
|
|
|
.pSemaphores = &vp->sem,
|
|
|
|
|
.pValues = &vp->sem_value,
|
|
|
|
|
.semaphoreCount = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* We do not have to lock the frame here because we're not interested
|
|
|
|
|
* in the actual current semaphore value, but only that it's later than
|
|
|
|
|
* the time we submitted the image for decoding. */
|
|
|
|
|
if (vp->sem)
|
2023-09-15 02:22:00 +02:00
|
|
|
vp->wait_semaphores(hwctx->act_dev, &sem_wait, UINT64_MAX);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
/* Free slices data */
|
|
|
|
|
av_buffer_unref(&vp->slices_buf);
|
|
|
|
|
|
|
|
|
|
/* Destroy image view (out) */
|
2025-02-21 01:33:54 +00:00
|
|
|
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++) {
|
|
|
|
|
if (vp->view.out[i] && vp->view.out[i] != vp->view.dst[i])
|
|
|
|
|
vp->destroy_image_view(hwctx->act_dev, vp->view.out[i], hwctx->alloc);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2025-02-21 01:33:54 +00:00
|
|
|
/* Destroy image view (ref, unlayered) */
|
|
|
|
|
if (vp->view.dst[i])
|
|
|
|
|
vp->destroy_image_view(hwctx->act_dev, vp->view.dst[i], hwctx->alloc);
|
|
|
|
|
}
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
av_frame_free(&vp->dpb_frame);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-15 22:37:56 +02:00
|
|
|
static void free_common(AVRefStructOpaque unused, void *obj)
|
2023-01-16 07:23:27 +01:00
|
|
|
{
|
2023-09-13 02:04:09 +02:00
|
|
|
FFVulkanDecodeShared *ctx = obj;
|
2023-01-16 07:23:27 +01:00
|
|
|
FFVulkanContext *s = &ctx->s;
|
|
|
|
|
FFVulkanFunctions *vk = &ctx->s.vkfn;
|
|
|
|
|
|
vulkan_decode: use a single execution pool
Originally, the decoder had a single execution pool, with one
execution context per thread. Execution pools were always intended
to be thread-safe, as long as there were enough execution contexts
in the pool to satisfy all threads.
Due to synchronization issues, the threading part was removed at some
point, and, for decoding, each thread had its own execution pool.
Having a single execution pool per context is hacky, not to mention
wasteful.
Most importantly, we *cannot* associate single shaders across multiple
execution pools for a single application. This means that we cannot
use shaders to either apply film grain, or use this framework for
software-defined decoders.
The recent commits added threading capabilities back to the execution
pool, and the number of contexts in each pool was increased. This was
done with the assumption that the execution pool was singular, which
it was not. This led to increased parallelism and number of frames
in flight, which is taxing on memory.
This commit finally restores proper threading behaviour.
The validation layer has isses that are reported and addressed in the
earlier commit.
2024-12-03 18:11:16 +09:00
|
|
|
/* Wait on and free execution pool */
|
|
|
|
|
ff_vk_exec_pool_free(&ctx->s, &ctx->exec_pool);
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
/* This also frees all references from this pool */
|
2024-08-31 01:43:48 +00:00
|
|
|
av_frame_free(&ctx->common.layered_frame);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
/* Destroy parameters */
|
|
|
|
|
if (ctx->empty_session_params)
|
|
|
|
|
vk->DestroyVideoSessionParametersKHR(s->hwctx->act_dev,
|
|
|
|
|
ctx->empty_session_params,
|
|
|
|
|
s->hwctx->alloc);
|
|
|
|
|
|
2024-07-18 10:12:09 +02:00
|
|
|
av_buffer_pool_uninit(&ctx->buf_pool);
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
ff_vk_video_common_uninit(s, &ctx->common);
|
|
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
if (ctx->sd_ctx_free)
|
|
|
|
|
ctx->sd_ctx_free(ctx);
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
ff_vk_uninit(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vulkan_decode_bootstrap(AVCodecContext *avctx, AVBufferRef *frames_ref)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2024-12-03 18:21:57 +09:00
|
|
|
const FFVulkanDecodeDescriptor *vk_desc = get_codecdesc(avctx->codec_id);
|
2023-01-16 07:23:27 +01:00
|
|
|
AVHWFramesContext *frames = (AVHWFramesContext *)frames_ref->data;
|
|
|
|
|
AVHWDeviceContext *device = (AVHWDeviceContext *)frames->device_ref->data;
|
|
|
|
|
AVVulkanDeviceContext *hwctx = device->hwctx;
|
|
|
|
|
FFVulkanDecodeShared *ctx;
|
|
|
|
|
|
2023-09-13 02:04:09 +02:00
|
|
|
if (dec->shared_ctx)
|
2023-01-16 07:23:27 +01:00
|
|
|
return 0;
|
|
|
|
|
|
2024-10-15 22:37:56 +02:00
|
|
|
dec->shared_ctx = av_refstruct_alloc_ext(sizeof(*ctx), 0, NULL,
|
2023-09-13 02:04:09 +02:00
|
|
|
free_common);
|
|
|
|
|
if (!dec->shared_ctx)
|
2023-01-16 07:23:27 +01:00
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
|
2023-09-13 02:04:09 +02:00
|
|
|
ctx = dec->shared_ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
ctx->s.extensions = ff_vk_extensions_to_mask(hwctx->enabled_dev_extensions,
|
|
|
|
|
hwctx->nb_enabled_dev_extensions);
|
|
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
if (vk_desc->queue_flags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) {
|
|
|
|
|
if (!(ctx->s.extensions & FF_VK_EXT_VIDEO_DECODE_QUEUE)) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Device does not support the %s extension!\n",
|
|
|
|
|
VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME);
|
|
|
|
|
av_refstruct_unref(&dec->shared_ctx);
|
|
|
|
|
return AVERROR(ENOSYS);
|
|
|
|
|
}
|
2023-01-16 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = ff_vk_load_functions(device, &ctx->s.vkfn, ctx->s.extensions, 1, 1);
|
|
|
|
|
if (err < 0) {
|
2024-10-15 22:37:56 +02:00
|
|
|
av_refstruct_unref(&dec->shared_ctx);
|
2023-01-16 07:23:27 +01:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static VkResult vulkan_setup_profile(AVCodecContext *avctx,
|
|
|
|
|
FFVulkanDecodeProfileData *prof,
|
|
|
|
|
AVVulkanDeviceContext *hwctx,
|
|
|
|
|
FFVulkanFunctions *vk,
|
2024-03-07 01:08:16 +01:00
|
|
|
const FFVulkanDecodeDescriptor *vk_desc,
|
2023-01-16 07:23:27 +01:00
|
|
|
VkVideoDecodeH264CapabilitiesKHR *h264_caps,
|
|
|
|
|
VkVideoDecodeH265CapabilitiesKHR *h265_caps,
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
VkVideoDecodeVP9CapabilitiesKHR *vp9_caps,
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2024-01-19 10:49:02 +10:00
|
|
|
VkVideoDecodeAV1CapabilitiesKHR *av1_caps,
|
2023-06-13 06:10:20 +02:00
|
|
|
VkVideoCapabilitiesKHR *caps,
|
|
|
|
|
VkVideoDecodeCapabilitiesKHR *dec_caps,
|
2023-01-16 07:23:27 +01:00
|
|
|
int cur_profile)
|
|
|
|
|
{
|
|
|
|
|
VkVideoDecodeUsageInfoKHR *usage = &prof->usage;
|
|
|
|
|
VkVideoProfileInfoKHR *profile = &prof->profile;
|
|
|
|
|
VkVideoProfileListInfoKHR *profile_list = &prof->profile_list;
|
|
|
|
|
|
|
|
|
|
VkVideoDecodeH264ProfileInfoKHR *h264_profile = &prof->h264_profile;
|
2023-08-23 22:05:58 +02:00
|
|
|
VkVideoDecodeH265ProfileInfoKHR *h265_profile = &prof->h265_profile;
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
VkVideoDecodeVP9ProfileInfoKHR *vp9_profile = &prof->vp9_profile;
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2024-01-19 10:49:02 +10:00
|
|
|
VkVideoDecodeAV1ProfileInfoKHR *av1_profile = &prof->av1_profile;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt);
|
|
|
|
|
if (!desc)
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
|
|
|
|
|
if (avctx->codec_id == AV_CODEC_ID_H264) {
|
|
|
|
|
dec_caps->pNext = h264_caps;
|
|
|
|
|
usage->pNext = h264_profile;
|
|
|
|
|
h264_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR;
|
2023-06-14 05:28:03 +02:00
|
|
|
|
|
|
|
|
/* Vulkan transmits all the constrant_set flags, rather than wanting them
|
|
|
|
|
* merged in the profile IDC */
|
2023-09-02 14:57:41 +02:00
|
|
|
h264_profile->stdProfileIdc = cur_profile & ~(AV_PROFILE_H264_CONSTRAINED |
|
|
|
|
|
AV_PROFILE_H264_INTRA);
|
2023-06-14 05:28:03 +02:00
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
h264_profile->pictureLayout = avctx->field_order == AV_FIELD_UNKNOWN ||
|
|
|
|
|
avctx->field_order == AV_FIELD_PROGRESSIVE ?
|
|
|
|
|
VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR :
|
|
|
|
|
VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_KHR;
|
|
|
|
|
} else if (avctx->codec_id == AV_CODEC_ID_H265) {
|
|
|
|
|
dec_caps->pNext = h265_caps;
|
|
|
|
|
usage->pNext = h265_profile;
|
|
|
|
|
h265_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR;
|
|
|
|
|
h265_profile->stdProfileIdc = cur_profile;
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
} 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;
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2023-02-17 04:09:16 +01:00
|
|
|
} else if (avctx->codec_id == AV_CODEC_ID_AV1) {
|
2023-05-29 23:16:33 +02:00
|
|
|
dec_caps->pNext = av1_caps;
|
2023-02-17 04:09:16 +01:00
|
|
|
usage->pNext = av1_profile;
|
2024-01-19 10:49:02 +10:00
|
|
|
av1_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_KHR;
|
|
|
|
|
av1_profile->stdProfile = cur_profile;
|
|
|
|
|
av1_profile->filmGrainSupport = !(avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN);
|
2023-01-16 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usage->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_USAGE_INFO_KHR;
|
|
|
|
|
usage->videoUsageHints = VK_VIDEO_DECODE_USAGE_DEFAULT_KHR;
|
|
|
|
|
|
|
|
|
|
profile->sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR;
|
|
|
|
|
profile->pNext = usage;
|
2024-03-07 01:08:16 +01:00
|
|
|
profile->videoCodecOperation = vk_desc->decode_op;
|
2023-01-16 07:23:27 +01:00
|
|
|
profile->chromaSubsampling = ff_vk_subsampling_from_av_desc(desc);
|
|
|
|
|
profile->lumaBitDepth = ff_vk_depth_from_av_depth(desc->comp[0].depth);
|
|
|
|
|
profile->chromaBitDepth = profile->lumaBitDepth;
|
|
|
|
|
|
|
|
|
|
profile_list->sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR;
|
|
|
|
|
profile_list->profileCount = 1;
|
|
|
|
|
profile_list->pProfiles = profile;
|
|
|
|
|
|
|
|
|
|
/* Get the capabilities of the decoder for the given profile */
|
|
|
|
|
caps->sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR;
|
|
|
|
|
caps->pNext = dec_caps;
|
|
|
|
|
dec_caps->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_CAPABILITIES_KHR;
|
|
|
|
|
/* dec_caps->pNext already filled in */
|
|
|
|
|
|
|
|
|
|
return vk->GetPhysicalDeviceVideoCapabilitiesKHR(hwctx->phys_dev, profile,
|
|
|
|
|
caps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_ref,
|
|
|
|
|
enum AVPixelFormat *pix_fmt, VkFormat *vk_fmt,
|
2023-06-13 06:10:20 +02:00
|
|
|
FFVulkanDecodeProfileData *prof,
|
2023-01-16 07:23:27 +01:00
|
|
|
int *dpb_dedicate)
|
|
|
|
|
{
|
|
|
|
|
VkResult ret;
|
|
|
|
|
int max_level, base_profile, cur_profile;
|
2024-03-07 01:08:16 +01:00
|
|
|
const FFVulkanDecodeDescriptor *vk_desc = get_codecdesc(avctx->codec_id);
|
2023-01-16 07:23:27 +01:00
|
|
|
AVHWFramesContext *frames = (AVHWFramesContext *)frames_ref->data;
|
|
|
|
|
AVHWDeviceContext *device = (AVHWDeviceContext *)frames->device_ref->data;
|
|
|
|
|
AVVulkanDeviceContext *hwctx = device->hwctx;
|
|
|
|
|
enum AVPixelFormat source_format;
|
|
|
|
|
enum AVPixelFormat best_format;
|
|
|
|
|
VkFormat best_vkfmt;
|
|
|
|
|
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2023-09-13 02:04:09 +02:00
|
|
|
FFVulkanDecodeShared *ctx = dec->shared_ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
FFVulkanFunctions *vk = &ctx->s.vkfn;
|
|
|
|
|
|
2023-06-13 06:10:20 +02:00
|
|
|
VkVideoCapabilitiesKHR *caps = &ctx->caps;
|
|
|
|
|
VkVideoDecodeCapabilitiesKHR *dec_caps = &ctx->dec_caps;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
VkVideoDecodeH264CapabilitiesKHR h264_caps = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_CAPABILITIES_KHR,
|
|
|
|
|
};
|
|
|
|
|
VkVideoDecodeH265CapabilitiesKHR h265_caps = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_KHR,
|
|
|
|
|
};
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
VkVideoDecodeVP9CapabilitiesKHR vp9_caps = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_VP9_CAPABILITIES_KHR,
|
|
|
|
|
};
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2024-01-19 10:49:02 +10:00
|
|
|
VkVideoDecodeAV1CapabilitiesKHR av1_caps = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_CAPABILITIES_KHR,
|
2023-02-17 04:09:16 +01:00
|
|
|
};
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
VkPhysicalDeviceVideoFormatInfoKHR fmt_info = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR,
|
|
|
|
|
.pNext = &prof->profile_list,
|
|
|
|
|
};
|
|
|
|
|
VkVideoFormatPropertiesKHR *ret_info;
|
|
|
|
|
uint32_t nb_out_fmts = 0;
|
|
|
|
|
|
2024-03-03 12:48:17 +01:00
|
|
|
if (!(vk_desc->decode_extension & ctx->s.extensions)) {
|
2023-01-16 07:23:27 +01:00
|
|
|
av_log(avctx, AV_LOG_ERROR, "Device does not support decoding %s!\n",
|
|
|
|
|
avcodec_get_name(avctx->codec_id));
|
|
|
|
|
return AVERROR(ENOSYS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur_profile = avctx->profile;
|
2023-09-02 14:57:41 +02:00
|
|
|
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 :
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
avctx->codec_id == AV_CODEC_ID_VP9 ? STD_VIDEO_VP9_PROFILE_0 :
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2024-01-19 10:49:02 +10:00
|
|
|
avctx->codec_id == AV_CODEC_ID_AV1 ? STD_VIDEO_AV1_PROFILE_MAIN :
|
2023-01-16 07:23:27 +01:00
|
|
|
0;
|
|
|
|
|
|
2024-03-07 01:08:16 +01:00
|
|
|
ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_desc,
|
2023-01-16 07:23:27 +01:00
|
|
|
&h264_caps,
|
|
|
|
|
&h265_caps,
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
&vp9_caps,
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2023-02-17 04:09:16 +01:00
|
|
|
&av1_caps,
|
2023-06-13 06:10:20 +02:00
|
|
|
caps,
|
|
|
|
|
dec_caps,
|
2023-01-16 07:23:27 +01:00
|
|
|
cur_profile);
|
|
|
|
|
if (ret == VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR &&
|
|
|
|
|
avctx->flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH &&
|
|
|
|
|
avctx->profile != base_profile) {
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "%s profile %s not supported, attempting "
|
|
|
|
|
"again with profile %s\n",
|
|
|
|
|
avcodec_get_name(avctx->codec_id),
|
|
|
|
|
avcodec_profile_name(avctx->codec_id, cur_profile),
|
|
|
|
|
avcodec_profile_name(avctx->codec_id, base_profile));
|
|
|
|
|
cur_profile = base_profile;
|
2024-03-07 01:08:16 +01:00
|
|
|
ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_desc,
|
2023-01-16 07:23:27 +01:00
|
|
|
&h264_caps,
|
|
|
|
|
&h265_caps,
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
&vp9_caps,
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2023-02-17 04:09:16 +01:00
|
|
|
&av1_caps,
|
2023-06-13 06:10:20 +02:00
|
|
|
caps,
|
|
|
|
|
dec_caps,
|
2023-01-16 07:23:27 +01:00
|
|
|
cur_profile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret == VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR) {
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "Unable to initialize video session: "
|
|
|
|
|
"%s profile \"%s\" not supported!\n",
|
|
|
|
|
avcodec_get_name(avctx->codec_id),
|
|
|
|
|
avcodec_profile_name(avctx->codec_id, cur_profile));
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
} else if (ret == VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR) {
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "Unable to initialize video session: "
|
|
|
|
|
"format (%s) not supported!\n",
|
|
|
|
|
av_get_pix_fmt_name(avctx->sw_pix_fmt));
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
} else if (ret == VK_ERROR_FEATURE_NOT_PRESENT ||
|
|
|
|
|
ret == VK_ERROR_FORMAT_NOT_SUPPORTED) {
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
} else if (ret != VK_SUCCESS) {
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-06 06:15:32 +02:00
|
|
|
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) :
|
2025-08-08 21:15:56 +09:00
|
|
|
#if CONFIG_VP9_VULKAN_HWACCEL
|
2025-03-27 12:50:30 +00:00
|
|
|
avctx->codec_id == AV_CODEC_ID_VP9 ? vp9_caps.maxLevel :
|
2025-08-08 21:15:56 +09:00
|
|
|
#endif
|
2024-01-19 10:49:02 +10:00
|
|
|
avctx->codec_id == AV_CODEC_ID_AV1 ? av1_caps.maxLevel :
|
2023-01-16 07:23:27 +01:00
|
|
|
0;
|
|
|
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "Decoder capabilities for %s profile \"%s\":\n",
|
|
|
|
|
avcodec_get_name(avctx->codec_id),
|
|
|
|
|
avcodec_profile_name(avctx->codec_id, cur_profile));
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Maximum level: %i (stream %i)\n",
|
|
|
|
|
max_level, avctx->level);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Width: from %i to %i\n",
|
|
|
|
|
caps->minCodedExtent.width, caps->maxCodedExtent.width);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Height: from %i to %i\n",
|
|
|
|
|
caps->minCodedExtent.height, caps->maxCodedExtent.height);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Width alignment: %i\n",
|
|
|
|
|
caps->pictureAccessGranularity.width);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Height alignment: %i\n",
|
|
|
|
|
caps->pictureAccessGranularity.height);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Bitstream offset alignment: %"PRIu64"\n",
|
|
|
|
|
caps->minBitstreamBufferOffsetAlignment);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Bitstream size alignment: %"PRIu64"\n",
|
|
|
|
|
caps->minBitstreamBufferSizeAlignment);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Maximum references: %u\n",
|
|
|
|
|
caps->maxDpbSlots);
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Maximum active references: %u\n",
|
|
|
|
|
caps->maxActiveReferencePictures);
|
2023-08-23 22:06:00 +02:00
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Codec header name: '%s' (driver), '%s' (compiled)\n",
|
|
|
|
|
caps->stdHeaderVersion.extensionName,
|
2024-03-07 01:08:16 +01:00
|
|
|
vk_desc->ext_props.extensionName);
|
2023-01-16 07:23:27 +01:00
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Codec header version: %i.%i.%i (driver), %i.%i.%i (compiled)\n",
|
|
|
|
|
CODEC_VER(caps->stdHeaderVersion.specVersion),
|
2024-03-07 01:08:16 +01:00
|
|
|
CODEC_VER(vk_desc->ext_props.specVersion));
|
2023-01-16 07:23:27 +01:00
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Decode modes:%s%s%s\n",
|
|
|
|
|
dec_caps->flags ? "" :
|
|
|
|
|
" invalid",
|
|
|
|
|
dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR ?
|
|
|
|
|
" reuse_dst_dpb" : "",
|
|
|
|
|
dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR ?
|
|
|
|
|
" dedicated_dpb" : "");
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, " Capability flags:%s%s%s\n",
|
|
|
|
|
caps->flags ? "" :
|
|
|
|
|
" none",
|
|
|
|
|
caps->flags & VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR ?
|
|
|
|
|
" protected" : "",
|
|
|
|
|
caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR ?
|
|
|
|
|
" separate_references" : "");
|
|
|
|
|
|
|
|
|
|
/* Check if decoding is possible with the given parameters */
|
2023-10-24 06:33:07 +02:00
|
|
|
if (avctx->coded_width < caps->minCodedExtent.width ||
|
|
|
|
|
avctx->coded_height < caps->minCodedExtent.height ||
|
|
|
|
|
avctx->coded_width > caps->maxCodedExtent.width ||
|
|
|
|
|
avctx->coded_height > caps->maxCodedExtent.height)
|
2023-01-16 07:23:27 +01:00
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
|
|
|
|
|
if (!(avctx->hwaccel_flags & AV_HWACCEL_FLAG_IGNORE_LEVEL) &&
|
|
|
|
|
avctx->level > max_level)
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
|
|
|
|
|
/* Some basic sanity checking */
|
|
|
|
|
if (!(dec_caps->flags & (VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR |
|
|
|
|
|
VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR))) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Buggy driver signals invalid decoding mode: neither "
|
|
|
|
|
"VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR nor "
|
|
|
|
|
"VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR are set!\n");
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
} else if ((dec_caps->flags & (VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR |
|
|
|
|
|
VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR) ==
|
|
|
|
|
VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR) &&
|
|
|
|
|
!(caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR)) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Cannot initialize Vulkan decoding session, buggy driver: "
|
|
|
|
|
"VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR set "
|
|
|
|
|
"but VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR is unset!\n");
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dec->dedicated_dpb = !(dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR);
|
2024-08-31 01:43:48 +00:00
|
|
|
ctx->common.layered_dpb = !dec->dedicated_dpb ? 0 :
|
|
|
|
|
!(caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR);
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
if (dec->dedicated_dpb) {
|
|
|
|
|
fmt_info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
|
|
|
|
|
} else {
|
|
|
|
|
fmt_info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
|
|
|
|
|
VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR |
|
|
|
|
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
|
|
|
|
VK_IMAGE_USAGE_SAMPLED_BIT;
|
2024-08-14 17:25:00 +02:00
|
|
|
|
|
|
|
|
if (ctx->s.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
|
|
|
|
|
FF_VK_EXT_VIDEO_MAINTENANCE_1))
|
|
|
|
|
fmt_info.imageUsage |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
|
2023-01-16 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the format of the images necessary */
|
|
|
|
|
ret = vk->GetPhysicalDeviceVideoFormatPropertiesKHR(hwctx->phys_dev,
|
|
|
|
|
&fmt_info,
|
|
|
|
|
&nb_out_fmts, NULL);
|
|
|
|
|
if (ret == VK_ERROR_FORMAT_NOT_SUPPORTED ||
|
|
|
|
|
(!nb_out_fmts && ret == VK_SUCCESS)) {
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
} else if (ret != VK_SUCCESS) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Unable to get Vulkan format properties: %s!\n",
|
|
|
|
|
ff_vk_ret2str(ret));
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret_info = av_mallocz(sizeof(*ret_info)*nb_out_fmts);
|
|
|
|
|
if (!ret_info)
|
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < nb_out_fmts; i++)
|
|
|
|
|
ret_info[i].sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR;
|
|
|
|
|
|
|
|
|
|
ret = vk->GetPhysicalDeviceVideoFormatPropertiesKHR(hwctx->phys_dev,
|
|
|
|
|
&fmt_info,
|
|
|
|
|
&nb_out_fmts, ret_info);
|
|
|
|
|
if (ret == VK_ERROR_FORMAT_NOT_SUPPORTED ||
|
|
|
|
|
(!nb_out_fmts && ret == VK_SUCCESS)) {
|
|
|
|
|
av_free(ret_info);
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
} else if (ret != VK_SUCCESS) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Unable to get Vulkan format properties: %s!\n",
|
|
|
|
|
ff_vk_ret2str(ret));
|
|
|
|
|
av_free(ret_info);
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find a format to use */
|
|
|
|
|
*pix_fmt = best_format = AV_PIX_FMT_NONE;
|
|
|
|
|
*vk_fmt = best_vkfmt = VK_FORMAT_UNDEFINED;
|
|
|
|
|
source_format = avctx->sw_pix_fmt;
|
|
|
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_DEBUG, "Choosing best pixel format for decoding from %i:\n", nb_out_fmts);
|
|
|
|
|
for (int i = 0; i < nb_out_fmts; i++) {
|
|
|
|
|
enum AVPixelFormat tmp = ff_vk_pix_fmt_from_vkfmt(ret_info[i].format);
|
|
|
|
|
if (tmp == AV_PIX_FMT_NONE) {
|
|
|
|
|
av_log(avctx, AV_LOG_WARNING, "Invalid/unknown Vulkan format %i!\n", ret_info[i].format);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
best_format = av_find_best_pix_fmt_of_2(tmp, best_format, source_format, 0, NULL);
|
|
|
|
|
if (tmp == best_format)
|
|
|
|
|
best_vkfmt = ret_info[i].format;
|
|
|
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_DEBUG, " %s%s (Vulkan ID: %i)\n",
|
|
|
|
|
av_get_pix_fmt_name(tmp), tmp == best_format ? "*" : "",
|
|
|
|
|
ret_info[i].format);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
av_free(ret_info);
|
|
|
|
|
|
|
|
|
|
if (best_format == AV_PIX_FMT_NONE) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "No valid/compatible pixel format found for decoding!\n");
|
|
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
} else {
|
|
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "Chosen frame pixfmt: %s (Vulkan ID: %i)\n",
|
|
|
|
|
av_get_pix_fmt_name(best_format), best_vkfmt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*pix_fmt = best_format;
|
|
|
|
|
*vk_fmt = best_vkfmt;
|
|
|
|
|
|
|
|
|
|
*dpb_dedicate = dec->dedicated_dpb;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-13 06:10:20 +02:00
|
|
|
static void free_profile_data(AVHWFramesContext *hwfc)
|
|
|
|
|
{
|
|
|
|
|
av_free(hwfc->user_opaque);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx)
|
|
|
|
|
{
|
2024-12-03 18:21:57 +09:00
|
|
|
VkFormat vkfmt = VK_FORMAT_UNDEFINED;
|
2023-05-29 03:50:25 +02:00
|
|
|
int err, dedicated_dpb;
|
2023-01-16 07:23:27 +01:00
|
|
|
AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hw_frames_ctx->data;
|
|
|
|
|
AVVulkanFramesContext *hwfc = frames_ctx->hwctx;
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2024-12-03 18:21:57 +09:00
|
|
|
FFVulkanDecodeProfileData *prof = NULL;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
err = vulkan_decode_bootstrap(avctx, hw_frames_ctx);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
frames_ctx->sw_format = avctx->sw_pix_fmt;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
if (!DECODER_IS_SDR(avctx->codec_id)) {
|
|
|
|
|
prof = av_mallocz(sizeof(FFVulkanDecodeProfileData));
|
|
|
|
|
if (!prof)
|
|
|
|
|
return AVERROR(ENOMEM);
|
2023-06-13 06:10:20 +02:00
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
err = vulkan_decode_get_profile(avctx, hw_frames_ctx,
|
|
|
|
|
&frames_ctx->sw_format, &vkfmt,
|
|
|
|
|
prof, &dedicated_dpb);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
av_free(prof);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frames_ctx->user_opaque = prof;
|
|
|
|
|
frames_ctx->free = free_profile_data;
|
|
|
|
|
|
|
|
|
|
hwfc->create_pnext = &prof->profile_list;
|
2025-03-10 03:04:39 +00:00
|
|
|
} else {
|
|
|
|
|
switch (frames_ctx->sw_format) {
|
|
|
|
|
case AV_PIX_FMT_GBRAP16:
|
|
|
|
|
/* This should be more efficient for downloading and using */
|
|
|
|
|
frames_ctx->sw_format = AV_PIX_FMT_RGBA64;
|
|
|
|
|
break;
|
|
|
|
|
case AV_PIX_FMT_GBRP10:
|
|
|
|
|
/* This saves memory bandwidth when downloading */
|
|
|
|
|
frames_ctx->sw_format = AV_PIX_FMT_X2BGR10;
|
|
|
|
|
break;
|
|
|
|
|
case AV_PIX_FMT_BGR0:
|
|
|
|
|
/* mpv has issues with bgr0 mapping, so just remap it */
|
|
|
|
|
frames_ctx->sw_format = AV_PIX_FMT_RGB0;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-12-03 18:21:57 +09:00
|
|
|
}
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2023-10-24 06:33:07 +02:00
|
|
|
frames_ctx->width = avctx->coded_width;
|
|
|
|
|
frames_ctx->height = avctx->coded_height;
|
2023-01-16 07:23:27 +01:00
|
|
|
frames_ctx->format = AV_PIX_FMT_VULKAN;
|
|
|
|
|
|
|
|
|
|
hwfc->format[0] = vkfmt;
|
|
|
|
|
hwfc->tiling = VK_IMAGE_TILING_OPTIMAL;
|
2025-04-18 16:27:19 +02:00
|
|
|
hwfc->usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
|
|
|
|
VK_IMAGE_USAGE_STORAGE_BIT |
|
2024-12-03 18:21:57 +09:00
|
|
|
VK_IMAGE_USAGE_SAMPLED_BIT;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
if (prof) {
|
|
|
|
|
FFVulkanDecodeShared *ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
hwfc->usage |= VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR;
|
|
|
|
|
if (!dec->dedicated_dpb)
|
|
|
|
|
hwfc->usage |= VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
|
|
|
|
|
|
|
|
|
|
ctx = dec->shared_ctx;
|
|
|
|
|
if (ctx->s.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
|
|
|
|
|
FF_VK_EXT_VIDEO_MAINTENANCE_1))
|
|
|
|
|
hwfc->usage |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
|
|
|
|
|
}
|
2024-08-14 17:25:00 +02:00
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-15 00:29:58 +02:00
|
|
|
static void vk_decode_free_params(void *opaque, uint8_t *data)
|
2023-01-16 07:23:27 +01:00
|
|
|
{
|
|
|
|
|
FFVulkanDecodeShared *ctx = opaque;
|
|
|
|
|
FFVulkanFunctions *vk = &ctx->s.vkfn;
|
|
|
|
|
VkVideoSessionParametersKHR *par = (VkVideoSessionParametersKHR *)data;
|
|
|
|
|
vk->DestroyVideoSessionParametersKHR(ctx->s.hwctx->act_dev, *par,
|
|
|
|
|
ctx->s.hwctx->alloc);
|
|
|
|
|
av_free(par);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-15 00:29:58 +02:00
|
|
|
int ff_vk_decode_create_params(AVBufferRef **par_ref, void *logctx, FFVulkanDecodeShared *ctx,
|
|
|
|
|
const VkVideoSessionParametersCreateInfoKHR *session_params_create)
|
|
|
|
|
{
|
|
|
|
|
VkVideoSessionParametersKHR *par = av_malloc(sizeof(*par));
|
|
|
|
|
const FFVulkanFunctions *vk = &ctx->s.vkfn;
|
|
|
|
|
VkResult ret;
|
|
|
|
|
|
|
|
|
|
if (!par)
|
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
|
|
|
|
|
/* Create session parameters */
|
|
|
|
|
ret = vk->CreateVideoSessionParametersKHR(ctx->s.hwctx->act_dev, session_params_create,
|
|
|
|
|
ctx->s.hwctx->alloc, par);
|
|
|
|
|
if (ret != VK_SUCCESS) {
|
|
|
|
|
av_log(logctx, AV_LOG_ERROR, "Unable to create Vulkan video session parameters: %s!\n",
|
|
|
|
|
ff_vk_ret2str(ret));
|
|
|
|
|
av_free(par);
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
}
|
|
|
|
|
*par_ref = av_buffer_create((uint8_t *)par, sizeof(*par),
|
|
|
|
|
vk_decode_free_params, ctx, 0);
|
|
|
|
|
if (!*par_ref) {
|
|
|
|
|
vk_decode_free_params(ctx, (uint8_t *)par);
|
|
|
|
|
return AVERROR(ENOMEM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
int ff_vk_decode_uninit(AVCodecContext *avctx)
|
|
|
|
|
{
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
2023-07-19 05:39:07 +02:00
|
|
|
|
2023-09-15 01:51:49 +02:00
|
|
|
av_freep(&dec->hevc_headers);
|
2023-01-16 07:23:27 +01:00
|
|
|
av_buffer_unref(&dec->session_params);
|
2024-10-15 22:37:56 +02:00
|
|
|
av_refstruct_unref(&dec->shared_ctx);
|
2023-06-20 03:37:34 +02:00
|
|
|
av_freep(&dec->slice_off);
|
2023-01-16 07:23:27 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-14 13:23:36 +01:00
|
|
|
static int create_empty_session_parameters(AVCodecContext *avctx,
|
|
|
|
|
FFVulkanDecodeShared *ctx)
|
2023-01-16 07:23:27 +01:00
|
|
|
{
|
|
|
|
|
VkResult ret;
|
2025-03-14 13:23:36 +01:00
|
|
|
FFVulkanContext *s = &ctx->s;
|
|
|
|
|
FFVulkanFunctions *vk = &s->vkfn;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
VkVideoDecodeH264SessionParametersCreateInfoKHR h264_params = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR,
|
|
|
|
|
};
|
|
|
|
|
VkVideoDecodeH265SessionParametersCreateInfoKHR h265_params = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR,
|
|
|
|
|
};
|
2024-01-19 10:49:02 +10:00
|
|
|
StdVideoAV1SequenceHeader av1_empty_seq = { 0 };
|
|
|
|
|
VkVideoDecodeAV1SessionParametersCreateInfoKHR av1_params = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_CREATE_INFO_KHR,
|
|
|
|
|
.pStdSequenceHeader = &av1_empty_seq,
|
2023-02-17 04:09:16 +01:00
|
|
|
};
|
2023-01-16 07:23:27 +01:00
|
|
|
VkVideoSessionParametersCreateInfoKHR session_params_create = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR,
|
|
|
|
|
.pNext = avctx->codec_id == AV_CODEC_ID_H264 ? (void *)&h264_params :
|
|
|
|
|
avctx->codec_id == AV_CODEC_ID_HEVC ? (void *)&h265_params :
|
2023-02-17 04:09:16 +01:00
|
|
|
avctx->codec_id == AV_CODEC_ID_AV1 ? (void *)&av1_params :
|
2023-01-16 07:23:27 +01:00
|
|
|
NULL,
|
2025-03-14 13:23:36 +01:00
|
|
|
.videoSession = ctx->common.session,
|
2023-01-16 07:23:27 +01:00
|
|
|
};
|
2025-03-14 13:23:36 +01:00
|
|
|
|
2025-03-27 12:50:30 +00:00
|
|
|
if (avctx->codec_id == AV_CODEC_ID_VP9)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2025-03-14 13:23:36 +01:00
|
|
|
ret = vk->CreateVideoSessionParametersKHR(s->hwctx->act_dev, &session_params_create,
|
|
|
|
|
s->hwctx->alloc, &ctx->empty_session_params);
|
|
|
|
|
if (ret != VK_SUCCESS) {
|
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Unable to create empty Vulkan video session parameters: %s!\n",
|
|
|
|
|
ff_vk_ret2str(ret));
|
|
|
|
|
return AVERROR_EXTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ff_vk_decode_init(AVCodecContext *avctx)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
|
|
|
|
|
FFVulkanDecodeShared *ctx;
|
|
|
|
|
FFVulkanContext *s;
|
|
|
|
|
int async_depth;
|
|
|
|
|
const VkVideoProfileInfoKHR *profile;
|
|
|
|
|
const FFVulkanDecodeDescriptor *vk_desc;
|
|
|
|
|
const VkPhysicalDeviceDriverProperties *driver_props;
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
VkVideoSessionCreateInfoKHR session_create = {
|
|
|
|
|
.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
err = ff_decode_get_hw_frames_ctx(avctx, AV_HWDEVICE_TYPE_VULKAN);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/* Initialize contexts */
|
2023-09-13 02:04:09 +02:00
|
|
|
ctx = dec->shared_ctx;
|
2023-01-16 07:23:27 +01:00
|
|
|
s = &ctx->s;
|
|
|
|
|
|
2024-09-01 02:36:10 +00:00
|
|
|
err = ff_vk_init(s, avctx, NULL, avctx->hw_frames_ctx);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-12-03 18:21:57 +09:00
|
|
|
vk_desc = get_codecdesc(avctx->codec_id);
|
|
|
|
|
|
2023-07-19 05:39:07 +02:00
|
|
|
profile = get_video_profile(ctx, avctx->codec_id);
|
2024-12-03 18:21:57 +09:00
|
|
|
if ((vk_desc->queue_flags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) && !profile) {
|
2023-07-19 05:39:07 +02:00
|
|
|
av_log(avctx, AV_LOG_ERROR, "Video profile missing from frames context!");
|
2023-06-13 06:10:20 +02:00
|
|
|
return AVERROR(EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
/* Create queue context */
|
2024-03-07 01:08:16 +01:00
|
|
|
vk_desc = get_codecdesc(avctx->codec_id);
|
2024-12-03 15:19:35 +09:00
|
|
|
ctx->qf = ff_vk_qf_find(s, vk_desc->queue_flags, vk_desc->decode_op);
|
2024-12-03 15:31:49 +09:00
|
|
|
if (!ctx->qf) {
|
2024-08-11 03:27:46 +02:00
|
|
|
av_log(avctx, AV_LOG_ERROR, "Decoding of %s is not supported by this device\n",
|
|
|
|
|
avcodec_get_name(avctx->codec_id));
|
|
|
|
|
return err;
|
2023-01-16 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
2024-12-03 15:31:49 +09:00
|
|
|
session_create.queueFamilyIndex = ctx->qf->idx;
|
2023-06-13 06:10:20 +02:00
|
|
|
session_create.maxCodedExtent = ctx->caps.maxCodedExtent;
|
|
|
|
|
session_create.maxDpbSlots = ctx->caps.maxDpbSlots;
|
|
|
|
|
session_create.maxActiveReferencePictures = ctx->caps.maxActiveReferencePictures;
|
2023-01-16 07:23:27 +01:00
|
|
|
session_create.pictureFormat = s->hwfc->format[0];
|
|
|
|
|
session_create.referencePictureFormat = session_create.pictureFormat;
|
2024-03-07 01:08:16 +01:00
|
|
|
session_create.pStdHeaderVersion = &vk_desc->ext_props;
|
2023-07-19 05:39:07 +02:00
|
|
|
session_create.pVideoProfile = profile;
|
2025-03-14 13:23:36 +01:00
|
|
|
#ifdef VK_KHR_video_maintenance2
|
|
|
|
|
if (ctx->s.extensions & FF_VK_EXT_VIDEO_MAINTENANCE_2)
|
|
|
|
|
session_create.flags = VK_VIDEO_SESSION_CREATE_INLINE_SESSION_PARAMETERS_BIT_KHR;
|
|
|
|
|
#endif
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2023-07-19 05:39:07 +02:00
|
|
|
/* Create decode exec context for this specific main thread.
|
2023-06-07 02:59:55 +02:00
|
|
|
* 2 async contexts per thread was experimentally determined to be optimal
|
|
|
|
|
* for a majority of streams. */
|
2025-03-07 21:07:41 +00:00
|
|
|
async_depth = 2*ctx->qf->num;
|
|
|
|
|
/* We don't need more than 2 per thread context */
|
|
|
|
|
async_depth = FFMIN(async_depth, 2*avctx->thread_count);
|
|
|
|
|
/* Make sure there are enough async contexts for each thread */
|
|
|
|
|
async_depth = FFMAX(async_depth, avctx->thread_count);
|
|
|
|
|
|
2024-12-03 15:31:49 +09:00
|
|
|
err = ff_vk_exec_pool_init(s, ctx->qf, &ctx->exec_pool,
|
2025-03-07 21:07:41 +00:00
|
|
|
async_depth, 0, 0, 0, profile);
|
2023-01-16 07:23:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
goto fail;
|
|
|
|
|
|
2025-03-14 13:23:36 +01:00
|
|
|
if (!DECODER_IS_SDR(avctx->codec_id)) {
|
2024-12-03 18:21:57 +09:00
|
|
|
err = ff_vk_video_common_init(avctx, s, &ctx->common, &session_create);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
/* If doing an out-of-place decoding, create a DPB pool */
|
2023-02-17 04:09:16 +01:00
|
|
|
if (dec->dedicated_dpb || avctx->codec_id == AV_CODEC_ID_AV1) {
|
2023-01-16 07:23:27 +01:00
|
|
|
AVHWFramesContext *dpb_frames;
|
|
|
|
|
AVVulkanFramesContext *dpb_hwfc;
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
ctx->common.dpb_hwfc_ref = av_hwframe_ctx_alloc(s->frames->device_ref);
|
|
|
|
|
if (!ctx->common.dpb_hwfc_ref) {
|
2023-01-16 07:23:27 +01:00
|
|
|
err = AVERROR(ENOMEM);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
dpb_frames = (AVHWFramesContext *)ctx->common.dpb_hwfc_ref->data;
|
2023-01-16 07:23:27 +01:00
|
|
|
dpb_frames->format = s->frames->format;
|
|
|
|
|
dpb_frames->sw_format = s->frames->sw_format;
|
2023-10-24 06:33:07 +02:00
|
|
|
dpb_frames->width = avctx->coded_width;
|
|
|
|
|
dpb_frames->height = avctx->coded_height;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
dpb_hwfc = dpb_frames->hwctx;
|
2023-07-19 05:39:07 +02:00
|
|
|
dpb_hwfc->create_pnext = (void *)ff_vk_find_struct(ctx->s.hwfc->create_pnext,
|
|
|
|
|
VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
|
2023-01-16 07:23:27 +01:00
|
|
|
dpb_hwfc->format[0] = s->hwfc->format[0];
|
|
|
|
|
dpb_hwfc->tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
|
dpb_hwfc->usage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
|
|
|
|
|
VK_IMAGE_USAGE_SAMPLED_BIT; /* Shuts validator up. */
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
if (ctx->common.layered_dpb)
|
2023-06-13 06:10:20 +02:00
|
|
|
dpb_hwfc->nb_layers = ctx->caps.maxDpbSlots;
|
2023-01-16 07:23:27 +01:00
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
err = av_hwframe_ctx_init(ctx->common.dpb_hwfc_ref);
|
2023-01-16 07:23:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
goto fail;
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
if (ctx->common.layered_dpb) {
|
|
|
|
|
ctx->common.layered_frame = vk_get_dpb_pool(ctx);
|
|
|
|
|
if (!ctx->common.layered_frame) {
|
2023-01-16 07:23:27 +01:00
|
|
|
err = AVERROR(ENOMEM);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-31 01:43:48 +00:00
|
|
|
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], 1);
|
2023-01-16 07:23:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-14 13:23:36 +01:00
|
|
|
if (!DECODER_IS_SDR(avctx->codec_id)) {
|
|
|
|
|
if (!(ctx->s.extensions & FF_VK_EXT_VIDEO_MAINTENANCE_2)) {
|
|
|
|
|
err = create_empty_session_parameters(avctx, ctx);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2024-12-03 18:21:57 +09:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* For SDR decoders, this alignment value will be 0. Since this will make
|
|
|
|
|
* add_slice() malfunction, set it to a sane default value. */
|
|
|
|
|
ctx->caps.minBitstreamBufferSizeAlignment = AV_INPUT_BUFFER_PADDING_SIZE;
|
2023-01-16 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
2024-04-14 14:11:44 +02:00
|
|
|
driver_props = &dec->shared_ctx->s.driver_props;
|
|
|
|
|
if (driver_props->driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY &&
|
|
|
|
|
driver_props->conformanceVersion.major == 1 &&
|
|
|
|
|
driver_props->conformanceVersion.minor == 3 &&
|
|
|
|
|
driver_props->conformanceVersion.subminor == 8 &&
|
|
|
|
|
driver_props->conformanceVersion.patch < 3)
|
|
|
|
|
dec->quirk_av1_offset = 1;
|
|
|
|
|
|
2023-01-16 07:23:27 +01:00
|
|
|
ff_vk_decode_flush(avctx);
|
|
|
|
|
|
2025-08-01 22:43:23 +02:00
|
|
|
av_log(avctx, AV_LOG_VERBOSE, "Vulkan decoder initialization successful\n");
|
2023-01-16 07:23:27 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
ff_vk_decode_uninit(avctx);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|