1 /*
2 * GStreamer
3 * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
4 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
5 * Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
6 * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "vkdisplay.h"
29
30 #if GST_VULKAN_HAVE_WINDOW_X11
31 #include "x11/vkdisplay_x11.h"
32 #endif
33 #if GST_VULKAN_HAVE_WINDOW_XCB
34 #include "xcb/vkdisplay_xcb.h"
35 #endif
36 #if GST_VULKAN_HAVE_WINDOW_WAYLAND
37 #include "wayland/vkdisplay_wayland.h"
38 #endif
39 #if GST_VULKAN_HAVE_WINDOW_COCOA
40 #include "cocoa/vkdisplay_cocoa.h"
41 #endif
42 #if GST_VULKAN_HAVE_WINDOW_IOS
43 #include "ios/vkdisplay_ios.h"
44 #endif
45
46 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
47 #define GST_CAT_DEFAULT gst_vulkan_display_debug
48 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
49
50 static void
_init_debug(void)51 _init_debug (void)
52 {
53 static volatile gsize _init = 0;
54
55 if (g_once_init_enter (&_init)) {
56 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandisplay", 0,
57 "Vulkan display");
58 GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
59 g_once_init_leave (&_init, 1);
60 }
61 }
62
63 enum
64 {
65 SIGNAL_0,
66 CREATE_CONTEXT,
67 LAST_SIGNAL
68 };
69
70 /* static guint gst_vulkan_display_signals[LAST_SIGNAL] = { 0 }; */
71
72 static void gst_vulkan_display_finalize (GObject * object);
73 static gpointer gst_vulkan_display_default_get_handle (GstVulkanDisplay *
74 display);
75 static GstVulkanWindow
76 * gst_vulkan_display_default_create_window (GstVulkanDisplay * display);
77
78 struct _GstVulkanDisplayPrivate
79 {
80 GThread *event_thread;
81
82 GMutex thread_lock;
83 GCond thread_cond;
84 };
85
86 G_DEFINE_TYPE_WITH_CODE (GstVulkanDisplay, gst_vulkan_display, GST_TYPE_OBJECT,
87 G_ADD_PRIVATE (GstVulkanDisplay) _init_debug ());
88
89 static gpointer
_event_thread_main(GstVulkanDisplay * display)90 _event_thread_main (GstVulkanDisplay * display)
91 {
92 g_mutex_lock (&display->priv->thread_lock);
93
94 display->main_context = g_main_context_new ();
95 display->main_loop = g_main_loop_new (display->main_context, FALSE);
96
97 g_cond_broadcast (&display->priv->thread_cond);
98 g_mutex_unlock (&display->priv->thread_lock);
99
100 g_main_loop_run (display->main_loop);
101
102 g_mutex_lock (&display->priv->thread_lock);
103
104 g_main_loop_unref (display->main_loop);
105 g_main_context_unref (display->main_context);
106
107 display->main_loop = NULL;
108 display->main_context = NULL;
109
110 g_cond_broadcast (&display->priv->thread_cond);
111 g_mutex_unlock (&display->priv->thread_lock);
112
113 return NULL;
114 }
115
116 static void
gst_vulkan_display_class_init(GstVulkanDisplayClass * klass)117 gst_vulkan_display_class_init (GstVulkanDisplayClass * klass)
118 {
119 klass->get_handle = gst_vulkan_display_default_get_handle;
120 klass->create_window = gst_vulkan_display_default_create_window;
121
122 G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_finalize;
123 }
124
125 static void
gst_vulkan_display_init(GstVulkanDisplay * display)126 gst_vulkan_display_init (GstVulkanDisplay * display)
127 {
128 display->priv = gst_vulkan_display_get_instance_private (display);
129 display->type = GST_VULKAN_DISPLAY_TYPE_ANY;
130
131 g_mutex_init (&display->priv->thread_lock);
132 g_cond_init (&display->priv->thread_cond);
133
134 display->priv->event_thread = g_thread_new ("vkdisplay-event",
135 (GThreadFunc) _event_thread_main, display);
136
137 g_mutex_lock (&display->priv->thread_lock);
138 while (!display->main_loop)
139 g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock);
140 g_mutex_unlock (&display->priv->thread_lock);
141 }
142
143 static void
gst_vulkan_display_finalize(GObject * object)144 gst_vulkan_display_finalize (GObject * object)
145 {
146 GstVulkanDisplay *display = GST_VULKAN_DISPLAY (object);
147
148 g_mutex_lock (&display->priv->thread_lock);
149
150 if (display->main_loop)
151 g_main_loop_quit (display->main_loop);
152
153 while (display->main_loop)
154 g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock);
155
156 if (display->priv->event_thread)
157 g_thread_unref (display->priv->event_thread);
158 display->priv->event_thread = NULL;
159 g_mutex_unlock (&display->priv->thread_lock);
160
161 if (display->main_context && display->event_source) {
162 g_source_destroy (display->event_source);
163 g_source_unref (display->event_source);
164 }
165 display->event_source = NULL;
166
167 if (display->instance) {
168 gst_object_unref (display->instance);
169 }
170
171 G_OBJECT_CLASS (gst_vulkan_display_parent_class)->finalize (object);
172 }
173
174 GstVulkanDisplay *
gst_vulkan_display_new_with_type(GstVulkanInstance * instance,GstVulkanDisplayType type)175 gst_vulkan_display_new_with_type (GstVulkanInstance * instance,
176 GstVulkanDisplayType type)
177 {
178 GstVulkanDisplay *display = NULL;
179
180 _init_debug ();
181
182 #if GST_VULKAN_HAVE_WINDOW_XCB
183 if (!display && type & GST_VULKAN_DISPLAY_TYPE_XCB) {
184 display = GST_VULKAN_DISPLAY (gst_vulkan_display_xcb_new (NULL));
185 }
186 #endif
187 #if GST_VULKAN_HAVE_WINDOW_WAYLAND
188 if (!display && type & GST_VULKAN_DISPLAY_TYPE_WAYLAND) {
189 display = GST_VULKAN_DISPLAY (gst_vulkan_display_wayland_new (NULL));
190 }
191 #endif
192
193 if (display)
194 display->instance = gst_object_ref (instance);
195
196 return display;
197 }
198
199 /**
200 * gst_vulkan_display_new:
201 *
202 * Returns: (transfer full): a new #GstVulkanDisplay
203 *
204 * Since: 1.10
205 */
206 GstVulkanDisplay *
gst_vulkan_display_new(GstVulkanInstance * instance)207 gst_vulkan_display_new (GstVulkanInstance * instance)
208 {
209 GstVulkanDisplayType type;
210 GstVulkanDisplay *display = NULL;
211
212 type = gst_vulkan_display_choose_type (instance);
213 display = gst_vulkan_display_new_with_type (instance, type);
214
215 if (!display) {
216 /* subclass returned a NULL display */
217 GST_FIXME ("creating dummy display");
218
219 display = g_object_new (GST_TYPE_VULKAN_DISPLAY, NULL);
220 gst_object_ref_sink (display);
221 display->instance = gst_object_ref (instance);
222 }
223
224 return display;
225 }
226
227 /**
228 * gst_vulkan_display_get_handle:
229 * @display: a #GstVulkanDisplay
230 *
231 * Returns: the winsys specific handle of @display
232 *
233 * Since: 1.10
234 */
235 gpointer
gst_vulkan_display_get_handle(GstVulkanDisplay * display)236 gst_vulkan_display_get_handle (GstVulkanDisplay * display)
237 {
238 GstVulkanDisplayClass *klass;
239
240 g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
241 klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
242 g_return_val_if_fail (klass->get_handle != NULL, NULL);
243
244 return klass->get_handle (display);
245 }
246
247 static gpointer
gst_vulkan_display_default_get_handle(GstVulkanDisplay * display)248 gst_vulkan_display_default_get_handle (GstVulkanDisplay * display)
249 {
250 return 0;
251 }
252
253 /**
254 * gst_vulkan_display_get_handle_type:
255 * @display: a #GstVulkanDisplay
256 *
257 * Returns: the #GstVulkanDisplayType of @display
258 *
259 * Since: 1.10
260 */
261 GstVulkanDisplayType
gst_vulkan_display_get_handle_type(GstVulkanDisplay * display)262 gst_vulkan_display_get_handle_type (GstVulkanDisplay * display)
263 {
264 g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display),
265 GST_VULKAN_DISPLAY_TYPE_NONE);
266
267 return display->type;
268 }
269
270 /**
271 * gst_vulkan_display_create_window:
272 * @display: a #GstVulkanDisplay
273 *
274 * Returns: a new #GstVulkanWindow for @display or %NULL.
275 */
276 GstVulkanWindow *
gst_vulkan_display_create_window(GstVulkanDisplay * display)277 gst_vulkan_display_create_window (GstVulkanDisplay * display)
278 {
279 GstVulkanDisplayClass *klass;
280 GstVulkanWindow *window;
281
282 g_return_val_if_fail (GST_IS_VULKAN_DISPLAY (display), NULL);
283 klass = GST_VULKAN_DISPLAY_GET_CLASS (display);
284 g_return_val_if_fail (klass->create_window != NULL, NULL);
285
286 window = klass->create_window (display);
287
288 if (window) {
289 GWeakRef *ref = g_new0 (GWeakRef, 1);
290
291 g_weak_ref_set (ref, window);
292
293 GST_OBJECT_LOCK (display);
294 display->windows = g_list_prepend (display->windows, ref);
295 GST_OBJECT_UNLOCK (display);
296 }
297
298 return window;
299 }
300
301 static GstVulkanWindow *
gst_vulkan_display_default_create_window(GstVulkanDisplay * display)302 gst_vulkan_display_default_create_window (GstVulkanDisplay * display)
303 {
304 return gst_vulkan_window_new (display);
305 }
306
307 static gint
_compare_vulkan_window(GWeakRef * ref,GstVulkanWindow * window)308 _compare_vulkan_window (GWeakRef * ref, GstVulkanWindow * window)
309 {
310 GstVulkanWindow *other = g_weak_ref_get (ref);
311 gboolean equal = window == other;
312
313 gst_object_unref (other);
314
315 return !equal;
316 }
317
318 static GList *
_find_window_list_item(GstVulkanDisplay * display,GstVulkanWindow * window)319 _find_window_list_item (GstVulkanDisplay * display, GstVulkanWindow * window)
320 {
321 GList *l;
322
323 if (!window)
324 return NULL;
325
326 l = g_list_find_custom (display->windows, window,
327 (GCompareFunc) _compare_vulkan_window);
328
329 return l;
330 }
331
332 gboolean
gst_vulkan_display_remove_window(GstVulkanDisplay * display,GstVulkanWindow * window)333 gst_vulkan_display_remove_window (GstVulkanDisplay * display,
334 GstVulkanWindow * window)
335 {
336 gboolean ret = FALSE;
337 GList *l;
338
339 GST_OBJECT_LOCK (display);
340 l = _find_window_list_item (display, window);
341 if (l) {
342 display->windows = g_list_delete_link (display->windows, l);
343 g_weak_ref_clear (l->data);
344 g_free (l->data);
345 ret = TRUE;
346 }
347 GST_OBJECT_UNLOCK (display);
348
349 return ret;
350 }
351
352 /**
353 * gst_context_set_vulkan_display:
354 * @context: a #GstContext
355 * @display: a #GstVulkanDisplay
356 *
357 * Sets @display on @context
358 *
359 * Since: 1.10
360 */
361 void
gst_context_set_vulkan_display(GstContext * context,GstVulkanDisplay * display)362 gst_context_set_vulkan_display (GstContext * context,
363 GstVulkanDisplay * display)
364 {
365 GstStructure *s;
366
367 g_return_if_fail (context != NULL);
368 g_return_if_fail (gst_context_is_writable (context));
369
370 if (display)
371 GST_CAT_LOG (GST_CAT_CONTEXT,
372 "setting GstVulkanDisplay(%" GST_PTR_FORMAT ") on context(%"
373 GST_PTR_FORMAT ")", display, context);
374
375 s = gst_context_writable_structure (context);
376 gst_structure_set (s, GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR,
377 GST_TYPE_VULKAN_DISPLAY, display, NULL);
378 }
379
380 /**
381 * gst_context_get_vulkan_display:
382 * @context: a #GstContext
383 * @display: resulting #GstVulkanDisplay
384 *
385 * Returns: Whether @display was in @context
386 *
387 * Since: 1.10
388 */
389 gboolean
gst_context_get_vulkan_display(GstContext * context,GstVulkanDisplay ** display)390 gst_context_get_vulkan_display (GstContext * context,
391 GstVulkanDisplay ** display)
392 {
393 const GstStructure *s;
394 gboolean ret;
395
396 g_return_val_if_fail (display != NULL, FALSE);
397 g_return_val_if_fail (context != NULL, FALSE);
398
399 s = gst_context_get_structure (context);
400 ret = gst_structure_get (s, GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR,
401 GST_TYPE_VULKAN_DISPLAY, display, NULL);
402
403 GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVulkanDisplay(%" GST_PTR_FORMAT
404 ") from context(%" GST_PTR_FORMAT ")", *display, context);
405
406 return ret;
407 }
408
409 GstVulkanDisplayType
gst_vulkan_display_choose_type(GstVulkanInstance * instance)410 gst_vulkan_display_choose_type (GstVulkanInstance * instance)
411 {
412 const gchar *window_str;
413 GstVulkanDisplayType type = GST_VULKAN_DISPLAY_TYPE_NONE;
414 GstVulkanDisplayType first_supported = GST_VULKAN_DISPLAY_TYPE_NONE;
415
416 window_str = g_getenv ("GST_VULKAN_WINDOW");
417
418 /* FIXME: enumerate instance extensions for the supported winsys' */
419
420 #define CHOOSE_WINSYS(lname,uname) \
421 G_STMT_START { \
422 if (!type && g_strcmp0 (window_str, G_STRINGIFY (lname)) == 0) { \
423 type = G_PASTE(GST_VULKAN_DISPLAY_TYPE_,uname); \
424 } \
425 if (!first_supported) \
426 first_supported = G_PASTE(GST_VULKAN_DISPLAY_TYPE_,uname); \
427 } G_STMT_END
428
429 #if GST_VULKAN_HAVE_WINDOW_XCB
430 CHOOSE_WINSYS (xcb, XCB);
431 #endif
432 #if GST_VULKAN_HAVE_WINDOW_WAYLAND
433 CHOOSE_WINSYS (wayland, WAYLAND);
434 #endif
435 #if GST_VULKAN_HAVE_WINDOW_COCOA
436 CHOOSE_WINSYS (cocoa, COCOA);
437 #endif
438 #if GST_VULKAN_HAVE_WINDOW_IOS
439 CHOOSE_WINSYS (ios, IOS);
440 #endif
441
442 #undef CHOOSE_WINSYS
443
444 if (type)
445 return type;
446
447 if (first_supported)
448 return first_supported;
449
450 return GST_VULKAN_DISPLAY_TYPE_NONE;
451 }
452
453 const gchar *
gst_vulkan_display_type_to_extension_string(GstVulkanDisplayType type)454 gst_vulkan_display_type_to_extension_string (GstVulkanDisplayType type)
455 {
456 if (type == GST_VULKAN_DISPLAY_TYPE_NONE)
457 return NULL;
458
459 #if GST_VULKAN_HAVE_WINDOW_XCB
460 if (type & GST_VULKAN_DISPLAY_TYPE_XCB)
461 return VK_KHR_XCB_SURFACE_EXTENSION_NAME;
462 #endif
463
464 #if GST_VULKAN_HAVE_WINDOW_WAYLAND
465 if (type & GST_VULKAN_DISPLAY_TYPE_WAYLAND)
466 return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
467 #endif
468 #if GST_VULKAN_HAVE_WINDOW_COCOA
469 if (type & GST_VULKAN_DISPLAY_TYPE_COCOA)
470 return VK_MVK_MACOS_SURFACE_EXTENSION_NAME;
471 #endif
472 #if GST_VULKAN_HAVE_WINDOW_IOS
473 if (type & GST_VULKAN_DISPLAY_TYPE_IOS)
474 return VK_MVK_IOS_SURFACE_EXTENSION_NAME;
475 #endif
476
477 return NULL;
478 }
479
480 gboolean
gst_vulkan_display_handle_context_query(GstElement * element,GstQuery * query,GstVulkanDisplay ** display)481 gst_vulkan_display_handle_context_query (GstElement * element, GstQuery * query,
482 GstVulkanDisplay ** display)
483 {
484 gboolean res = FALSE;
485 const gchar *context_type;
486 GstContext *context, *old_context;
487
488 g_return_val_if_fail (element != NULL, FALSE);
489 g_return_val_if_fail (query != NULL, FALSE);
490 g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT, FALSE);
491 g_return_val_if_fail (display != NULL, FALSE);
492
493 gst_query_parse_context_type (query, &context_type);
494
495 if (g_strcmp0 (context_type, GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR) == 0) {
496 gst_query_parse_context (query, &old_context);
497
498 if (old_context)
499 context = gst_context_copy (old_context);
500 else
501 context = gst_context_new (GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR, TRUE);
502
503 gst_context_set_vulkan_display (context, *display);
504 gst_query_set_context (query, context);
505 gst_context_unref (context);
506
507 res = *display != NULL;
508 }
509
510 return res;
511 }
512
513 gboolean
gst_vulkan_display_run_context_query(GstElement * element,GstVulkanDisplay ** display)514 gst_vulkan_display_run_context_query (GstElement * element,
515 GstVulkanDisplay ** display)
516 {
517 g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
518 g_return_val_if_fail (display != NULL, FALSE);
519
520 if (*display && GST_IS_VULKAN_DISPLAY (*display))
521 return TRUE;
522
523 gst_vulkan_global_context_query (element,
524 GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR);
525
526 GST_DEBUG_OBJECT (element, "found display %p", *display);
527
528 if (*display)
529 return TRUE;
530
531 return FALSE;
532 }
533