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