vulkan: enable video maintenance2 for inline session parameters

.. in decoders.

Inline session parameters allows to not create session parameters handlers for
every new stream parameters (such as SPS and PPS for H.264, for example), but
instead to pass them as a chained structure in the decoding main structure. This
is completely align with GStreamer decoder base classes.

Even that the previous approach is kept, if the devices doesn't support video
maintenance2, it shows a lot of validation errors.

Also it was required to add another parameter when enabling extension to verify
if the extension is linked with a device feature and if it is enabled.

Bump Vulkan API (and driver version for both decoders and encoders) to 1.4.306

Also bumped the ABI_CHECK_TAG because the CI finally catches up with the vulkan
video symbols that are not exposed by a public header (tough they are binary
public).

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9585>
This commit is contained in:
Víctor Manuel Jáquez Leal 2025-08-20 14:24:17 +02:00 committed by GStreamer Marge Bot
parent 8ce2ed05ac
commit fc647552fa
12 changed files with 154 additions and 35 deletions

View File

@ -11,6 +11,6 @@ variables:
CHECKS_TAG: '2025-05-24.0'
ABI_CHECK_TAG: '2025-08-05.0'
ABI_CHECK_TAG: '2025-08-20.0'
WINDOWS_TAG: '2025-08-10.0'

View File

@ -27,6 +27,7 @@
#include <gst/vulkan/vulkan.h>
#include "gst/vulkan/gstvkdecoder-private.h"
#include "gst/vulkan/gstvkphysicaldevice-private.h"
#include "gstvulkanelements.h"
@ -1291,6 +1292,12 @@ gst_vulkan_h264_decoder_end_picture (GstH264Decoder * decoder,
GstVulkanH264Decoder *self = GST_VULKAN_H264_DECODER (decoder);
GstVulkanH264Picture *pic;
GError *error = NULL;
VkVideoDecodeH264InlineSessionParametersInfoKHR inline_params = {
.sType =
VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_INLINE_SESSION_PARAMETERS_INFO_KHR,
.pStdSPS = &self->std_sps.sps,
.pStdPPS = &self->std_pps.pps,
};
GST_TRACE_OBJECT (self, "End picture");
@ -1300,6 +1307,11 @@ gst_vulkan_h264_decoder_end_picture (GstH264Decoder * decoder,
pic->vk_h264pic.sliceCount = pic->base.slice_offs->len - 1;
pic->vk_h264pic.pSliceOffsets = (const guint32 *) pic->base.slice_offs->data;
if ((self->
decoder->features & GST_VULKAN_DECODER_FEATURES_VIDEO_MAINTEINANCE2)
!= 0)
vk_link_struct (&pic->base.decode_info, &inline_params);
GST_LOG_OBJECT (self, "Decoding frame, %d bytes %d slices",
pic->vk_h264pic.pSliceOffsets[pic->vk_h264pic.sliceCount],
pic->vk_h264pic.sliceCount);

View File

@ -25,8 +25,9 @@
#include <gst/video/video.h>
#include <gst/vulkan/vulkan.h>
#include "gst/vulkan/gstvkdecoder-private.h"
#include "gst/vulkan/gstvkdecoder-private.h"
#include "gst/vulkan/gstvkphysicaldevice-private.h"
#include "gstvulkanelements.h"
GST_DEBUG_CATEGORY_STATIC (gst_vulkan_h265_decoder_debug);
@ -1608,6 +1609,13 @@ gst_vulkan_h265_decoder_end_picture (GstH265Decoder * decoder,
GstVulkanH265Decoder *self = GST_VULKAN_H265_DECODER (decoder);
GstVulkanH265Picture *pic;
GError *error = NULL;
VkVideoDecodeH265InlineSessionParametersInfoKHR inline_params = {
.sType =
VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_INLINE_SESSION_PARAMETERS_INFO_KHR,
.pStdSPS = &self->std_sps.sps,
.pStdPPS = &self->std_pps.pps,
.pStdVPS = &self->std_vps.vps,
};
GST_TRACE_OBJECT (self, "End picture");
@ -1621,6 +1629,11 @@ gst_vulkan_h265_decoder_end_picture (GstH265Decoder * decoder,
pic->vk_h265pic.pSliceSegmentOffsets =
(const guint32 *) pic->base.slice_offs->data;
if ((self->
decoder->features & GST_VULKAN_DECODER_FEATURES_VIDEO_MAINTEINANCE2)
!= 0)
vk_link_struct (&pic->base.decode_info, &inline_params);
GST_LOG_OBJECT (self, "Decoding frame, %d bytes %d slices",
pic->vk_h265pic.pSliceSegmentOffsets[pic->vk_h265pic.sliceSegmentCount],
pic->vk_h265pic.sliceSegmentCount);

View File

@ -116,6 +116,37 @@ gst_vulkan_decoder_class_init (GstVulkanDecoderClass * klass)
gobject_class->finalize = gst_vulkan_decoder_finalize;
}
static gboolean
_create_empty_params (GstVulkanDecoder * self, GError ** error)
{
GstVulkanDecoderParameters empty_params;
GstVulkanDecoderPrivate *priv =
gst_vulkan_decoder_get_instance_private (self);
switch (self->profile.profile.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
/* *INDENT-OFF* */
empty_params.h264 = (VkVideoDecodeH264SessionParametersCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR,
};
/* *INDENT-ON* */
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
/* *INDENT-OFF* */
empty_params.h265 = (VkVideoDecodeH265SessionParametersCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR,
};
/* *INDENT-ON* */
break;
default:
g_assert_not_reached ();
}
priv->empty_params = gst_vulkan_decoder_new_video_session_parameters (self,
&empty_params, error);
return (!priv->empty_params);
}
/**
* gst_vulkan_decoder_start:
* @self: a #GstVulkanDecoder
@ -144,7 +175,6 @@ gst_vulkan_decoder_start (GstVulkanDecoder * self,
.pNext = &profile_list,
};
VkVideoSessionCreateInfoKHR session_create;
GstVulkanDecoderParameters empty_params;
guint i, maxlevel, n_fmts, codec_idx;
GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
VkFormat vk_format = VK_FORMAT_UNDEFINED;
@ -378,30 +408,12 @@ gst_vulkan_decoder_start (GstVulkanDecoder * self,
&priv->vk, &session_create, error))
goto failed;
/* create empty codec params */
switch (self->profile.profile.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
/* *INDENT-OFF* */
empty_params.h264 = (VkVideoDecodeH264SessionParametersCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR,
};
/* *INDENT-ON* */
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
/* *INDENT-OFF* */
empty_params.h265 = (VkVideoDecodeH265SessionParametersCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR,
};
/* *INDENT-ON* */
break;
default:
g_assert_not_reached ();
if ((session_create.flags &
VK_VIDEO_SESSION_CREATE_INLINE_SESSION_PARAMETERS_BIT_KHR) == 0) {
if (!_create_empty_params (self, error))
goto failed;
}
priv->empty_params = gst_vulkan_decoder_new_video_session_parameters (self,
&empty_params, error);
if (!priv->empty_params)
goto failed;
cmd_pool = gst_vulkan_queue_create_command_pool (self->queue, error);
if (!cmd_pool)
goto failed;
@ -512,14 +524,15 @@ gst_vulkan_decoder_flush (GstVulkanDecoder * self, GError ** error)
priv = gst_vulkan_decoder_get_instance_private (self);
if (!(priv->empty_params && priv->exec))
if (!priv->exec)
return FALSE;
/* *INDENT-OFF* */
decode_start = (VkVideoBeginCodingInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR,
.videoSession = priv->session.session->handle,
.videoSessionParameters = priv->empty_params->handle,
.videoSessionParameters =
priv->empty_params ? priv->empty_params->handle : VK_NULL_HANDLE,
};
/* *INDENT-ON* */
@ -646,13 +659,14 @@ gst_vulkan_decoder_decode (GstVulkanDecoder * self,
decode_start = (VkVideoBeginCodingInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR,
.videoSession = priv->session.session->handle,
.videoSessionParameters = priv->session_params->handle,
.videoSessionParameters =
priv->session_params ? priv->session_params->handle : VK_NULL_HANDLE,
.referenceSlotCount = pic->decode_info.referenceSlotCount,
.pReferenceSlots = pic->decode_info.pReferenceSlots,
};
/* *INDENT-ON* */
if (!(priv->started && priv->session_params)) {
if (!priv->started) {
g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
"Vulkan Decoder has not started or no session parameters are set");
return FALSE;
@ -955,6 +969,11 @@ gst_vulkan_decoder_update_video_session_parameters (GstVulkanDecoder * self,
g_return_val_if_fail (GST_IS_VULKAN_DECODER (self), FALSE);
g_return_val_if_fail (params, FALSE);
/* if inline session parameters are enabled, there's no need to update session
* parameters. This function is no-op */
if ((self->features & GST_VULKAN_DECODER_FEATURES_VIDEO_MAINTEINANCE2) != 0)
return TRUE;
handle =
gst_vulkan_decoder_new_video_session_parameters (self, params, error);
if (!handle)
@ -1305,9 +1324,9 @@ gst_vulkan_decoder_new_from_queue (GstVulkanQueue * queue, guint codec)
}
/* XXX: sync with the meson version for vulkan video enabling */
if (!gst_vulkan_physical_device_check_api_version (device, 1, 3, 275)) {
if (!gst_vulkan_physical_device_check_api_version (device, 1, 4, 306)) {
GST_WARNING_OBJECT (queue,
"Driver API version [%d.%d.%d] doesn't support Video extensions",
"Driver version [%d.%d.%d] doesn't support required video extensions",
VK_VERSION_MAJOR (device->properties.apiVersion),
VK_VERSION_MINOR (device->properties.apiVersion),
VK_VERSION_PATCH (device->properties.apiVersion));
@ -1342,5 +1361,10 @@ gst_vulkan_decoder_new_from_queue (GstVulkanQueue * queue, guint codec)
decoder->queue = gst_object_ref (queue);
decoder->codec = codec;
/* physical device features getters aren't exported. This is a bitwise proxy
* for elements */
decoder->features =
gst_vulkan_physical_device_has_feature_video_maintenance2 (device);
return decoder;
}

View File

@ -34,6 +34,10 @@ G_BEGIN_DECLS
GST_VULKAN_API
GType gst_vulkan_decoder_get_type (void);
enum {
GST_VULKAN_DECODER_FEATURES_VIDEO_MAINTEINANCE2 = 1 << 0,
};
typedef struct _GstVulkanDecoder GstVulkanDecoder;
typedef struct _GstVulkanDecoderClass GstVulkanDecoderClass;
typedef struct _GstVulkanDecoderPicture GstVulkanDecoderPicture;
@ -104,6 +108,8 @@ struct _GstVulkanDecoder
gboolean dedicated_dpb;
gboolean layered_dpb;
guint32 features;
/*< private >*/
gpointer _reserved [GST_PADDING];
};

View File

@ -289,6 +289,10 @@ static const struct extension optional_extensions[] = {
OPTIONAL_VIDEO_EXTENSION (VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME,
VK_KHR_VIDEO_QUEUE_EXTENSION_NAME, video_maintenance1),
# endif
# if defined(VK_KHR_video_maintenance2)
OPTIONAL_VIDEO_EXTENSION (VK_KHR_VIDEO_MAINTENANCE_2_EXTENSION_NAME,
VK_KHR_VIDEO_QUEUE_EXTENSION_NAME, video_maintenance2),
# endif
#endif /* GST_VULKAN_HAVE_VIDEO_EXTENSIONS */
};

View File

@ -1407,9 +1407,9 @@ gst_vulkan_encoder_create_from_queue (GstVulkanQueue * queue, guint codec)
}
/* XXX: sync with the meson version for vulkan video enabling */
if (!gst_vulkan_physical_device_check_api_version (device, 1, 3, 275)) {
if (!gst_vulkan_physical_device_check_api_version (device, 1, 4, 306)) {
GST_WARNING_OBJECT (queue,
"API version %d.%d.%d doesn't support video encode extensions",
"Driver version [%d.%d.%d] doesn't support required video extensions",
VK_VERSION_MAJOR (device->properties.apiVersion),
VK_VERSION_MINOR (device->properties.apiVersion),
VK_VERSION_PATCH (device->properties.apiVersion));

View File

@ -40,6 +40,9 @@ gboolean gst_vulkan_physical_device_has_feature_timeline_semp
gboolean gst_vulkan_physical_device_has_feature_video_maintenance1
(GstVulkanPhysicalDevice * device);
gboolean gst_vulkan_physical_device_has_feature_video_maintenance2
(GstVulkanPhysicalDevice * device);
static inline void
vk_link_struct (gpointer chain, gconstpointer in)
{

View File

@ -84,6 +84,9 @@ struct _GstVulkanPhysicalDevicePrivate
#if defined (VK_KHR_video_maintenance1)
VkPhysicalDeviceVideoMaintenance1FeaturesKHR videomaintenance1;
#endif
#if defined (VK_KHR_video_maintenance2)
VkPhysicalDeviceVideoMaintenance2FeaturesKHR videomaintenance2;
#endif
};
static void
@ -561,6 +564,13 @@ dump_features_extras (GstVulkanPhysicalDevice * device,
videoMaintenance1);
}
#endif
#if defined (VK_KHR_video_maintenance2)
if (chain->sType ==
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_2_FEATURES_KHR) {
DEBUG_BOOL_STRUCT ("support for", &priv->videomaintenance2,
videoMaintenance2);
}
#endif
}
static gboolean
@ -1066,6 +1076,11 @@ add_extra_features (GstVulkanPhysicalDevice * device)
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_1_FEATURES_KHR;
vk_link_struct (&priv->features13, &priv->videomaintenance1);
#endif
#if defined (VK_KHR_video_maintenance2)
priv->videomaintenance2.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_MAINTENANCE_2_FEATURES_KHR;
vk_link_struct (&priv->features13, &priv->videomaintenance2);
#endif
}
#endif /* VK_API_VERSION_1_2 */
@ -1473,6 +1488,20 @@ gboolean
return FALSE;
}
gboolean
gst_vulkan_physical_device_has_feature_video_maintenance2
(GstVulkanPhysicalDevice * device) {
#if defined (VK_KHR_video_maintenance2)
GstVulkanPhysicalDevicePrivate *priv;
g_return_val_if_fail (GST_IS_VULKAN_PHYSICAL_DEVICE (device), FALSE);
priv = GET_PRIV (device);
return priv->videomaintenance2.videoMaintenance2;
#endif
return FALSE;
}
/**
* gst_vulkan_physical_device_get_api_version:
* @device: a #GstVulkanPhysicalDevice

View File

@ -119,6 +119,13 @@ gst_vulkan_video_session_create (GstVulkanVideoSession * session,
(device->physical_device)) {
session_create->flags |= VK_VIDEO_SESSION_CREATE_INLINE_QUERIES_BIT_KHR;
}
/* for now we only support decoders with inline session parameters */
if (((session_create->pVideoProfile->videoCodecOperation & 0xFF) != 0)
&& gst_vulkan_physical_device_has_feature_video_maintenance2
(device->physical_device)) {
session_create->flags |=
VK_VIDEO_SESSION_CREATE_INLINE_SESSION_PARAMETERS_BIT_KHR;
}
res = vk->CreateVideoSession (device->device, session_create, NULL,
&vk_session);

View File

@ -320,8 +320,8 @@ if get_option('vulkan-video').allowed()
video_test = '''
#include <vulkan/vulkan.h>
#if !(defined(VK_VERSION_1_4) || (defined(VK_VERSION_1_3) && VK_HEADER_VERSION >= 275))
#error "Need at least Vulkan 1.3.275"
#if !(defined(VK_VERSION_1_5) || (defined(VK_VERSION_1_4) && VK_HEADER_VERSION >= 306))
#error "Need at least Vulkan 1.4.306"
#endif
/* vk_video/vulkan_video_codec_h264std.h */

View File

@ -395,6 +395,12 @@ GST_START_TEST (test_h264_decoder)
.sliceCount = pic.slice_offs->len - 1,
.pSliceOffsets = (guint32 *) pic.slice_offs->data,
};
VkVideoDecodeH264InlineSessionParametersInfoKHR inline_params = {
.sType =
VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_INLINE_SESSION_PARAMETERS_INFO_KHR,
.pStdSPS = &h264_std_sps,
.pStdPPS = &h264_std_pps,
};
/* *INDENT-OFF* */
pic.pic_res = (VkVideoPictureResourceInfoKHR) {
@ -428,6 +434,10 @@ GST_START_TEST (test_h264_decoder)
};
/* *INDENT-ON* */
if ((dec->features & GST_VULKAN_DECODER_FEATURES_VIDEO_MAINTEINANCE2) != 0) {
vk_pic.pNext = &inline_params;
}
fail_unless (gst_vulkan_decoder_decode (dec, &pic, &err));
}
@ -562,6 +572,13 @@ GST_START_TEST (test_h265_decoder)
.sliceSegmentCount = pic.slice_offs->len - 1,
.pSliceSegmentOffsets = (guint32 *) pic.slice_offs->data,
};
VkVideoDecodeH265InlineSessionParametersInfoKHR inline_params = {
.sType =
VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_INLINE_SESSION_PARAMETERS_INFO_KHR,
.pStdSPS = &h265_std_sps,
.pStdPPS = &h265_std_pps,
.pStdVPS = &h265_std_vps,
};
/* *INDENT-OFF* */
pic.pic_res = (VkVideoPictureResourceInfoKHR) {
@ -595,6 +612,10 @@ GST_START_TEST (test_h265_decoder)
};
/* *INDENT-ON* */
if ((dec->features & GST_VULKAN_DECODER_FEATURES_VIDEO_MAINTEINANCE2) != 0) {
vk_pic.pNext = &inline_params;
}
fail_unless (gst_vulkan_decoder_decode (dec, &pic, &err));
}