vulkan: move fullscreenquad object to library
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / gstvkutils.c
1 /*
2  * GStreamer
3  * Copyright (C) 2015 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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstvkutils.h"
26
27 /**
28  * SECTION:vkutils
29  * @title: Vulkan Utils
30  * @short_description: Vulkan utilities
31  * @see_also: #GstVulkanInstance, #GstVulkanDevice
32  *
33  * GstVulkanQueue encapsulates the vulkan command queue.
34  */
35
36 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
37
38 static void
39 _init_context_debug (void)
40 {
41 #ifndef GST_DISABLE_GST_DEBUG
42   static volatile gsize _init = 0;
43
44   if (g_once_init_enter (&_init)) {
45     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
46     g_once_init_leave (&_init, 1);
47   }
48 #endif
49 }
50
51 static gboolean
52 _vk_pad_query (const GValue * item, GValue * value, gpointer user_data)
53 {
54   GstPad *pad = g_value_get_object (item);
55   GstQuery *query = user_data;
56   gboolean res;
57
58   _init_context_debug ();
59
60   res = gst_pad_peer_query (pad, query);
61
62   if (res) {
63     g_value_set_boolean (value, TRUE);
64     return FALSE;
65   }
66
67   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
68   return TRUE;
69 }
70
71 /**
72  * gst_vulkan_run_query:
73  * @element: a #GstElement
74  * @query: the #GstQuery to perform
75  * @direction: the #GstPadDirection to perform query on
76  *
77  * Returns: whether @query was answered successfully
78  *
79  * Since: 1.18
80  */
81 gboolean
82 gst_vulkan_run_query (GstElement * element, GstQuery * query,
83     GstPadDirection direction)
84 {
85   GstIterator *it;
86   GstIteratorFoldFunction func = _vk_pad_query;
87   GValue res = { 0 };
88
89   g_value_init (&res, G_TYPE_BOOLEAN);
90   g_value_set_boolean (&res, FALSE);
91
92   /* Ask neighbor */
93   if (direction == GST_PAD_SRC)
94     it = gst_element_iterate_src_pads (element);
95   else
96     it = gst_element_iterate_sink_pads (element);
97
98   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
99     gst_iterator_resync (it);
100
101   gst_iterator_free (it);
102
103   return g_value_get_boolean (&res);
104 }
105
106 static GstQuery *
107 _vulkan_local_context_query (GstElement * element,
108     const gchar * context_type, gboolean set_context)
109 {
110   GstQuery *query;
111   GstContext *ctxt;
112
113   _init_context_debug ();
114
115   /*  2a) Query downstream with GST_QUERY_CONTEXT for the context and
116    *      check if downstream already has a context of the specific type
117    *  2b) Query upstream as above.
118    */
119   query = gst_query_new_context (context_type);
120   if (gst_vulkan_run_query (element, query, GST_PAD_SRC)) {
121     gst_query_parse_context (query, &ctxt);
122     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
123         "found context (%p) in downstream query", ctxt);
124     if (set_context)
125       gst_element_set_context (element, ctxt);
126   } else if (gst_vulkan_run_query (element, query, GST_PAD_SINK)) {
127     gst_query_parse_context (query, &ctxt);
128     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
129         "found context (%p) in upstream query", ctxt);
130     if (set_context)
131       gst_element_set_context (element, ctxt);
132   } else {
133     gst_query_unref (query);
134     query = NULL;
135   }
136
137   return query;
138 }
139
140 /**
141  * gst_vulkan_global_context_query:
142  * @element: a #GstElement
143  * @context_type: the context type to query for
144  *
145  * Performs the steps necessary for executing a context query including
146  * posting a message for the application to respond.
147  *
148  * Since: 1.18
149  */
150 void
151 gst_vulkan_global_context_query (GstElement * element,
152     const gchar * context_type)
153 {
154   GstQuery *query;
155   GstMessage *msg;
156
157   if ((query = _vulkan_local_context_query (element, context_type, TRUE))) {
158     gst_query_unref (query);
159     return;
160   }
161
162   /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
163    *    the required context type and afterwards check if a
164    *    usable context was set now as in 1). The message could
165    *    be handled by the parent bins of the element and the
166    *    application.
167    */
168   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
169       "posting need context message");
170   msg = gst_message_new_need_context (GST_OBJECT_CAST (element), context_type);
171   gst_element_post_message (element, msg);
172
173   /*
174    * Whomever responds to the need-context message performs a
175    * GstElement::set_context() with the required context in which the element
176    * is required to update the display_ptr or call gst_vulkan_handle_set_context().
177    */
178 }
179
180 /**
181  * gst_vulkan_local_context_query:
182  * @element: a #GstElement
183  * @context_type: the context type to query for
184  *
185  * Performs the steps necessary for executing a context query between only
186  * other elements in the pipeline
187  *
188  * Since: 1.18
189  */
190 GstQuery *
191 gst_vulkan_local_context_query (GstElement * element,
192     const gchar * context_type)
193 {
194   return _vulkan_local_context_query (element, context_type, FALSE);
195 }
196
197 static void
198 _vk_display_context_query (GstElement * element,
199     GstVulkanDisplay ** display_ptr)
200 {
201   gst_vulkan_global_context_query (element,
202       GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR);
203 }
204
205 /*  4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT
206  *     message.
207  */
208 /*
209  * @element: (transfer none):
210  * @context: (transfer full):
211  */
212 static void
213 _vk_context_propagate (GstElement * element, GstContext * context)
214 {
215   GstMessage *msg;
216
217   _init_context_debug ();
218
219   gst_element_set_context (element, context);
220
221   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
222       "posting have context (%" GST_PTR_FORMAT ") message", context);
223   msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
224   gst_element_post_message (GST_ELEMENT_CAST (element), msg);
225 }
226
227 /**
228  * gst_vulkan_ensure_element_data:
229  * @element: a #GstElement
230  * @display_ptr: (inout) (optional): the resulting #GstVulkanDisplay
231  * @instance_ptr: (inout): the resulting #GstVulkanInstance
232  *
233  * Perform the steps necessary for retrieving a #GstVulkanInstance and
234  * (optionally) an #GstVulkanDisplay from the surrounding elements or from
235  * the application using the #GstContext mechanism.
236  *
237  * If the contents of @display_ptr or @instance_ptr are not %NULL, then no
238  * #GstContext query is necessary and no #GstVulkanInstance or #GstVulkanDisplay
239  * retrieval is performed.
240  *
241  * Returns: whether a #GstVulkanInstance exists in @instance_ptr and if
242  *          @display_ptr is not %NULL, whether a #GstVulkanDisplay exists in
243  *          @display_ptr
244  *
245  * Since: 1.18
246  */
247 gboolean
248 gst_vulkan_ensure_element_data (GstElement * element,
249     GstVulkanDisplay ** display_ptr, GstVulkanInstance ** instance_ptr)
250 {
251   g_return_val_if_fail (element != NULL, FALSE);
252   g_return_val_if_fail (instance_ptr != NULL, FALSE);
253
254   /*  1) Check if the element already has a context of the specific
255    *     type.
256    */
257   if (!*instance_ptr) {
258     GError *error = NULL;
259     GstContext *context = NULL;
260
261     gst_vulkan_global_context_query (element,
262         GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR);
263
264     /* Neighbour found and it updated the display */
265     if (!*instance_ptr) {
266       /* If no neighboor, or application not interested, use system default */
267       *instance_ptr = gst_vulkan_instance_new ();
268
269       context = gst_context_new (GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR, TRUE);
270       gst_context_set_vulkan_instance (context, *instance_ptr);
271     }
272
273     if (!gst_vulkan_instance_open (*instance_ptr, &error)) {
274       GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
275           ("Failed to create vulkan instance"), ("%s", error->message));
276       gst_object_unref (*instance_ptr);
277       *instance_ptr = NULL;
278       g_clear_error (&error);
279       return FALSE;
280     }
281
282     if (context)
283       _vk_context_propagate (element, context);
284   }
285
286   /* we don't care about a display */
287   if (!display_ptr)
288     return *instance_ptr != NULL;
289
290   if (!*display_ptr) {
291     _vk_display_context_query (element, display_ptr);
292
293     /* Neighbour found and it updated the display */
294     if (!*display_ptr) {
295       GstContext *context;
296
297       /* instance is required before the display */
298       g_return_val_if_fail (*instance_ptr != NULL, FALSE);
299
300       /* If no neighboor, or application not interested, use system default */
301       *display_ptr = gst_vulkan_display_new (*instance_ptr);
302
303       context = gst_context_new (GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR, TRUE);
304       gst_context_set_vulkan_display (context, *display_ptr);
305
306       _vk_context_propagate (element, context);
307     }
308   }
309
310   return *display_ptr != NULL && *instance_ptr != NULL;
311 }
312
313 /**
314  * gst_vulkan_handle_set_context:
315  * @element: a #GstElement
316  * @context: a #GstContext
317  * @display: (inout) (transfer full) (optional): location of a #GstVulkanDisplay
318  * @instance: (inout) (transfer full): location of a #GstVulkanInstance
319  *
320  * Helper function for implementing #GstElementClass.set_context() in
321  * Vulkan capable elements.
322  *
323  * Retrieve's the #GstVulkanDisplay or #GstVulkanInstance in @context and places
324  * the result in @display or @instance respectively.
325  *
326  * Returns: whether the @display or @instance could be set successfully
327  *
328  * Since: 1.18
329  */
330 gboolean
331 gst_vulkan_handle_set_context (GstElement * element, GstContext * context,
332     GstVulkanDisplay ** display, GstVulkanInstance ** instance)
333 {
334   GstVulkanDisplay *display_replacement = NULL;
335   GstVulkanInstance *instance_replacement = NULL;
336   const gchar *context_type;
337
338   g_return_val_if_fail (instance != NULL, FALSE);
339
340   if (!context)
341     return FALSE;
342
343   context_type = gst_context_get_context_type (context);
344
345   if (display
346       && g_strcmp0 (context_type, GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR) == 0) {
347     if (!gst_context_get_vulkan_display (context, &display_replacement)) {
348       GST_WARNING_OBJECT (element, "Failed to get display from context");
349       return FALSE;
350     }
351   } else if (g_strcmp0 (context_type,
352           GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR) == 0) {
353     if (!gst_context_get_vulkan_instance (context, &instance_replacement)) {
354       GST_WARNING_OBJECT (element, "Failed to get instance from context");
355       return FALSE;
356     }
357   }
358
359   if (display_replacement) {
360     GstVulkanDisplay *old = *display;
361     *display = display_replacement;
362
363     if (old)
364       gst_object_unref (old);
365   }
366
367   if (instance_replacement) {
368     GstVulkanInstance *old = *instance;
369     *instance = instance_replacement;
370
371     if (old)
372       gst_object_unref (old);
373   }
374
375   return TRUE;
376 }
377
378 /**
379  * gst_vulkan_handle_context_query:
380  * @element: a #GstElement
381  * @query: a #GstQuery of type %GST_QUERY_CONTEXT
382  * @display: (transfer none) (nullable): a #GstVulkanDisplay
383  * @instance: (transfer none) (nullable): a #GstVulkanInstance
384  * @device: (transfer none) (nullable): a #GstVulkanInstance
385  *
386  * Returns: Whether the @query was successfully responded to from the passed
387  *          @display, @instance, and @device.
388  *
389  * Since: 1.18
390  */
391 gboolean
392 gst_vulkan_handle_context_query (GstElement * element, GstQuery * query,
393     GstVulkanDisplay * display, GstVulkanInstance * instance,
394     GstVulkanDevice * device)
395 {
396   if (gst_vulkan_display_handle_context_query (element, query, display))
397     return TRUE;
398   if (gst_vulkan_instance_handle_context_query (element, query, instance))
399     return TRUE;
400   if (gst_vulkan_device_handle_context_query (element, query, device))
401     return TRUE;
402
403   return FALSE;
404 }
405
406 static void
407 fill_vulkan_image_view_info (VkImage image, VkFormat format,
408     VkImageViewCreateInfo * info)
409 {
410   /* *INDENT-OFF* */
411   *info = (VkImageViewCreateInfo) {
412       .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
413       .pNext = NULL,
414       .image = image,
415       .format = format,
416       .viewType = VK_IMAGE_VIEW_TYPE_2D,
417       .flags = 0,
418       .components = (VkComponentMapping) {
419           VK_COMPONENT_SWIZZLE_R,
420           VK_COMPONENT_SWIZZLE_G,
421           VK_COMPONENT_SWIZZLE_B,
422           VK_COMPONENT_SWIZZLE_A
423       },
424       .subresourceRange = (VkImageSubresourceRange) {
425           .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
426           .baseMipLevel = 0,
427           .levelCount = 1,
428           .baseArrayLayer = 0,
429           .layerCount = 1,
430       }
431   };
432   /* *INDENT-ON* */
433 }
434
435 static gboolean
436 find_compatible_view (GstVulkanImageView * view, VkImageViewCreateInfo * info)
437 {
438   return view->create_info.image == info->image
439       && view->create_info.format == info->format
440       && view->create_info.viewType == info->viewType
441       && view->create_info.flags == info->flags
442       && view->create_info.components.r == info->components.r
443       && view->create_info.components.g == info->components.g
444       && view->create_info.components.b == info->components.b
445       && view->create_info.components.a == info->components.a
446       && view->create_info.subresourceRange.aspectMask ==
447       info->subresourceRange.aspectMask
448       && view->create_info.subresourceRange.baseMipLevel ==
449       info->subresourceRange.baseMipLevel
450       && view->create_info.subresourceRange.levelCount ==
451       info->subresourceRange.levelCount
452       && view->create_info.subresourceRange.levelCount ==
453       info->subresourceRange.levelCount
454       && view->create_info.subresourceRange.baseArrayLayer ==
455       info->subresourceRange.baseArrayLayer
456       && view->create_info.subresourceRange.layerCount ==
457       info->subresourceRange.layerCount;
458 }
459
460 GstVulkanImageView *
461 gst_vulkan_get_or_create_image_view (GstVulkanImageMemory * image)
462 {
463   VkImageViewCreateInfo create_info;
464   GstVulkanImageView *ret = NULL;
465
466   fill_vulkan_image_view_info (image->image, image->create_info.format,
467       &create_info);
468
469   ret = gst_vulkan_image_memory_find_view (image,
470       (GstVulkanImageMemoryFindViewFunc) find_compatible_view, &create_info);
471   if (!ret) {
472     ret = gst_vulkan_image_view_new (image, &create_info);
473     gst_vulkan_image_memory_add_view (image, ret);
474   }
475
476   return ret;
477 }
478
479 #define SPIRV_MAGIC_NUMBER_NE 0x07230203
480 #define SPIRV_MAGIC_NUMBER_OE 0x03022307
481
482 GstVulkanHandle *
483 gst_vulkan_create_shader (GstVulkanDevice * device, gchar * code, gsize size,
484     GError ** error)
485 {
486   VkShaderModule shader;
487   VkResult res;
488
489   /* *INDENT-OFF* */
490   VkShaderModuleCreateInfo info = {
491       .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
492       .pNext = NULL,
493       .flags = 0,
494       .codeSize = size,
495       .pCode = (guint32 *) code
496   };
497   /* *INDENT-ON* */
498   guint32 first_word;
499   guint32 *new_code = NULL;
500
501   g_return_val_if_fail (size >= 4, VK_NULL_HANDLE);
502   g_return_val_if_fail (size % 4 == 0, VK_NULL_HANDLE);
503
504   first_word = code[0] | code[1] << 8 | code[2] << 16 | code[3] << 24;
505   g_return_val_if_fail (first_word == SPIRV_MAGIC_NUMBER_NE
506       || first_word == SPIRV_MAGIC_NUMBER_OE, VK_NULL_HANDLE);
507   if (first_word == SPIRV_MAGIC_NUMBER_OE) {
508     /* endianness swap... */
509     guint32 *old_code = (guint32 *) code;
510     gsize i;
511
512     GST_DEBUG ("performaing endianness conversion on spirv shader of size %"
513         G_GSIZE_FORMAT, size);
514     new_code = g_new0 (guint32, size / 4);
515
516     for (i = 0; i < size / 4; i++) {
517       guint32 old = old_code[i];
518       guint32 new = 0;
519
520       new |= (old & 0xff) << 24;
521       new |= (old & 0xff00) << 8;
522       new |= (old & 0xff0000) >> 8;
523       new |= (old & 0xff000000) >> 24;
524       new_code[i] = new;
525     }
526
527     first_word = ((guint32 *) new_code)[0];
528     g_assert (first_word == SPIRV_MAGIC_NUMBER_NE);
529
530     info.pCode = new_code;
531   }
532
533   res = vkCreateShaderModule (device->device, &info, NULL, &shader);
534   g_free (new_code);
535   if (gst_vulkan_error_to_g_error (res, error, "VkCreateShaderModule") < 0)
536     return NULL;
537
538   return gst_vulkan_handle_new_wrapped (device, GST_VULKAN_HANDLE_TYPE_SHADER,
539       (GstVulkanHandleTypedef) shader, gst_vulkan_handle_free_shader, NULL);
540 }