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