1c25cb8122c982a853f2eb7da04007dd8274dc98
[platform/upstream/gstreamer.git] / ext / vulkan / vkutils.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 "vkutils.h"
26 #include "vkutils_private.h"
27
28 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
29
30 gboolean
31 _check_for_all_layers (uint32_t check_count, const char **check_names,
32     uint32_t layer_count, VkLayerProperties * layers,
33     guint32 * supported_layers_count, gchar *** supported_layers)
34 {
35   uint32_t i, j, k;
36
37   if (check_count <= 0 || layer_count <= 0) {
38     GST_WARNING ("no layers requested or supported");
39     *supported_layers = NULL;
40     return FALSE;
41   }
42
43   *supported_layers = g_new0 (gchar *, check_count + 1);
44   k = 0;
45
46   for (i = 0; i < check_count; i++) {
47     gboolean found = FALSE;
48     for (j = 0; j < layer_count; j++) {
49       if (g_strcmp0 (check_names[i], layers[j].layerName) == 0) {
50         GST_TRACE ("found layer: %s", check_names[i]);
51         found = TRUE;
52         (*supported_layers)[k++] = g_strdup (check_names[i]);
53       }
54     }
55     if (!found)
56       GST_WARNING ("Cannot find layer: %s", check_names[i]);
57   }
58
59   (*supported_layers)[k] = NULL;
60   *supported_layers_count = g_strv_length (*supported_layers);
61
62   return TRUE;
63 }
64
65 static void
66 _init_context_debug (void)
67 {
68 #ifndef GST_DISABLE_GST_DEBUG
69   static volatile gsize _init = 0;
70
71   if (g_once_init_enter (&_init)) {
72     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
73     g_once_init_leave (&_init, 1);
74   }
75 #endif
76 }
77
78 static gboolean
79 _vk_pad_query (const GValue * item, GValue * value, gpointer user_data)
80 {
81   GstPad *pad = g_value_get_object (item);
82   GstQuery *query = user_data;
83   gboolean res;
84
85   _init_context_debug ();
86
87   res = gst_pad_peer_query (pad, query);
88
89   if (res) {
90     g_value_set_boolean (value, TRUE);
91     return FALSE;
92   }
93
94   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
95   return TRUE;
96 }
97
98 gboolean
99 gst_vulkan_run_query (GstElement * element, GstQuery * query,
100     GstPadDirection direction)
101 {
102   GstIterator *it;
103   GstIteratorFoldFunction func = _vk_pad_query;
104   GValue res = { 0 };
105
106   g_value_init (&res, G_TYPE_BOOLEAN);
107   g_value_set_boolean (&res, FALSE);
108
109   /* Ask neighbor */
110   if (direction == GST_PAD_SRC)
111     it = gst_element_iterate_src_pads (element);
112   else
113     it = gst_element_iterate_sink_pads (element);
114
115   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
116     gst_iterator_resync (it);
117
118   gst_iterator_free (it);
119
120   return g_value_get_boolean (&res);
121 }
122
123 void
124 gst_vulkan_global_context_query (GstElement * element,
125     const gchar * context_type)
126 {
127   GstQuery *query;
128   GstMessage *msg;
129
130   if ((query = gst_vulkan_local_context_query (element, context_type, TRUE))) {
131     gst_query_unref (query);
132     return;
133   }
134
135   /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
136    *    the required context type and afterwards check if a
137    *    usable context was set now as in 1). The message could
138    *    be handled by the parent bins of the element and the
139    *    application.
140    */
141   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
142       "posting need context message");
143   msg = gst_message_new_need_context (GST_OBJECT_CAST (element), context_type);
144   gst_element_post_message (element, msg);
145
146   /*
147    * Whomever responds to the need-context message performs a
148    * GstElement::set_context() with the required context in which the element
149    * is required to update the display_ptr or call gst_vulkan_handle_set_context().
150    */
151 }
152
153 GstQuery *
154 gst_vulkan_local_context_query (GstElement * element,
155     const gchar * context_type, gboolean set_context)
156 {
157   GstQuery *query;
158   GstContext *ctxt;
159
160   _init_context_debug ();
161
162   /*  2a) Query downstream with GST_QUERY_CONTEXT for the context and
163    *      check if downstream already has a context of the specific type
164    *  2b) Query upstream as above.
165    */
166   query = gst_query_new_context (context_type);
167   if (gst_vulkan_run_query (element, query, GST_PAD_SRC)) {
168     gst_query_parse_context (query, &ctxt);
169     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
170         "found context (%p) in downstream query", ctxt);
171     if (set_context)
172       gst_element_set_context (element, ctxt);
173   } else if (gst_vulkan_run_query (element, query, GST_PAD_SINK)) {
174     gst_query_parse_context (query, &ctxt);
175     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
176         "found context (%p) in upstream query", ctxt);
177     if (set_context)
178       gst_element_set_context (element, ctxt);
179   } else {
180     gst_query_unref (query);
181     query = NULL;
182   }
183
184   return query;
185 }
186
187 static void
188 _vk_display_context_query (GstElement * element,
189     GstVulkanDisplay ** display_ptr)
190 {
191   gst_vulkan_global_context_query (element,
192       GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR);
193 }
194
195 /*  4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT
196  *     message.
197  */
198 /*
199  * @element: (transfer none):
200  * @context: (transfer full):
201  */
202 static void
203 _vk_context_propagate (GstElement * element, GstContext * context)
204 {
205   GstMessage *msg;
206
207   _init_context_debug ();
208
209   gst_element_set_context (element, context);
210
211   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
212       "posting have context (%" GST_PTR_FORMAT ") message", context);
213   msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
214   gst_element_post_message (GST_ELEMENT_CAST (element), msg);
215 }
216
217 gboolean
218 gst_vulkan_ensure_element_data (gpointer element,
219     GstVulkanDisplay ** display_ptr, GstVulkanInstance ** instance_ptr)
220 {
221   g_return_val_if_fail (element != NULL, FALSE);
222   g_return_val_if_fail (display_ptr != NULL, FALSE);
223   g_return_val_if_fail (instance_ptr != NULL, FALSE);
224
225   /*  1) Check if the element already has a context of the specific
226    *     type.
227    */
228   if (!*instance_ptr) {
229     GError *error = NULL;
230
231     gst_vulkan_global_context_query (element,
232         GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR);
233
234     /* Neighbour found and it updated the display */
235     if (!*instance_ptr) {
236       GstContext *context;
237
238       /* If no neighboor, or application not interested, use system default */
239       *instance_ptr = gst_vulkan_instance_new ();
240
241       context = gst_context_new (GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR, TRUE);
242       gst_context_set_vulkan_instance (context, *instance_ptr);
243
244       _vk_context_propagate (element, context);
245     }
246
247     if (!gst_vulkan_instance_open (*instance_ptr, &error)) {
248       GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
249           ("Failed to create vulkan instance"), ("%s", error->message));
250       g_clear_error (&error);
251       return FALSE;
252     }
253   }
254
255   if (!*display_ptr) {
256     _vk_display_context_query (element, display_ptr);
257
258     /* Neighbour found and it updated the display */
259     if (!*display_ptr) {
260       GstContext *context;
261
262       /* instance is required before the display */
263       g_return_val_if_fail (*instance_ptr != NULL, FALSE);
264
265       /* If no neighboor, or application not interested, use system default */
266       *display_ptr = gst_vulkan_display_new (*instance_ptr);
267
268       context = gst_context_new (GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR, TRUE);
269       gst_context_set_vulkan_display (context, *display_ptr);
270
271       _vk_context_propagate (element, context);
272     }
273   }
274
275   return *display_ptr != NULL && *instance_ptr != NULL;
276 }
277
278 gboolean
279 gst_vulkan_handle_set_context (GstElement * element, GstContext * context,
280     GstVulkanDisplay ** display, GstVulkanInstance ** instance)
281 {
282   GstVulkanDisplay *display_replacement = NULL;
283   GstVulkanInstance *instance_replacement = NULL;
284   const gchar *context_type;
285
286   g_return_val_if_fail (display != NULL, FALSE);
287   g_return_val_if_fail (instance != NULL, FALSE);
288
289   if (!context)
290     return FALSE;
291
292   context_type = gst_context_get_context_type (context);
293
294   if (g_strcmp0 (context_type, GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR) == 0) {
295     if (!gst_context_get_vulkan_display (context, &display_replacement)) {
296       GST_WARNING_OBJECT (element, "Failed to get display from context");
297       return FALSE;
298     }
299   } else if (g_strcmp0 (context_type,
300           GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR) == 0) {
301     if (!gst_context_get_vulkan_instance (context, &instance_replacement)) {
302       GST_WARNING_OBJECT (element, "Failed to get instance from context");
303       return FALSE;
304     }
305   }
306
307   if (display_replacement) {
308     GstVulkanDisplay *old = *display;
309     *display = display_replacement;
310
311     if (old)
312       gst_object_unref (old);
313   }
314
315   if (instance_replacement) {
316     GstVulkanInstance *old = *instance;
317     *instance = instance_replacement;
318
319     if (old)
320       gst_object_unref (old);
321   }
322
323   return TRUE;
324 }
325
326 gboolean
327 gst_vulkan_handle_context_query (GstElement * element, GstQuery * query,
328     GstVulkanDisplay ** display, GstVulkanInstance ** instance,
329     GstVulkanDevice ** device)
330 {
331   if (gst_vulkan_display_handle_context_query (element, query, display))
332     return TRUE;
333   if (gst_vulkan_instance_handle_context_query (element, query, instance))
334     return TRUE;
335   if (gst_vulkan_device_handle_context_query (element, query, device))
336     return TRUE;
337
338   return FALSE;
339 }