vulkan: move fullscreenquad object to library
[platform/upstream/gstreamer.git] / ext / vulkan / vkcolorconvert.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-vulkancolorconvert
23  * @title: vulkancolorconvert
24  *
25  * vulkancolorconvert converts between different video colorspaces.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <string.h>
33
34 #include "vkcolorconvert.h"
35
36 #include "shaders/identity.vert.h"
37 #include "shaders/swizzle.frag.h"
38 #include "shaders/swizzle_and_clobber_alpha.frag.h"
39 #include "shaders/yuy2_to_rgb.frag.h"
40 #include "shaders/ayuv_to_rgb.frag.h"
41 #include "shaders/nv12_to_rgb.frag.h"
42 #include "shaders/rgb_to_ayuv.frag.h"
43 #include "shaders/rgb_to_yuy2.frag.h"
44 #include "shaders/rgb_to_nv12.frag.h"
45
46 GST_DEBUG_CATEGORY (gst_debug_vulkan_color_convert);
47 #define GST_CAT_DEFAULT gst_debug_vulkan_color_convert
48
49 #define N_SHADER_INFO (8*8 + 8*3*2)
50 static shader_info shader_infos[N_SHADER_INFO];
51
52 static void
53 get_rgb_format_swizzle_order (GstVideoFormat format,
54     gint swizzle[GST_VIDEO_MAX_COMPONENTS])
55 {
56   const GstVideoFormatInfo *finfo = gst_video_format_get_info (format);
57   int c_i = 0, i;
58
59   g_return_if_fail (finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB
60       || format == GST_VIDEO_FORMAT_AYUV);
61
62   for (i = 0; i < finfo->n_components; i++) {
63     swizzle[c_i++] = finfo->poffset[i];
64   }
65
66   /* special case spaced RGB formats as the space does not contain a poffset
67    * value and we need all four components to be valid in order to swizzle
68    * correctly */
69   if (format == GST_VIDEO_FORMAT_xRGB || format == GST_VIDEO_FORMAT_xBGR) {
70     swizzle[c_i++] = 0;
71   } else if (format == GST_VIDEO_FORMAT_RGBx || format == GST_VIDEO_FORMAT_BGRx) {
72     swizzle[c_i++] = 3;
73   } else {
74     for (i = finfo->n_components; i < GST_VIDEO_MAX_COMPONENTS; i++) {
75       swizzle[c_i++] = -1;
76     }
77   }
78 }
79
80 static void
81 get_vulkan_rgb_format_swizzle_order (VkFormat format, gint * swizzle,
82     guint swizzle_count, guint offset)
83 {
84   const GstVulkanFormatInfo *finfo = gst_vulkan_format_get_info (format);
85   int i;
86
87   g_return_if_fail (finfo->flags & GST_VULKAN_FORMAT_FLAG_RGB);
88   g_return_if_fail (finfo->n_components <= swizzle_count);
89
90   for (i = 0; i < finfo->n_components; i++) {
91     swizzle[i] = offset + finfo->poffset[i];
92   }
93   for (i = finfo->n_components; i < swizzle_count; i++) {
94     swizzle[i] = -1;
95   }
96 }
97
98 /* given a swizzle index, produce an index such that:
99  *
100  * swizzle[idx[i]] == identity[i] where:
101  * - swizzle is the original swizzle
102  * - idx is the result
103  * - identity = {0, 1, 2,...}
104  * - unset fields are marked by -1
105  */
106 static void
107 swizzle_identity_order (gint * swizzle, gint * idx)
108 {
109   int i;
110
111   for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
112     idx[i] = -1;
113   }
114
115   for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
116     if (swizzle[i] >= 0 && swizzle[i] < 4 && idx[swizzle[i]] == -1) {
117       idx[swizzle[i]] = i;
118     }
119   }
120 }
121
122 typedef struct
123 {
124   double dm[4][4];
125 } Matrix4;
126
127 static void
128 matrix_debug (const Matrix4 * s)
129 {
130   GST_DEBUG ("[%f %f %f %f]", s->dm[0][0], s->dm[0][1], s->dm[0][2],
131       s->dm[0][3]);
132   GST_DEBUG ("[%f %f %f %f]", s->dm[1][0], s->dm[1][1], s->dm[1][2],
133       s->dm[1][3]);
134   GST_DEBUG ("[%f %f %f %f]", s->dm[2][0], s->dm[2][1], s->dm[2][2],
135       s->dm[2][3]);
136   GST_DEBUG ("[%f %f %f %f]", s->dm[3][0], s->dm[3][1], s->dm[3][2],
137       s->dm[3][3]);
138 }
139
140 static void
141 matrix_to_float (const Matrix4 * m, float *ret)
142 {
143   int i, j;
144
145   for (i = 0; i < 4; i++) {
146     for (j = 0; j < 4; j++) {
147       ret[j * 4 + i] = m->dm[i][j];
148     }
149   }
150 }
151
152 static void
153 matrix_set_identity (Matrix4 * m)
154 {
155   int i, j;
156
157   for (i = 0; i < 4; i++) {
158     for (j = 0; j < 4; j++) {
159       m->dm[i][j] = (i == j);
160     }
161   }
162 }
163
164 static void
165 matrix_copy (Matrix4 * d, const Matrix4 * s)
166 {
167   gint i, j;
168
169   for (i = 0; i < 4; i++)
170     for (j = 0; j < 4; j++)
171       d->dm[i][j] = s->dm[i][j];
172 }
173
174 /* Perform 4x4 matrix multiplication:
175  *  - @dst@ = @a@ * @b@
176  *  - @dst@ may be a pointer to @a@ andor @b@
177  */
178 static void
179 matrix_multiply (Matrix4 * dst, Matrix4 * a, Matrix4 * b)
180 {
181   Matrix4 tmp;
182   int i, j, k;
183
184   for (i = 0; i < 4; i++) {
185     for (j = 0; j < 4; j++) {
186       double x = 0;
187       for (k = 0; k < 4; k++) {
188         x += a->dm[i][k] * b->dm[k][j];
189       }
190       tmp.dm[i][j] = x;
191     }
192   }
193   matrix_copy (dst, &tmp);
194 }
195
196 #if 0
197 static void
198 matrix_invert (Matrix4 * d, Matrix4 * s)
199 {
200   Matrix4 tmp;
201   int i, j;
202   double det;
203
204   matrix_set_identity (&tmp);
205   for (j = 0; j < 3; j++) {
206     for (i = 0; i < 3; i++) {
207       tmp.dm[j][i] =
208           s->dm[(i + 1) % 3][(j + 1) % 3] * s->dm[(i + 2) % 3][(j + 2) % 3] -
209           s->dm[(i + 1) % 3][(j + 2) % 3] * s->dm[(i + 2) % 3][(j + 1) % 3];
210     }
211   }
212   det =
213       tmp.dm[0][0] * s->dm[0][0] + tmp.dm[0][1] * s->dm[1][0] +
214       tmp.dm[0][2] * s->dm[2][0];
215   for (j = 0; j < 3; j++) {
216     for (i = 0; i < 3; i++) {
217       tmp.dm[i][j] /= det;
218     }
219   }
220   matrix_copy (d, &tmp);
221 }
222 #endif
223 static void
224 matrix_offset_components (Matrix4 * m, double a1, double a2, double a3)
225 {
226   Matrix4 a;
227
228   matrix_set_identity (&a);
229   a.dm[0][3] = a1;
230   a.dm[1][3] = a2;
231   a.dm[2][3] = a3;
232   matrix_debug (&a);
233   matrix_multiply (m, &a, m);
234 }
235
236 static void
237 matrix_scale_components (Matrix4 * m, double a1, double a2, double a3)
238 {
239   Matrix4 a;
240
241   matrix_set_identity (&a);
242   a.dm[0][0] = a1;
243   a.dm[1][1] = a2;
244   a.dm[2][2] = a3;
245   matrix_multiply (m, &a, m);
246 }
247
248 static void
249 matrix_YCbCr_to_RGB (Matrix4 * m, double Kr, double Kb)
250 {
251   double Kg = 1.0 - Kr - Kb;
252   Matrix4 k = {
253     {
254           {1., 0., 2 * (1 - Kr), 0.},
255           {1., -2 * Kb * (1 - Kb) / Kg, -2 * Kr * (1 - Kr) / Kg, 0.},
256           {1., 2 * (1 - Kb), 0., 0.},
257           {0., 0., 0., 1.},
258         }
259   };
260
261   matrix_multiply (m, &k, m);
262 }
263
264 typedef struct
265 {
266   GstVideoInfo in_info;
267   GstVideoInfo out_info;
268
269   Matrix4 to_RGB_matrix;
270   Matrix4 to_YUV_matrix;
271   Matrix4 convert_matrix;
272 } ConvertInfo;
273
274 static void
275 convert_to_RGB (ConvertInfo * conv, Matrix4 * m)
276 {
277   GstVideoInfo *info = &conv->in_info;
278
279   {
280     const GstVideoFormatInfo *uinfo;
281     gint offset[4], scale[4], depth[4];
282     int i;
283
284     uinfo = gst_video_format_get_info (GST_VIDEO_INFO_FORMAT (info));
285
286     /* bring color components to [0..1.0] range */
287     gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
288         scale);
289
290     for (i = 0; i < uinfo->n_components; i++)
291       depth[i] = (1 << uinfo->depth[i]) - 1;
292
293     matrix_offset_components (m, -offset[0] / (float) depth[0],
294         -offset[1] / (float) depth[1], -offset[2] / (float) depth[2]);
295     matrix_scale_components (m, depth[0] / ((float) scale[0]),
296         depth[1] / ((float) scale[1]), depth[2] / ((float) scale[2]));
297     GST_DEBUG ("to RGB scale/offset matrix");
298     matrix_debug (m);
299   }
300
301   if (GST_VIDEO_INFO_IS_YUV (info)) {
302     gdouble Kr, Kb;
303
304     if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
305       matrix_YCbCr_to_RGB (m, Kr, Kb);
306     GST_DEBUG ("to RGB matrix");
307     matrix_debug (m);
308   }
309 }
310
311 static void
312 matrix_RGB_to_YCbCr (Matrix4 * m, double Kr, double Kb)
313 {
314   double Kg = 1.0 - Kr - Kb;
315   Matrix4 k;
316   double x;
317
318   k.dm[0][0] = Kr;
319   k.dm[0][1] = Kg;
320   k.dm[0][2] = Kb;
321   k.dm[0][3] = 0;
322
323   x = 1 / (2 * (1 - Kb));
324   k.dm[1][0] = -x * Kr;
325   k.dm[1][1] = -x * Kg;
326   k.dm[1][2] = x * (1 - Kb);
327   k.dm[1][3] = 0;
328
329   x = 1 / (2 * (1 - Kr));
330   k.dm[2][0] = x * (1 - Kr);
331   k.dm[2][1] = -x * Kg;
332   k.dm[2][2] = -x * Kb;
333   k.dm[2][3] = 0;
334
335   k.dm[3][0] = 0;
336   k.dm[3][1] = 0;
337   k.dm[3][2] = 0;
338   k.dm[3][3] = 1;
339
340   matrix_multiply (m, &k, m);
341 }
342
343 static void
344 convert_to_YUV (ConvertInfo * conv, Matrix4 * m)
345 {
346   GstVideoInfo *info = &conv->out_info;
347
348   if (GST_VIDEO_INFO_IS_YUV (info)) {
349     gdouble Kr, Kb;
350
351     if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
352       matrix_RGB_to_YCbCr (m, Kr, Kb);
353     GST_DEBUG ("to YUV matrix");
354     matrix_debug (m);
355   }
356
357   {
358     const GstVideoFormatInfo *uinfo;
359     gint offset[4], scale[4], depth[4];
360     int i;
361
362     uinfo = gst_video_format_get_info (GST_VIDEO_INFO_FORMAT (info));
363
364     /* bring color components to nominal range */
365     gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
366         scale);
367
368     for (i = 0; i < uinfo->n_components; i++)
369       depth[i] = (1 << uinfo->depth[i]) - 1;
370
371     matrix_scale_components (m, scale[0] / (float) depth[0],
372         scale[1] / (float) depth[1], scale[2] / (float) depth[2]);
373     matrix_offset_components (m, offset[0] / (float) depth[0],
374         offset[1] / (float) depth[1], offset[2] / (float) depth[2]);
375     GST_DEBUG ("to YUV scale/offset matrix");
376     matrix_debug (m);
377   }
378 }
379
380 #if 0
381 static void
382 matrix_RGB_to_XYZ (Matrix4 * dst, double Rx, double Ry, double Gx,
383     double Gy, double Bx, double By, double Wx, double Wy)
384 {
385   Matrix4 m, im;
386   double sx, sy, sz;
387   double wx, wy, wz;
388
389   matrix_set_identity (&m);
390
391   m.dm[0][0] = Rx;
392   m.dm[1][0] = Ry;
393   m.dm[2][0] = (1.0 - Rx - Ry);
394   m.dm[0][1] = Gx;
395   m.dm[1][1] = Gy;
396   m.dm[2][1] = (1.0 - Gx - Gy);
397   m.dm[0][2] = Bx;
398   m.dm[1][2] = By;
399   m.dm[2][2] = (1.0 - Bx - By);
400
401   matrix_invert (&im, &m);
402
403   wx = Wx / Wy;
404   wy = 1.0;
405   wz = (1.0 - Wx - Wy) / Wy;
406
407   sx = im.dm[0][0] * wx + im.dm[0][1] * wy + im.dm[0][2] * wz;
408   sy = im.dm[1][0] * wx + im.dm[1][1] * wy + im.dm[1][2] * wz;
409   sz = im.dm[2][0] * wx + im.dm[2][1] * wy + im.dm[2][2] * wz;
410
411   m.dm[0][0] *= sx;
412   m.dm[1][0] *= sx;
413   m.dm[2][0] *= sx;
414   m.dm[0][1] *= sy;
415   m.dm[1][1] *= sy;
416   m.dm[2][1] *= sy;
417   m.dm[0][2] *= sz;
418   m.dm[1][2] *= sz;
419   m.dm[2][2] *= sz;
420
421   matrix_copy (dst, &m);
422 }
423
424 static void
425 convert_primaries (ConvertInfo * conv)
426 {
427   gboolean same_matrix, same_primaries;
428   Matrix4 p1, p2;
429
430   same_matrix =
431       conv->in_info.colorimetry.matrix == conv->out_info.colorimetry.matrix;
432   same_primaries =
433       conv->in_info.colorimetry.primaries ==
434       conv->out_info.colorimetry.primaries;
435
436   GST_DEBUG ("matrix %d -> %d (%d)", conv->in_info.colorimetry.matrix,
437       conv->out_info.colorimetry.matrix, same_matrix);
438   GST_DEBUG ("primaries %d -> %d (%d)", conv->in_info.colorimetry.primaries,
439       conv->out_info.colorimetry.primaries, same_primaries);
440
441   matrix_set_identity (&conv->convert_matrix);
442
443   if (!same_primaries) {
444     const GstVideoColorPrimariesInfo *pi;
445
446     pi = gst_video_color_primaries_get_info (conv->in_info.colorimetry.
447         primaries);
448     matrix_RGB_to_XYZ (&p1, pi->Rx, pi->Ry, pi->Gx, pi->Gy, pi->Bx, pi->By,
449         pi->Wx, pi->Wy);
450     GST_DEBUG ("to XYZ matrix");
451     matrix_debug (&p1);
452     GST_DEBUG ("current matrix");
453     matrix_multiply (&conv->convert_matrix, &conv->convert_matrix, &p1);
454     matrix_debug (&conv->convert_matrix);
455
456     pi = gst_video_color_primaries_get_info (conv->out_info.colorimetry.
457         primaries);
458     matrix_RGB_to_XYZ (&p2, pi->Rx, pi->Ry, pi->Gx, pi->Gy, pi->Bx, pi->By,
459         pi->Wx, pi->Wy);
460     matrix_invert (&p2, &p2);
461     GST_DEBUG ("to RGB matrix");
462     matrix_debug (&p2);
463     matrix_multiply (&conv->convert_matrix, &conv->convert_matrix, &p2);
464     GST_DEBUG ("current matrix");
465     matrix_debug (&conv->convert_matrix);
466   }
467 }
468 #endif
469 static ConvertInfo *
470 convert_info_new (GstVideoInfo * in_info, GstVideoInfo * out_info)
471 {
472   ConvertInfo *conv = g_new0 (ConvertInfo, 1);
473
474   matrix_set_identity (&conv->to_RGB_matrix);
475   matrix_set_identity (&conv->convert_matrix);
476   matrix_set_identity (&conv->to_YUV_matrix);
477
478   memcpy (&conv->in_info, in_info, sizeof (*in_info));
479   memcpy (&conv->out_info, out_info, sizeof (*out_info));
480
481   convert_to_RGB (conv, &conv->to_RGB_matrix);
482   /* by default videoconvert does not convert primaries
483      convert_primaries (conv); */
484   convert_to_YUV (conv, &conv->to_YUV_matrix);
485
486   return conv;
487 }
488
489 static void
490 video_format_to_reorder (GstVideoFormat v_format, gint * reorder,
491     gboolean input)
492 {
493   switch (v_format) {
494     case GST_VIDEO_FORMAT_RGBA:
495     case GST_VIDEO_FORMAT_RGBx:
496     case GST_VIDEO_FORMAT_BGRA:
497     case GST_VIDEO_FORMAT_BGRx:
498     case GST_VIDEO_FORMAT_ARGB:
499     case GST_VIDEO_FORMAT_xRGB:
500     case GST_VIDEO_FORMAT_ABGR:
501     case GST_VIDEO_FORMAT_xBGR:
502     case GST_VIDEO_FORMAT_AYUV:
503       get_rgb_format_swizzle_order (v_format, reorder);
504       break;
505     case GST_VIDEO_FORMAT_UYVY:
506       reorder[0] = 1;
507       reorder[1] = 0;
508       reorder[2] = input ? 3 : 2;
509       reorder[3] = 0;
510       break;
511     case GST_VIDEO_FORMAT_YUY2:
512       reorder[0] = 0;
513       reorder[1] = 1;
514       reorder[2] = 0;
515       reorder[3] = input ? 3 : 2;
516       break;
517     case GST_VIDEO_FORMAT_NV12:
518       reorder[0] = 0;
519       reorder[1] = 1;
520       reorder[2] = 2;
521       reorder[3] = 0;
522       break;
523     default:
524       g_assert_not_reached ();
525       break;
526   }
527
528   GST_TRACE ("swizzle: %u, %u, %u, %u", reorder[0], reorder[1], reorder[2],
529       reorder[3]);
530 }
531
532 static guint
533 finfo_get_plane_n_components (const GstVideoFormatInfo * finfo, guint plane)
534 {
535   guint n_components = 0, i;
536
537   switch (finfo->format) {
538     case GST_VIDEO_FORMAT_RGBx:
539     case GST_VIDEO_FORMAT_xRGB:
540     case GST_VIDEO_FORMAT_BGRx:
541     case GST_VIDEO_FORMAT_xBGR:
542       /* fixup spaced RGB formats as we treat the space as a normal alpha
543        * component */
544       return plane == 0 ? 4 : 0;
545     default:
546       break;
547   }
548
549   for (i = 0; i < finfo->n_components; i++) {
550     if (finfo->plane[i] == plane)
551       n_components++;
552   }
553
554   return n_components;
555 }
556
557 static void
558 get_vulkan_format_swizzle_order (GstVideoFormat v_format,
559     VkFormat vk_format[GST_VIDEO_MAX_PLANES],
560     gint swizzle[GST_VIDEO_MAX_COMPONENTS])
561 {
562   const GstVideoFormatInfo *finfo;
563   int i, prev_in_i = 0;
564
565   finfo = gst_video_format_get_info (v_format);
566   for (i = 0; i < finfo->n_planes; i++) {
567     guint plane_components = finfo_get_plane_n_components (finfo, i);
568     get_vulkan_rgb_format_swizzle_order (vk_format[i],
569         &swizzle[prev_in_i], plane_components, prev_in_i);
570     prev_in_i += plane_components;
571   }
572
573   if (v_format == GST_VIDEO_FORMAT_YUY2 || v_format == GST_VIDEO_FORMAT_UYVY) {
574     /* Fixup these packed YUV formats as we use a two component format for
575      * a 4-component pixel and access two samples in the shader */
576     g_assert (swizzle[0] == 0);
577     g_assert (swizzle[1] == 1);
578     swizzle[2] = 2;
579     swizzle[3] = 3;
580   }
581
582   GST_TRACE ("%s: %i, %i, %i, %i", finfo->name, swizzle[0], swizzle[1],
583       swizzle[2], swizzle[3]);
584 }
585
586 static void
587 calculate_reorder_indexes (GstVideoFormat in_format,
588     GstVulkanImageView * in_views[GST_VIDEO_MAX_COMPONENTS],
589     GstVideoFormat out_format,
590     GstVulkanImageView * out_views[GST_VIDEO_MAX_COMPONENTS],
591     int ret_in[GST_VIDEO_MAX_COMPONENTS], int ret_out[GST_VIDEO_MAX_COMPONENTS])
592 {
593   const GstVideoFormatInfo *in_finfo, *out_finfo;
594   VkFormat in_vk_formats[GST_VIDEO_MAX_COMPONENTS];
595   VkFormat out_vk_formats[GST_VIDEO_MAX_COMPONENTS];
596   int in_vk_order[GST_VIDEO_MAX_COMPONENTS] = { 0, };
597   int in_reorder[GST_VIDEO_MAX_COMPONENTS] = { 0, };
598   int out_vk_order[GST_VIDEO_MAX_COMPONENTS] = { 0, };
599   int out_reorder[GST_VIDEO_MAX_COMPONENTS] = { 0, };
600   int tmp[GST_VIDEO_MAX_PLANES] = { 0, };
601   int i;
602
603   in_finfo = gst_video_format_get_info (in_format);
604   out_finfo = gst_video_format_get_info (out_format);
605
606   for (i = 0; i < in_finfo->n_planes; i++)
607     in_vk_formats[i] = in_views[i]->image->create_info.format;
608   for (i = 0; i < out_finfo->n_planes; i++)
609     out_vk_formats[i] = out_views[i]->image->create_info.format;
610
611   get_vulkan_format_swizzle_order (in_format, in_vk_formats, in_vk_order);
612   video_format_to_reorder (in_format, in_reorder, TRUE);
613
614   video_format_to_reorder (out_format, out_reorder, FALSE);
615   get_vulkan_format_swizzle_order (out_format, out_vk_formats, out_vk_order);
616
617   for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
618     tmp[i] = out_vk_order[out_reorder[i]];
619   /* find the identity order for RGBA->$format */
620   GST_TRACE ("pre-invert: %u, %u, %u, %u", tmp[0], tmp[1], tmp[2], tmp[3]);
621   if (out_format == GST_VIDEO_FORMAT_YUY2
622       || out_format == GST_VIDEO_FORMAT_UYVY) {
623     for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
624       ret_out[i] = tmp[i];
625   } else {
626     swizzle_identity_order (tmp, ret_out);
627   }
628
629   for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++)
630     ret_in[i] = in_reorder[in_vk_order[i]];
631   GST_TRACE ("in reorder: %u, %u, %u, %u", ret_in[0], ret_in[1], ret_in[2],
632       ret_in[3]);
633   GST_TRACE ("out reorder: %u, %u, %u, %u", ret_out[0], ret_out[1], ret_out[2],
634       ret_out[3]);
635 }
636
637 struct RGBUpdateData
638 {
639   int in_reorder[4];
640   int out_reorder[4];
641 };
642
643 static GstMemory *
644 swizzle_rgb_create_uniform_memory (GstVulkanColorConvert * conv,
645     shader_info * sinfo, GstVulkanImageView ** in_views,
646     GstVulkanImageView ** out_views)
647 {
648   GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (conv);
649
650   if (sinfo->user_data) {
651     return gst_memory_ref (sinfo->user_data);
652   } else {
653     struct RGBUpdateData data = { 0, };
654     GstMapInfo map_info;
655     GstMemory *uniforms;
656
657     uniforms =
658         gst_vulkan_buffer_memory_alloc (vfilter->device,
659         sizeof (struct RGBUpdateData),
660         VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
661         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
662         VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
663
664     calculate_reorder_indexes (GST_VIDEO_INFO_FORMAT (&conv->quad->in_info),
665         in_views, GST_VIDEO_INFO_FORMAT (&conv->quad->out_info),
666         out_views, data.in_reorder, data.out_reorder);
667
668     if (!gst_memory_map (uniforms, &map_info, GST_MAP_WRITE)) {
669       gst_memory_unref (uniforms);
670       return NULL;
671     }
672     memcpy (map_info.data, &data, sizeof (data));
673     gst_memory_unmap (uniforms, &map_info);
674
675     sinfo->user_data = gst_memory_ref (uniforms);
676     return uniforms;
677   }
678 }
679
680 struct ColorMatrices
681 {
682   float to_RGB[16];
683   float primaries[16];
684   float to_YUV[16];
685 };
686
687 struct YUVUpdateData
688 {
689   int in_reorder[4];
690   int out_reorder[4];
691   int tex_size[2];
692   /* each member is aligned on 4x previous component size boundaries */
693   int _padding[2];
694   struct ColorMatrices matrices;
695 };
696
697 static GstMemory *
698 yuv_to_rgb_create_uniform_memory (GstVulkanColorConvert * conv,
699     shader_info * sinfo, GstVulkanImageView ** in_views,
700     GstVulkanImageView ** out_views)
701 {
702   GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (conv);
703
704   if (sinfo->user_data) {
705     return gst_memory_ref (sinfo->user_data);
706   } else {
707     struct YUVUpdateData data;
708     ConvertInfo *conv_info;
709     GstMapInfo map_info;
710     GstMemory *uniforms;
711
712     uniforms =
713         gst_vulkan_buffer_memory_alloc (vfilter->device,
714         sizeof (struct YUVUpdateData),
715         VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
716         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
717         VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
718
719     calculate_reorder_indexes (GST_VIDEO_INFO_FORMAT (&conv->quad->in_info),
720         in_views, GST_VIDEO_INFO_FORMAT (&conv->quad->out_info),
721         out_views, data.in_reorder, data.out_reorder);
722
723     conv_info = convert_info_new (&conv->quad->in_info, &conv->quad->out_info);
724     matrix_to_float (&conv_info->to_RGB_matrix, data.matrices.to_RGB);
725     matrix_to_float (&conv_info->convert_matrix, data.matrices.primaries);
726     matrix_to_float (&conv_info->to_YUV_matrix, data.matrices.to_YUV);
727     /* FIXME: keep this around */
728     g_free (conv_info);
729
730     data.tex_size[0] = GST_VIDEO_INFO_WIDTH (&conv->quad->in_info);
731     data.tex_size[1] = GST_VIDEO_INFO_HEIGHT (&conv->quad->in_info);
732
733     if (!gst_memory_map (uniforms, &map_info, GST_MAP_WRITE)) {
734       gst_memory_unref (uniforms);
735       return NULL;
736     }
737     memcpy (map_info.data, &data, sizeof (data));
738     gst_memory_unmap (uniforms, &map_info);
739
740     sinfo->user_data = gst_memory_ref (uniforms);
741     return uniforms;
742   }
743
744 }
745
746 static void
747 unref_memory_if_set (shader_info * sinfo)
748 {
749   if (sinfo->user_data)
750     gst_memory_unref (sinfo->user_data);
751   sinfo->user_data = NULL;
752 }
753
754 static gboolean gst_vulkan_color_convert_start (GstBaseTransform * bt);
755 static gboolean gst_vulkan_color_convert_stop (GstBaseTransform * bt);
756
757 static GstCaps *gst_vulkan_color_convert_transform_caps (GstBaseTransform * bt,
758     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
759 static GstFlowReturn gst_vulkan_color_convert_transform (GstBaseTransform * bt,
760     GstBuffer * inbuf, GstBuffer * outbuf);
761 static gboolean gst_vulkan_color_convert_set_caps (GstBaseTransform * bt,
762     GstCaps * in_caps, GstCaps * out_caps);
763
764 static GstStaticPadTemplate gst_vulkan_sink_template =
765 GST_STATIC_PAD_TEMPLATE ("sink",
766     GST_PAD_SINK,
767     GST_PAD_ALWAYS,
768     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
769         (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
770             "{ BGRA, RGBA, ABGR, ARGB, BGRx, RGBx, xBGR, xRGB, AYUV, YUY2, NV12 }")));
771
772 static GstStaticPadTemplate gst_vulkan_src_template =
773 GST_STATIC_PAD_TEMPLATE ("src",
774     GST_PAD_SRC,
775     GST_PAD_ALWAYS,
776     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
777         (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE,
778             "{ BGRA, RGBA, ABGR, ARGB, BGRx, RGBx, xBGR, xRGB, AYUV, YUY2, NV12 }")));
779
780 enum
781 {
782   PROP_0,
783 };
784
785 enum
786 {
787   SIGNAL_0,
788   LAST_SIGNAL
789 };
790
791 /* static guint gst_vulkan_color_convert_signals[LAST_SIGNAL] = { 0 }; */
792
793 #define gst_vulkan_color_convert_parent_class parent_class
794 G_DEFINE_TYPE_WITH_CODE (GstVulkanColorConvert, gst_vulkan_color_convert,
795     GST_TYPE_VULKAN_VIDEO_FILTER,
796     GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_color_convert,
797         "vulkancolorconvert", 0, "Vulkan Color Convert"));
798
799 struct yuv_info
800 {
801   GstVideoFormat format;
802   gchar *from_frag;
803   gsize from_frag_size;
804   gchar *to_frag;
805   gsize to_frag_size;
806 };
807
808 static void
809 fill_shader_info (void)
810 {
811   GstVideoFormat rgbs[] = { GST_VIDEO_FORMAT_RGBA, GST_VIDEO_FORMAT_ARGB,
812     GST_VIDEO_FORMAT_BGRA, GST_VIDEO_FORMAT_ABGR, GST_VIDEO_FORMAT_RGBx,
813     GST_VIDEO_FORMAT_xRGB, GST_VIDEO_FORMAT_BGRx, GST_VIDEO_FORMAT_xBGR
814   };
815   struct yuv_info yuvs[] = {
816     {GST_VIDEO_FORMAT_AYUV, ayuv_to_rgb_frag, ayuv_to_rgb_frag_size,
817         rgb_to_ayuv_frag, rgb_to_ayuv_frag_size},
818     {GST_VIDEO_FORMAT_YUY2, yuy2_to_rgb_frag, yuy2_to_rgb_frag_size,
819         rgb_to_yuy2_frag, rgb_to_yuy2_frag_size},
820 /*    {GST_VIDEO_FORMAT_UYVY, yuy2_to_rgb_frag, yuy2_to_rgb_frag_size,
821         rgb_to_yuy2_frag, rgb_to_yuy2_frag_size},*/
822     {GST_VIDEO_FORMAT_NV12, nv12_to_rgb_frag, nv12_to_rgb_frag_size,
823         rgb_to_nv12_frag, rgb_to_nv12_frag_size},
824   };
825   guint info_i = 0;
826   guint i, j;
827
828   /* standard RGB with alpha conversion all components are copied */
829   /* *INDENT-OFF* */
830   for (i = 0; i < G_N_ELEMENTS (rgbs); i++) {
831     const GstVideoFormatInfo *from_finfo = gst_video_format_get_info (rgbs[i]);
832
833     for (j = 0; j < G_N_ELEMENTS (rgbs); j++) {
834       const GstVideoFormatInfo *to_finfo = gst_video_format_get_info (rgbs[j]);
835       gboolean clobber_alpha = FALSE;
836
837       GST_TRACE ("Initializing info for %s -> %s", from_finfo->name, to_finfo->name);
838
839       /* copying to an RGBx variant means we can store whatever we like in the 'x'
840        * component we choose to copy the alpha component like a standard RGBA->RGBA
841        * swizzle.
842        * Copying from an rgbx to a rgba format means we need to reset the
843        * alpha value */
844       clobber_alpha = !GST_VIDEO_FORMAT_INFO_HAS_ALPHA (from_finfo) && GST_VIDEO_FORMAT_INFO_HAS_ALPHA (to_finfo);
845       shader_infos[info_i++] = (shader_info) {
846           .from = rgbs[i],
847           .to = rgbs[j],
848           .cmd_create_uniform = swizzle_rgb_create_uniform_memory,
849           .frag_code = clobber_alpha ? swizzle_and_clobber_alpha_frag : swizzle_frag,
850           .frag_size = clobber_alpha ? swizzle_and_clobber_alpha_frag_size : swizzle_frag_size,
851           .uniform_size = sizeof (struct RGBUpdateData),
852           .notify = (GDestroyNotify) unref_memory_if_set,
853           .user_data = NULL,
854       };
855     }
856
857     for (j = 0; j < G_N_ELEMENTS (yuvs); j++) {
858       const GstVideoFormatInfo *to_finfo = gst_video_format_get_info (yuvs[j].format);
859       GST_TRACE ("Initializing info for %s -> %s", from_finfo->name, to_finfo->name);
860       shader_infos[info_i++] = (shader_info) {
861           .from = rgbs[i],
862           .to = yuvs[j].format,
863           .cmd_create_uniform = yuv_to_rgb_create_uniform_memory,
864           .frag_code = yuvs[j].to_frag,
865           .frag_size = yuvs[j].to_frag_size,
866           .uniform_size = sizeof(struct YUVUpdateData),
867           .notify = (GDestroyNotify) unref_memory_if_set,
868           .user_data = NULL,
869       };
870       GST_TRACE ("Initializing info for %s -> %s", to_finfo->name, from_finfo->name);
871       shader_infos[info_i++] = (shader_info) {
872           .from = yuvs[j].format,
873           .to = rgbs[i],
874           .cmd_create_uniform = yuv_to_rgb_create_uniform_memory,
875           .frag_code = yuvs[j].from_frag,
876           .frag_size = yuvs[j].from_frag_size,
877           .uniform_size = sizeof(struct YUVUpdateData),
878           .notify = (GDestroyNotify) unref_memory_if_set,
879           .user_data = NULL,
880       };
881     }
882   }
883   /* *INDENT-ON* */
884   GST_TRACE ("initialized %u formats", info_i);
885
886   g_assert (info_i == N_SHADER_INFO);
887 }
888
889 static void
890 gst_vulkan_color_convert_class_init (GstVulkanColorConvertClass * klass)
891 {
892   GstElementClass *gstelement_class;
893   GstBaseTransformClass *gstbasetransform_class;
894
895   gstelement_class = (GstElementClass *) klass;
896   gstbasetransform_class = (GstBaseTransformClass *) klass;
897
898   gst_element_class_set_metadata (gstelement_class, "Vulkan Uploader",
899       "Filter/Video/Convert", "A Vulkan Color Convert",
900       "Matthew Waters <matthew@centricular.com>");
901
902   gst_element_class_add_static_pad_template (gstelement_class,
903       &gst_vulkan_sink_template);
904   gst_element_class_add_static_pad_template (gstelement_class,
905       &gst_vulkan_src_template);
906
907   gstbasetransform_class->start =
908       GST_DEBUG_FUNCPTR (gst_vulkan_color_convert_start);
909   gstbasetransform_class->stop =
910       GST_DEBUG_FUNCPTR (gst_vulkan_color_convert_stop);
911   gstbasetransform_class->transform_caps =
912       gst_vulkan_color_convert_transform_caps;
913   gstbasetransform_class->set_caps = gst_vulkan_color_convert_set_caps;
914   gstbasetransform_class->transform = gst_vulkan_color_convert_transform;
915
916   fill_shader_info ();
917 }
918
919 static void
920 gst_vulkan_color_convert_init (GstVulkanColorConvert * conv)
921 {
922 }
923
924 static void
925 _init_value_string_list (GValue * list, ...)
926 {
927   GValue item = G_VALUE_INIT;
928   gchar *str;
929   va_list args;
930
931   g_value_init (list, GST_TYPE_LIST);
932
933   va_start (args, list);
934   while ((str = va_arg (args, gchar *))) {
935     g_value_init (&item, G_TYPE_STRING);
936     g_value_set_string (&item, str);
937
938     gst_value_list_append_value (list, &item);
939     g_value_unset (&item);
940   }
941   va_end (args);
942 }
943
944 static void
945 _append_value_string_list (GValue * list, ...)
946 {
947   GValue item = G_VALUE_INIT;
948   gchar *str;
949   va_list args;
950
951   va_start (args, list);
952   while ((str = va_arg (args, gchar *))) {
953     g_value_init (&item, G_TYPE_STRING);
954     g_value_set_string (&item, str);
955
956     gst_value_list_append_value (list, &item);
957     g_value_unset (&item);
958   }
959   va_end (args);
960 }
961
962 static void
963 _init_supported_formats (GstVulkanDevice * device, gboolean output,
964     GValue * supported_formats)
965 {
966   /* Assume if device == NULL that we don't have a Vulkan device and can
967    * do the conversion */
968
969   /* Always supported input and output formats */
970   _init_value_string_list (supported_formats, "RGBA", "RGB", "RGBx", "BGR",
971       "BGRx", "BGRA", "xRGB", "xBGR", "ARGB", "ABGR", NULL);
972
973   _append_value_string_list (supported_formats, "AYUV", "YUY2", /*"UYVY", */
974       "NV12", NULL);
975 }
976
977 /* copies the given caps */
978 static GstCaps *
979 gst_vulkan_color_convert_transform_format_info (GstVulkanDevice * device,
980     gboolean output, GstCaps * caps)
981 {
982   GstStructure *st;
983   GstCapsFeatures *f;
984   gint i, n;
985   GstCaps *res;
986   GValue supported_formats = G_VALUE_INIT;
987   GValue rgb_formats = G_VALUE_INIT;
988   GValue supported_rgb_formats = G_VALUE_INIT;
989
990   /* There are effectively two modes here with the RGB/YUV transition:
991    * 1. There is a RGB-like format as input and we can transform to YUV or,
992    * 2. No RGB-like format as input so we can only transform to RGB-like formats
993    *
994    * We also filter down the list of formats depending on what the OpenGL
995    * device supports (when provided).
996    */
997
998   _init_value_string_list (&rgb_formats, "RGBA", "ARGB", "BGRA", "ABGR", "RGBx",
999       "xRGB", "BGRx", "xBGR", "RGB", "BGR", "ARGB64", NULL);
1000   _init_supported_formats (device, output, &supported_formats);
1001   gst_value_intersect (&supported_rgb_formats, &rgb_formats,
1002       &supported_formats);
1003
1004   res = gst_caps_new_empty ();
1005
1006   n = gst_caps_get_size (caps);
1007   for (i = 0; i < n; i++) {
1008     const GValue *format;
1009
1010     st = gst_caps_get_structure (caps, i);
1011     f = gst_caps_get_features (caps, i);
1012
1013     format = gst_structure_get_value (st, "format");
1014     st = gst_structure_copy (st);
1015     if (GST_VALUE_HOLDS_LIST (format)) {
1016       gboolean have_rgb_formats = FALSE;
1017       GValue passthrough_formats = G_VALUE_INIT;
1018       gint j, len;
1019
1020       g_value_init (&passthrough_formats, GST_TYPE_LIST);
1021       len = gst_value_list_get_size (format);
1022       for (j = 0; j < len; j++) {
1023         const GValue *val;
1024
1025         val = gst_value_list_get_value (format, j);
1026         if (G_VALUE_HOLDS_STRING (val)) {
1027           const gchar *format_str = g_value_get_string (val);
1028           GstVideoFormat v_format = gst_video_format_from_string (format_str);
1029           const GstVideoFormatInfo *t_info =
1030               gst_video_format_get_info (v_format);
1031           if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) & (GST_VIDEO_FORMAT_FLAG_YUV
1032                   | GST_VIDEO_FORMAT_FLAG_GRAY)) {
1033             gst_value_list_append_value (&passthrough_formats, val);
1034           } else if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) &
1035               GST_VIDEO_FORMAT_FLAG_RGB) {
1036             have_rgb_formats = TRUE;
1037             break;
1038           }
1039         }
1040       }
1041       if (have_rgb_formats) {
1042         gst_structure_set_value (st, "format", &supported_formats);
1043       } else {
1044         /* add passthrough structure, then the rgb conversion structure */
1045         gst_structure_set_value (st, "format", &passthrough_formats);
1046         gst_caps_append_structure_full (res, gst_structure_copy (st),
1047             gst_caps_features_copy (f));
1048         gst_structure_set_value (st, "format", &supported_rgb_formats);
1049       }
1050       g_value_unset (&passthrough_formats);
1051     } else if (G_VALUE_HOLDS_STRING (format)) {
1052       const gchar *format_str = g_value_get_string (format);
1053       GstVideoFormat v_format = gst_video_format_from_string (format_str);
1054       const GstVideoFormatInfo *t_info = gst_video_format_get_info (v_format);
1055       if (GST_VIDEO_FORMAT_INFO_FLAGS (t_info) & (GST_VIDEO_FORMAT_FLAG_YUV |
1056               GST_VIDEO_FORMAT_FLAG_GRAY)) {
1057         /* add passthrough structure, then the rgb conversion structure */
1058         gst_structure_set_value (st, "format", format);
1059         gst_caps_append_structure_full (res, gst_structure_copy (st),
1060             gst_caps_features_copy (f));
1061         gst_structure_set_value (st, "format", &supported_rgb_formats);
1062       } else {                  /* RGB */
1063         gst_structure_set_value (st, "format", &supported_formats);
1064       }
1065     }
1066     gst_structure_remove_fields (st, "colorimetry", "chroma-site", NULL);
1067
1068     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
1069   }
1070
1071   g_value_unset (&supported_formats);
1072   g_value_unset (&rgb_formats);
1073   g_value_unset (&supported_rgb_formats);
1074
1075   return res;
1076 }
1077
1078 static GstCaps *
1079 gst_vulkan_color_convert_transform_caps (GstBaseTransform * bt,
1080     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1081 {
1082   GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (bt);
1083
1084   caps = gst_vulkan_color_convert_transform_format_info (vfilter->device,
1085       direction == GST_PAD_SRC, caps);
1086
1087   if (filter) {
1088     GstCaps *tmp;
1089
1090     tmp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1091     gst_caps_unref (caps);
1092     caps = tmp;
1093   }
1094
1095   return caps;
1096 }
1097
1098 static gboolean
1099 gst_vulkan_color_convert_start (GstBaseTransform * bt)
1100 {
1101   GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
1102   GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (conv);
1103
1104   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->start (bt))
1105     return FALSE;
1106
1107   conv->quad = gst_vulkan_full_screen_quad_new (vfilter->queue);
1108
1109   return TRUE;
1110 }
1111
1112 static gboolean
1113 gst_vulkan_color_convert_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
1114     GstCaps * out_caps)
1115 {
1116   GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (bt);
1117   GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
1118   GstVulkanHandle *vert, *frag;
1119   int i;
1120
1121   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->set_caps (bt, in_caps,
1122           out_caps))
1123     return FALSE;
1124
1125   if (!gst_vulkan_full_screen_quad_set_info (conv->quad, &vfilter->in_info,
1126           &vfilter->out_info))
1127     return FALSE;
1128
1129   if (conv->current_shader) {
1130     conv->current_shader->notify (conv->current_shader);
1131     conv->current_shader = NULL;
1132   }
1133
1134   for (i = 0; i < G_N_ELEMENTS (shader_infos); i++) {
1135     if (shader_infos[i].from != GST_VIDEO_INFO_FORMAT (&vfilter->in_info))
1136       continue;
1137     if (shader_infos[i].to != GST_VIDEO_INFO_FORMAT (&vfilter->out_info))
1138       continue;
1139
1140     GST_INFO_OBJECT (conv,
1141         "Found compatible conversion information from %s to %s",
1142         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&vfilter->in_info)),
1143         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT
1144             (&vfilter->out_info)));
1145     conv->current_shader = &shader_infos[i];
1146   }
1147
1148   if (!conv->current_shader) {
1149     GST_ERROR_OBJECT (conv, "Could not find a conversion info for the "
1150         "requested formats");
1151     return FALSE;
1152   }
1153
1154   if (!(vert =
1155           gst_vulkan_create_shader (vfilter->device, identity_vert,
1156               identity_vert_size, NULL))) {
1157     return FALSE;
1158   }
1159   if (!(frag =
1160           gst_vulkan_create_shader (vfilter->device,
1161               conv->current_shader->frag_code, conv->current_shader->frag_size,
1162               NULL))) {
1163     gst_vulkan_handle_unref (vert);
1164     return FALSE;
1165   }
1166
1167   if (!gst_vulkan_full_screen_quad_set_shaders (conv->quad, vert, frag)) {
1168     gst_vulkan_handle_unref (vert);
1169     gst_vulkan_handle_unref (frag);
1170     return FALSE;
1171   }
1172
1173   gst_vulkan_handle_unref (vert);
1174   gst_vulkan_handle_unref (frag);
1175
1176   return TRUE;
1177 }
1178
1179 static gboolean
1180 gst_vulkan_color_convert_stop (GstBaseTransform * bt)
1181 {
1182   GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
1183
1184   if (conv->current_shader) {
1185     conv->current_shader->notify (conv->current_shader);
1186     conv->current_shader = NULL;
1187   }
1188
1189   gst_clear_object (&conv->quad);
1190
1191   return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
1192 }
1193
1194 static GstFlowReturn
1195 gst_vulkan_color_convert_transform (GstBaseTransform * bt, GstBuffer * inbuf,
1196     GstBuffer * outbuf)
1197 {
1198   GstVulkanColorConvert *conv = GST_VULKAN_COLOR_CONVERT (bt);
1199   GstVulkanVideoFilter *vfilter = GST_VULKAN_VIDEO_FILTER (bt);
1200   GstVulkanImageView *in_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
1201   GstVulkanImageMemory *render_img_mems[GST_VIDEO_MAX_PLANES] = { NULL, };
1202   GstVulkanImageView *render_img_views[GST_VIDEO_MAX_PLANES] = { NULL, };
1203   GstVulkanImageMemory *out_img_mems[GST_VIDEO_MAX_PLANES] = { NULL, };
1204   GstBuffer *render_buf = NULL;
1205   GstVulkanFence *fence = NULL;
1206   GstVulkanCommandBuffer *cmd_buf;
1207   GError *error = NULL;
1208   VkResult err;
1209   int i;
1210
1211   fence = gst_vulkan_device_create_fence (vfilter->device, &error);
1212   if (!fence)
1213     goto error;
1214
1215   if (!gst_vulkan_full_screen_quad_set_input_buffer (conv->quad, inbuf, &error))
1216     goto error;
1217
1218   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&conv->quad->in_info); i++) {
1219     GstMemory *img_mem = gst_buffer_peek_memory (inbuf, i);
1220     if (!gst_is_vulkan_image_memory (img_mem)) {
1221       g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
1222           "Input memory must be a GstVulkanImageMemory");
1223       goto error;
1224     }
1225     in_img_views[i] =
1226         gst_vulkan_get_or_create_image_view ((GstVulkanImageMemory *) img_mem);
1227     gst_vulkan_trash_list_add (conv->quad->trash_list,
1228         gst_vulkan_trash_list_acquire (conv->quad->trash_list, fence,
1229             gst_vulkan_trash_mini_object_unref,
1230             (GstMiniObject *) in_img_views[i]));
1231   }
1232
1233   {
1234     gboolean need_render_buf = FALSE;
1235
1236     for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&conv->quad->out_info); i++) {
1237       GstMemory *mem = gst_buffer_peek_memory (outbuf, i);
1238       if (!gst_is_vulkan_image_memory (mem)) {
1239         g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
1240             "Output memory must be a GstVulkanImageMemory");
1241         goto error;
1242       }
1243       out_img_mems[i] = (GstVulkanImageMemory *) mem;
1244
1245       if (GST_VIDEO_INFO_WIDTH (&conv->quad->out_info) ==
1246           GST_VIDEO_INFO_COMP_WIDTH (&conv->quad->out_info, i)
1247           && GST_VIDEO_INFO_HEIGHT (&conv->quad->out_info) ==
1248           GST_VIDEO_INFO_COMP_HEIGHT (&conv->quad->out_info, i)) {
1249         render_img_mems[i] = out_img_mems[i];
1250         GST_LOG_OBJECT (conv, "using original output memory %p for plane %u",
1251             out_img_mems[i], i);
1252       } else {
1253         /* we need a scratch buffer because framebuffers can only output to
1254          * attachments of at least the same size which means no sub-sampled
1255          * rendering */
1256         VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
1257         VkFormat vk_format;
1258         GstMemory *mem;
1259
1260         vk_format =
1261             gst_vulkan_format_from_video_info (&conv->quad->out_info, i);
1262
1263         mem = gst_vulkan_image_memory_alloc (vfilter->device,
1264             vk_format, GST_VIDEO_INFO_WIDTH (&conv->quad->out_info),
1265             GST_VIDEO_INFO_HEIGHT (&conv->quad->out_info), tiling,
1266             VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
1267             VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1268             VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
1269         render_img_mems[i] = (GstVulkanImageMemory *) mem;
1270         need_render_buf = TRUE;
1271         GST_LOG_OBJECT (conv, "using replacement output memory %p for plane %u",
1272             mem, i);
1273       }
1274     }
1275
1276     if (need_render_buf) {
1277       render_buf = gst_buffer_new ();
1278       for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&conv->quad->out_info); i++) {
1279         gst_buffer_append_memory (render_buf,
1280             gst_memory_ref ((GstMemory *) render_img_mems[i]));
1281       }
1282       gst_vulkan_trash_list_add (conv->quad->trash_list,
1283           gst_vulkan_trash_list_acquire (conv->quad->trash_list, fence,
1284               gst_vulkan_trash_mini_object_unref,
1285               (GstMiniObject *) render_buf));
1286     } else {
1287       render_buf = outbuf;
1288     }
1289
1290     for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&conv->quad->out_info); i++) {
1291       GstMemory *img_mem = gst_buffer_peek_memory (render_buf, i);
1292       if (!gst_is_vulkan_image_memory (img_mem)) {
1293         g_set_error_literal (&error, GST_VULKAN_ERROR, GST_VULKAN_FAILED,
1294             "Input memory must be a GstVulkanImageMemory");
1295         goto error;
1296       }
1297       render_img_views[i] =
1298           gst_vulkan_get_or_create_image_view ((GstVulkanImageMemory *)
1299           img_mem);
1300       gst_vulkan_trash_list_add (conv->quad->trash_list,
1301           gst_vulkan_trash_list_acquire (conv->quad->trash_list, fence,
1302               gst_vulkan_trash_mini_object_unref,
1303               (GstMiniObject *) render_img_views[i]));
1304     }
1305   }
1306
1307   if (!gst_vulkan_full_screen_quad_set_output_buffer (conv->quad, render_buf,
1308           &error))
1309     goto error;
1310
1311   {
1312     GstMemory *uniforms = conv->current_shader->cmd_create_uniform (conv,
1313         conv->current_shader, in_img_views, render_img_views);
1314     if (!gst_vulkan_full_screen_quad_set_uniform_buffer (conv->quad, uniforms,
1315             &error)) {
1316       gst_memory_unref (uniforms);
1317       goto error;
1318     }
1319     gst_memory_unref (uniforms);
1320   }
1321
1322   if (!gst_vulkan_full_screen_quad_prepare_draw (conv->quad, fence, &error))
1323     goto error;
1324
1325   if (!(cmd_buf =
1326           gst_vulkan_command_pool_create (conv->quad->cmd_pool, &error)))
1327     goto error;
1328
1329   {
1330     VkCommandBufferBeginInfo cmd_buf_info = { 0, };
1331
1332     /* *INDENT-OFF* */
1333     cmd_buf_info = (VkCommandBufferBeginInfo) {
1334         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
1335         .pNext = NULL,
1336         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
1337         .pInheritanceInfo = NULL
1338     };
1339     /* *INDENT-ON* */
1340
1341     gst_vulkan_command_buffer_lock (cmd_buf);
1342     err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
1343     if (gst_vulkan_error_to_g_error (err, &error, "vkBeginCommandBuffer") < 0)
1344       goto error;
1345   }
1346
1347   if (!gst_vulkan_full_screen_quad_fill_command_buffer (conv->quad, cmd_buf,
1348           fence, &error))
1349     goto unlock_error;
1350
1351   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&conv->quad->out_info); i++) {
1352     if (render_img_mems[i] != out_img_mems[i]) {
1353       VkImageMemoryBarrier out_image_memory_barrier;
1354       VkImageMemoryBarrier render_image_memory_barrier;
1355       VkImageBlit blit;
1356
1357       /* *INDENT-OFF* */
1358       render_image_memory_barrier = (VkImageMemoryBarrier) {
1359           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1360           .pNext = NULL,
1361           .srcAccessMask = render_img_mems[i]->barrier.parent.access_flags,
1362           .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
1363           .oldLayout = render_img_mems[i]->barrier.image_layout,
1364           .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1365           /* FIXME: implement exclusive transfers */
1366           .srcQueueFamilyIndex = 0,
1367           .dstQueueFamilyIndex = 0,
1368           .image = render_img_mems[i]->image,
1369           .subresourceRange = render_img_mems[i]->barrier.subresource_range
1370       };
1371       out_image_memory_barrier = (VkImageMemoryBarrier) {
1372           .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1373           .pNext = NULL,
1374           .srcAccessMask = out_img_mems[i]->barrier.parent.access_flags,
1375           .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
1376           .oldLayout = out_img_mems[i]->barrier.image_layout,
1377           .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1378           /* FIXME: implement exclusive transfers */
1379           .srcQueueFamilyIndex = 0,
1380           .dstQueueFamilyIndex = 0,
1381           .image = out_img_mems[i]->image,
1382           .subresourceRange = out_img_mems[i]->barrier.subresource_range
1383       };
1384       blit = (VkImageBlit) {
1385           .srcSubresource = {
1386               .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1387               .mipLevel = 0,
1388               .baseArrayLayer = 0,
1389               .layerCount = 1,
1390           },
1391           .srcOffsets = {
1392               { 0, 0, 0 },
1393               {
1394                   GST_VIDEO_INFO_COMP_WIDTH (&conv->quad->out_info, i),
1395                   GST_VIDEO_INFO_COMP_HEIGHT (&conv->quad->out_info, i),
1396                   1
1397               },
1398           },
1399           .dstSubresource = {
1400               .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1401               .mipLevel = 0,
1402               .baseArrayLayer = 0,
1403               .layerCount = 1,
1404           },
1405           .dstOffsets = {
1406               { 0, 0, 0 },
1407               {
1408                   GST_VIDEO_INFO_COMP_WIDTH (&conv->quad->out_info, i),
1409                   GST_VIDEO_INFO_COMP_HEIGHT (&conv->quad->out_info, i),
1410                   1
1411               },
1412           },
1413       };
1414       /* *INDENT-ON* */
1415
1416       GST_LOG_OBJECT (conv, "blitting plane %u from %p to %p", i,
1417           render_img_mems[i], out_img_mems[i]);
1418
1419       vkCmdPipelineBarrier (cmd_buf->cmd,
1420           render_img_mems[i]->barrier.parent.pipeline_stages,
1421           VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1422           &render_image_memory_barrier);
1423
1424       render_img_mems[i]->barrier.parent.pipeline_stages =
1425           VK_PIPELINE_STAGE_TRANSFER_BIT;
1426       render_img_mems[i]->barrier.parent.access_flags =
1427           render_image_memory_barrier.dstAccessMask;
1428       render_img_mems[i]->barrier.image_layout =
1429           render_image_memory_barrier.newLayout;
1430
1431       vkCmdPipelineBarrier (cmd_buf->cmd,
1432           out_img_mems[i]->barrier.parent.pipeline_stages,
1433           VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1434           &out_image_memory_barrier);
1435
1436       out_img_mems[i]->barrier.parent.pipeline_stages =
1437           VK_PIPELINE_STAGE_TRANSFER_BIT;
1438       out_img_mems[i]->barrier.parent.access_flags =
1439           out_image_memory_barrier.dstAccessMask;
1440       out_img_mems[i]->barrier.image_layout =
1441           out_image_memory_barrier.newLayout;
1442
1443       /* XXX: This is mostly right for a downsampling pass however if
1444        * anything is more complicated, then we will need a new render pass */
1445       vkCmdBlitImage (cmd_buf->cmd, render_img_mems[i]->image,
1446           render_img_mems[i]->barrier.image_layout, out_img_mems[i]->image,
1447           out_img_mems[i]->barrier.image_layout, 1, &blit, VK_FILTER_LINEAR);
1448
1449       /* XXX: try to reuse this image later */
1450       gst_vulkan_trash_list_add (conv->quad->trash_list,
1451           gst_vulkan_trash_list_acquire (conv->quad->trash_list, fence,
1452               gst_vulkan_trash_mini_object_unref,
1453               (GstMiniObject *) render_img_mems[i]));
1454     }
1455   }
1456
1457   err = vkEndCommandBuffer (cmd_buf->cmd);
1458   gst_vulkan_command_buffer_unlock (cmd_buf);
1459   if (gst_vulkan_error_to_g_error (err, &error, "vkEndCommandBuffer") < 0)
1460     goto error;
1461
1462   if (!gst_vulkan_full_screen_quad_submit (conv->quad, cmd_buf, fence, &error))
1463     goto error;
1464
1465   gst_vulkan_fence_unref (fence);
1466
1467   return GST_FLOW_OK;
1468
1469 unlock_error:
1470   if (cmd_buf) {
1471     gst_vulkan_command_buffer_unlock (cmd_buf);
1472     gst_vulkan_command_buffer_unref (cmd_buf);
1473   }
1474 error:
1475   gst_clear_mini_object ((GstMiniObject **) & fence);
1476
1477   GST_ELEMENT_ERROR (bt, LIBRARY, FAILED, ("%s", error->message), (NULL));
1478   g_clear_error (&error);
1479   return GST_FLOW_ERROR;
1480 }