3 * Copyright (C) 2019 Matthew Waters <matthew@centricular.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-vulkanimageidentity
23 * @title: vulkanimgeidentity
25 * vulkanimageidentity produces a vulkan image that is a copy of the input image.
34 #include "vkviewconvert.h"
36 #include "vkelementutils.h"
38 #include "shaders/identity.vert.h"
39 #include "shaders/view_convert.frag.h"
40 #include "gstvulkan-plugins-enumtypes.h"
42 GST_DEBUG_CATEGORY (gst_debug_vulkan_view_convert);
43 #define GST_CAT_DEFAULT gst_debug_vulkan_view_convert
46 /* These match the order and number of DOWNMIX_ANAGLYPH_* modes */
47 static float downmix_matrices[][2][12] = {
48 { /* Green-Magenta Dubois */
49 {-0.062f, 0.284f, -0.015f, 0.0, -0.158f, 0.668f, -0.027f, 0.0, -0.039f, 0.143f, 0.021f, 0.0},
50 {0.529f, -0.016f, 0.009f, 0.0, 0.705f, -0.015f, 0.075f, 0.0, 0.024f, -0.065f, 0.937f, 0.0}
52 { /* Red-Cyan Dubois */
53 /* Source of this matrix: http://www.site.uottawa.ca/~edubois/anaglyph/LeastSquaresHowToPhotoshop.pdf */
54 {0.437f, -0.062f, -0.048f, 0.0, 0.449f, -0.062f, -0.050f, 0.0, 0.164f, -0.024f, -0.017f},
55 {-0.011f, 0.377f, -0.026f, 0.0, -0.032f, 0.761f, -0.093f, 0.0, -0.007f, 0.009f, 1.234f}
57 { /* Amber-blue Dubois */
58 {1.062f, -0.026f, -0.038f, 0.0, -0.205f, 0.908f, -0.173f, 0.0, 0.299f, 0.068f, 0.022f},
59 {-0.016f, 0.006f, 0.094f, 0.0, -0.123f, 0.062f, 0.185f, 0.0, -0.017f, -0.017f, 0.911f}
66 int in_reorder_idx[4];
67 int out_reorder_idx[4];
68 float tex_offset[2][2];
69 float tex_scale[2][2];
77 get_rgb_format_swizzle_order (GstVideoFormat format,
78 gint swizzle[GST_VIDEO_MAX_COMPONENTS])
80 const GstVideoFormatInfo *finfo = gst_video_format_get_info (format);
83 g_return_if_fail (finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB
84 || format == GST_VIDEO_FORMAT_AYUV);
86 for (i = 0; i < finfo->n_components; i++) {
87 swizzle[c_i++] = finfo->poffset[i];
90 /* special case spaced RGB formats as the space does not contain a poffset
91 * value and we need all four components to be valid in order to swizzle
93 if (format == GST_VIDEO_FORMAT_xRGB || format == GST_VIDEO_FORMAT_xBGR) {
95 } else if (format == GST_VIDEO_FORMAT_RGBx || format == GST_VIDEO_FORMAT_BGRx) {
98 for (i = finfo->n_components; i < GST_VIDEO_MAX_COMPONENTS; i++) {
105 get_vulkan_rgb_format_swizzle_order (VkFormat format, gint * swizzle,
106 guint swizzle_count, guint offset)
108 const GstVulkanFormatInfo *finfo = gst_vulkan_format_get_info (format);
111 g_return_if_fail (finfo->flags & GST_VULKAN_FORMAT_FLAG_RGB);
112 g_return_if_fail (finfo->n_components <= swizzle_count);
114 for (i = 0; i < finfo->n_components; i++) {
115 swizzle[i] = offset + finfo->poffset[i];
117 for (i = finfo->n_components; i < swizzle_count; i++) {
122 /* given a swizzle index, produce an index such that:
124 * swizzle[idx[i]] == identity[i] where:
125 * - swizzle is the original swizzle
126 * - idx is the result
127 * - identity = {0, 1, 2,...}
128 * - unset fields are marked by -1
131 swizzle_identity_order (gint * swizzle, gint * idx)
135 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
139 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
140 if (swizzle[i] >= 0 && swizzle[i] < 4 && idx[swizzle[i]] == -1) {
147 video_format_to_reorder (GstVideoFormat v_format, gint * reorder,
151 case GST_VIDEO_FORMAT_RGBA:
152 case GST_VIDEO_FORMAT_RGBx:
153 case GST_VIDEO_FORMAT_BGRA:
154 case GST_VIDEO_FORMAT_BGRx:
155 case GST_VIDEO_FORMAT_ARGB:
156 case GST_VIDEO_FORMAT_xRGB:
157 case GST_VIDEO_FORMAT_ABGR:
158 case GST_VIDEO_FORMAT_xBGR:
159 case GST_VIDEO_FORMAT_AYUV:
160 get_rgb_format_swizzle_order (v_format, reorder);
162 case GST_VIDEO_FORMAT_UYVY:
165 reorder[2] = input ? 3 : 2;
168 case GST_VIDEO_FORMAT_YUY2:
172 reorder[3] = input ? 3 : 2;
174 case GST_VIDEO_FORMAT_NV12:
181 g_assert_not_reached ();
185 GST_TRACE ("swizzle: %u, %u, %u, %u", reorder[0], reorder[1], reorder[2],
190 finfo_get_plane_n_components (const GstVideoFormatInfo * finfo, guint plane)
192 guint n_components = 0, i;
194 switch (finfo->format) {
195 case GST_VIDEO_FORMAT_RGBx:
196 case GST_VIDEO_FORMAT_xRGB:
197 case GST_VIDEO_FORMAT_BGRx:
198 case GST_VIDEO_FORMAT_xBGR:
199 /* fixup spaced RGB formats as we treat the space as a normal alpha
201 return plane == 0 ? 4 : 0;
206 for (i = 0; i < finfo->n_components; i++) {
207 if (finfo->plane[i] == plane)
215 get_vulkan_format_swizzle_order (GstVideoFormat v_format,
216 VkFormat vk_format[GST_VIDEO_MAX_PLANES],
217 gint swizzle[GST_VIDEO_MAX_COMPONENTS])
219 const GstVideoFormatInfo *finfo;
220 int i, prev_in_i = 0;
222 finfo = gst_video_format_get_info (v_format);
223 for (i = 0; i < finfo->n_planes; i++) {
224 guint plane_components = finfo_get_plane_n_components (finfo, i);
225 get_vulkan_rgb_format_swizzle_order (vk_format[i],
226 &swizzle[prev_in_i], plane_components, prev_in_i);
227 prev_in_i += plane_components;
230 if (v_format == GST_VIDEO_FORMAT_YUY2 || v_format == GST_VIDEO_FORMAT_UYVY) {
231 /* Fixup these packed YUV formats as we use a two component format for
232 * a 4-component pixel and access two samples in the shader */
233 g_assert (swizzle[0] == 0);
234 g_assert (swizzle[1] == 1);
239 GST_TRACE ("%s: %i, %i, %i, %i", finfo->name, swizzle[0], swizzle[1],
240 swizzle[2], swizzle[3]);
244 calculate_reorder_indexes (GstVideoFormat in_format,
245 GstVulkanImageView * in_views[GST_VIDEO_MAX_COMPONENTS],
246 GstVideoFormat out_format,
247 GstVulkanImageView * out_views[GST_VIDEO_MAX_COMPONENTS],
248 int ret_in[GST_VIDEO_MAX_COMPONENTS], int ret_out[GST_VIDEO_MAX_COMPONENTS])
250 const GstVideoFormatInfo *in_finfo, *out_finfo;
251 VkFormat in_vk_formats[GST_VIDEO_MAX_COMPONENTS];
252 VkFormat out_vk_formats[GST_VIDEO_MAX_COMPONENTS];
253 int in_vk_order[GST_VIDEO_MAX_COMPONENTS],
254 in_reorder[GST_VIDEO_MAX_COMPONENTS];
255 int out_vk_order[GST_VIDEO_MAX_COMPONENTS],
256 out_reorder[GST_VIDEO_MAX_COMPONENTS];
257 int tmp[GST_VIDEO_MAX_PLANES];
260 in_finfo = gst_video_format_get_info (in_format);
261 out_finfo = gst_video_format_get_info (out_format);
263 for (i = 0; i < in_finfo->n_planes; i++)
264 in_vk_formats[i] = in_views[i]->image->create_info.format;
265 for (i = 0; i < out_finfo->n_planes; i++)
266 out_vk_formats[i] = out_views[i]->image->create_info.format;
268 get_vulkan_format_swizzle_order (in_format, in_vk_formats, in_vk_order);
269 video_format_to_reorder (in_format, in_reorder, TRUE);
271 video_format_to_reorder (out_format, out_reorder, FALSE);
272 get_vulkan_format_swizzle_order (out_format, out_vk_formats, out_vk_order);
274 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
275 tmp[i] = out_vk_order[out_reorder[i]];
276 /* find the identity order for RGBA->$format */
277 GST_TRACE ("pre-invert: %u, %u, %u, %u", tmp[0], tmp[1], tmp[2], tmp[3]);
278 if (out_format == GST_VIDEO_FORMAT_YUY2
279 || out_format == GST_VIDEO_FORMAT_UYVY) {
280 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
283 swizzle_identity_order (tmp, ret_out);
286 for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
287 ret_in[i] = in_reorder[in_vk_order[i]];
288 GST_TRACE ("in reorder: %u, %u, %u, %u", ret_in[0], ret_in[1], ret_in[2],
290 GST_TRACE ("out reorder: %u, %u, %u, %u", ret_out[0], ret_out[1], ret_out[2],
295 update_descriptor_set (GstVulkanViewConvert * conv, VkImageView * views,
298 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
299 VkDescriptorBufferInfo buffer_info;
300 VkDescriptorImageInfo image_info[2 * GST_VIDEO_MAX_PLANES];
301 VkWriteDescriptorSet writes[10];
304 for (; i < GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2; i++) {
306 image_info[i] = (VkDescriptorImageInfo) {
307 .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
308 .imageView = views[i],
309 .sampler = conv->sampler
312 g_assert (i < n_views);
313 g_assert (i < GST_VIDEO_MAX_PLANES);
315 writes[i] = (VkWriteDescriptorSet) {
316 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
318 .dstSet = conv->descriptor_set,
320 .dstArrayElement = 0,
321 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
322 .descriptorCount = 1,
323 .pImageInfo = &image_info[i]
328 buffer_info = (VkDescriptorBufferInfo) {
329 .buffer = ((GstVulkanBufferMemory *) conv->uniform)->buffer,
331 .range = sizeof (struct ViewUpdate),
333 writes[i] = (VkWriteDescriptorSet) {
334 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
336 .dstSet = conv->descriptor_set,
338 .dstArrayElement = 0,
339 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
340 .descriptorCount = 1,
341 .pBufferInfo = &buffer_info
345 g_assert (i <= G_N_ELEMENTS (writes));
347 vkUpdateDescriptorSets (render->device->device, i, writes, 0, NULL);
351 _update_uniform (GstVulkanViewConvert * conv, GstVulkanImageView ** in_views,
352 GstVulkanImageView ** out_views, VkImageView views[GST_VIDEO_MAX_PLANES])
354 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
355 GstVideoMultiviewMode in_mode, out_mode;
356 GstVideoMultiviewFlags in_flags, out_flags;
357 struct ViewUpdate data;
359 guint l_index, r_index;
360 gboolean mono_input = FALSE;
362 calculate_reorder_indexes (GST_VIDEO_INFO_FORMAT (&render->in_info),
363 in_views, GST_VIDEO_INFO_FORMAT (&render->out_info),
364 out_views, data.in_reorder_idx, data.out_reorder_idx);
366 data.tex_scale[0][0] = data.tex_scale[0][1] = 1.;
367 data.tex_scale[1][0] = data.tex_scale[1][1] = 1.;
368 data.tex_offset[0][0] = data.tex_offset[0][1] = 0.;
369 data.tex_offset[1][0] = data.tex_offset[1][1] = 0.;
371 in_mode = conv->input_mode_override;
372 in_flags = conv->input_flags_override;
373 if (in_mode == GST_VIDEO_MULTIVIEW_MODE_NONE) {
374 in_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&render->in_info);
375 in_flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&render->in_info);
378 /* Configured output mode already takes any override
380 out_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&render->out_info);
381 out_flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&render->out_info);
383 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST) ==
384 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)) {
388 VkImageView scratch_view;
390 GST_LOG_OBJECT (conv, "Switching left/right views");
394 scratch_view = views[0];
396 views[1] = scratch_view;
399 if (in_mode < GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE) { /* unknown/mono/left/right single image */
400 } else if (in_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE ||
401 in_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX) {
402 /* Side-by-side input */
403 data.tex_offset[r_index][0] += 0.5 * data.tex_scale[r_index][0];
404 data.tex_scale[0][0] *= 0.5f; /* Half horizontal scale */
405 data.tex_scale[1][0] *= 0.5f;
406 } else if (in_mode == GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM) { /* top-bottom */
407 data.tex_offset[r_index][1] += 0.5 * data.tex_scale[r_index][1];
408 data.tex_scale[0][1] *= 0.5f; /* Half vertical scale */
409 data.tex_scale[1][1] *= 0.5f;
412 /* Flipped is vertical, flopped is horizontal.
413 * Adjust and offset per-view scaling. This needs to be done
414 * after the input scaling already splits the views, before
415 * adding any output scaling. */
416 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED) !=
417 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED)) {
418 data.tex_offset[l_index][1] += data.tex_scale[l_index][1];
419 data.tex_scale[l_index][1] *= -1.0;
421 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED) !=
422 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED)) {
423 data.tex_offset[l_index][0] += data.tex_scale[l_index][0];
424 data.tex_scale[l_index][0] *= -1.0;
426 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED) !=
427 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED)) {
428 data.tex_offset[r_index][1] += data.tex_scale[r_index][1];
429 data.tex_scale[r_index][1] *= -1.0;
431 if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED) !=
432 (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED)) {
433 data.tex_offset[r_index][0] += data.tex_scale[r_index][0];
434 data.tex_scale[r_index][0] *= -1.0;
437 if (out_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE ||
438 out_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX) {
440 data.tex_offset[1][0] -= data.tex_scale[1][0];
441 data.tex_scale[0][0] *= 2.0f;
442 data.tex_scale[1][0] *= 2.0f;
443 } else if (out_mode == GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM) {
444 data.tex_offset[1][1] -= data.tex_scale[1][1];
445 data.tex_scale[0][1] *= 2.0f;
446 data.tex_scale[1][1] *= 2.0f;
449 GST_DEBUG_OBJECT (conv,
450 "Scaling matrix [ %f, %f ] [ %f %f]. Offsets [ %f, %f ] [ %f, %f ]",
451 data.tex_scale[0][0], data.tex_scale[0][1],
452 data.tex_scale[1][0], data.tex_scale[1][1],
453 data.tex_offset[0][0], data.tex_offset[0][1], data.tex_offset[1][0],
454 data.tex_offset[1][1]);
456 if (in_mode == GST_VIDEO_MULTIVIEW_MODE_NONE ||
457 in_mode == GST_VIDEO_MULTIVIEW_MODE_MONO ||
458 in_mode == GST_VIDEO_MULTIVIEW_MODE_LEFT ||
459 in_mode == GST_VIDEO_MULTIVIEW_MODE_RIGHT)
462 data.output_type = out_mode;
463 if (data.output_type == GST_VIDEO_MULTIVIEW_MODE_NONE ||
464 data.output_type == GST_VIDEO_MULTIVIEW_MODE_MONO) {
466 data.output_type = GST_VIDEO_MULTIVIEW_MODE_LEFT;
468 data.output_type = GST_VIDEO_MULTIVIEW_MODE_MONO;
469 } else if (data.output_type == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX) {
470 data.output_type = GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
473 data.tex_size[0] = GST_VIDEO_INFO_WIDTH (&render->out_info);
474 data.tex_size[1] = GST_VIDEO_INFO_HEIGHT (&render->out_info);
475 memcpy (&data.downmix[0], &downmix_matrices[conv->downmix_mode][0],
476 sizeof (data.downmix[0]));
477 memcpy (&data.downmix[1], &downmix_matrices[conv->downmix_mode][1],
478 sizeof (data.downmix[1]));
480 if (!gst_memory_map (conv->uniform, &map_info, GST_MAP_WRITE)) {
483 memcpy (map_info.data, &data, sizeof (data));
484 gst_memory_unmap (conv->uniform, &map_info);
485 conv->descriptor_up_to_date = TRUE;
491 view_convert_update_command_state (GstVulkanViewConvert * conv,
492 VkCommandBuffer cmd, GstVulkanImageView ** in_views,
493 GstVulkanImageView ** out_views)
495 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
496 VkImageView views[GST_VIDEO_MAX_PLANES];
499 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
500 views[2 * i] = in_views[i]->view;
501 views[2 * i + 1] = in_views[i]->view;
504 if (!conv->descriptor_up_to_date) {
505 if (!_update_uniform (conv, in_views, out_views, views))
507 update_descriptor_set (conv, views,
508 GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2);
511 vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
512 render->pipeline_layout, 0, 1, &conv->descriptor_set, 0, NULL);
517 static void gst_vulkan_view_convert_set_property (GObject * object,
518 guint prop_id, const GValue * value, GParamSpec * pspec);
519 static void gst_vulkan_view_convert_get_property (GObject * object,
520 guint prop_id, GValue * value, GParamSpec * pspec);
522 static gboolean gst_vulkan_view_convert_start (GstBaseTransform * bt);
523 static gboolean gst_vulkan_view_convert_stop (GstBaseTransform * bt);
525 static GstCaps *gst_vulkan_view_convert_transform_caps (GstBaseTransform * bt,
526 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
527 static GstCaps *gst_vulkan_view_convert_fixate_caps (GstBaseTransform * bt,
528 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
529 static GstFlowReturn gst_vulkan_view_convert_transform (GstBaseTransform * bt,
530 GstBuffer * inbuf, GstBuffer * outbuf);
531 static gboolean gst_vulkan_view_convert_set_caps (GstBaseTransform * bt,
532 GstCaps * in_caps, GstCaps * out_caps);
534 static VkAttachmentReference
535 * gst_vulkan_view_convert_render_pass_attachment_references
536 (GstVulkanFullScreenRender * render, guint * n_attachments);
537 static VkAttachmentDescription
538 * gst_vulkan_view_convert_render_pass_attachment_descriptions
539 (GstVulkanFullScreenRender * render, guint * n_descriptions);
540 static VkDescriptorSetLayoutBinding
541 * gst_vulkan_view_convert_descriptor_set_layout_bindings
542 (GstVulkanFullScreenRender * render, guint * n_bindings);
544 gst_vulkan_view_convert_shader_create_info (GstVulkanFullScreenRender * render);
545 static VkPushConstantRange
546 * gst_vulkan_view_convert_push_constant_ranges (GstVulkanFullScreenRender *
547 render, guint * n_constants);
549 static GstStaticPadTemplate gst_vulkan_sink_template =
550 GST_STATIC_PAD_TEMPLATE ("sink",
553 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
554 (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
557 static GstStaticPadTemplate gst_vulkan_src_template =
558 GST_STATIC_PAD_TEMPLATE ("src",
561 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
562 (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
572 PROP_OUTPUT_DOWNMIX_MODE
575 #define DEFAULT_DOWNMIX GST_VULKAN_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
577 #define gst_vulkan_view_convert_parent_class parent_class
578 G_DEFINE_TYPE_WITH_CODE (GstVulkanViewConvert, gst_vulkan_view_convert,
579 GST_TYPE_VULKAN_FULL_SCREEN_RENDER,
580 GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_view_convert,
581 "vulkanviewconvert", 0, "Vulkan View Convert"));
584 gst_vulkan_view_convert_class_init (GstVulkanViewConvertClass * klass)
586 GObjectClass *gobject_class;
587 GstElementClass *gstelement_class;
588 GstBaseTransformClass *gstbasetransform_class;
589 GstVulkanFullScreenRenderClass *fullscreenrender_class;
591 gobject_class = (GObjectClass *) klass;
592 gstelement_class = (GstElementClass *) klass;
593 gstbasetransform_class = (GstBaseTransformClass *) klass;
594 fullscreenrender_class = (GstVulkanFullScreenRenderClass *) klass;
596 gobject_class->set_property = gst_vulkan_view_convert_set_property;
597 gobject_class->get_property = gst_vulkan_view_convert_get_property;
599 g_object_class_install_property (gobject_class, PROP_INPUT_LAYOUT,
600 g_param_spec_enum ("input-mode-override",
601 "Input Multiview Mode Override",
602 "Override any input information about multiview layout",
603 GST_TYPE_VIDEO_MULTIVIEW_MODE,
604 GST_VIDEO_MULTIVIEW_MODE_NONE,
605 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
606 g_object_class_install_property (gobject_class, PROP_INPUT_FLAGS,
607 g_param_spec_flags ("input-flags-override",
608 "Input Multiview Flags Override",
609 "Override any input information about multiview layout flags",
610 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
611 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
612 g_object_class_install_property (gobject_class, PROP_OUTPUT_LAYOUT,
613 g_param_spec_enum ("output-mode-override",
614 "Output Multiview Mode Override",
615 "Override automatic output mode selection for multiview layout",
616 GST_TYPE_VIDEO_MULTIVIEW_MODE, GST_VIDEO_MULTIVIEW_MODE_NONE,
617 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
618 g_object_class_install_property (gobject_class, PROP_OUTPUT_FLAGS,
619 g_param_spec_flags ("output-flags-override",
620 "Output Multiview Flags Override",
621 "Override automatic negotiation for output multiview layout flags",
622 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
623 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
624 g_object_class_install_property (gobject_class, PROP_OUTPUT_DOWNMIX_MODE,
625 g_param_spec_enum ("downmix-mode", "Mode for mono downmixed output",
626 "Output anaglyph type to generate when downmixing to mono",
627 GST_TYPE_VULKAN_STEREO_DOWNMIX, DEFAULT_DOWNMIX,
628 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
630 gst_element_class_set_metadata (gstelement_class, "Vulkan Uploader",
631 "Filter/Video/Convert", "A Vulkan View Convert",
632 "Matthew Waters <matthew@centricular.com>");
634 gst_element_class_add_static_pad_template (gstelement_class,
635 &gst_vulkan_sink_template);
636 gst_element_class_add_static_pad_template (gstelement_class,
637 &gst_vulkan_src_template);
639 gstbasetransform_class->start =
640 GST_DEBUG_FUNCPTR (gst_vulkan_view_convert_start);
641 gstbasetransform_class->stop =
642 GST_DEBUG_FUNCPTR (gst_vulkan_view_convert_stop);
643 gstbasetransform_class->transform_caps =
644 gst_vulkan_view_convert_transform_caps;
645 gstbasetransform_class->fixate_caps = gst_vulkan_view_convert_fixate_caps;
646 gstbasetransform_class->set_caps = gst_vulkan_view_convert_set_caps;
647 gstbasetransform_class->transform = gst_vulkan_view_convert_transform;
649 fullscreenrender_class->render_pass_attachment_references =
650 gst_vulkan_view_convert_render_pass_attachment_references;
651 fullscreenrender_class->render_pass_attachment_descriptions =
652 gst_vulkan_view_convert_render_pass_attachment_descriptions;
653 fullscreenrender_class->descriptor_set_layout_bindings =
654 gst_vulkan_view_convert_descriptor_set_layout_bindings;
655 fullscreenrender_class->shader_create_info =
656 gst_vulkan_view_convert_shader_create_info;
657 fullscreenrender_class->push_constant_ranges =
658 gst_vulkan_view_convert_push_constant_ranges;
662 gst_vulkan_view_convert_init (GstVulkanViewConvert * conv)
664 conv->downmix_mode = DEFAULT_DOWNMIX;
666 conv->input_mode_override = GST_VIDEO_MULTIVIEW_MODE_NONE;
667 conv->input_flags_override = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
668 conv->output_mode_override = GST_VIDEO_MULTIVIEW_MODE_NONE;
669 conv->output_flags_override = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
673 gst_vulkan_view_convert_set_property (GObject * object, guint prop_id,
674 const GValue * value, GParamSpec * pspec)
676 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (object);
679 case PROP_INPUT_LAYOUT:
680 conv->input_mode_override = g_value_get_enum (value);
681 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
683 case PROP_INPUT_FLAGS:
684 conv->input_flags_override = g_value_get_flags (value);
685 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
687 case PROP_OUTPUT_LAYOUT:
688 conv->output_mode_override = g_value_get_enum (value);
689 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
691 case PROP_OUTPUT_FLAGS:
692 conv->output_flags_override = g_value_get_flags (value);
693 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (conv));
695 case PROP_OUTPUT_DOWNMIX_MODE:
696 conv->downmix_mode = g_value_get_enum (value);
699 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
702 GST_OBJECT_LOCK (conv);
703 conv->descriptor_up_to_date = FALSE;
704 GST_OBJECT_UNLOCK (conv);
708 gst_vulkan_view_convert_get_property (GObject * object, guint prop_id,
709 GValue * value, GParamSpec * pspec)
711 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (object);
714 case PROP_INPUT_LAYOUT:
715 g_value_set_enum (value, conv->input_mode_override);
717 case PROP_INPUT_FLAGS:
718 g_value_set_flags (value, conv->input_flags_override);
720 case PROP_OUTPUT_LAYOUT:
721 g_value_set_enum (value, conv->output_mode_override);
723 case PROP_OUTPUT_FLAGS:
724 g_value_set_flags (value, conv->output_flags_override);
726 case PROP_OUTPUT_DOWNMIX_MODE:
727 g_value_set_enum (value, conv->downmix_mode);
730 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
735 /* Function that can halve the value
736 * of ints, fractions, int/fraction ranges and lists of ints/fractions */
738 _halve_value (GValue * out, const GValue * in_value)
740 /* Fundamental fixed types first */
741 if (G_VALUE_HOLDS_INT (in_value)) {
742 g_value_init (out, G_TYPE_INT);
743 g_value_set_int (out, MAX (g_value_get_int (in_value) / 2, 1));
744 } else if (GST_VALUE_HOLDS_FRACTION (in_value)) {
746 num = gst_value_get_fraction_numerator (in_value);
747 den = gst_value_get_fraction_denominator (in_value);
748 g_value_init (out, GST_TYPE_FRACTION);
749 /* Don't adjust 'infinite' fractions */
750 if ((num != 1 || den != 2147483647) && (num != 2147483647 || den != 1)) {
751 /* FIXME - could do better approximation when den > G_MAXINT/2? */
752 den = den > G_MAXINT / 2 ? G_MAXINT : den * 2;
754 gst_value_set_fraction (out, num, den);
755 } else if (GST_VALUE_HOLDS_INT_RANGE (in_value)) {
756 gint range_min = gst_value_get_int_range_min (in_value);
757 gint range_max = gst_value_get_int_range_max (in_value);
758 gint range_step = gst_value_get_int_range_step (in_value);
759 g_value_init (out, GST_TYPE_INT_RANGE);
761 range_min = MAX (1, range_min / 2);
762 if (range_max != G_MAXINT)
763 range_max = MAX (1, range_max / 2);
764 gst_value_set_int_range_step (out, range_min,
765 range_max, MAX (1, range_step / 2));
766 } else if (GST_VALUE_HOLDS_FRACTION_RANGE (in_value)) {
767 GValue min_out = G_VALUE_INIT;
768 GValue max_out = G_VALUE_INIT;
769 const GValue *range_min = gst_value_get_fraction_range_min (in_value);
770 const GValue *range_max = gst_value_get_fraction_range_max (in_value);
771 _halve_value (&min_out, range_min);
772 _halve_value (&max_out, range_max);
773 g_value_init (out, GST_TYPE_FRACTION_RANGE);
774 gst_value_set_fraction_range (out, &min_out, &max_out);
775 g_value_unset (&min_out);
776 g_value_unset (&max_out);
777 } else if (GST_VALUE_HOLDS_LIST (in_value)) {
779 g_value_init (out, GST_TYPE_LIST);
780 for (i = 0; i < gst_value_list_get_size (in_value); i++) {
782 GValue tmp = G_VALUE_INIT;
784 entry = gst_value_list_get_value (in_value, i);
785 /* Random list values might not be the right type */
786 if (!_halve_value (&tmp, entry))
788 gst_value_list_append_and_take_value (out, &tmp);
800 static GstStructure *
801 _halve_structure_field (const GstStructure * in, const gchar * field_name)
804 const GValue *in_value = gst_structure_get_value (in, field_name);
805 GValue tmp = G_VALUE_INIT;
807 if (G_UNLIKELY (in_value == NULL))
808 return gst_structure_copy (in); /* Field doesn't exist, leave it as is */
810 if (!_halve_value (&tmp, in_value))
813 out = gst_structure_copy (in);
814 gst_structure_set_value (out, field_name, &tmp);
815 g_value_unset (&tmp);
820 /* Function that can double the value
821 * of ints, fractions, int/fraction ranges and lists of ints/fractions */
823 _double_value (GValue * out, const GValue * in_value)
825 /* Fundamental fixed types first */
826 if (G_VALUE_HOLDS_INT (in_value)) {
827 gint n = g_value_get_int (in_value);
828 g_value_init (out, G_TYPE_INT);
829 if (n <= G_MAXINT / 2)
830 g_value_set_int (out, n * 2);
832 g_value_set_int (out, G_MAXINT);
833 } else if (GST_VALUE_HOLDS_FRACTION (in_value)) {
835 num = gst_value_get_fraction_numerator (in_value);
836 den = gst_value_get_fraction_denominator (in_value);
837 g_value_init (out, GST_TYPE_FRACTION);
838 /* Don't adjust 'infinite' fractions */
839 if ((num != 1 || den != 2147483647) && (num != 2147483647 || den != 1)) {
840 /* FIXME - could do better approximation when num > G_MAXINT/2? */
841 num = num > G_MAXINT / 2 ? G_MAXINT : num * 2;
843 gst_value_set_fraction (out, num, den);
844 } else if (GST_VALUE_HOLDS_INT_RANGE (in_value)) {
845 gint range_min = gst_value_get_int_range_min (in_value);
846 gint range_max = gst_value_get_int_range_max (in_value);
847 gint range_step = gst_value_get_int_range_step (in_value);
848 if (range_min != 1) {
849 range_min = MIN (G_MAXINT / 2, range_min);
852 if (range_max != G_MAXINT) {
853 range_max = MIN (G_MAXINT / 2, range_max);
856 range_step = MIN (G_MAXINT / 2, range_step);
857 g_value_init (out, GST_TYPE_INT_RANGE);
858 gst_value_set_int_range_step (out, range_min, range_max, range_step);
859 } else if (GST_VALUE_HOLDS_FRACTION_RANGE (in_value)) {
860 GValue min_out = G_VALUE_INIT;
861 GValue max_out = G_VALUE_INIT;
862 const GValue *range_min = gst_value_get_fraction_range_min (in_value);
863 const GValue *range_max = gst_value_get_fraction_range_max (in_value);
864 _double_value (&min_out, range_min);
865 _double_value (&max_out, range_max);
866 g_value_init (out, GST_TYPE_FRACTION_RANGE);
867 gst_value_set_fraction_range (out, &min_out, &max_out);
868 g_value_unset (&min_out);
869 g_value_unset (&max_out);
870 } else if (GST_VALUE_HOLDS_LIST (in_value)) {
872 g_value_init (out, GST_TYPE_LIST);
873 for (i = 0; i < gst_value_list_get_size (in_value); i++) {
875 GValue tmp = G_VALUE_INIT;
877 entry = gst_value_list_get_value (in_value, i);
878 /* Random list values might not be the right type */
879 if (!_double_value (&tmp, entry))
881 gst_value_list_append_and_take_value (out, &tmp);
893 static GstStructure *
894 _double_structure_field (const GstStructure * in, const gchar * field_name)
897 const GValue *in_value = gst_structure_get_value (in, field_name);
898 GValue tmp = G_VALUE_INIT;
900 if (G_UNLIKELY (in_value == NULL))
901 return gst_structure_copy (in); /* Field doesn't exist, leave it as is */
903 if (!_double_value (&tmp, in_value))
906 out = gst_structure_copy (in);
907 gst_structure_set_value (out, field_name, &tmp);
908 g_value_unset (&tmp);
913 /* Return a copy of the caps with the requested field doubled in value/range */
915 _double_caps_field (const GstCaps * in, const gchar * field_name)
918 GstCaps *out = gst_caps_new_empty ();
920 for (i = 0; i < gst_caps_get_size (in); i++) {
921 const GstStructure *cur = gst_caps_get_structure (in, i);
922 GstCapsFeatures *f = gst_caps_get_features (in, i);
924 GstStructure *res = _double_structure_field (cur, field_name);
926 gst_caps_merge_structure_full (out, res,
927 f ? gst_caps_features_copy (f) : NULL);
933 /* Takes ownership of the input caps */
935 _expand_par_for_half_aspect (GstCaps * in, gboolean vertical_half_aspect)
937 guint mview_flags, mview_flags_mask;
941 out = gst_caps_new_empty ();
943 while (gst_caps_get_size (in) > 0) {
945 GstCapsFeatures *features;
947 features = gst_caps_get_features (in, 0);
949 features = gst_caps_features_copy (features);
951 s = gst_caps_steal_structure (in, 0);
953 if (!gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
954 &mview_flags_mask)) {
955 gst_caps_append_structure_full (out, s, features);
958 /* If the input doesn't care about the half-aspect flag, allow current PAR in either variant */
959 if ((mview_flags_mask & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) == 0) {
960 gst_caps_append_structure_full (out, s, features);
963 if (!gst_structure_has_field (s, "pixel-aspect-ratio")) {
964 /* No par field, dont-care the half-aspect flag */
965 gst_structure_set (s, "multiview-flags",
966 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
967 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
968 mview_flags_mask & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
969 gst_caps_append_structure_full (out, s, features);
973 /* Halve or double PAR base on inputs input specified. */
975 /* Append a copy with the half-aspect flag as-is */
976 tmp = gst_structure_copy (s);
977 out = gst_caps_merge_structure_full (out, tmp,
978 features ? gst_caps_features_copy (features) : NULL);
980 /* and then a copy inverted */
981 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
982 /* Input is half-aspect. Double/halve the PAR, clear the flag */
983 if (vertical_half_aspect)
984 tmp = _halve_structure_field (s, "pixel-aspect-ratio");
986 tmp = _double_structure_field (s, "pixel-aspect-ratio");
988 gst_structure_set (tmp, "multiview-flags",
989 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
990 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
991 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
993 if (vertical_half_aspect)
994 tmp = _double_structure_field (s, "pixel-aspect-ratio");
996 tmp = _halve_structure_field (s, "pixel-aspect-ratio");
998 gst_structure_set (tmp, "multiview-flags",
999 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1000 mview_flags | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1001 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1004 out = gst_caps_merge_structure_full (out, tmp,
1005 features ? gst_caps_features_copy (features) : NULL);
1007 gst_structure_free (s);
1009 gst_caps_features_free (features);
1012 gst_caps_unref (in);
1017 /* If input supports top-bottom or row-interleaved, we may halve height to mono frames.
1018 * If input supports left-right, checkerboard, quincunx or column-interleaved,
1019 * we may halve width to mono frames.
1020 * For output of top-bottom or row-interleaved, we may double the mono height
1021 * For output of left-right, checkerboard, quincunx or column-interleaved,
1022 * we may double the mono width.
1023 * In all cases, if input has half-aspect and output does not, we may double the PAR
1024 * And if input does *not* have half-aspect flag and output does not, we may halve the PAR
1027 _expand_structure (GstVulkanViewConvert * viewconvert,
1028 GstCaps * out_caps, GstStructure * structure, GstCapsFeatures * features)
1030 GstCaps *expanded_caps, *tmp;
1032 const gchar *default_mview_mode_str = NULL;
1033 guint mview_flags, mview_flags_mask;
1034 const GValue *in_modes;
1037 /* Empty caps to accumulate into */
1038 expanded_caps = gst_caps_new_empty ();
1040 /* First, set defaults if multiview flags are missing */
1041 default_mview_mode_str =
1042 gst_video_multiview_mode_to_caps_string (GST_VIDEO_MULTIVIEW_MODE_MONO);
1044 mview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1045 mview_flags_mask = GST_FLAG_SET_MASK_EXACT;
1047 if (!gst_structure_has_field (structure, "multiview-mode")) {
1048 gst_structure_set (structure,
1049 "multiview-mode", G_TYPE_STRING, default_mview_mode_str, NULL);
1051 if (!gst_structure_has_field (structure, "multiview-flags")) {
1052 gst_structure_set (structure,
1053 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, mview_flags,
1054 mview_flags_mask, NULL);
1056 gst_structure_get_flagset (structure, "multiview-flags",
1057 &mview_flags, &mview_flags_mask);
1060 in_modes = gst_structure_get_value (structure, "multiview-mode");
1061 mono_caps = gst_caps_new_empty ();
1062 if (gst_value_intersect (NULL, in_modes,
1063 gst_video_multiview_get_mono_modes ())) {
1064 GstStructure *new_struct = gst_structure_copy (structure);
1065 gst_structure_set_value (new_struct, "multiview-mode",
1066 gst_video_multiview_get_mono_modes ());
1067 /* Half-aspect makes no sense for mono or unpacked, get rid of it */
1068 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1069 gst_structure_set (new_struct, "multiview-flags",
1070 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1071 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1072 mview_flags_mask & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1074 gst_caps_append_structure_full (mono_caps, new_struct,
1075 features ? gst_caps_features_copy (features) : NULL);
1077 if (gst_value_intersect (NULL, in_modes,
1078 gst_video_multiview_get_unpacked_modes ())) {
1079 GstStructure *new_struct = gst_structure_copy (structure);
1081 gst_structure_set_value (new_struct, "multiview-mode",
1082 gst_video_multiview_get_mono_modes ());
1084 /* Half-aspect makes no sense for mono or unpacked, get rid of it */
1085 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1086 gst_structure_set (new_struct, "multiview-flags",
1087 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1088 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1089 mview_flags_mask & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1091 gst_caps_append_structure_full (mono_caps, new_struct,
1092 features ? gst_caps_features_copy (features) : NULL);
1095 if (gst_value_intersect (NULL, in_modes,
1096 gst_video_multiview_get_doubled_height_modes ())) {
1097 /* Append mono formats with height halved */
1098 GstStructure *new_struct = _halve_structure_field (structure, "height");
1099 gst_structure_set_value (new_struct, "multiview-mode",
1100 gst_video_multiview_get_mono_modes ());
1101 /* Normalise the half-aspect flag away */
1102 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1104 _halve_structure_field (new_struct, "pixel-aspect-ratio");
1105 gst_structure_set (structure, "multiview-flags",
1106 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1107 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1108 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1109 gst_structure_free (new_struct);
1112 mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct,
1113 features ? gst_caps_features_copy (features) : NULL);
1115 if (gst_value_intersect (NULL, in_modes,
1116 gst_video_multiview_get_doubled_width_modes ())) {
1117 /* Append mono formats with width halved */
1118 GstStructure *new_struct = _halve_structure_field (structure, "width");
1119 gst_structure_set_value (new_struct, "multiview-mode",
1120 gst_video_multiview_get_mono_modes ());
1121 /* Normalise the half-aspect flag away */
1122 if (mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
1124 _double_structure_field (new_struct, "pixel-aspect-ratio");
1125 gst_structure_set (structure, "multiview-flags",
1126 GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1127 mview_flags & ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT,
1128 mview_flags_mask | GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT, NULL);
1129 gst_structure_free (new_struct);
1132 mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct,
1133 features ? gst_caps_features_copy (features) : NULL);
1135 if (gst_value_intersect (NULL, in_modes,
1136 gst_video_multiview_get_doubled_size_modes ())) {
1137 /* Append checkerboard/doubled size formats with width & height halved */
1138 GstStructure *new_struct_w = _halve_structure_field (structure, "width");
1139 GstStructure *new_struct_wh =
1140 _halve_structure_field (new_struct_w, "height");
1141 gst_structure_free (new_struct_w);
1142 gst_structure_set_value (new_struct_wh, "multiview-mode",
1143 gst_video_multiview_get_mono_modes ());
1144 mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct_wh,
1145 features ? gst_caps_features_copy (features) : NULL);
1148 /* Everything is normalised now, unset the flags we can change */
1149 /* Remove the views field, as these are all 'mono' modes
1150 * Need to do this before we expand caps back out to frame packed modes */
1151 for (i = 0; i < gst_caps_get_size (mono_caps); i++) {
1152 GstStructure *s = gst_caps_get_structure (mono_caps, i);
1153 gst_structure_remove_fields (s, "views", NULL);
1154 if (gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
1155 &mview_flags_mask)) {
1156 /* Preserve only the half-aspect and mixed-mono flags, for now.
1157 * The rest we can change */
1159 (GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT |
1160 GST_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO);
1161 gst_structure_set (s, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1162 mview_flags, mview_flags_mask, NULL);
1166 GST_TRACE_OBJECT (viewconvert,
1167 "Collected single-view caps %" GST_PTR_FORMAT, mono_caps);
1168 /* Put unpacked and mono modes first. We don't care about flags. Clear them */
1169 tmp = gst_caps_copy (mono_caps);
1170 for (i = 0; i < gst_caps_get_size (tmp); i++) {
1171 GstStructure *s = gst_caps_get_structure (tmp, i);
1172 gst_structure_remove_fields (s, "views", NULL);
1173 if (gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
1174 &mview_flags_mask)) {
1175 /* We can change any flags for mono modes - half-aspect and mixed-mono have no meaning */
1176 mview_flags_mask = 0;
1177 gst_structure_set (s, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1178 mview_flags, mview_flags_mask, NULL);
1181 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1183 /* Unpacked output modes have 2 views, for now */
1184 tmp = gst_caps_copy (mono_caps);
1185 gst_caps_set_value (tmp, "multiview-mode",
1186 gst_video_multiview_get_unpacked_modes ());
1187 for (i = 0; i < gst_caps_get_size (tmp); i++) {
1188 GstStructure *s = gst_caps_get_structure (tmp, i);
1189 gst_structure_set (s, "views", G_TYPE_INT, 2, NULL);
1190 if (gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
1191 &mview_flags_mask)) {
1192 /* We can change any flags for unpacked modes - half-aspect and mixed-mono have no meaning */
1193 mview_flags_mask = 0;
1194 gst_structure_set (s, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
1195 mview_flags, mview_flags_mask, NULL);
1198 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1200 /* Double height output modes */
1201 tmp = _double_caps_field (mono_caps, "height");
1202 gst_caps_set_value (tmp, "multiview-mode",
1203 gst_video_multiview_get_doubled_height_modes ());
1204 tmp = _expand_par_for_half_aspect (tmp, TRUE);
1206 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1208 /* Double width output modes */
1209 tmp = _double_caps_field (mono_caps, "width");
1210 gst_caps_set_value (tmp, "multiview-mode",
1211 gst_video_multiview_get_doubled_width_modes ());
1212 tmp = _expand_par_for_half_aspect (tmp, FALSE);
1214 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1216 /* Double size output modes */
1218 GstCaps *tmp_w = _double_caps_field (mono_caps, "width");
1219 tmp = _double_caps_field (tmp_w, "height");
1220 gst_caps_unref (tmp_w);
1221 gst_caps_set_value (tmp, "multiview-mode",
1222 gst_video_multiview_get_doubled_size_modes ());
1223 expanded_caps = gst_caps_merge (expanded_caps, tmp);
1226 /* We're done with the mono caps now */
1227 gst_caps_unref (mono_caps);
1229 GST_TRACE_OBJECT (viewconvert,
1230 "expanded transform caps now %" GST_PTR_FORMAT, expanded_caps);
1232 if (gst_caps_is_empty (expanded_caps)) {
1233 gst_caps_unref (expanded_caps);
1236 /* Really, we can rescale - so at this point we can append full-range
1237 * height/width/PAR as an unpreferred final option. */
1238 /* tmp = gst_caps_copy (expanded_caps);
1239 gst_caps_set_simple (tmp, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1240 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
1242 out_caps = gst_caps_merge (out_caps, expanded_caps);
1243 /* out_caps = gst_caps_merge (out_caps, tmp);*/
1248 _intersect_with_mview_mode (GstCaps * caps,
1249 GstVideoMultiviewMode mode, GstVideoMultiviewFlags flags)
1251 GstCaps *filter, *result;
1252 const gchar *caps_str;
1254 caps_str = gst_video_multiview_mode_to_caps_string (mode);
1256 filter = gst_caps_new_simple ("video/x-raw",
1257 "multiview-mode", G_TYPE_STRING,
1258 caps_str, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, flags,
1259 GST_FLAG_SET_MASK_EXACT, NULL);
1261 if (mode == GST_VIDEO_MULTIVIEW_MODE_SEPARATED ||
1262 mode == GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME)
1263 gst_caps_set_simple (filter, "views", G_TYPE_INT, 2, NULL);
1264 gst_caps_set_features (filter, 0, gst_caps_features_new_any ());
1266 GST_DEBUG ("Intersecting target caps %" GST_PTR_FORMAT
1267 " with caps %" GST_PTR_FORMAT, caps, filter);
1269 result = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1270 gst_caps_unref (filter);
1275 _intersect_with_mview_modes (GstCaps * caps, const GValue * modes)
1277 GstCaps *filter, *result;
1279 filter = gst_caps_new_empty_simple ("video/x-raw");
1281 gst_caps_set_value (filter, "multiview-mode", modes);
1282 gst_caps_set_features (filter, 0, gst_caps_features_new_any ());
1284 GST_DEBUG ("Intersecting target caps %" GST_PTR_FORMAT
1285 " with caps %" GST_PTR_FORMAT, caps, filter);
1287 result = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1288 gst_caps_unref (filter);
1293 gst_vulkan_view_convert_transform_caps (GstBaseTransform * bt,
1294 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1296 GstVulkanViewConvert *viewconvert = GST_VULKAN_VIEW_CONVERT (bt);
1297 GstCaps *base_caps =
1298 gst_static_pad_template_get_caps (&gst_vulkan_sink_template);
1299 GstCaps *out_caps, *tmp_caps;
1302 GST_DEBUG_OBJECT (viewconvert, "Direction %s "
1303 "input caps %" GST_PTR_FORMAT " filter %" GST_PTR_FORMAT,
1304 direction == GST_PAD_SINK ? "sink" : "src", caps, filter);
1306 /* We can only process VulkanImage caps, start from that */
1307 caps = gst_caps_intersect (caps, base_caps);
1308 gst_caps_unref (base_caps);
1310 /* Change input/output to the formats we can convert to/from,
1311 * but keep the original caps at the start - we will always prefer
1313 if (direction == GST_PAD_SINK) {
1314 out_caps = gst_caps_copy (caps);
1315 if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1316 GstVideoMultiviewMode mode = viewconvert->input_mode_override;
1317 GstVideoMultiviewFlags flags = viewconvert->input_flags_override;
1319 const gchar *caps_str = gst_video_multiview_mode_to_caps_string (mode);
1320 /* Coerce the input caps before transforming, so the sizes come out right */
1321 gst_caps_set_simple (out_caps, "multiview-mode", G_TYPE_STRING,
1322 caps_str, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, flags,
1323 GST_FLAG_SET_MASK_EXACT, NULL);
1326 out_caps = gst_caps_new_empty ();
1329 for (i = 0; i < gst_caps_get_size (caps); i++) {
1330 GstStructure *structure = gst_caps_get_structure (caps, i);
1331 GstCapsFeatures *features = gst_caps_get_features (caps, i);
1332 out_caps = _expand_structure (viewconvert, out_caps, structure, features);
1335 if (gst_caps_is_empty (out_caps))
1338 /* If we have an output mode override, limit things to that */
1339 if (direction == GST_PAD_SINK &&
1340 viewconvert->output_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1342 tmp_caps = _intersect_with_mview_mode (out_caps,
1343 viewconvert->output_mode_override, viewconvert->output_flags_override);
1345 gst_caps_unref (out_caps);
1346 out_caps = tmp_caps;
1347 } else if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1348 /* Prepend a copy of our preferred input caps in case the peer
1349 * can handle them */
1350 tmp_caps = _intersect_with_mview_mode (out_caps,
1351 viewconvert->input_mode_override, viewconvert->input_flags_override);
1352 out_caps = gst_caps_merge (out_caps, tmp_caps);
1354 if (direction == GST_PAD_SRC) {
1356 /* When generating input caps, we also need a copy of the mono caps
1357 * without multiview-mode or flags for backwards compat, at the end */
1358 tmp_caps = _intersect_with_mview_mode (caps,
1359 GST_VIDEO_MULTIVIEW_MODE_MONO, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
1360 if (!gst_caps_is_empty (tmp_caps)) {
1361 s = gst_caps_get_structure (tmp_caps, 0);
1362 gst_structure_remove_fields (s, "multiview-mode", "multiview-flags",
1364 out_caps = gst_caps_merge (out_caps, tmp_caps);
1366 gst_caps_unref (tmp_caps);
1369 gst_caps_unref (caps);
1371 GST_DEBUG_OBJECT (viewconvert, "Have caps %" GST_PTR_FORMAT
1372 " filtering with caps %" GST_PTR_FORMAT, out_caps, filter);
1376 gst_caps_intersect_full (filter, out_caps, GST_CAPS_INTERSECT_FIRST);
1377 gst_caps_unref (out_caps);
1381 GST_DEBUG_OBJECT (viewconvert, "Returning caps %" GST_PTR_FORMAT, out_caps);
1386 fixate_size (GstVulkanViewConvert * viewconvert,
1387 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1389 GstStructure *ins, *outs;
1390 const GValue *from_par, *to_par;
1391 GValue fpar = { 0, }, tpar = {
1394 othercaps = gst_caps_make_writable (othercaps);
1395 othercaps = gst_caps_truncate (othercaps);
1397 GST_DEBUG_OBJECT (viewconvert, "trying to fixate othercaps %" GST_PTR_FORMAT
1398 " based on caps %" GST_PTR_FORMAT, othercaps, caps);
1400 ins = gst_caps_get_structure (caps, 0);
1401 outs = gst_caps_get_structure (othercaps, 0);
1403 from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1404 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1406 /* If we're fixating from the sinkpad we always set the PAR and
1407 * assume that missing PAR on the sinkpad means 1/1 and
1408 * missing PAR on the srcpad means undefined
1410 if (direction == GST_PAD_SINK) {
1412 g_value_init (&fpar, GST_TYPE_FRACTION);
1413 gst_value_set_fraction (&fpar, 1, 1);
1417 g_value_init (&tpar, GST_TYPE_FRACTION);
1418 gst_value_set_fraction (&tpar, 1, 1);
1423 g_value_init (&tpar, GST_TYPE_FRACTION);
1424 gst_value_set_fraction (&tpar, 1, 1);
1427 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1431 g_value_init (&fpar, GST_TYPE_FRACTION);
1432 gst_value_set_fraction (&fpar, 1, 1);
1437 /* we have both PAR but they might not be fixated */
1439 gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1441 gint from_dar_n, from_dar_d;
1444 /* from_par should be fixed */
1445 g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
1447 from_par_n = gst_value_get_fraction_numerator (from_par);
1448 from_par_d = gst_value_get_fraction_denominator (from_par);
1450 gst_structure_get_int (ins, "width", &from_w);
1451 gst_structure_get_int (ins, "height", &from_h);
1453 gst_structure_get_int (outs, "width", &w);
1454 gst_structure_get_int (outs, "height", &h);
1456 /* if both width and height are already fixed, we can't do anything
1457 * about it anymore */
1459 GST_DEBUG_OBJECT (viewconvert,
1460 "dimensions already set to %dx%d, not fixating", w, h);
1461 if (!gst_value_is_fixed (to_par)) {
1462 GST_DEBUG_OBJECT (viewconvert, "fixating to_par to %dx%d", 1, 1);
1463 if (gst_structure_has_field (outs, "pixel-aspect-ratio"))
1464 gst_structure_fixate_field_nearest_fraction (outs,
1465 "pixel-aspect-ratio", 1, 1);
1470 /* Calculate input DAR */
1471 if (!gst_util_fraction_multiply (from_w, from_h, from_par_n, from_par_d,
1472 &from_dar_n, &from_dar_d)) {
1473 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1474 ("Error calculating the output scaled size - integer overflow"));
1478 GST_DEBUG_OBJECT (viewconvert, "Input DAR is %d/%d", from_dar_n,
1481 /* If either width or height are fixed there's not much we
1482 * can do either except choosing a height or width and PAR
1483 * that matches the DAR as good as possible
1488 GST_DEBUG_OBJECT (viewconvert, "height is fixed (%d)", h);
1490 if (!gst_value_is_fixed (to_par)) {
1491 /* (shortcut) copy-paste (??) of videoscale seems to aim for 1/1,
1492 * so let's make it so ...
1493 * especially if following code assumes fixed */
1494 GST_DEBUG_OBJECT (viewconvert, "fixating to_par to 1x1");
1495 gst_structure_fixate_field_nearest_fraction (outs,
1496 "pixel-aspect-ratio", 1, 1);
1497 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1500 /* PAR is fixed, choose the height that is nearest to the
1501 * height with the same DAR */
1502 to_par_n = gst_value_get_fraction_numerator (to_par);
1503 to_par_d = gst_value_get_fraction_denominator (to_par);
1505 GST_DEBUG_OBJECT (viewconvert, "PAR is fixed %d/%d", to_par_n, to_par_d);
1507 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1508 to_par_n, &num, &den)) {
1509 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1510 ("Error calculating the output scaled size - integer overflow"));
1514 w = (guint) gst_util_uint64_scale_int (h, num, den);
1515 gst_structure_fixate_field_nearest_int (outs, "width", w);
1521 GST_DEBUG_OBJECT (viewconvert, "width is fixed (%d)", w);
1523 if (!gst_value_is_fixed (to_par)) {
1524 /* (shortcut) copy-paste (??) of videoscale seems to aim for 1/1,
1525 * so let's make it so ...
1526 * especially if following code assumes fixed */
1527 GST_DEBUG_OBJECT (viewconvert, "fixating to_par to 1x1");
1528 gst_structure_fixate_field_nearest_fraction (outs,
1529 "pixel-aspect-ratio", 1, 1);
1530 to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1533 /* PAR is fixed, choose the height that is nearest to the
1534 * height with the same DAR */
1535 to_par_n = gst_value_get_fraction_numerator (to_par);
1536 to_par_d = gst_value_get_fraction_denominator (to_par);
1538 GST_DEBUG_OBJECT (viewconvert, "PAR is fixed %d/%d", to_par_n, to_par_d);
1540 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_d,
1541 to_par_n, &num, &den)) {
1542 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1543 ("Error calculating the output scaled size - integer overflow"));
1547 h = (guint) gst_util_uint64_scale_int (w, den, num);
1548 gst_structure_fixate_field_nearest_int (outs, "height", h);
1551 } else if (gst_value_is_fixed (to_par)) {
1553 gint set_h, set_w, f_h, f_w;
1555 to_par_n = gst_value_get_fraction_numerator (to_par);
1556 to_par_d = gst_value_get_fraction_denominator (to_par);
1558 /* Calculate scale factor for the PAR change */
1559 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
1560 to_par_d, &num, &den)) {
1561 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1562 ("Error calculating the output scaled size - integer overflow"));
1566 /* Try to keep the input height */
1567 tmp = gst_structure_copy (outs);
1568 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1569 gst_structure_get_int (tmp, "height", &set_h);
1571 /* This might have failed but try to scale the width
1572 * to keep the DAR nonetheless */
1573 w = (guint) gst_util_uint64_scale_int (set_h, num, den);
1574 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1575 gst_structure_get_int (tmp, "width", &set_w);
1576 gst_structure_free (tmp);
1578 /* We kept the DAR and the height is nearest to the original height */
1580 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1581 G_TYPE_INT, set_h, NULL);
1588 /* If the former failed, try to keep the input width at least */
1589 tmp = gst_structure_copy (outs);
1590 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1591 gst_structure_get_int (tmp, "width", &set_w);
1593 /* This might have failed but try to scale the width
1594 * to keep the DAR nonetheless */
1595 h = (guint) gst_util_uint64_scale_int (set_w, den, num);
1596 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1597 gst_structure_get_int (tmp, "height", &set_h);
1598 gst_structure_free (tmp);
1600 /* We kept the DAR and the width is nearest to the original width */
1602 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1603 G_TYPE_INT, set_h, NULL);
1607 /* If all this failed, keep the height that was nearest to the original
1608 * height and the nearest possible width. This changes the DAR but
1609 * there's not much else to do here.
1611 gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
1616 gint set_h, set_w, set_par_n, set_par_d, tmp2;
1618 /* width, height and PAR are not fixed */
1620 /* First try to keep the height and width as good as possible
1622 tmp = gst_structure_copy (outs);
1623 gst_structure_fixate_field_nearest_int (tmp, "height", from_h);
1624 gst_structure_get_int (tmp, "height", &set_h);
1625 gst_structure_fixate_field_nearest_int (tmp, "width", from_w);
1626 gst_structure_get_int (tmp, "width", &set_w);
1628 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_h, set_w,
1629 &to_par_n, &to_par_d)) {
1630 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1631 ("Error calculating the output scaled size - integer overflow"));
1632 gst_structure_free (tmp);
1636 if (!gst_structure_has_field (tmp, "pixel-aspect-ratio"))
1637 gst_structure_set_value (tmp, "pixel-aspect-ratio", to_par);
1638 gst_structure_fixate_field_nearest_fraction (tmp, "pixel-aspect-ratio",
1639 to_par_n, to_par_d);
1640 gst_structure_get_fraction (tmp, "pixel-aspect-ratio", &set_par_n,
1642 gst_structure_free (tmp);
1644 if (set_par_n == to_par_n && set_par_d == to_par_d) {
1645 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1646 G_TYPE_INT, set_h, NULL);
1648 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1649 set_par_n != set_par_d)
1650 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1651 set_par_n, set_par_d, NULL);
1655 /* Otherwise try to scale width to keep the DAR with the set
1657 if (!gst_util_fraction_multiply (from_dar_n, from_dar_d, set_par_d,
1658 set_par_n, &num, &den)) {
1659 GST_ELEMENT_ERROR (viewconvert, CORE, NEGOTIATION, (NULL),
1660 ("Error calculating the output scaled size - integer overflow"));
1664 w = (guint) gst_util_uint64_scale_int (set_h, num, den);
1665 tmp = gst_structure_copy (outs);
1666 gst_structure_fixate_field_nearest_int (tmp, "width", w);
1667 gst_structure_get_int (tmp, "width", &tmp2);
1668 gst_structure_free (tmp);
1671 gst_structure_set (outs, "width", G_TYPE_INT, tmp2, "height",
1672 G_TYPE_INT, set_h, NULL);
1673 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1674 set_par_n != set_par_d)
1675 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1676 set_par_n, set_par_d, NULL);
1680 /* ... or try the same with the height */
1681 h = (guint) gst_util_uint64_scale_int (set_w, den, num);
1682 tmp = gst_structure_copy (outs);
1683 gst_structure_fixate_field_nearest_int (tmp, "height", h);
1684 gst_structure_get_int (tmp, "height", &tmp2);
1685 gst_structure_free (tmp);
1688 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1689 G_TYPE_INT, tmp2, NULL);
1690 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1691 set_par_n != set_par_d)
1692 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1693 set_par_n, set_par_d, NULL);
1697 /* If all fails we can't keep the DAR and take the nearest values
1698 * for everything from the first try */
1699 gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1700 G_TYPE_INT, set_h, NULL);
1701 if (gst_structure_has_field (outs, "pixel-aspect-ratio") ||
1702 set_par_n != set_par_d)
1703 gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1704 set_par_n, set_par_d, NULL);
1709 othercaps = gst_caps_fixate (othercaps);
1711 GST_DEBUG_OBJECT (viewconvert, "fixated othercaps to %" GST_PTR_FORMAT,
1714 if (from_par == &fpar)
1715 g_value_unset (&fpar);
1716 if (to_par == &tpar)
1717 g_value_unset (&tpar);
1723 gst_vulkan_view_convert_fixate_caps (GstBaseTransform * bt,
1724 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1726 GstVulkanViewConvert *viewconvert = GST_VULKAN_VIEW_CONVERT (bt);
1727 GstVideoMultiviewMode mode = viewconvert->output_mode_override;
1728 GstVideoMultiviewFlags flags = viewconvert->output_flags_override;
1731 othercaps = gst_caps_make_writable (othercaps);
1732 GST_LOG_OBJECT (viewconvert, "dir %s fixating %" GST_PTR_FORMAT
1733 " against caps %" GST_PTR_FORMAT,
1734 direction == GST_PAD_SINK ? "sink" : "src", othercaps, caps);
1736 if (direction == GST_PAD_SINK) {
1737 if (mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1738 /* We have a requested output mode and are fixating source caps, try and enforce it */
1739 GST_DEBUG_OBJECT (bt, "fixating multiview mode using the configured "
1740 "output override mode 0x%x and flags 0x%x", mode, flags);
1741 tmp = _intersect_with_mview_mode (othercaps, mode, flags);
1742 gst_caps_unref (othercaps);
1745 /* See if we can do passthrough */
1748 if (gst_video_info_from_caps (&info, caps)) {
1749 GstVideoMultiviewMode mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&info);
1750 GstVideoMultiviewFlags flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&info);
1752 if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1753 mode = viewconvert->input_mode_override;
1754 flags = viewconvert->input_flags_override;
1757 tmp = _intersect_with_mview_mode (othercaps, mode, flags);
1758 if (gst_caps_is_empty (tmp)) {
1759 /* Nope, we can't pass our input caps downstream */
1760 gst_caps_unref (tmp);
1762 GST_DEBUG_OBJECT (bt, "can configure a passthrough multiview mode "
1763 "using the input override mode 0x%x and flags 0x%x", mode, flags);
1764 gst_caps_unref (othercaps);
1770 /* Prefer an unpacked mode for output */
1772 _intersect_with_mview_modes (othercaps,
1773 gst_video_multiview_get_unpacked_modes ());
1774 if (!gst_caps_is_empty (tmp)) {
1775 GST_DEBUG_OBJECT (bt, "preferring an unpacked multiview mode");
1776 gst_caps_unref (othercaps);
1779 gst_caps_unref (tmp);
1782 } else if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1783 /* See if we can coerce the caps into matching input mode/flags,
1784 * in case it doesn't care at all, but allow it not to too */
1785 mode = viewconvert->input_mode_override;
1786 flags = viewconvert->input_flags_override;
1787 tmp = _intersect_with_mview_mode (othercaps, mode, flags);
1788 if (gst_caps_is_empty (tmp)) {
1789 /* Nope, we can pass our input caps downstream */
1790 gst_caps_unref (tmp);
1792 GST_DEBUG_OBJECT (bt, "can configure a passthrough multiview mode "
1793 "using the input override mode 0x%x and flags 0x%x", mode, flags);
1794 gst_caps_unref (othercaps);
1800 othercaps = fixate_size (viewconvert, direction, caps, othercaps);
1801 othercaps = gst_caps_fixate (othercaps);
1803 GST_DEBUG_OBJECT (viewconvert, "dir %s fixated to %" GST_PTR_FORMAT
1804 " against caps %" GST_PTR_FORMAT,
1805 direction == GST_PAD_SINK ? "sink" : "src", othercaps, caps);
1810 destroy_shader_create_info (GstVulkanFullScreenRender * render, gpointer data)
1812 VkPipelineShaderStageCreateInfo *info = data;
1815 for (i = 0; i < render->n_shader_stages; i++) {
1816 vkDestroyShaderModule (render->device->device, info[i].module, NULL);
1823 gst_vulkan_view_convert_shader_create_info (GstVulkanFullScreenRender * render)
1825 VkShaderModule vert_module, frag_module;
1828 _vk_create_shader (render->device, identity_vert, identity_vert_size,
1831 _vk_create_shader (render->device, view_convert_frag,
1832 view_convert_frag_size, NULL);
1834 render->n_shader_stages = 2;
1835 render->shader_create_info =
1836 g_new0 (VkPipelineShaderStageCreateInfo, render->n_shader_stages);
1837 render->destroy_shader_create_info = destroy_shader_create_info;
1840 render->shader_create_info[0] = (VkPipelineShaderStageCreateInfo) {
1841 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1843 .stage = VK_SHADER_STAGE_VERTEX_BIT,
1844 .module = vert_module,
1848 render->shader_create_info[1] = (VkPipelineShaderStageCreateInfo) {
1849 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1851 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
1852 .module = frag_module,
1858 static VkPushConstantRange *
1859 gst_vulkan_view_convert_push_constant_ranges (GstVulkanFullScreenRender *
1860 render, guint * n_constants)
1866 static VkDescriptorSetLayoutBinding
1867 * gst_vulkan_view_convert_descriptor_set_layout_bindings
1868 (GstVulkanFullScreenRender * render, guint * n_bindings)
1870 VkDescriptorSetLayoutBinding *bindings;
1874 *n_bindings += GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2;
1875 *n_bindings += 1; /* uniform binding */
1876 bindings = g_new0 (VkDescriptorSetLayoutBinding, *n_bindings);
1878 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2; i++) {
1880 bindings[i] = (VkDescriptorSetLayoutBinding) {
1882 .descriptorCount = 1,
1883 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1884 .pImmutableSamplers = NULL,
1885 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
1888 bindings[i] = (VkDescriptorSetLayoutBinding) {
1890 .descriptorCount = 1,
1891 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1892 .pImmutableSamplers = NULL,
1893 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
1898 bindings[i] = (VkDescriptorSetLayoutBinding) {
1900 .descriptorCount = 1,
1901 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1902 .pImmutableSamplers = NULL,
1903 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
1908 g_assert (i == *n_bindings);
1913 static VkAttachmentReference
1914 * gst_vulkan_view_convert_render_pass_attachment_references
1915 (GstVulkanFullScreenRender * render, guint * n_attachments)
1917 VkAttachmentReference *attachments;
1920 *n_attachments = GST_VIDEO_INFO_N_PLANES (&render->out_info);
1921 attachments = g_new0 (VkAttachmentReference, *n_attachments);
1923 for (i = 0; i < *n_attachments; i++) {
1925 attachments[i] = (VkAttachmentReference) {
1927 .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
1935 static VkAttachmentDescription
1936 * gst_vulkan_view_convert_render_pass_attachment_descriptions
1937 (GstVulkanFullScreenRender * render, guint * n_descriptions)
1939 VkAttachmentDescription *color_attachments;
1942 *n_descriptions = GST_VIDEO_INFO_N_PLANES (&render->out_info);
1943 color_attachments = g_new0 (VkAttachmentDescription, *n_descriptions);
1944 for (i = 0; i < *n_descriptions; i++) {
1946 color_attachments[i] = (VkAttachmentDescription) {
1947 .format = gst_vulkan_format_from_video_format (GST_VIDEO_INFO_FORMAT (&render->out_info), i),
1948 .samples = VK_SAMPLE_COUNT_1_BIT,
1949 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1950 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
1951 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1952 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1953 /* FIXME: share this between elements to avoid pipeline barriers */
1954 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
1955 .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
1960 return color_attachments;
1964 _create_sampler (GstVulkanViewConvert * conv)
1966 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
1969 VkSamplerCreateInfo samplerInfo = {
1970 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1971 .magFilter = VK_FILTER_NEAREST,
1972 .minFilter = VK_FILTER_NEAREST,
1973 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1974 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1975 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
1976 .anisotropyEnable = VK_FALSE,
1978 .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
1979 .unnormalizedCoordinates = VK_FALSE,
1980 .compareEnable = VK_FALSE,
1981 .compareOp = VK_COMPARE_OP_ALWAYS,
1982 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
1988 GError *error = NULL;
1992 err = vkCreateSampler (render->device->device, &samplerInfo, NULL, &sampler);
1993 if (gst_vulkan_error_to_g_error (err, &error, "vkCreateSampler") < 0) {
1994 g_clear_error (&error);
2002 gst_vulkan_view_convert_start (GstBaseTransform * bt)
2004 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
2006 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->start (bt))
2009 if (!(conv->sampler = _create_sampler (conv)))
2015 static VkDescriptorPool
2016 _create_descriptor_pool (GstVulkanViewConvert * conv)
2018 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2021 VkDescriptorPoolSize pool_sizes[] = {
2023 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
2024 .descriptorCount = GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2,
2027 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
2028 .descriptorCount = 1
2032 VkDescriptorPoolCreateInfo pool_info = {
2033 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
2035 .poolSizeCount = G_N_ELEMENTS (pool_sizes),
2036 .pPoolSizes = pool_sizes,
2040 VkDescriptorPool pool;
2041 GError *error = NULL;
2045 vkCreateDescriptorPool (render->device->device, &pool_info, NULL, &pool);
2046 if (gst_vulkan_error_to_g_error (err, &error, "vkCreateDescriptorPool") < 0) {
2047 GST_ERROR_OBJECT (render, "Failed to create descriptor pool: %s",
2049 g_clear_error (&error);
2056 static VkDescriptorSet
2057 _create_descriptor_set (GstVulkanViewConvert * conv)
2059 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2062 VkDescriptorSetAllocateInfo alloc_info = {
2063 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
2065 .descriptorPool = conv->descriptor_pool,
2066 .descriptorSetCount = 1,
2067 .pSetLayouts = &render->descriptor_set_layout
2070 VkDescriptorSet descriptor;
2071 GError *error = NULL;
2075 vkAllocateDescriptorSets (render->device->device, &alloc_info,
2077 if (gst_vulkan_error_to_g_error (err, &error, "vkAllocateDescriptorSets") < 0) {
2078 GST_ERROR_OBJECT (conv, "Failed to allocate descriptor: %s",
2080 g_clear_error (&error);
2088 _create_uniform_buffer (GstVulkanViewConvert * conv)
2090 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2093 gst_vulkan_buffer_memory_alloc (render->device,
2094 sizeof (struct ViewUpdate),
2095 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2096 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
2097 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
2103 gst_vulkan_view_convert_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
2106 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
2107 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
2109 if (render->last_fence) {
2110 if (conv->descriptor_pool)
2111 gst_vulkan_trash_list_add (render->trash_list,
2112 gst_vulkan_trash_new_free_descriptor_pool (gst_vulkan_fence_ref
2113 (render->last_fence), conv->descriptor_pool));
2114 conv->descriptor_set = NULL;
2115 conv->descriptor_pool = NULL;
2117 gst_vulkan_trash_list_add (render->trash_list,
2118 gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref
2119 (render->last_fence), (GstMiniObject *) conv->uniform));
2120 conv->uniform = NULL;
2122 if (conv->descriptor_pool)
2123 vkDestroyDescriptorPool (render->device->device,
2124 conv->descriptor_pool, NULL);
2125 conv->descriptor_set = NULL;
2126 conv->descriptor_pool = NULL;
2128 gst_memory_unref (conv->uniform);
2129 conv->uniform = NULL;
2132 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->set_caps (bt, in_caps,
2136 if (!(conv->descriptor_pool = _create_descriptor_pool (conv)))
2138 if (!(conv->descriptor_set = _create_descriptor_set (conv)))
2141 if (!_create_uniform_buffer (conv))
2148 gst_vulkan_view_convert_stop (GstBaseTransform * bt)
2150 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
2151 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
2153 conv->descriptor_up_to_date = FALSE;
2155 if (render->device) {
2156 if (render->last_fence) {
2157 if (conv->descriptor_pool)
2158 gst_vulkan_trash_list_add (render->trash_list,
2159 gst_vulkan_trash_new_free_descriptor_pool (gst_vulkan_fence_ref
2160 (render->last_fence), conv->descriptor_pool));
2161 conv->descriptor_set = NULL;
2162 conv->descriptor_pool = NULL;
2164 gst_vulkan_trash_list_add (render->trash_list,
2165 gst_vulkan_trash_new_free_sampler (gst_vulkan_fence_ref
2166 (render->last_fence), conv->sampler));
2167 conv->sampler = NULL;
2169 gst_vulkan_trash_list_add (render->trash_list,
2170 gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref
2171 (render->last_fence), (GstMiniObject *) conv->uniform));
2172 conv->uniform = NULL;
2174 if (conv->descriptor_pool)
2175 vkDestroyDescriptorPool (render->device->device,
2176 conv->descriptor_pool, NULL);
2177 conv->descriptor_set = NULL;
2178 conv->descriptor_pool = NULL;
2180 vkDestroySampler (render->device->device, conv->sampler, NULL);
2181 conv->sampler = NULL;
2183 gst_memory_unref (conv->uniform);
2184 conv->uniform = NULL;
2189 gst_object_unref (conv->cmd_pool);
2190 conv->cmd_pool = NULL;
2192 return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
2195 static VkFramebuffer
2196 _create_framebuffer (GstVulkanViewConvert * conv, guint n_views,
2197 VkImageView * views)
2199 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2202 VkFramebufferCreateInfo framebuffer_info = {
2203 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
2205 .renderPass = render->render_pass,
2206 .attachmentCount = n_views,
2207 .pAttachments = views,
2208 .width = GST_VIDEO_INFO_WIDTH (&render->out_info),
2209 .height = GST_VIDEO_INFO_HEIGHT (&render->out_info),
2213 VkFramebuffer framebuffer;
2214 GError *error = NULL;
2218 vkCreateFramebuffer (render->device->device, &framebuffer_info, NULL,
2220 if (gst_vulkan_error_to_g_error (err, &error, "vkCreateFramebuffer") < 0) {
2221 GST_ERROR_OBJECT (render, "Failed to create framebuffer: %s",
2223 g_clear_error (&error);
2230 static GstFlowReturn
2231 gst_vulkan_view_convert_transform (GstBaseTransform * bt, GstBuffer * inbuf,
2234 GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
2235 GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
2236 GstVulkanImageMemory *in_img_mems[GST_VIDEO_MAX_PLANES] = { NULL, };
2237 GstVulkanImageView *in_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
2238 GstVulkanImageMemory *out_img_mems[GST_VIDEO_MAX_PLANES] = { NULL, };
2239 GstVulkanImageView *out_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
2240 GstVulkanFence *fence = NULL;
2241 GstVulkanCommandBuffer *cmd_buf;
2242 VkFramebuffer framebuffer;
2243 GError *error = NULL;
2247 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
2248 GstMemory *mem = gst_buffer_peek_memory (inbuf, i);
2249 if (!gst_is_vulkan_image_memory (mem)) {
2250 g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
2251 "Input memory must be a GstVulkanImageMemory");
2254 in_img_mems[i] = (GstVulkanImageMemory *) mem;
2255 in_img_views[i] = get_or_create_image_view (in_img_mems[i]);
2256 gst_vulkan_trash_list_add (render->trash_list,
2257 gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
2258 (GstMiniObject *) in_img_views[i]));
2261 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
2262 GstMemory *mem = gst_buffer_peek_memory (outbuf, i);
2263 if (!gst_is_vulkan_image_memory (mem)) {
2264 g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
2265 "Output memory must be a GstVulkanImageMemory");
2268 out_img_mems[i] = (GstVulkanImageMemory *) mem;
2269 in_img_views[i] = get_or_create_image_view (out_img_mems[i]);
2270 gst_vulkan_trash_list_add (render->trash_list,
2271 gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
2272 (GstMiniObject *) out_img_views[i]));
2275 if (!conv->cmd_pool) {
2276 if (!(conv->cmd_pool =
2277 gst_vulkan_queue_create_command_pool (render->queue, &error)))
2281 if (!(cmd_buf = gst_vulkan_command_pool_create (conv->cmd_pool, &error)))
2284 fence = gst_vulkan_fence_new (render->device, 0, &error);
2289 VkCommandBufferBeginInfo cmd_buf_info = { 0, };
2292 cmd_buf_info = (VkCommandBufferBeginInfo) {
2293 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
2295 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
2296 .pInheritanceInfo = NULL
2300 gst_vulkan_command_buffer_lock (cmd_buf);
2301 err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
2302 if (gst_vulkan_error_to_g_error (err, &error, "vkBeginCommandBuffer") < 0)
2306 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
2308 VkImageMemoryBarrier in_image_memory_barrier = {
2309 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
2311 .srcAccessMask = in_img_mems[i]->barrier.parent.access_flags,
2312 .dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
2313 .oldLayout = in_img_mems[i]->barrier.image_layout,
2314 .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
2315 /* FIXME: implement exclusive transfers */
2316 .srcQueueFamilyIndex = 0,
2317 .dstQueueFamilyIndex = 0,
2318 .image = in_img_mems[i]->image,
2319 .subresourceRange = in_img_mems[i]->barrier.subresource_range
2323 vkCmdPipelineBarrier (cmd_buf->cmd,
2324 in_img_mems[i]->barrier.parent.pipeline_stages,
2325 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1,
2326 &in_image_memory_barrier);
2328 in_img_mems[i]->barrier.parent.pipeline_stages =
2329 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2330 in_img_mems[i]->barrier.parent.access_flags =
2331 in_image_memory_barrier.dstAccessMask;
2332 in_img_mems[i]->barrier.image_layout = in_image_memory_barrier.newLayout;
2335 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
2336 VkImageMemoryBarrier out_image_memory_barrier;
2339 out_image_memory_barrier = (VkImageMemoryBarrier) {
2340 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
2342 .srcAccessMask = out_img_mems[i]->barrier.parent.access_flags,
2343 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
2344 .oldLayout = out_img_mems[i]->barrier.image_layout,
2345 .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
2346 /* FIXME: implement exclusive transfers */
2347 .srcQueueFamilyIndex = 0,
2348 .dstQueueFamilyIndex = 0,
2349 .image = out_img_mems[i]->image,
2350 .subresourceRange = out_img_mems[i]->barrier.subresource_range
2354 vkCmdPipelineBarrier (cmd_buf->cmd,
2355 out_img_mems[i]->barrier.parent.pipeline_stages,
2356 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1,
2357 &out_image_memory_barrier);
2359 out_img_mems[i]->barrier.parent.pipeline_stages =
2360 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2361 out_img_mems[i]->barrier.parent.access_flags =
2362 out_image_memory_barrier.dstAccessMask;
2363 out_img_mems[i]->barrier.image_layout = out_image_memory_barrier.newLayout;
2367 VkImageView attachments[4] = { 0, };
2368 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
2369 attachments[i] = out_img_views[i]->view;
2372 if (!(framebuffer = _create_framebuffer (conv,
2373 GST_VIDEO_INFO_N_PLANES (&render->out_info), attachments))) {
2374 g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
2375 "Failed to create framebuffer");
2380 view_convert_update_command_state (conv, cmd_buf->cmd, in_img_views,
2383 if (!gst_vulkan_full_screen_render_fill_command_buffer (render, cmd_buf->cmd,
2385 g_set_error (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
2386 "Failed to fill framebuffer");
2390 err = vkEndCommandBuffer (cmd_buf->cmd);
2391 gst_vulkan_command_buffer_unlock (cmd_buf);
2392 if (gst_vulkan_error_to_g_error (err, &error, "vkEndCommandBuffer") < 0)
2395 gst_vulkan_trash_list_add (render->trash_list,
2396 gst_vulkan_trash_new_free_framebuffer (gst_vulkan_fence_ref (fence),
2398 gst_vulkan_trash_list_add (render->trash_list,
2399 gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
2400 GST_MINI_OBJECT_CAST (cmd_buf)));
2402 if (!gst_vulkan_full_screen_render_submit (render, cmd_buf->cmd, fence))
2403 return GST_FLOW_ERROR;
2409 gst_vulkan_command_buffer_unlock (cmd_buf);
2410 gst_vulkan_command_buffer_unref (cmd_buf);
2413 GST_ELEMENT_ERROR (bt, LIBRARY, FAILED, ("%s", error->message), (NULL));
2414 g_clear_error (&error);
2415 return GST_FLOW_ERROR;