3 * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
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.
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.
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.
23 * @title: GstVulkanWindow
24 * @short_description: window/surface abstraction
25 * @see_also: #GstVulkanDisplay
27 * GstVulkanWindow represents a window that elements can render into. A window can
28 * either be a user visible window (onscreen) or hidden (offscreen).
38 #include "gstvkwindow.h"
40 #if GST_VULKAN_HAVE_WINDOW_XCB
41 #include "xcb/gstvkwindow_xcb.h"
43 #if GST_VULKAN_HAVE_WINDOW_WAYLAND
44 #include "wayland/gstvkwindow_wayland.h"
46 #if GST_VULKAN_HAVE_WINDOW_COCOA
47 #include "cocoa/gstvkwindow_cocoa.h"
49 #if GST_VULKAN_HAVE_WINDOW_IOS
50 #include "ios/gstvkwindow_ios.h"
52 #if GST_VULKAN_HAVE_WINDOW_WIN32
53 #include "win32/gstvkwindow_win32.h"
56 #define GST_CAT_DEFAULT gst_vulkan_window_debug
57 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
59 struct _GstVulkanWindowPrivate
65 #define gst_vulkan_window_parent_class parent_class
66 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstVulkanWindow, gst_vulkan_window,
69 static void gst_vulkan_window_finalize (GObject * object);
71 typedef struct _GstVulkanDummyWindow
73 GstVulkanWindow parent;
76 } GstVulkanDummyWindow;
78 typedef struct _GstVulkanDummyWindowCass
80 GstVulkanWindowClass parent;
81 } GstVulkanDummyWindowClass;
83 GstVulkanDummyWindow *gst_vulkan_dummy_window_new (void);
100 static guint gst_vulkan_window_signals[LAST_SIGNAL] = { 0 };
103 _accum_logical_and (GSignalInvocationHint * ihint, GValue * return_accu,
104 const GValue * handler_return, gpointer data)
106 gboolean val = g_value_get_boolean (handler_return);
107 gboolean val2 = g_value_get_boolean (return_accu);
109 g_value_set_boolean (return_accu, val && val2);
115 gst_vulkan_window_error_quark (void)
117 return g_quark_from_static_string ("gst-gl-window-error-quark");
121 gst_vulkan_window_default_open (GstVulkanWindow * window, GError ** error)
127 gst_vulkan_window_default_close (GstVulkanWindow * window)
134 static volatile gsize _init = 0;
136 if (g_once_init_enter (&_init)) {
137 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindow", 0,
139 g_once_init_leave (&_init, 1);
144 gst_vulkan_window_set_property (GObject * object, guint prop_id,
145 const GValue * value, GParamSpec * pspec)
147 GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
151 window->display = g_value_dup_object (value);
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160 gst_vulkan_window_get_property (GObject * object, guint prop_id,
161 GValue * value, GParamSpec * pspec)
163 GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
167 g_value_set_object (value, window->display);
170 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176 gst_vulkan_window_init (GstVulkanWindow * window)
178 window->priv = gst_vulkan_window_get_instance_private (window);
182 gst_vulkan_window_class_init (GstVulkanWindowClass * klass)
184 GObjectClass *gobject_class = (GObjectClass *) klass;
186 klass->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_open);
187 klass->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_default_close);
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);
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);
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);
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;
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));
214 * gst_vulkan_window_new:
215 * @display: a #GstVulkanDisplay
217 * Returns: (transfer full): a new #GstVulkanWindow using @display's connection
222 gst_vulkan_window_new (GstVulkanDisplay * display)
224 GstVulkanWindow *window = NULL;
225 const gchar *user_choice;
227 g_return_val_if_fail (display != NULL, NULL);
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));
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));
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));
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));
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));
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)");
258 window = GST_VULKAN_WINDOW (gst_vulkan_dummy_window_new ());
261 window->display = gst_object_ref (display);
267 gst_vulkan_window_finalize (GObject * object)
269 GstVulkanWindow *window = GST_VULKAN_WINDOW (object);
271 gst_object_unref (window->display);
273 G_OBJECT_CLASS (gst_vulkan_window_parent_class)->finalize (object);
277 * gst_vulkan_window_get_display:
278 * @window: a #GstVulkanWindow
280 * Returns: (transfer full): the #GstVulkanDisplay for @window
285 gst_vulkan_window_get_display (GstVulkanWindow * window)
287 g_return_val_if_fail (GST_IS_VULKAN_WINDOW (window), NULL);
289 return gst_object_ref (window->display);
293 * gst_vulkan_window_get_surface: (skip)
294 * @window: a #GstVulkanWindow
297 * Returns: the VkSurface for displaying into. The caller is responsible for
298 * calling `VkDestroySurface` on the returned surface.
303 gst_vulkan_window_get_surface (GstVulkanWindow * window, GError ** error)
305 GstVulkanWindowClass *klass;
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);
311 return klass->get_surface (window, error);
315 * gst_vulkan_window_get_presentation_support:
316 * @window: a #GstVulkanWindow
317 * @device: a #GstVulkanDevice
318 * @queue_family_idx: the queue family
320 * Returns: whether the given combination of @window, @device and
321 * @queue_family_idx supports presentation
326 gst_vulkan_window_get_presentation_support (GstVulkanWindow * window,
327 GstVulkanDevice * device, guint32 queue_family_idx)
329 GstVulkanWindowClass *klass;
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);
335 return klass->get_presentation_support (window, device, queue_family_idx);
339 * gst_vulkan_window_open:
340 * @window: a #GstVulkanWindow
343 * Returns: whether @window could be successfully opened
348 gst_vulkan_window_open (GstVulkanWindow * window, GError ** error)
350 GstVulkanWindowClass *klass;
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);
356 return klass->open (window, error);
360 * gst_vulkan_window_close:
361 * @window: a #GstVulkanWindow
363 * Attempt to close the window.
368 gst_vulkan_window_close (GstVulkanWindow * window)
370 GstVulkanWindowClass *klass;
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);
377 g_signal_emit (window, gst_vulkan_window_signals[SIGNAL_CLOSE], 0, &to_close);
380 klass->close (window);
384 * gst_vulkan_window_resize:
385 * @window: a #GstVulkanWindow
386 * @width: the new width
387 * @height: the new height
389 * Resize the output surface.
391 * Currently intended for subclasses to update internal state.
396 gst_vulkan_window_resize (GstVulkanWindow * window, gint width, gint height)
398 g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
400 window->priv->surface_width = width;
401 window->priv->surface_height = height;
403 g_signal_emit (window, gst_vulkan_window_signals[SIGNAL_RESIZE], 0, width,
408 * gst_vulkan_window_redraw:
409 * @window: a #GstVulkanWindow
411 * Ask the @window to redraw its contents
416 gst_vulkan_window_redraw (GstVulkanWindow * window)
418 g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
420 g_signal_emit (window, gst_vulkan_window_signals[SIGNAL_DRAW], 0);
424 gst_vulkan_window_set_window_handle (GstVulkanWindow * window, guintptr handle)
426 GstVulkanWindowClass *klass;
428 g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
429 klass = GST_VULKAN_WINDOW_GET_CLASS (window);
431 if (!klass->set_window_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));
437 klass->set_window_handle (window, handle);
442 gst_vulkan_window_get_surface_dimensions (GstVulkanWindow * window,
443 guint * width, guint * height)
445 GstVulkanWindowClass *klass;
447 g_return_if_fail (GST_IS_VULKAN_WINDOW (window));
448 klass = GST_VULKAN_WINDOW_GET_CLASS (window);
450 if (klass->get_surface_dimensions) {
451 klass->get_surface_dimensions (window, width, height);
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;
460 GType gst_vulkan_dummy_window_get_type (void);
461 G_DEFINE_TYPE (GstVulkanDummyWindow, gst_vulkan_dummy_window,
462 GST_TYPE_VULKAN_WINDOW);
465 gst_vulkan_dummy_window_class_init (GstVulkanDummyWindowClass * klass)
470 gst_vulkan_dummy_window_init (GstVulkanDummyWindow * dummy)
475 GstVulkanDummyWindow *
476 gst_vulkan_dummy_window_new (void)
478 GstVulkanDummyWindow *window;
480 window = g_object_new (gst_vulkan_dummy_window_get_type (), NULL);
481 gst_object_ref_sink (window);