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
_check_for_all_layers(uint32_t check_count,const char ** check_names,uint32_t layer_count,VkLayerProperties * layers,guint32 * supported_layers_count,gchar *** supported_layers)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
_init_context_debug(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
_vk_pad_query(const GValue * item,GValue * value,gpointer user_data)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
gst_vulkan_run_query(GstElement * element,GstQuery * query,GstPadDirection direction)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
gst_vulkan_global_context_query(GstElement * element,const gchar * context_type)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 *
gst_vulkan_local_context_query(GstElement * element,const gchar * context_type,gboolean set_context)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
_vk_display_context_query(GstElement * element,GstVulkanDisplay ** display_ptr)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
_vk_context_propagate(GstElement * element,GstContext * context)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
gst_vulkan_ensure_element_data(gpointer element,GstVulkanDisplay ** display_ptr,GstVulkanInstance ** instance_ptr)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       gst_object_unref (*instance_ptr);
251       *instance_ptr = NULL;
252       g_clear_error (&error);
253       return FALSE;
254     }
255   }
256 
257   if (!*display_ptr) {
258     _vk_display_context_query (element, display_ptr);
259 
260     /* Neighbour found and it updated the display */
261     if (!*display_ptr) {
262       GstContext *context;
263 
264       /* instance is required before the display */
265       g_return_val_if_fail (*instance_ptr != NULL, FALSE);
266 
267       /* If no neighboor, or application not interested, use system default */
268       *display_ptr = gst_vulkan_display_new (*instance_ptr);
269 
270       context = gst_context_new (GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR, TRUE);
271       gst_context_set_vulkan_display (context, *display_ptr);
272 
273       _vk_context_propagate (element, context);
274     }
275   }
276 
277   return *display_ptr != NULL && *instance_ptr != NULL;
278 }
279 
280 gboolean
gst_vulkan_handle_set_context(GstElement * element,GstContext * context,GstVulkanDisplay ** display,GstVulkanInstance ** instance)281 gst_vulkan_handle_set_context (GstElement * element, GstContext * context,
282     GstVulkanDisplay ** display, GstVulkanInstance ** instance)
283 {
284   GstVulkanDisplay *display_replacement = NULL;
285   GstVulkanInstance *instance_replacement = NULL;
286   const gchar *context_type;
287 
288   g_return_val_if_fail (display != NULL, FALSE);
289   g_return_val_if_fail (instance != NULL, FALSE);
290 
291   if (!context)
292     return FALSE;
293 
294   context_type = gst_context_get_context_type (context);
295 
296   if (g_strcmp0 (context_type, GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR) == 0) {
297     if (!gst_context_get_vulkan_display (context, &display_replacement)) {
298       GST_WARNING_OBJECT (element, "Failed to get display from context");
299       return FALSE;
300     }
301   } else if (g_strcmp0 (context_type,
302           GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR) == 0) {
303     if (!gst_context_get_vulkan_instance (context, &instance_replacement)) {
304       GST_WARNING_OBJECT (element, "Failed to get instance from context");
305       return FALSE;
306     }
307   }
308 
309   if (display_replacement) {
310     GstVulkanDisplay *old = *display;
311     *display = display_replacement;
312 
313     if (old)
314       gst_object_unref (old);
315   }
316 
317   if (instance_replacement) {
318     GstVulkanInstance *old = *instance;
319     *instance = instance_replacement;
320 
321     if (old)
322       gst_object_unref (old);
323   }
324 
325   return TRUE;
326 }
327 
328 gboolean
gst_vulkan_handle_context_query(GstElement * element,GstQuery * query,GstVulkanDisplay ** display,GstVulkanInstance ** instance,GstVulkanDevice ** device)329 gst_vulkan_handle_context_query (GstElement * element, GstQuery * query,
330     GstVulkanDisplay ** display, GstVulkanInstance ** instance,
331     GstVulkanDevice ** device)
332 {
333   if (gst_vulkan_display_handle_context_query (element, query, display))
334     return TRUE;
335   if (gst_vulkan_instance_handle_context_query (element, query, instance))
336     return TRUE;
337   if (gst_vulkan_device_handle_context_query (element, query, device))
338     return TRUE;
339 
340   return FALSE;
341 }
342