b4e6afbf578f2d049914c81f3b36b5da2aeb391d
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / gstvkwindow.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 /**
22  * SECTION:vkwindow
23  * @title: GstVulkanWindow
24  * @short_description: window/surface abstraction
25  * @see_also: #GstVulkanDisplay
26  *
27  * GstVulkanWindow represents a window that elements can render into.  A window can
28  * either be a user visible window (onscreen) or hidden (offscreen).
29  */
30
31 #if HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <gmodule.h>
36 #include <stdio.h>
37
38 #include "gstvkwindow.h"
39
40 #if GST_VULKAN_HAVE_WINDOW_XCB
41 #include "xcb/gstvkwindow_xcb.h"
42 #endif
43 #if GST_VULKAN_HAVE_WINDOW_WAYLAND
44 #include "wayland/gstvkwindow_wayland.h"
45 #endif
46 #if GST_VULKAN_HAVE_WINDOW_COCOA
47 #include "cocoa/gstvkwindow_cocoa.h"
48 #endif
49 #if GST_VULKAN_HAVE_WINDOW_IOS
50 #include "ios/gstvkwindow_ios.h"
51 #endif
52 #if GST_VULKAN_HAVE_WINDOW_WIN32
53 #include "win32/gstvkwindow_win32.h"
54 #endif
55
56 #define GST_CAT_DEFAULT gst_vulkan_window_debug
57 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
58
59 struct _GstVulkanWindowPrivate
60 {
61   guint surface_width;
62   guint surface_height;
63 };
64
65 #define gst_vulkan_window_parent_class parent_class
66 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstVulkanWindow, gst_vulkan_window,
67     GST_TYPE_OBJECT);
68
69 static void gst_vulkan_window_finalize (GObject * object);
70
71 typedef struct _GstVulkanDummyWindow
72 {
73   GstVulkanWindow parent;
74
75   guintptr handle;
76 } GstVulkanDummyWindow;
77
78 typedef struct _GstVulkanDummyWindowCass
79 {
80   GstVulkanWindowClass parent;
81 } GstVulkanDummyWindowClass;
82
83 GstVulkanDummyWindow *gst_vulkan_dummy_window_new (void);
84
85 enum
86 {
87   PROP_0,
88   PROP_DISPLAY,
89 };
90
91 enum
92 {
93   SIGNAL_0,
94   SIGNAL_CLOSE,
95   SIGNAL_DRAW,
96   SIGNAL_RESIZE,
97   LAST_SIGNAL
98 };
99
100 static guint gst_vulkan_window_signals[LAST_SIGNAL] = { 0 };
101
102 static gboolean
103 _accum_logical_and (GSignalInvocationHint * ihint, GValue * return_accu,
104     const GValue * handler_return, gpointer data)
105 {
106   gboolean val = g_value_get_boolean (handler_return);
107   gboolean val2 = g_value_get_boolean (return_accu);
108
109   g_value_set_boolean (return_accu, val && val2);
110
111   return TRUE;
112 }
113
114 GQuark
115 gst_vulkan_window_error_quark (void)
116 {
117   return g_quark_from_static_string ("gst-gl-window-error-quark");
118 }
119
120 static gboolean
121 gst_vulkan_window_default_open (GstVulkanWindow * window, GError ** error)
122 {
123   return TRUE;
124 }
125
126 static void
127 gst_vulkan_window_default_close (GstVulkanWindow * window)
128 {
129 }
130
131 static void
132 _init_debug (void)
133 {
134   static volatile gsize _init = 0;
135
136   if (g_once_init_enter (&_init)) {
137     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindow", 0,
138         "Vulkan Window");
139     g_once_init_leave (&_init, 1);
140   }
141 }
142
143 static void
144 gst_vulkan_window_set_property (GObject * object, guint prop_id,
145     const GValue * value, GParamSpec * pspec)
146 {
147   GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
148
149   switch (prop_id) {
150     case PROP_DISPLAY:
151       window->display = g_value_dup_object (value);
152       break;
153     default:
154       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
155       break;
156   }
157 }
158
159 static void
160 gst_vulkan_window_get_property (GObject * object, guint prop_id,
161     GValue * value, GParamSpec * pspec)
162 {
163   GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
164
165   switch (prop_id) {
166     case PROP_DISPLAY:
167       g_value_set_object (value, window->display);
168       break;
169     default:
170       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
171       break;
172   }
173 }
174
175 static void
176 gst_vulkan_window_init (GstVulkanWindow * window)
177 {
178   window->priv = gst_vulkan_window_get_instance_private (window);
179 }
180
181 static void
182 gst_vulkan_window_class_init (GstVulkanWindowClass * klass)
183 {
184   GObjectClass *gobject_class = (GObjectClass *) klass;
185
186   klass->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_open);
187   klass->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_close);
188
189   gst_vulkan_window_signals[SIGNAL_CLOSE] =
190       g_signal_new ("close", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
191       (GSignalAccumulator) _accum_logical_and, NULL, NULL, G_TYPE_BOOLEAN, 0);
192
193   gst_vulkan_window_signals[SIGNAL_DRAW] =
194       g_signal_new ("draw", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
195       NULL, NULL, NULL, G_TYPE_NONE, 0);
196
197   gst_vulkan_window_signals[SIGNAL_RESIZE] =
198       g_signal_new ("resize", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
199       NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
200
201   gobject_class->set_property = gst_vulkan_window_set_property;
202   gobject_class->get_property = gst_vulkan_window_get_property;
203   gobject_class->finalize = gst_vulkan_window_finalize;
204
205   g_object_class_install_property (gobject_class, PROP_DISPLAY,
206       g_param_spec_object ("display", "Display",
207           "Associated Vulkan Display",
208           GST_TYPE_VULKAN_DISPLAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
209
210   _init_debug ();
211 }
212
213 /**
214  * gst_vulkan_window_new:
215  * @display: a #GstVulkanDisplay
216  *
217  * Returns: (transfer full): a new #GstVulkanWindow using @display's connection
218  *
219  * Since: 1.18
220  */
221 GstVulkanWindow *
222 gst_vulkan_window_new (GstVulkanDisplay * display)
223 {
224   GstVulkanWindow *window = NULL;
225   const gchar *user_choice;
226
227   g_return_val_if_fail (display != NULL, NULL);
228
229   _init_debug ();
230
231   user_choice = g_getenv ("GST_VULKAN_WINDOW");
232   GST_INFO ("creating a window, user choice:%s", user_choice);
233 #if GST_VULKAN_HAVE_WINDOW_XCB
234   if (!window && (!user_choice || g_strstr_len (user_choice, 3, "xcb")))
235     window = GST_VULKAN_WINDOW (gst_vulkan_window_xcb_new (display));
236 #endif
237 #if GST_VULKAN_HAVE_WINDOW_WAYLAND
238   if (!window && (!user_choice || g_strstr_len (user_choice, 7, "wayland")))
239     window = GST_VULKAN_WINDOW (gst_vulkan_window_wayland_new (display));
240 #endif
241 #if GST_VULKAN_HAVE_WINDOW_COCOA
242   if (!window && (!user_choice || g_strstr_len (user_choice, 5, "cocoa")))
243     window = GST_VULKAN_WINDOW (gst_vulkan_window_cocoa_new (display));
244 #endif
245 #if GST_VULKAN_HAVE_WINDOW_IOS
246   if (!window && (!user_choice || g_strstr_len (user_choice, 3, "ios")))
247     window = GST_VULKAN_WINDOW (gst_vulkan_window_ios_new (display));
248 #endif
249 #if GST_VULKAN_HAVE_WINDOW_WIN32
250   if (!window && (!user_choice || g_strstr_len (user_choice, 5, "win32")))
251     window = GST_VULKAN_WINDOW (gst_vulkan_window_win32_new (display));
252 #endif
253   if (!window) {
254     /* subclass returned a NULL window */
255     GST_WARNING ("Could not create window. user specified %s, creating dummy"
256         " window", user_choice ? user_choice : "(null)");
257
258     window = GST_VULKAN_WINDOW (gst_vulkan_dummy_window_new ());
259   }
260
261   window->display = gst_object_ref (display);
262
263   return window;
264 }
265
266 static void
267 gst_vulkan_window_finalize (GObject * object)
268 {
269   GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
270
271   gst_object_unref (window->display);
272
273   G_OBJECT_CLASS (gst_vulkan_window_parent_class)->finalize (object);
274 }
275
276 /**
277  * gst_vulkan_window_get_display:
278  * @window: a #GstVulkanWindow
279  *
280  * Returns: (transfer full): the #GstVulkanDisplay for @window
281  *
282  * Since: 1.18
283  */
284 GstVulkanDisplay *
285 gst_vulkan_window_get_display (GstVulkanWindow * window)
286 {
287   g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL);
288
289   return gst_object_ref (window->display);
290 }
291
292 /**
293  * gst_vulkan_window_get_surface: (skip)
294  * @window: a #GstVulkanWindow
295  * @error: a #GError
296  *
297  * Returns: the VkSurface for displaying into.  The caller is responsible for
298  *     calling `VkDestroySurface` on the returned surface.
299  *
300  * Since: 1.18
301  */
302 VkSurfaceKHR
303 gst_vulkan_window_get_surface (GstVulkanWindow * window, GError ** error)
304 {
305   GstVulkanWindowClass *klass;
306
307   g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), (VkSurfaceKHR) 0);
308   klass = GST_VULKAN_WINDOW_GET_CLASS (window);
309   g_return_val_if_fail (klass->get_surface != NULL, (VkSurfaceKHR) 0);
310
311   return klass->get_surface (window, error);
312 }
313
314 /**
315  * gst_vulkan_window_get_presentation_support:
316  * @window: a #GstVulkanWindow
317  * @device: a #GstVulkanDevice
318  * @queue_family_idx: the queue family
319  *
320  * Returns: whether the given combination of @window, @device and
321  *          @queue_family_idx supports presentation
322  *
323  * Since: 1.18
324  */
325 gboolean
326 gst_vulkan_window_get_presentation_support (GstVulkanWindow * window,
327     GstVulkanDevice * device, guint32 queue_family_idx)
328 {
329   GstVulkanWindowClass *klass;
330
331   g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), FALSE);
332   klass = GST_VULKAN_WINDOW_GET_CLASS (window);
333   g_return_val_if_fail (klass->get_presentation_support != NULL, FALSE);
334
335   return klass->get_presentation_support (window, device, queue_family_idx);
336 }
337
338 /**
339  * gst_vulkan_window_open:
340  * @window: a #GstVulkanWindow
341  * @error: a #GError
342  *
343  * Returns: whether @window could be sucessfully opened
344  *
345  * Since: 1.18
346  */
347 gboolean
348 gst_vulkan_window_open (GstVulkanWindow * window, GError ** error)
349 {
350   GstVulkanWindowClass *klass;
351
352   g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), FALSE);
353   klass = GST_VULKAN_WINDOW_GET_CLASS (window);
354   g_return_val_if_fail (klass->open != NULL, FALSE);
355
356   return klass->open (window, error);
357 }
358
359 /**
360  * gst_vulkan_window_close:
361  * @window: a #GstVulkanWindow
362  *
363  * Attempt to close the window.
364  *
365  * Since: 1.18
366  */
367 void
368 gst_vulkan_window_close (GstVulkanWindow * window)
369 {
370   GstVulkanWindowClass *klass;
371   gboolean to_close;
372
373   g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
374   klass = GST_VULKAN_WINDOW_GET_CLASS (window);
375   g_return_if_fail (klass->close != NULL);
376
377   g_signal_emit (window, gst_vulkan_window_signals[SIGNAL_CLOSE], 0, &to_close);
378
379   if (to_close)
380     klass->close (window);
381 }
382
383 /**
384  * gst_vulkan_window_resize:
385  * @window: a #GstVulkanWindow
386  * @width: the new width
387  * @height: the new height
388  *
389  * Resize the output surface.
390  *
391  * Currently intended for subclasses to update internal state.
392  *
393  * Since: 1.18
394  */
395 void
396 gst_vulkan_window_resize (GstVulkanWindow * window, gint width, gint height)
397 {
398   g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
399
400   window->priv->surface_width = width;
401   window->priv->surface_height = height;
402
403   g_signal_emit (window, gst_vulkan_window_signals[SIGNAL_RESIZE], 0, width,
404       height);
405 }
406
407 /**
408  * gst_vulkan_window_redraw:
409  * @window: a #GstVulkanWindow
410  *
411  * Ask the @window to redraw its contents
412  *
413  * Since: 1.18
414  */
415 void
416 gst_vulkan_window_redraw (GstVulkanWindow * window)
417 {
418   g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
419
420   g_signal_emit (window, gst_vulkan_window_signals[SIGNAL_DRAW], 0);
421 }
422
423 void
424 gst_vulkan_window_set_window_handle (GstVulkanWindow * window, guintptr handle)
425 {
426   GstVulkanWindowClass *klass;
427
428   g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
429   klass = GST_VULKAN_WINDOW_GET_CLASS (window);
430
431   if (!klass->set_window_handle) {
432     if (handle)
433       g_warning ("%s does not implement the set_window_handle vfunc. "
434           "Output will not be embedded into the specified surface.",
435           GST_OBJECT_NAME (window));
436   } else {
437     klass->set_window_handle (window, handle);
438   }
439 }
440
441 void
442 gst_vulkan_window_get_surface_dimensions (GstVulkanWindow * window,
443     guint * width, guint * height)
444 {
445   GstVulkanWindowClass *klass;
446
447   g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
448   klass = GST_VULKAN_WINDOW_GET_CLASS (window);
449
450   if (klass->get_surface_dimensions) {
451     klass->get_surface_dimensions (window, width, height);
452   } else {
453     GST_DEBUG_OBJECT (window, "Returning size %ix%i",
454         window->priv->surface_width, window->priv->surface_height);
455     *width = window->priv->surface_width;
456     *height = window->priv->surface_height;
457   }
458 }
459
460 GType gst_vulkan_dummy_window_get_type (void);
461 G_DEFINE_TYPE (GstVulkanDummyWindow, gst_vulkan_dummy_window,
462     GST_TYPE_VULKAN_WINDOW);
463
464 static void
465 gst_vulkan_dummy_window_class_init (GstVulkanDummyWindowClass * klass)
466 {
467 }
468
469 static void
470 gst_vulkan_dummy_window_init (GstVulkanDummyWindow * dummy)
471 {
472   dummy->handle = 0;
473 }
474
475 GstVulkanDummyWindow *
476 gst_vulkan_dummy_window_new (void)
477 {
478   GstVulkanDummyWindow *window;
479
480   window = g_object_new (gst_vulkan_dummy_window_get_type (), NULL);
481   gst_object_ref_sink (window);
482
483   return window;
484 }