documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / ext / vulkan / vkviewconvert.c
1 /*
2  * GStreamer
3  * Copyright (C) 2019 Matthew Waters <matthew@centricular.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:element-vulkanimageidentity
23  * @title: vulkanimgeidentity
24  *
25  * vulkanimageidentity produces a vulkan image that is a copy of the input image.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <string.h>
33
34 #include "vkviewconvert.h"
35 #include "vkshader.h"
36 #include "vkelementutils.h"
37
38 #include "shaders/identity.vert.h"
39 #include "shaders/view_convert.frag.h"
40 #include "gstvulkan-plugins-enumtypes.h"
41
42 GST_DEBUG_CATEGORY (gst_debug_vulkan_view_convert);
43 #define GST_CAT_DEFAULT gst_debug_vulkan_view_convert
44
45 /* *INDENT-OFF* */
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}
51   },
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}
56   },
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}
60   }
61 };
62 /* *INDENT-ON* */
63
64 struct ViewUpdate
65 {
66   int in_reorder_idx[4];
67   int out_reorder_idx[4];
68   float tex_offset[2][2];
69   float tex_scale[2][2];
70   int tex_size[2];
71   int output_type;
72   int _padding;
73   float downmix[2][12];
74 };
75
76 static void
77 get_rgb_format_swizzle_order (GstVideoFormat format,
78     gint swizzle[GST_VIDEO_MAX_COMPONENTS])
79 {
80   const GstVideoFormatInfo *finfo = gst_video_format_get_info (format);
81   int c_i = 0, i;
82
83   g_return_if_fail (finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB
84       || format == GST_VIDEO_FORMAT_AYUV);
85
86   for (i = 0; i < finfo->n_components; i++) {
87     swizzle[c_i++] = finfo->poffset[i];
88   }
89
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
92    * correctly */
93   if (format == GST_VIDEO_FORMAT_xRGB || format == GST_VIDEO_FORMAT_xBGR) {
94     swizzle[c_i++] = 0;
95   } else if (format == GST_VIDEO_FORMAT_RGBx || format == GST_VIDEO_FORMAT_BGRx) {
96     swizzle[c_i++] = 3;
97   } else {
98     for (i = finfo->n_components; i < GST_VIDEO_MAX_COMPONENTS; i++) {
99       swizzle[c_i++] = -1;
100     }
101   }
102 }
103
104 static void
105 get_vulkan_rgb_format_swizzle_order (VkFormat format, gint * swizzle,
106     guint swizzle_count, guint offset)
107 {
108   const GstVulkanFormatInfo *finfo = gst_vulkan_format_get_info (format);
109   int i;
110
111   g_return_if_fail (finfo->flags & GST_VULKAN_FORMAT_FLAG_RGB);
112   g_return_if_fail (finfo->n_components <= swizzle_count);
113
114   for (i = 0; i < finfo->n_components; i++) {
115     swizzle[i] = offset + finfo->poffset[i];
116   }
117   for (i = finfo->n_components; i < swizzle_count; i++) {
118     swizzle[i] = -1;
119   }
120 }
121
122 /* given a swizzle index, produce an index such that:
123  *
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
129  */
130 static void
131 swizzle_identity_order (gint * swizzle, gint * idx)
132 {
133   int i;
134
135   for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
136     idx[i] = -1;
137   }
138
139   for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
140     if (swizzle[i] >= 0 && swizzle[i] < 4 && idx[swizzle[i]] == -1) {
141       idx[swizzle[i]] = i;
142     }
143   }
144 }
145
146 static void
147 video_format_to_reorder (GstVideoFormat v_format, gint * reorder,
148     gboolean input)
149 {
150   switch (v_format) {
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);
161       break;
162     case GST_VIDEO_FORMAT_UYVY:
163       reorder[0] = 1;
164       reorder[1] = 0;
165       reorder[2] = input ? 3 : 2;
166       reorder[3] = 0;
167       break;
168     case GST_VIDEO_FORMAT_YUY2:
169       reorder[0] = 0;
170       reorder[1] = 1;
171       reorder[2] = 0;
172       reorder[3] = input ? 3 : 2;
173       break;
174     case GST_VIDEO_FORMAT_NV12:
175       reorder[0] = 0;
176       reorder[1] = 1;
177       reorder[2] = 2;
178       reorder[3] = 0;
179       break;
180     default:
181       g_assert_not_reached ();
182       break;
183   }
184
185   GST_TRACE ("swizzle: %u, %u, %u, %u", reorder[0], reorder[1], reorder[2],
186       reorder[3]);
187 }
188
189 static guint
190 finfo_get_plane_n_components (const GstVideoFormatInfo * finfo, guint plane)
191 {
192   guint n_components = 0, i;
193
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
200        * component */
201       return plane == 0 ? 4 : 0;
202     default:
203       break;
204   }
205
206   for (i = 0; i < finfo->n_components; i++) {
207     if (finfo->plane[i] == plane)
208       n_components++;
209   }
210
211   return n_components;
212 }
213
214 static void
215 get_vulkan_format_swizzle_order (GstVideoFormat v_format,
216     VkFormat vk_format[GST_VIDEO_MAX_PLANES],
217     gint swizzle[GST_VIDEO_MAX_COMPONENTS])
218 {
219   const GstVideoFormatInfo *finfo;
220   int i, prev_in_i = 0;
221
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;
228   }
229
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);
235     swizzle[2] = 2;
236     swizzle[3] = 3;
237   }
238
239   GST_TRACE ("%s: %i, %i, %i, %i", finfo->name, swizzle[0], swizzle[1],
240       swizzle[2], swizzle[3]);
241 }
242
243 static void
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])
249 {
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];
258   int i;
259
260   in_finfo = gst_video_format_get_info (in_format);
261   out_finfo = gst_video_format_get_info (out_format);
262
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;
267
268   get_vulkan_format_swizzle_order (in_format, in_vk_formats, in_vk_order);
269   video_format_to_reorder (in_format, in_reorder, TRUE);
270
271   video_format_to_reorder (out_format, out_reorder, FALSE);
272   get_vulkan_format_swizzle_order (out_format, out_vk_formats, out_vk_order);
273
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++)
281       ret_out[i] = tmp[i];
282   } else {
283     swizzle_identity_order (tmp, ret_out);
284   }
285
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],
289       ret_in[3]);
290   GST_TRACE ("out reorder: %u, %u, %u, %u", ret_out[0], ret_out[1], ret_out[2],
291       ret_out[3]);
292 }
293
294 static void
295 update_descriptor_set (GstVulkanViewConvert * conv, VkImageView * views,
296     guint n_views)
297 {
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];
302   guint i = 0;
303
304   for (; i < GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2; i++) {
305     /* *INDENT-OFF* */
306     image_info[i] = (VkDescriptorImageInfo) {
307         .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
308         .imageView = views[i],
309         .sampler = conv->sampler
310     };
311
312     g_assert (i < n_views);
313     g_assert (i < GST_VIDEO_MAX_PLANES);
314
315     writes[i] = (VkWriteDescriptorSet) {
316         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
317         .pNext = NULL,
318         .dstSet = conv->descriptor_set,
319         .dstBinding = i,
320         .dstArrayElement = 0,
321         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
322         .descriptorCount = 1,
323         .pImageInfo = &image_info[i]
324     };
325     /* *INDENT-ON* */
326   }
327   /* *INDENT-OFF* */
328   buffer_info = (VkDescriptorBufferInfo) {
329       .buffer = ((GstVulkanBufferMemory *) conv->uniform)->buffer,
330       .offset = 0,
331       .range = sizeof (struct ViewUpdate),
332   };
333   writes[i] = (VkWriteDescriptorSet) {
334       .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
335       .pNext = NULL,
336       .dstSet = conv->descriptor_set,
337       .dstBinding = i,
338       .dstArrayElement = 0,
339       .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
340       .descriptorCount = 1,
341       .pBufferInfo = &buffer_info
342   };
343   /* *INDENT-ON* */
344   i++;
345   g_assert (i <= G_N_ELEMENTS (writes));
346
347   vkUpdateDescriptorSets (render->device->device, i, writes, 0, NULL);
348 }
349
350 static gboolean
351 _update_uniform (GstVulkanViewConvert * conv, GstVulkanImageView ** in_views,
352     GstVulkanImageView ** out_views, VkImageView views[GST_VIDEO_MAX_PLANES])
353 {
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;
358   GstMapInfo map_info;
359   guint l_index, r_index;
360   gboolean mono_input = FALSE;
361
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);
365
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.;
370
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);
376   }
377
378   /* Configured output mode already takes any override
379    * into account */
380   out_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&render->out_info);
381   out_flags = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&render->out_info);
382
383   if ((in_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST) ==
384       (out_flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)) {
385     l_index = 0;
386     r_index = 1;
387   } else {
388     VkImageView scratch_view;
389
390     GST_LOG_OBJECT (conv, "Switching left/right views");
391
392     l_index = 1;
393     r_index = 0;
394     scratch_view = views[0];
395     views[0] = views[1];
396     views[1] = scratch_view;
397   }
398
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;
410   }
411
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;
420   }
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;
425   }
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;
430   }
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;
435   }
436
437   if (out_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE ||
438       out_mode == GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX) {
439     /* Side-by-Side */
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;
447   }
448
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]);
455
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)
460     mono_input = TRUE;
461
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) {
465     if (mono_input)
466       data.output_type = GST_VIDEO_MULTIVIEW_MODE_LEFT;
467     else
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;
471   }
472
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]));
479
480   if (!gst_memory_map (conv->uniform, &map_info, GST_MAP_WRITE)) {
481     return FALSE;
482   }
483   memcpy (map_info.data, &data, sizeof (data));
484   gst_memory_unmap (conv->uniform, &map_info);
485   conv->descriptor_up_to_date = TRUE;
486
487   return TRUE;
488 }
489
490 static gboolean
491 view_convert_update_command_state (GstVulkanViewConvert * conv,
492     VkCommandBuffer cmd, GstVulkanImageView ** in_views,
493     GstVulkanImageView ** out_views)
494 {
495   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
496   VkImageView views[GST_VIDEO_MAX_PLANES];
497   int i;
498
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;
502   }
503
504   if (!conv->descriptor_up_to_date) {
505     if (!_update_uniform (conv, in_views, out_views, views))
506       return FALSE;
507     update_descriptor_set (conv, views,
508         GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2);
509   }
510
511   vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
512       render->pipeline_layout, 0, 1, &conv->descriptor_set, 0, NULL);
513
514   return TRUE;
515 }
516
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);
521
522 static gboolean gst_vulkan_view_convert_start (GstBaseTransform * bt);
523 static gboolean gst_vulkan_view_convert_stop (GstBaseTransform * bt);
524
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);
533
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);
543 static void
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);
548
549 static GstStaticPadTemplate gst_vulkan_sink_template =
550 GST_STATIC_PAD_TEMPLATE ("sink",
551     GST_PAD_SINK,
552     GST_PAD_ALWAYS,
553     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
554         (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
555             "{ BGRA, RGBA }")));
556
557 static GstStaticPadTemplate gst_vulkan_src_template =
558 GST_STATIC_PAD_TEMPLATE ("src",
559     GST_PAD_SRC,
560     GST_PAD_ALWAYS,
561     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
562         (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
563             "{ BGRA, RGBA }")));
564
565 enum
566 {
567   PROP_0,
568   PROP_INPUT_LAYOUT,
569   PROP_INPUT_FLAGS,
570   PROP_OUTPUT_LAYOUT,
571   PROP_OUTPUT_FLAGS,
572   PROP_OUTPUT_DOWNMIX_MODE
573 };
574
575 #define DEFAULT_DOWNMIX GST_VULKAN_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
576
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"));
582
583 static void
584 gst_vulkan_view_convert_class_init (GstVulkanViewConvertClass * klass)
585 {
586   GObjectClass *gobject_class;
587   GstElementClass *gstelement_class;
588   GstBaseTransformClass *gstbasetransform_class;
589   GstVulkanFullScreenRenderClass *fullscreenrender_class;
590
591   gobject_class = (GObjectClass *) klass;
592   gstelement_class = (GstElementClass *) klass;
593   gstbasetransform_class = (GstBaseTransformClass *) klass;
594   fullscreenrender_class = (GstVulkanFullScreenRenderClass *) klass;
595
596   gobject_class->set_property = gst_vulkan_view_convert_set_property;
597   gobject_class->get_property = gst_vulkan_view_convert_get_property;
598
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));
629
630   gst_element_class_set_metadata (gstelement_class, "Vulkan Uploader",
631       "Filter/Video/Convert", "A Vulkan View Convert",
632       "Matthew Waters <matthew@centricular.com>");
633
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);
638
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;
648
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;
659 }
660
661 static void
662 gst_vulkan_view_convert_init (GstVulkanViewConvert * conv)
663 {
664   conv->downmix_mode = DEFAULT_DOWNMIX;
665
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;
670 }
671
672 static void
673 gst_vulkan_view_convert_set_property (GObject * object, guint prop_id,
674     const GValue * value, GParamSpec * pspec)
675 {
676   GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (object);
677
678   switch (prop_id) {
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));
682       break;
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));
686       break;
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));
690       break;
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));
694       break;
695     case PROP_OUTPUT_DOWNMIX_MODE:
696       conv->downmix_mode = g_value_get_enum (value);
697       break;
698     default:
699       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
700       break;
701   }
702   GST_OBJECT_LOCK (conv);
703   conv->descriptor_up_to_date = FALSE;
704   GST_OBJECT_UNLOCK (conv);
705 }
706
707 static void
708 gst_vulkan_view_convert_get_property (GObject * object, guint prop_id,
709     GValue * value, GParamSpec * pspec)
710 {
711   GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (object);
712
713   switch (prop_id) {
714     case PROP_INPUT_LAYOUT:
715       g_value_set_enum (value, conv->input_mode_override);
716       break;
717     case PROP_INPUT_FLAGS:
718       g_value_set_flags (value, conv->input_flags_override);
719       break;
720     case PROP_OUTPUT_LAYOUT:
721       g_value_set_enum (value, conv->output_mode_override);
722       break;
723     case PROP_OUTPUT_FLAGS:
724       g_value_set_flags (value, conv->output_flags_override);
725       break;
726     case PROP_OUTPUT_DOWNMIX_MODE:
727       g_value_set_enum (value, conv->downmix_mode);
728       break;
729     default:
730       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
731       break;
732   }
733 }
734
735 /* Function that can halve the value
736  * of ints, fractions, int/fraction ranges and lists of ints/fractions */
737 static gboolean
738 _halve_value (GValue * out, const GValue * in_value)
739 {
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)) {
745     gint num, den;
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;
753     }
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);
760     if (range_min != 1)
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)) {
778     gint i;
779     g_value_init (out, GST_TYPE_LIST);
780     for (i = 0; i < gst_value_list_get_size (in_value); i++) {
781       const GValue *entry;
782       GValue tmp = G_VALUE_INIT;
783
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))
787         goto fail;
788       gst_value_list_append_and_take_value (out, &tmp);
789     }
790   } else {
791     return FALSE;
792   }
793
794   return TRUE;
795 fail:
796   g_value_unset (out);
797   return FALSE;
798 }
799
800 static GstStructure *
801 _halve_structure_field (const GstStructure * in, const gchar * field_name)
802 {
803   GstStructure *out;
804   const GValue *in_value = gst_structure_get_value (in, field_name);
805   GValue tmp = G_VALUE_INIT;
806
807   if (G_UNLIKELY (in_value == NULL))
808     return gst_structure_copy (in);     /* Field doesn't exist, leave it as is */
809
810   if (!_halve_value (&tmp, in_value))
811     return NULL;
812
813   out = gst_structure_copy (in);
814   gst_structure_set_value (out, field_name, &tmp);
815   g_value_unset (&tmp);
816
817   return out;
818 }
819
820 /* Function that can double the value
821  * of ints, fractions, int/fraction ranges and lists of ints/fractions */
822 static gboolean
823 _double_value (GValue * out, const GValue * in_value)
824 {
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);
831     else
832       g_value_set_int (out, G_MAXINT);
833   } else if (GST_VALUE_HOLDS_FRACTION (in_value)) {
834     gint num, den;
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;
842     }
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);
850       range_min *= 2;
851     }
852     if (range_max != G_MAXINT) {
853       range_max = MIN (G_MAXINT / 2, range_max);
854       range_max *= 2;
855     }
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)) {
871     gint i;
872     g_value_init (out, GST_TYPE_LIST);
873     for (i = 0; i < gst_value_list_get_size (in_value); i++) {
874       const GValue *entry;
875       GValue tmp = G_VALUE_INIT;
876
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))
880         goto fail;
881       gst_value_list_append_and_take_value (out, &tmp);
882     }
883   } else {
884     return FALSE;
885   }
886
887   return TRUE;
888 fail:
889   g_value_unset (out);
890   return FALSE;
891 }
892
893 static GstStructure *
894 _double_structure_field (const GstStructure * in, const gchar * field_name)
895 {
896   GstStructure *out;
897   const GValue *in_value = gst_structure_get_value (in, field_name);
898   GValue tmp = G_VALUE_INIT;
899
900   if (G_UNLIKELY (in_value == NULL))
901     return gst_structure_copy (in);     /* Field doesn't exist, leave it as is */
902
903   if (!_double_value (&tmp, in_value))
904     return NULL;
905
906   out = gst_structure_copy (in);
907   gst_structure_set_value (out, field_name, &tmp);
908   g_value_unset (&tmp);
909
910   return out;
911 }
912
913 /* Return a copy of the caps with the requested field doubled in value/range */
914 static GstCaps *
915 _double_caps_field (const GstCaps * in, const gchar * field_name)
916 {
917   gint i;
918   GstCaps *out = gst_caps_new_empty ();
919
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);
923
924     GstStructure *res = _double_structure_field (cur, field_name);
925     out =
926         gst_caps_merge_structure_full (out, res,
927         f ? gst_caps_features_copy (f) : NULL);
928   }
929
930   return out;
931 }
932
933 /* Takes ownership of the input caps  */
934 static GstCaps *
935 _expand_par_for_half_aspect (GstCaps * in, gboolean vertical_half_aspect)
936 {
937   guint mview_flags, mview_flags_mask;
938   GstCaps *out;
939   GstStructure *tmp;
940
941   out = gst_caps_new_empty ();
942
943   while (gst_caps_get_size (in) > 0) {
944     GstStructure *s;
945     GstCapsFeatures *features;
946
947     features = gst_caps_get_features (in, 0);
948     if (features)
949       features = gst_caps_features_copy (features);
950
951     s = gst_caps_steal_structure (in, 0);
952
953     if (!gst_structure_get_flagset (s, "multiview-flags", &mview_flags,
954             &mview_flags_mask)) {
955       gst_caps_append_structure_full (out, s, features);
956       continue;
957     }
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);
961       continue;
962     }
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);
970       continue;
971     }
972
973     /* Halve or double PAR base on inputs input specified. */
974
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);
979
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");
985       else
986         tmp = _double_structure_field (s, "pixel-aspect-ratio");
987       /* Clear the flag */
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);
992     } else {
993       if (vertical_half_aspect)
994         tmp = _double_structure_field (s, "pixel-aspect-ratio");
995       else
996         tmp = _halve_structure_field (s, "pixel-aspect-ratio");
997       /* Set the flag */
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);
1002     }
1003
1004     out = gst_caps_merge_structure_full (out, tmp,
1005         features ? gst_caps_features_copy (features) : NULL);
1006
1007     gst_structure_free (s);
1008     if (features)
1009       gst_caps_features_free (features);
1010   }
1011
1012   gst_caps_unref (in);
1013
1014   return out;
1015 }
1016
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
1025  */
1026 static GstCaps *
1027 _expand_structure (GstVulkanViewConvert * viewconvert,
1028     GstCaps * out_caps, GstStructure * structure, GstCapsFeatures * features)
1029 {
1030   GstCaps *expanded_caps, *tmp;
1031   GstCaps *mono_caps;
1032   const gchar *default_mview_mode_str = NULL;
1033   guint mview_flags, mview_flags_mask;
1034   const GValue *in_modes;
1035   gint i;
1036
1037   /* Empty caps to accumulate into */
1038   expanded_caps = gst_caps_new_empty ();
1039
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);
1043
1044   mview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1045   mview_flags_mask = GST_FLAG_SET_MASK_EXACT;
1046
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);
1050   }
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);
1055   } else {
1056     gst_structure_get_flagset (structure, "multiview-flags",
1057         &mview_flags, &mview_flags_mask);
1058   }
1059
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);
1073     }
1074     gst_caps_append_structure_full (mono_caps, new_struct,
1075         features ? gst_caps_features_copy (features) : NULL);
1076   }
1077   if (gst_value_intersect (NULL, in_modes,
1078           gst_video_multiview_get_unpacked_modes ())) {
1079     GstStructure *new_struct = gst_structure_copy (structure);
1080
1081     gst_structure_set_value (new_struct, "multiview-mode",
1082         gst_video_multiview_get_mono_modes ());
1083
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);
1090     }
1091     gst_caps_append_structure_full (mono_caps, new_struct,
1092         features ? gst_caps_features_copy (features) : NULL);
1093   }
1094
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) {
1103       GstStructure *s =
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);
1110       new_struct = s;
1111     }
1112     mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct,
1113         features ? gst_caps_features_copy (features) : NULL);
1114   }
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) {
1123       GstStructure *s =
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);
1130       new_struct = s;
1131     }
1132     mono_caps = gst_caps_merge_structure_full (mono_caps, new_struct,
1133         features ? gst_caps_features_copy (features) : NULL);
1134   }
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);
1146   }
1147
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 */
1158       mview_flags_mask &=
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);
1163     }
1164   }
1165
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);
1179     }
1180   }
1181   expanded_caps = gst_caps_merge (expanded_caps, tmp);
1182
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);
1196     }
1197   }
1198   expanded_caps = gst_caps_merge (expanded_caps, tmp);
1199
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);
1205
1206   expanded_caps = gst_caps_merge (expanded_caps, tmp);
1207
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);
1213
1214   expanded_caps = gst_caps_merge (expanded_caps, tmp);
1215
1216   /* Double size output modes */
1217   {
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);
1224   }
1225
1226   /* We're done with the mono caps now */
1227   gst_caps_unref (mono_caps);
1228
1229   GST_TRACE_OBJECT (viewconvert,
1230       "expanded transform caps now %" GST_PTR_FORMAT, expanded_caps);
1231
1232   if (gst_caps_is_empty (expanded_caps)) {
1233     gst_caps_unref (expanded_caps);
1234     return out_caps;
1235   }
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);
1241 */
1242   out_caps = gst_caps_merge (out_caps, expanded_caps);
1243 /*  out_caps = gst_caps_merge (out_caps, tmp);*/
1244   return out_caps;
1245 }
1246
1247 static GstCaps *
1248 _intersect_with_mview_mode (GstCaps * caps,
1249     GstVideoMultiviewMode mode, GstVideoMultiviewFlags flags)
1250 {
1251   GstCaps *filter, *result;
1252   const gchar *caps_str;
1253
1254   caps_str = gst_video_multiview_mode_to_caps_string (mode);
1255
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);
1260
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 ());
1265
1266   GST_DEBUG ("Intersecting target caps %" GST_PTR_FORMAT
1267       " with caps %" GST_PTR_FORMAT, caps, filter);
1268
1269   result = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1270   gst_caps_unref (filter);
1271   return result;
1272 }
1273
1274 static GstCaps *
1275 _intersect_with_mview_modes (GstCaps * caps, const GValue * modes)
1276 {
1277   GstCaps *filter, *result;
1278
1279   filter = gst_caps_new_empty_simple ("video/x-raw");
1280
1281   gst_caps_set_value (filter, "multiview-mode", modes);
1282   gst_caps_set_features (filter, 0, gst_caps_features_new_any ());
1283
1284   GST_DEBUG ("Intersecting target caps %" GST_PTR_FORMAT
1285       " with caps %" GST_PTR_FORMAT, caps, filter);
1286
1287   result = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1288   gst_caps_unref (filter);
1289   return result;
1290 }
1291
1292 static GstCaps *
1293 gst_vulkan_view_convert_transform_caps (GstBaseTransform * bt,
1294     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1295 {
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;
1300   gint i;
1301
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);
1305
1306   /* We can only process VulkanImage caps, start from that */
1307   caps = gst_caps_intersect (caps, base_caps);
1308   gst_caps_unref (base_caps);
1309
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
1312    * passthrough */
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;
1318
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);
1324     }
1325   } else {
1326     out_caps = gst_caps_new_empty ();
1327   }
1328
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);
1333   }
1334
1335   if (gst_caps_is_empty (out_caps))
1336     goto out;
1337
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) {
1341
1342     tmp_caps = _intersect_with_mview_mode (out_caps,
1343         viewconvert->output_mode_override, viewconvert->output_flags_override);
1344
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);
1353   }
1354   if (direction == GST_PAD_SRC) {
1355     GstStructure *s;
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",
1363           NULL);
1364       out_caps = gst_caps_merge (out_caps, tmp_caps);
1365     } else
1366       gst_caps_unref (tmp_caps);
1367   }
1368 out:
1369   gst_caps_unref (caps);
1370
1371   GST_DEBUG_OBJECT (viewconvert, "Have caps %" GST_PTR_FORMAT
1372       " filtering with caps %" GST_PTR_FORMAT, out_caps, filter);
1373
1374   if (filter) {
1375     GstCaps *tmp =
1376         gst_caps_intersect_full (filter, out_caps, GST_CAPS_INTERSECT_FIRST);
1377     gst_caps_unref (out_caps);
1378     out_caps = tmp;
1379   }
1380
1381   GST_DEBUG_OBJECT (viewconvert, "Returning caps %" GST_PTR_FORMAT, out_caps);
1382   return out_caps;
1383 }
1384
1385 static GstCaps *
1386 fixate_size (GstVulkanViewConvert * viewconvert,
1387     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1388 {
1389   GstStructure *ins, *outs;
1390   const GValue *from_par, *to_par;
1391   GValue fpar = { 0, }, tpar = {
1392   0,};
1393
1394   othercaps = gst_caps_make_writable (othercaps);
1395   othercaps = gst_caps_truncate (othercaps);
1396
1397   GST_DEBUG_OBJECT (viewconvert, "trying to fixate othercaps %" GST_PTR_FORMAT
1398       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
1399
1400   ins = gst_caps_get_structure (caps, 0);
1401   outs = gst_caps_get_structure (othercaps, 0);
1402
1403   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
1404   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
1405
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
1409    */
1410   if (direction == GST_PAD_SINK) {
1411     if (!from_par) {
1412       g_value_init (&fpar, GST_TYPE_FRACTION);
1413       gst_value_set_fraction (&fpar, 1, 1);
1414       from_par = &fpar;
1415     }
1416     if (!to_par) {
1417       g_value_init (&tpar, GST_TYPE_FRACTION);
1418       gst_value_set_fraction (&tpar, 1, 1);
1419       to_par = &tpar;
1420     }
1421   } else {
1422     if (!to_par) {
1423       g_value_init (&tpar, GST_TYPE_FRACTION);
1424       gst_value_set_fraction (&tpar, 1, 1);
1425       to_par = &tpar;
1426
1427       gst_structure_set (outs, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
1428           NULL);
1429     }
1430     if (!from_par) {
1431       g_value_init (&fpar, GST_TYPE_FRACTION);
1432       gst_value_set_fraction (&fpar, 1, 1);
1433       from_par = &fpar;
1434     }
1435   }
1436
1437   /* we have both PAR but they might not be fixated */
1438   {
1439     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
1440     gint w = 0, h = 0;
1441     gint from_dar_n, from_dar_d;
1442     gint num, den;
1443
1444     /* from_par should be fixed */
1445     g_return_val_if_fail (gst_value_is_fixed (from_par), othercaps);
1446
1447     from_par_n = gst_value_get_fraction_numerator (from_par);
1448     from_par_d = gst_value_get_fraction_denominator (from_par);
1449
1450     gst_structure_get_int (ins, "width", &from_w);
1451     gst_structure_get_int (ins, "height", &from_h);
1452
1453     gst_structure_get_int (outs, "width", &w);
1454     gst_structure_get_int (outs, "height", &h);
1455
1456     /* if both width and height are already fixed, we can't do anything
1457      * about it anymore */
1458     if (w && h) {
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);
1466       }
1467       goto done;
1468     }
1469
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"));
1475       goto done;
1476     }
1477
1478     GST_DEBUG_OBJECT (viewconvert, "Input DAR is %d/%d", from_dar_n,
1479         from_dar_d);
1480
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
1484      */
1485     if (h) {
1486       gint num, den;
1487
1488       GST_DEBUG_OBJECT (viewconvert, "height is fixed (%d)", h);
1489
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");
1498       }
1499
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);
1504
1505       GST_DEBUG_OBJECT (viewconvert, "PAR is fixed %d/%d", to_par_n, to_par_d);
1506
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"));
1511         goto done;
1512       }
1513
1514       w = (guint) gst_util_uint64_scale_int (h, num, den);
1515       gst_structure_fixate_field_nearest_int (outs, "width", w);
1516
1517       goto done;
1518     } else if (w) {
1519       gint num, den;
1520
1521       GST_DEBUG_OBJECT (viewconvert, "width is fixed (%d)", w);
1522
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");
1531       }
1532
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);
1537
1538       GST_DEBUG_OBJECT (viewconvert, "PAR is fixed %d/%d", to_par_n, to_par_d);
1539
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"));
1544         goto done;
1545       }
1546
1547       h = (guint) gst_util_uint64_scale_int (w, den, num);
1548       gst_structure_fixate_field_nearest_int (outs, "height", h);
1549
1550       goto done;
1551     } else if (gst_value_is_fixed (to_par)) {
1552       GstStructure *tmp;
1553       gint set_h, set_w, f_h, f_w;
1554
1555       to_par_n = gst_value_get_fraction_numerator (to_par);
1556       to_par_d = gst_value_get_fraction_denominator (to_par);
1557
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"));
1563         goto done;
1564       }
1565
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);
1570
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);
1577
1578       /* We kept the DAR and the height is nearest to the original height */
1579       if (set_w == w) {
1580         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1581             G_TYPE_INT, set_h, NULL);
1582         goto done;
1583       }
1584
1585       f_h = set_h;
1586       f_w = set_w;
1587
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);
1592
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);
1599
1600       /* We kept the DAR and the width is nearest to the original width */
1601       if (set_h == h) {
1602         gst_structure_set (outs, "width", G_TYPE_INT, set_w, "height",
1603             G_TYPE_INT, set_h, NULL);
1604         goto done;
1605       }
1606
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.
1610        */
1611       gst_structure_set (outs, "width", G_TYPE_INT, f_w, "height", G_TYPE_INT,
1612           f_h, NULL);
1613       goto done;
1614     } else {
1615       GstStructure *tmp;
1616       gint set_h, set_w, set_par_n, set_par_d, tmp2;
1617
1618       /* width, height and PAR are not fixed */
1619
1620       /* First try to keep the height and width as good as possible
1621        * and scale PAR */
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);
1627
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);
1633         goto done;
1634       }
1635
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,
1641           &set_par_d);
1642       gst_structure_free (tmp);
1643
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);
1647
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);
1652         goto done;
1653       }
1654
1655       /* Otherwise try to scale width to keep the DAR with the set
1656        * PAR and height */
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"));
1661         goto done;
1662       }
1663
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);
1669
1670       if (tmp2 == w) {
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);
1677         goto done;
1678       }
1679
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);
1686
1687       if (tmp2 == h) {
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);
1694         goto done;
1695       }
1696
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);
1705     }
1706   }
1707
1708 done:
1709   othercaps = gst_caps_fixate (othercaps);
1710
1711   GST_DEBUG_OBJECT (viewconvert, "fixated othercaps to %" GST_PTR_FORMAT,
1712       othercaps);
1713
1714   if (from_par == &fpar)
1715     g_value_unset (&fpar);
1716   if (to_par == &tpar)
1717     g_value_unset (&tpar);
1718
1719   return othercaps;
1720 }
1721
1722 static GstCaps *
1723 gst_vulkan_view_convert_fixate_caps (GstBaseTransform * bt,
1724     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1725 {
1726   GstVulkanViewConvert *viewconvert = GST_VULKAN_VIEW_CONVERT (bt);
1727   GstVideoMultiviewMode mode = viewconvert->output_mode_override;
1728   GstVideoMultiviewFlags flags = viewconvert->output_flags_override;
1729   GstCaps *tmp;
1730
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);
1735
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);
1743       othercaps = tmp;
1744     } else {
1745       /* See if we can do passthrough */
1746       GstVideoInfo info;
1747
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);
1751
1752         if (viewconvert->input_mode_override != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1753           mode = viewconvert->input_mode_override;
1754           flags = viewconvert->input_flags_override;
1755         }
1756
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);
1761         } else {
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);
1765           othercaps = tmp;
1766           goto done;
1767         }
1768       }
1769
1770       /* Prefer an unpacked mode for output */
1771       tmp =
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);
1777         othercaps = tmp;
1778       } else {
1779         gst_caps_unref (tmp);
1780       }
1781     }
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);
1791     } else {
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);
1795       othercaps = tmp;
1796     }
1797   }
1798
1799 done:
1800   othercaps = fixate_size (viewconvert, direction, caps, othercaps);
1801   othercaps = gst_caps_fixate (othercaps);
1802
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);
1806   return othercaps;
1807 }
1808
1809 static void
1810 destroy_shader_create_info (GstVulkanFullScreenRender * render, gpointer data)
1811 {
1812   VkPipelineShaderStageCreateInfo *info = data;
1813   int i;
1814
1815   for (i = 0; i < render->n_shader_stages; i++) {
1816     vkDestroyShaderModule (render->device->device, info[i].module, NULL);
1817   }
1818
1819   g_free (info);
1820 }
1821
1822 static void
1823 gst_vulkan_view_convert_shader_create_info (GstVulkanFullScreenRender * render)
1824 {
1825   VkShaderModule vert_module, frag_module;
1826
1827   vert_module =
1828       _vk_create_shader (render->device, identity_vert, identity_vert_size,
1829       NULL);
1830   frag_module =
1831       _vk_create_shader (render->device, view_convert_frag,
1832       view_convert_frag_size, NULL);
1833
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;
1838
1839   /* *INDENT-OFF* */
1840   render->shader_create_info[0] = (VkPipelineShaderStageCreateInfo) {
1841       .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1842       .pNext = NULL,
1843       .stage = VK_SHADER_STAGE_VERTEX_BIT,
1844       .module = vert_module,
1845       .pName = "main"
1846   };
1847
1848   render->shader_create_info[1] = (VkPipelineShaderStageCreateInfo) {
1849       .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1850       .pNext = NULL,
1851       .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
1852       .module = frag_module,
1853       .pName = "main"
1854   };
1855   /* *INDENT-ON* */
1856 }
1857
1858 static VkPushConstantRange *
1859 gst_vulkan_view_convert_push_constant_ranges (GstVulkanFullScreenRender *
1860     render, guint * n_constants)
1861 {
1862   *n_constants = 0;
1863   return NULL;
1864 }
1865
1866 static VkDescriptorSetLayoutBinding
1867     * gst_vulkan_view_convert_descriptor_set_layout_bindings
1868     (GstVulkanFullScreenRender * render, guint * n_bindings)
1869 {
1870   VkDescriptorSetLayoutBinding *bindings;
1871   guint i;
1872
1873   *n_bindings = 0;
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);
1877
1878   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2; i++) {
1879     /* *INDENT-OFF* */
1880     bindings[i] = (VkDescriptorSetLayoutBinding) {
1881         .binding = i,
1882         .descriptorCount = 1,
1883         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1884         .pImmutableSamplers = NULL,
1885         .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
1886     };
1887     i++;
1888     bindings[i] = (VkDescriptorSetLayoutBinding) {
1889         .binding = i,
1890         .descriptorCount = 1,
1891         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1892         .pImmutableSamplers = NULL,
1893         .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
1894     };
1895     /* *INDENT-ON* */
1896   }
1897   /* *INDENT-OFF* */
1898   bindings[i] = (VkDescriptorSetLayoutBinding) {
1899       .binding = i,
1900       .descriptorCount = 1,
1901       .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1902       .pImmutableSamplers = NULL,
1903       .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
1904   };
1905   /* *INDENT-ON* */
1906   i++;
1907
1908   g_assert (i == *n_bindings);
1909
1910   return bindings;
1911 }
1912
1913 static VkAttachmentReference
1914     * gst_vulkan_view_convert_render_pass_attachment_references
1915     (GstVulkanFullScreenRender * render, guint * n_attachments)
1916 {
1917   VkAttachmentReference *attachments;
1918   int i;
1919
1920   *n_attachments = GST_VIDEO_INFO_N_PLANES (&render->out_info);
1921   attachments = g_new0 (VkAttachmentReference, *n_attachments);
1922
1923   for (i = 0; i < *n_attachments; i++) {
1924     /* *INDENT-OFF* */
1925     attachments[i] = (VkAttachmentReference) {
1926        .attachment = i,
1927        .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
1928     };
1929     /* *INDENT-ON* */
1930   }
1931
1932   return attachments;
1933 }
1934
1935 static VkAttachmentDescription
1936     * gst_vulkan_view_convert_render_pass_attachment_descriptions
1937     (GstVulkanFullScreenRender * render, guint * n_descriptions)
1938 {
1939   VkAttachmentDescription *color_attachments;
1940   int i;
1941
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++) {
1945     /* *INDENT-OFF* */
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
1956     };
1957     /* *INDENT-ON* */
1958   }
1959
1960   return color_attachments;
1961 }
1962
1963 static VkSampler
1964 _create_sampler (GstVulkanViewConvert * conv)
1965 {
1966   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
1967
1968   /* *INDENT-OFF* */
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,
1977       .maxAnisotropy = 1,
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,
1983       .mipLodBias = 0.0f,
1984       .minLod = 0.0f,
1985       .maxLod = 0.0f
1986   };
1987   /* *INDENT-ON* */
1988   GError *error = NULL;
1989   VkSampler sampler;
1990   VkResult err;
1991
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);
1995     return NULL;
1996   }
1997
1998   return sampler;
1999 }
2000
2001 static gboolean
2002 gst_vulkan_view_convert_start (GstBaseTransform * bt)
2003 {
2004   GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
2005
2006   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->start (bt))
2007     return FALSE;
2008
2009   if (!(conv->sampler = _create_sampler (conv)))
2010     return FALSE;
2011
2012   return TRUE;
2013 }
2014
2015 static VkDescriptorPool
2016 _create_descriptor_pool (GstVulkanViewConvert * conv)
2017 {
2018   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2019
2020   /* *INDENT-OFF* */
2021   VkDescriptorPoolSize pool_sizes[] = {
2022       {
2023           .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
2024           .descriptorCount = GST_VIDEO_INFO_N_PLANES (&render->in_info) * 2,
2025       },
2026       {
2027           .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
2028           .descriptorCount = 1
2029       },
2030   };
2031
2032   VkDescriptorPoolCreateInfo pool_info = {
2033       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
2034       .pNext = NULL,
2035       .poolSizeCount = G_N_ELEMENTS (pool_sizes),
2036       .pPoolSizes = pool_sizes,
2037       .maxSets = 1
2038   };
2039   /* *INDENT-ON* */
2040   VkDescriptorPool pool;
2041   GError *error = NULL;
2042   VkResult err;
2043
2044   err =
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",
2048         error->message);
2049     g_clear_error (&error);
2050     return NULL;
2051   }
2052
2053   return pool;
2054 }
2055
2056 static VkDescriptorSet
2057 _create_descriptor_set (GstVulkanViewConvert * conv)
2058 {
2059   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2060
2061   /* *INDENT-OFF* */
2062   VkDescriptorSetAllocateInfo alloc_info = {
2063       .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
2064       .pNext = NULL,
2065       .descriptorPool = conv->descriptor_pool,
2066       .descriptorSetCount = 1,
2067       .pSetLayouts = &render->descriptor_set_layout
2068   };
2069   /* *INDENT-ON* */
2070   VkDescriptorSet descriptor;
2071   GError *error = NULL;
2072   VkResult err;
2073
2074   err =
2075       vkAllocateDescriptorSets (render->device->device, &alloc_info,
2076       &descriptor);
2077   if (gst_vulkan_error_to_g_error (err, &error, "vkAllocateDescriptorSets") < 0) {
2078     GST_ERROR_OBJECT (conv, "Failed to allocate descriptor: %s",
2079         error->message);
2080     g_clear_error (&error);
2081     return NULL;
2082   }
2083
2084   return descriptor;
2085 }
2086
2087 static gboolean
2088 _create_uniform_buffer (GstVulkanViewConvert * conv)
2089 {
2090   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2091
2092   conv->uniform =
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);
2098
2099   return TRUE;
2100 }
2101
2102 static gboolean
2103 gst_vulkan_view_convert_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
2104     GstCaps * out_caps)
2105 {
2106   GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
2107   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
2108
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;
2116     if (conv->uniform)
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;
2121   } else {
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;
2127     if (conv->uniform)
2128       gst_memory_unref (conv->uniform);
2129     conv->uniform = NULL;
2130   }
2131
2132   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->set_caps (bt, in_caps,
2133           out_caps))
2134     return FALSE;
2135
2136   if (!(conv->descriptor_pool = _create_descriptor_pool (conv)))
2137     return FALSE;
2138   if (!(conv->descriptor_set = _create_descriptor_set (conv)))
2139     return FALSE;
2140
2141   if (!_create_uniform_buffer (conv))
2142     return FALSE;
2143
2144   return TRUE;
2145 }
2146
2147 static gboolean
2148 gst_vulkan_view_convert_stop (GstBaseTransform * bt)
2149 {
2150   GstVulkanViewConvert *conv = GST_VULKAN_VIEW_CONVERT (bt);
2151   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (bt);
2152
2153   conv->descriptor_up_to_date = FALSE;
2154
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;
2163       if (conv->sampler)
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;
2168       if (conv->uniform)
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;
2173     } else {
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;
2179       if (conv->sampler)
2180         vkDestroySampler (render->device->device, conv->sampler, NULL);
2181       conv->sampler = NULL;
2182       if (conv->uniform)
2183         gst_memory_unref (conv->uniform);
2184       conv->uniform = NULL;
2185     }
2186   }
2187
2188   if (conv->cmd_pool)
2189     gst_object_unref (conv->cmd_pool);
2190   conv->cmd_pool = NULL;
2191
2192   return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
2193 }
2194
2195 static VkFramebuffer
2196 _create_framebuffer (GstVulkanViewConvert * conv, guint n_views,
2197     VkImageView * views)
2198 {
2199   GstVulkanFullScreenRender *render = GST_VULKAN_FULL_SCREEN_RENDER (conv);
2200
2201   /* *INDENT-OFF* */
2202   VkFramebufferCreateInfo framebuffer_info = {
2203       .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
2204       .pNext = NULL,
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),
2210       .layers = 1
2211   };
2212   /* *INDENT-ON* */
2213   VkFramebuffer framebuffer;
2214   GError *error = NULL;
2215   VkResult err;
2216
2217   err =
2218       vkCreateFramebuffer (render->device->device, &framebuffer_info, NULL,
2219       &framebuffer);
2220   if (gst_vulkan_error_to_g_error (err, &error, "vkCreateFramebuffer") < 0) {
2221     GST_ERROR_OBJECT (render, "Failed to create framebuffer: %s",
2222         error->message);
2223     g_clear_error (&error);
2224     return NULL;
2225   }
2226
2227   return framebuffer;
2228 }
2229
2230 static GstFlowReturn
2231 gst_vulkan_view_convert_transform (GstBaseTransform * bt, GstBuffer * inbuf,
2232     GstBuffer * outbuf)
2233 {
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;
2244   VkResult err;
2245   int i;
2246
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");
2252       goto error;
2253     }
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]));
2259   }
2260
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");
2266       goto error;
2267     }
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]));
2273   }
2274
2275   if (!conv->cmd_pool) {
2276     if (!(conv->cmd_pool =
2277             gst_vulkan_queue_create_command_pool (render->queue, &error)))
2278       goto error;
2279   }
2280
2281   if (!(cmd_buf = gst_vulkan_command_pool_create (conv->cmd_pool, &error)))
2282     goto error;
2283
2284   fence = gst_vulkan_fence_new (render->device, 0, &error);
2285   if (!fence)
2286     goto error;
2287
2288   {
2289     VkCommandBufferBeginInfo cmd_buf_info = { 0, };
2290
2291     /* *INDENT-OFF* */
2292     cmd_buf_info = (VkCommandBufferBeginInfo) {
2293         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
2294         .pNext = NULL,
2295         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
2296         .pInheritanceInfo = NULL
2297     };
2298     /* *INDENT-ON* */
2299
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)
2303       goto unlock_error;
2304   }
2305
2306   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->in_info); i++) {
2307     /* *INDENT-OFF* */
2308     VkImageMemoryBarrier in_image_memory_barrier = {
2309         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
2310         .pNext = NULL,
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
2320     };
2321     /* *INDENT-ON* */
2322
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);
2327
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;
2333   }
2334
2335   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&render->out_info); i++) {
2336     VkImageMemoryBarrier out_image_memory_barrier;
2337
2338     /* *INDENT-OFF* */
2339     out_image_memory_barrier = (VkImageMemoryBarrier) {
2340         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
2341         .pNext = NULL,
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
2351     };
2352     /* *INDENT-ON* */
2353
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);
2358
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;
2364   }
2365
2366   {
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;
2370     }
2371
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");
2376       goto unlock_error;
2377     }
2378   }
2379
2380   view_convert_update_command_state (conv, cmd_buf->cmd, in_img_views,
2381       out_img_views);
2382
2383   if (!gst_vulkan_full_screen_render_fill_command_buffer (render, cmd_buf->cmd,
2384           framebuffer)) {
2385     g_set_error (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
2386         "Failed to fill framebuffer");
2387     goto unlock_error;
2388   }
2389
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)
2393     goto error;
2394
2395   gst_vulkan_trash_list_add (render->trash_list,
2396       gst_vulkan_trash_new_free_framebuffer (gst_vulkan_fence_ref (fence),
2397           framebuffer));
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)));
2401
2402   if (!gst_vulkan_full_screen_render_submit (render, cmd_buf->cmd, fence))
2403     return GST_FLOW_ERROR;
2404
2405   return GST_FLOW_OK;
2406
2407 unlock_error:
2408   if (cmd_buf) {
2409     gst_vulkan_command_buffer_unlock (cmd_buf);
2410     gst_vulkan_command_buffer_unref (cmd_buf);
2411   }
2412 error:
2413   GST_ELEMENT_ERROR (bt, LIBRARY, FAILED, ("%s", error->message), (NULL));
2414   g_clear_error (&error);
2415   return GST_FLOW_ERROR;
2416 }