From 65770c2af944ba2b222c74bd08dd8a1d01431149 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sun, 10 Apr 2016 17:46:15 +1000 Subject: [PATCH] vulkan: add a wayland winsys implementation --- configure.ac | 16 +- ext/vulkan/Makefile.am | 7 +- ext/vulkan/vkapi.h | 6 + ext/vulkan/vkdevice.c | 5 + ext/vulkan/vkdisplay.c | 25 +- ext/vulkan/vkinstance.c | 5 + ext/vulkan/vkwindow.c | 7 + ext/vulkan/wayland/Makefile.am | 29 +++ ext/vulkan/wayland/vkdisplay_wayland.c | 189 ++++++++++++++ ext/vulkan/wayland/vkdisplay_wayland.h | 78 ++++++ ext/vulkan/wayland/vkwindow_wayland.c | 285 ++++++++++++++++++++++ ext/vulkan/wayland/vkwindow_wayland.h | 74 ++++++ ext/vulkan/wayland/wayland_event_source.c | 205 ++++++++++++++++ ext/vulkan/wayland/wayland_event_source.h | 34 +++ 14 files changed, 951 insertions(+), 14 deletions(-) create mode 100644 ext/vulkan/wayland/Makefile.am create mode 100644 ext/vulkan/wayland/vkdisplay_wayland.c create mode 100644 ext/vulkan/wayland/vkdisplay_wayland.h create mode 100644 ext/vulkan/wayland/vkwindow_wayland.c create mode 100644 ext/vulkan/wayland/vkwindow_wayland.h create mode 100644 ext/vulkan/wayland/wayland_event_source.c create mode 100644 ext/vulkan/wayland/wayland_event_source.h diff --git a/configure.ac b/configure.ac index 08e975b2cb..374022d9d6 100644 --- a/configure.ac +++ b/configure.ac @@ -1485,15 +1485,14 @@ AC_SUBST(HAVE_JPEG) AM_CONDITIONAL(HAVE_JPEG, test "x$HAVE_JPEG" = "xyes") dnl Vulkan -VULKAN_CONFIG_DEFINES="" +PKG_CHECK_MODULES(XCB, xcb >= 1.10, GST_VULKAN_HAVE_WINDOW_XCB=1, GST_VULKAN_HAVE_WINDOW_XCB=0) +AM_CONDITIONAL(USE_XCB, test "x$GST_VULKAN_HAVE_WINDOW_XCB" = "x1") -PKG_CHECK_MODULES(XCB, xcb >= 1.10, HAVE_XCB=yes, HAVE_XCB=no) - -AM_CONDITIONAL(USE_XCB, test "x$HAVE_XCB" = "xyes") -if test "x$HAVE_XCB" = "xyes"; then - VULKAN_CONFIG_DEFINES="$VULKAN_CONFIG_DEFINES - #define GST_VULKAN_HAVE_WINDOW_XCB 1" -fi +PKG_CHECK_MODULES(WAYLAND, wayland-client >= 1.4, GST_VULKAN_HAVE_WINDOW_WAYLAND=1, GST_VULKAN_HAVE_WINDOW_WAYLAND=0) +AM_CONDITIONAL(USE_WAYLAND, test "x$GST_VULKAN_HAVE_WINDOW_WAYLAND" = "x1") +VULKAN_CONFIG_DEFINES=" +#define GST_VULKAN_HAVE_WINDOW_XCB $GST_VULKAN_HAVE_WINDOW_XCB +#define GST_VULKAN_HAVE_WINDOW_WAYLAND $GST_VULKAN_HAVE_WINDOW_WAYLAND" AC_CONFIG_COMMANDS([ext/vulkan/vkconfig.h], [ outfile=vkconfig.h-tmp @@ -3724,6 +3723,7 @@ ext/spc/Makefile ext/timidity/Makefile ext/vulkan/Makefile ext/vulkan/xcb/Makefile +ext/vulkan/wayland/Makefile ext/webp/Makefile ext/x265/Makefile ext/xvid/Makefile diff --git a/ext/vulkan/Makefile.am b/ext/vulkan/Makefile.am index eaf959753a..673190fdd2 100644 --- a/ext/vulkan/Makefile.am +++ b/ext/vulkan/Makefile.am @@ -1,7 +1,7 @@ plugin_LTLIBRARIES = libgstvulkan.la SUBDIRS = -DIST_SUBDIRS = xcb +DIST_SUBDIRS = xcb wayland DISTCLEANFILES = vkconfig.h libgstvulkan_la_SOURCES = \ @@ -62,6 +62,11 @@ SUBDIRS += xcb libgstvulkan_la_LIBADD += xcb/libgstvulkan-xcb.la endif +if USE_WAYLAND +SUBDIRS += wayland +libgstvulkan_la_LIBADD += wayland/libgstvulkan-wayland.la +endif + libgstvulkan_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvulkan_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) diff --git a/ext/vulkan/vkapi.h b/ext/vulkan/vkapi.h index 525a6c0551..e9c23aa920 100644 --- a/ext/vulkan/vkapi.h +++ b/ext/vulkan/vkapi.h @@ -34,6 +34,12 @@ #endif #endif +#if GST_VULKAN_HAVE_WINDOW_WAYLAND +#ifndef VK_USE_PLATFORM_WAYLAND_KHR +#define VK_USE_PLATFORM_WAYLAND_KHR +#endif +#endif + #include #endif /* _VK_H_ */ diff --git a/ext/vulkan/vkdevice.c b/ext/vulkan/vkdevice.c index 5825391e8d..24af95891d 100644 --- a/ext/vulkan/vkdevice.c +++ b/ext/vulkan/vkdevice.c @@ -289,8 +289,13 @@ gst_vulkan_device_open (GstVulkanDevice * device, GError ** error) device_info.pNext = NULL; device_info.queueCreateInfoCount = 1; device_info.pQueueCreateInfos = &queue_info; +#if 0 device_info.enabledLayerCount = enabled_layer_count; device_info.ppEnabledLayerNames = (const char *const *) enabled_layers; +#else + device_info.enabledLayerCount = 0; + device_info.ppEnabledLayerNames = NULL; +#endif device_info.enabledExtensionCount = enabled_extension_count; device_info.ppEnabledExtensionNames = (const char *const *) extension_names; device_info.pEnabledFeatures = NULL; diff --git a/ext/vulkan/vkdisplay.c b/ext/vulkan/vkdisplay.c index a0668212e6..096f251a8d 100644 --- a/ext/vulkan/vkdisplay.c +++ b/ext/vulkan/vkdisplay.c @@ -33,6 +33,9 @@ #if GST_VULKAN_HAVE_WINDOW_XCB #include "xcb/vkdisplay_xcb.h" #endif +#if GST_VULKAN_HAVE_WINDOW_WAYLAND +#include "wayland/vkdisplay_wayland.h" +#endif GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT); #define GST_CAT_DEFAULT gst_vulkan_display_debug @@ -142,11 +145,6 @@ gst_vulkan_display_finalize (GObject * object) GstVulkanDisplay *display = GST_VULKAN_DISPLAY (object); g_mutex_lock (&display->priv->thread_lock); - if (display->main_context && display->event_source) { - g_source_destroy (display->event_source); - g_source_unref (display->event_source); - } - display->event_source = NULL; if (display->main_loop) g_main_loop_quit (display->main_loop); @@ -159,6 +157,12 @@ gst_vulkan_display_finalize (GObject * object) display->priv->event_thread = NULL; g_mutex_unlock (&display->priv->thread_lock); + if (display->main_context && display->event_source) { + g_source_destroy (display->event_source); + g_source_unref (display->event_source); + } + display->event_source = NULL; + if (display->instance) { gst_object_unref (display->instance); } @@ -179,6 +183,11 @@ gst_vulkan_display_new_with_type (GstVulkanInstance * instance, display = GST_VULKAN_DISPLAY (gst_vulkan_display_xcb_new (NULL)); } #endif +#if GST_VULKAN_HAVE_WINDOW_WAYLAND + if (!display && type & GST_VULKAN_DISPLAY_TYPE_WAYLAND) { + display = GST_VULKAN_DISPLAY (gst_vulkan_display_wayland_new (NULL)); + } +#endif if (display) display->instance = gst_object_ref (instance); @@ -387,6 +396,9 @@ gst_vulkan_display_choose_type (GstVulkanInstance * instance) #if GST_VULKAN_HAVE_WINDOW_XCB CHOOSE_WINSYS (xcb, XCB); #endif +#if GST_VULKAN_HAVE_WINDOW_WAYLAND + CHOOSE_WINSYS (wayland, WAYLAND); +#endif #undef CHOOSE_WINSYS @@ -408,6 +420,9 @@ gst_vulkan_display_type_to_extension_string (GstVulkanDisplayType type) if (type & GST_VULKAN_DISPLAY_TYPE_XCB) return VK_KHR_XCB_SURFACE_EXTENSION_NAME; + if (type & GST_VULKAN_DISPLAY_TYPE_WAYLAND) + return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; + return NULL; } diff --git a/ext/vulkan/vkinstance.c b/ext/vulkan/vkinstance.c index 0e48a17ff2..d61458e9bf 100644 --- a/ext/vulkan/vkinstance.c +++ b/ext/vulkan/vkinstance.c @@ -299,8 +299,13 @@ gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error) inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; inst_info.pNext = NULL; inst_info.pApplicationInfo = &app; +#if 0 inst_info.enabledLayerCount = enabled_layer_count; inst_info.ppEnabledLayerNames = (const char *const *) enabled_layers; +#else + inst_info.enabledLayerCount = 0; + inst_info.ppEnabledLayerNames = NULL; +#endif inst_info.enabledExtensionCount = enabled_extension_count; inst_info.ppEnabledExtensionNames = (const char *const *) extension_names; diff --git a/ext/vulkan/vkwindow.c b/ext/vulkan/vkwindow.c index caa3c47f16..56b3e60636 100644 --- a/ext/vulkan/vkwindow.c +++ b/ext/vulkan/vkwindow.c @@ -43,6 +43,9 @@ #if GST_VULKAN_HAVE_WINDOW_XCB #include "xcb/vkwindow_xcb.h" #endif +#if GST_VULKAN_HAVE_WINDOW_WAYLAND +#include "wayland/vkwindow_wayland.h" +#endif #define GST_CAT_DEFAULT gst_vulkan_window_debug GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); @@ -182,6 +185,10 @@ gst_vulkan_window_new (GstVulkanDisplay * display) #if GST_VULKAN_HAVE_WINDOW_XCB if (!window && (!user_choice || g_strstr_len (user_choice, 3, "xcb"))) window = GST_VULKAN_WINDOW (gst_vulkan_window_xcb_new (display)); +#endif +#if GST_VULKAN_HAVE_WINDOW_WAYLAND + if (!window && (!user_choice || g_strstr_len (user_choice, 7, "wayland"))) + window = GST_VULKAN_WINDOW (gst_vulkan_window_wayland_new (display)); #endif if (!window) { /* subclass returned a NULL window */ diff --git a/ext/vulkan/wayland/Makefile.am b/ext/vulkan/wayland/Makefile.am new file mode 100644 index 0000000000..f92d85e2c2 --- /dev/null +++ b/ext/vulkan/wayland/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in + +noinst_LTLIBRARIES = libgstvulkan-wayland.la + +libgstvulkan_wayland_la_SOURCES = \ + vkdisplay_wayland.c \ + vkwindow_wayland.c \ + wayland_event_source.c + +noinst_HEADERS = \ + vkdisplay_wayland.h \ + vkwindow_wayland.h \ + wayland_event_source.h + +libgstvulkan_wayland_la_CFLAGS = \ + -I$(top_srcdir)/gst-libs \ + -I$(top_srcdir)/ext/vulkan \ + -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(WAYLAND_CFLAGS) + +libgstvulkan_wayland_la_LIBADD = \ + $(WAYLAND_LIBS) + +libgstvulkan_wayland_la_LDFLAGS = \ + $(GST_LIB_LDFLAGS) \ + $(GST_ALL_LDFLAGS) diff --git a/ext/vulkan/wayland/vkdisplay_wayland.c b/ext/vulkan/wayland/vkdisplay_wayland.c new file mode 100644 index 0000000000..686d67103f --- /dev/null +++ b/ext/vulkan/wayland/vkdisplay_wayland.c @@ -0,0 +1,189 @@ +/* + * GStreamer + * Copyright (C) 2016 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "wayland/vkdisplay_wayland.h" +#include "wayland_event_source.h" + +GST_DEBUG_CATEGORY_STATIC (gst_vulkan_display_wayland_debug); +#define GST_CAT_DEFAULT gst_vulkan_display_wayland_debug + +G_DEFINE_TYPE_WITH_CODE (GstVulkanDisplayWayland, gst_vulkan_display_wayland, + GST_TYPE_VULKAN_DISPLAY, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, + "vulkandisplaywayland", 0, "Vulkan Wayland Display"); + ); + +static void gst_vulkan_display_wayland_finalize (GObject * object); +static gpointer gst_vulkan_display_wayland_get_handle (GstVulkanDisplay * + display); + +static void +registry_handle_global (void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + GstVulkanDisplayWayland *display = data; + + GST_TRACE_OBJECT (display, "registry_handle_global with registry %p, " + "interface %s, version %u", registry, interface, version); + + if (g_strcmp0 (interface, "wl_compositor") == 0) { + display->compositor = + wl_registry_bind (registry, name, &wl_compositor_interface, 1); + } else if (g_strcmp0 (interface, "wl_subcompositor") == 0) { + display->subcompositor = + wl_registry_bind (registry, name, &wl_subcompositor_interface, 1); + } else if (g_strcmp0 (interface, "wl_shell") == 0) { + display->shell = wl_registry_bind (registry, name, &wl_shell_interface, 1); + } +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global +}; + +static void +_connect_listeners (GstVulkanDisplayWayland * display) +{ + display->registry = wl_display_get_registry (display->display); + wl_registry_add_listener (display->registry, ®istry_listener, display); + + wl_display_roundtrip (display->display); +} + +static void +gst_vulkan_display_wayland_class_init (GstVulkanDisplayWaylandClass * klass) +{ + GST_VULKAN_DISPLAY_CLASS (klass)->get_handle = + GST_DEBUG_FUNCPTR (gst_vulkan_display_wayland_get_handle); + + G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_wayland_finalize; +} + +static void +gst_vulkan_display_wayland_init (GstVulkanDisplayWayland * display_wayland) +{ + GstVulkanDisplay *display = (GstVulkanDisplay *) display_wayland; + + display->type = GST_VULKAN_DISPLAY_TYPE_WAYLAND; + display_wayland->foreign_display = FALSE; +} + +static void +gst_vulkan_display_wayland_finalize (GObject * object) +{ + GstVulkanDisplayWayland *display_wayland = + GST_VULKAN_DISPLAY_WAYLAND (object); + + if (!display_wayland->foreign_display && display_wayland->display) { + wl_display_flush (display_wayland->display); + wl_display_disconnect (display_wayland->display); + } + + G_OBJECT_CLASS (gst_vulkan_display_wayland_parent_class)->finalize (object); +} + +/** + * gst_vulkan_display_wayland_new: + * @name: (allow-none): a display name + * + * Create a new #GstVulkanDisplayWayland from the wayland display name. See wl_display_connect() + * for details on what is a valid name. + * + * Returns: (transfer full): a new #GstVulkanDisplayWayland or %NULL + */ +GstVulkanDisplayWayland * +gst_vulkan_display_wayland_new (const gchar * name) +{ + GstVulkanDisplayWayland *ret; + + ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_WAYLAND, NULL); + ret->display = wl_display_connect (name); + + if (!ret->display) { + GST_ERROR ("Failed to open Wayland display connection with name, \'%s\'", + name); + return NULL; + } + + /* connecting the listeners after attaching the event source will race with + * the source and the source may eat an event that we're waiting for and + * deadlock */ + _connect_listeners (ret); + + GST_VULKAN_DISPLAY (ret)->event_source = + wayland_event_source_new (ret->display, NULL); + g_source_attach (GST_VULKAN_DISPLAY (ret)->event_source, + GST_VULKAN_DISPLAY (ret)->main_context); + + return ret; +} + +/** + * gst_vulkan_display_wayland_new_with_display: + * @display: an existing, wayland display + * + * Creates a new display connection from a wl_display Display. + * + * Returns: (transfer full): a new #GstVulkanDisplayWayland + */ +GstVulkanDisplayWayland * +gst_vulkan_display_wayland_new_with_display (struct wl_display * display) +{ + GstVulkanDisplayWayland *ret; + + g_return_val_if_fail (display != NULL, NULL); + + ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_WAYLAND, NULL); + + ret->display = display; + ret->foreign_display = TRUE; + + _connect_listeners (ret); + + return ret; +} + +static gpointer +gst_vulkan_display_wayland_get_handle (GstVulkanDisplay * display) +{ + return GST_VULKAN_DISPLAY_WAYLAND (display)->display; +} + +static gboolean +_roundtrip_async (gpointer data) +{ + GstVulkanDisplayWayland *display = data; + + wl_display_roundtrip (display->display); + + return G_SOURCE_REMOVE; +} + +void +gst_vulkan_display_wayland_roundtrip_async (GstVulkanDisplayWayland * display) +{ + g_return_if_fail (GST_IS_VULKAN_DISPLAY_WAYLAND (display)); + + g_main_context_invoke (GST_VULKAN_DISPLAY (display)->main_context, + (GSourceFunc) _roundtrip_async, display); +} diff --git a/ext/vulkan/wayland/vkdisplay_wayland.h b/ext/vulkan/wayland/vkdisplay_wayland.h new file mode 100644 index 0000000000..44c13401a4 --- /dev/null +++ b/ext/vulkan/wayland/vkdisplay_wayland.h @@ -0,0 +1,78 @@ +/* + * GStreamer + * Copyright (C) 2016 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_VK_DISPLAY_WAYLAND_H__ +#define __GST_VK_DISPLAY_WAYLAND_H__ + +#include + +#include + +#include + +G_BEGIN_DECLS + +GType gst_vulkan_display_wayland_get_type (void); + +#define GST_TYPE_VULKAN_DISPLAY_WAYLAND (gst_vulkan_display_wayland_get_type()) +#define GST_VULKAN_DISPLAY_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_DISPLAY_WAYLAND,GstVulkanDisplayWayland)) +#define GST_VULKAN_DISPLAY_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VULKAN_DISPLAY_WAYLAND,GstVulkanDisplayWaylandClass)) +#define GST_IS_VULKAN_DISPLAY_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_DISPLAY_WAYLAND)) +#define GST_IS_VULKAN_DISPLAY_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VULKAN_DISPLAY_WAYLAND)) +#define GST_VULKAN_DISPLAY_WAYLAND_CAST(obj) ((GstVulkanDisplayWayland*)(obj)) + +typedef struct _GstVulkanDisplayWayland GstVulkanDisplayWayland; +typedef struct _GstVulkanDisplayWaylandClass GstVulkanDisplayWaylandClass; + +/** + * GstVulkanDisplayWayland: + * + * the contents of a #GstVulkanDisplayWayland are private and should only be accessed + * through the provided API + */ +struct _GstVulkanDisplayWayland +{ + GstVulkanDisplay parent; + + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_subcompositor *subcompositor; + struct wl_shell *shell; + + /* */ + gboolean foreign_display; +}; + +struct _GstVulkanDisplayWaylandClass +{ + GstVulkanDisplayClass object_class; +}; + +#define GST_VULKAN_DISPLAY_WAYLAND_DISPLAY(display_) (GST_VULKAN_DISPLAY_WAYLAND (display_)->display) + +GstVulkanDisplayWayland *gst_vulkan_display_wayland_new (const gchar * name); +GstVulkanDisplayWayland *gst_vulkan_display_wayland_new_with_display (struct wl_display *display); + +void gst_vulkan_display_wayland_roundtrip_async (GstVulkanDisplayWayland * display); + +G_END_DECLS + +#endif /* __GST_VULKAN_DISPLAY_WAYLAND_H__ */ diff --git a/ext/vulkan/wayland/vkwindow_wayland.c b/ext/vulkan/wayland/vkwindow_wayland.c new file mode 100644 index 0000000000..dc9902778e --- /dev/null +++ b/ext/vulkan/wayland/vkwindow_wayland.c @@ -0,0 +1,285 @@ +/* + * GStreamer + * Copyright (C) 2016 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "wayland_event_source.h" + +#include "vkdisplay_wayland.h" +#include "vkwindow_wayland.h" + +#define GST_CAT_DEFAULT gst_vulkan_window_wayland_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define gst_vulkan_window_wayland_parent_class parent_class +G_DEFINE_TYPE (GstVulkanWindowWayland, gst_vulkan_window_wayland, + GST_TYPE_VULKAN_WINDOW) + + static void gst_vulkan_window_wayland_close (GstVulkanWindow * window); + static gboolean gst_vulkan_window_wayland_open (GstVulkanWindow * window, + GError ** error); + static VkSurfaceKHR gst_vulkan_window_wayland_get_surface (GstVulkanWindow + * window, GError ** error); + static gboolean + gst_vulkan_window_wayland_get_presentation_support (GstVulkanWindow * + window, GstVulkanDevice * device, guint32 queue_family_idx); + + static void + handle_ping (void *data, struct wl_shell_surface *shell_surface, + uint32_t serial) +{ + GstVulkanWindowWayland *window_wl = data; + + GST_TRACE_OBJECT (window_wl, "ping received serial %u", serial); + + wl_shell_surface_pong (shell_surface, serial); +} + +/* +static void window_resize (GstVulkanWindowWayland * window_wl, guint width, + guint height);*/ + +static void +handle_configure (void *data, struct wl_shell_surface *shell_surface, + uint32_t edges, int32_t width, int32_t height) +{ + GstVulkanWindowWayland *window_wl = data; + + GST_DEBUG_OBJECT (window_wl, "configure event on surface %p, %ix%i", + shell_surface, width, height); + + /*window_resize (window_wl, width, height); */ +} + +static void +handle_popup_done (void *data, struct wl_shell_surface *shell_surface) +{ +} + +static const struct wl_shell_surface_listener shell_surface_listener = { + handle_ping, + handle_configure, + handle_popup_done +}; + +static void +destroy_surfaces (GstVulkanWindowWayland * window_wl) +{ + GST_DEBUG_OBJECT (window_wl, "destroying created surfaces"); + + if (window_wl->shell_surface) { + wl_shell_surface_destroy (window_wl->shell_surface); + window_wl->shell_surface = NULL; + } + if (window_wl->surface) { + wl_surface_destroy (window_wl->surface); + window_wl->surface = NULL; + } +} + +static void +create_surfaces (GstVulkanWindowWayland * window_wl) +{ + GstVulkanDisplayWayland *display = + GST_VULKAN_DISPLAY_WAYLAND (GST_VULKAN_WINDOW (window_wl)->display); + gint width, height; + + if (!window_wl->surface) { + window_wl->surface = wl_compositor_create_surface (display->compositor); + if (window_wl->queue) + wl_proxy_set_queue ((struct wl_proxy *) window_wl->surface, + window_wl->queue); + } + + if (!window_wl->shell_surface) { + window_wl->shell_surface = + wl_shell_get_shell_surface (display->shell, window_wl->surface); + if (window_wl->queue) + wl_proxy_set_queue ((struct wl_proxy *) window_wl->shell_surface, + window_wl->queue); + + wl_shell_surface_add_listener (window_wl->shell_surface, + &shell_surface_listener, window_wl); + + wl_shell_surface_set_title (window_wl->shell_surface, "Vulkan Renderer"); + wl_shell_surface_set_toplevel (window_wl->shell_surface); + GST_DEBUG_OBJECT (window_wl, "Successfully created shell surface %p", + window_wl->shell_surface); + } + + if (window_wl->window_width > 0) + width = window_wl->window_width; + else + width = 320; + window_wl->window_width = width; + + if (window_wl->window_height > 0) + height = window_wl->window_height; + else + height = 240; + window_wl->window_height = height; +} + +static void +gst_vulkan_window_wayland_class_init (GstVulkanWindowWaylandClass * klass) +{ + GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass; + + window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_close); + window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_open); + window_class->get_surface = + GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_get_surface); + window_class->get_presentation_support = + GST_DEBUG_FUNCPTR (gst_vulkan_window_wayland_get_presentation_support); +} + +static void +gst_vulkan_window_wayland_init (GstVulkanWindowWayland * window) +{ +} + +GstVulkanWindowWayland * +gst_vulkan_window_wayland_new (GstVulkanDisplay * display) +{ + if ((gst_vulkan_display_get_handle_type (display) & + GST_VULKAN_DISPLAY_TYPE_WAYLAND) + == 0) + /* we require a wayland display to create wayland surfaces */ + return NULL; + + GST_DEBUG ("creating Wayland window"); + + return g_object_new (GST_TYPE_VULKAN_WINDOW_WAYLAND, NULL); +} + +static void +gst_vulkan_window_wayland_close (GstVulkanWindow * window) +{ + GstVulkanWindowWayland *window_wl; + + window_wl = GST_VULKAN_WINDOW_WAYLAND (window); + + destroy_surfaces (window_wl); + + g_source_destroy (window_wl->wl_source); + g_source_unref (window_wl->wl_source); + window_wl->wl_source = NULL; + + GST_VULKAN_WINDOW_CLASS (parent_class)->close (window); +} + +static gboolean +gst_vulkan_window_wayland_open (GstVulkanWindow * window, GError ** error) +{ + GstVulkanDisplayWayland *display; + GstVulkanWindowWayland *window_wl = GST_VULKAN_WINDOW_WAYLAND (window); + + if (!GST_IS_VULKAN_DISPLAY_WAYLAND (window->display)) { + g_set_error (error, GST_VULKAN_WINDOW_ERROR, + GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE, + "Failed to retrieve Wayland display (wrong type?)"); + return FALSE; + } + display = GST_VULKAN_DISPLAY_WAYLAND (window->display); + + if (!display->display) { + g_set_error (error, GST_VULKAN_WINDOW_ERROR, + GST_VULKAN_WINDOW_ERROR_RESOURCE_UNAVAILABLE, + "Failed to retrieve Wayland display"); + return FALSE; + } + + window_wl->queue = NULL; + + if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error)) + return FALSE; + + create_surfaces (window_wl); + + gst_vulkan_display_wayland_roundtrip_async (display); + + return TRUE; +} + +static VkSurfaceKHR +gst_vulkan_window_wayland_get_surface (GstVulkanWindow * window, + GError ** error) +{ + GstVulkanWindowWayland *window_wl = GST_VULKAN_WINDOW_WAYLAND (window); + VkWaylandSurfaceCreateInfoKHR info = { 0, }; + VkSurfaceKHR ret; + VkResult err; + + info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + info.pNext = NULL; + info.flags = 0; + info.display = GST_VULKAN_DISPLAY_WAYLAND_DISPLAY (window->display); + info.surface = window_wl->surface; + + if (!window_wl->CreateWaylandSurface) + window_wl->CreateWaylandSurface = + gst_vulkan_instance_get_proc_address (window->display->instance, + "vkCreateWaylandSurfaceKHR"); + if (!window_wl->CreateWaylandSurface) { + g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT, + "Could not retrieve \"vkCreateWaylandSurfaceKHR\" function pointer"); + return NULL; + } + + err = + window_wl->CreateWaylandSurface (window->display->instance->instance, + &info, NULL, &ret); + if (gst_vulkan_error_to_g_error (err, error, "vkCreateWaylandSurfaceKHR") < 0) + return NULL; + + return ret; +} + +static gboolean +gst_vulkan_window_wayland_get_presentation_support (GstVulkanWindow * window, + GstVulkanDevice * device, guint32 queue_family_idx) +{ + GstVulkanWindowWayland *window_wl = GST_VULKAN_WINDOW_WAYLAND (window); + VkPhysicalDevice gpu; + + if (!window_wl->GetPhysicalDeviceWaylandPresentationSupport) + window_wl->GetPhysicalDeviceWaylandPresentationSupport = + gst_vulkan_instance_get_proc_address (window->display->instance, + "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); + if (!window_wl->GetPhysicalDeviceWaylandPresentationSupport) { + GST_WARNING_OBJECT (window, "Could not retrieve " + "\"vkGetPhysicalDeviceWaylandPresentationSupportKHR\" " + "function pointer"); + return FALSE; + } + + gpu = gst_vulkan_device_get_physical_device (device); + if (window_wl->GetPhysicalDeviceWaylandPresentationSupport (gpu, + queue_family_idx, + GST_VULKAN_DISPLAY_WAYLAND_DISPLAY (window->display))) + return TRUE; + return FALSE; +} diff --git a/ext/vulkan/wayland/vkwindow_wayland.h b/ext/vulkan/wayland/vkwindow_wayland.h new file mode 100644 index 0000000000..3d181f84c0 --- /dev/null +++ b/ext/vulkan/wayland/vkwindow_wayland.h @@ -0,0 +1,74 @@ +/* + * GStreamer + * Copyright (C) 2016 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_VK_WINDOW_WAYLAND_H__ +#define __GST_VK_WINDOW_WAYLAND_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_WINDOW_WAYLAND (gst_vulkan_window_wayland_get_type()) +#define GST_VULKAN_WINDOW_WAYLAND(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW_WAYLAND, GstVulkanWindowWayland)) +#define GST_VULKAN_WINDOW_WAYLAND_CLASS(k) (G_TYPE_CHECK_CLASS((k), GST_TYPE_VULKAN_WINDOW_WAYLAND, GstVulkanWindowWaylandClass)) +#define GST_IS_VULKAN_WINDOW_WAYLAND(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW_WAYLAND)) +#define GST_IS_VULKAN_WINDOW_WAYLAND_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW_WAYLAND)) +#define GST_VULKAN_WINDOW_WAYLAND_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW_WAYLAND, GstVulkanWindowWaylandClass)) + +typedef struct _GstVulkanWindowWayland GstVulkanWindowWayland; +typedef struct _GstVulkanWindowWaylandClass GstVulkanWindowWaylandClass; + +struct _GstVulkanWindowWayland { + /*< private >*/ + GstVulkanWindow parent; + + PFN_vkCreateWaylandSurfaceKHR CreateWaylandSurface; + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR GetPhysicalDeviceWaylandPresentationSupport; + + struct wl_event_queue *queue; + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + struct wl_callback *callback; + + int window_width, window_height; + + GSource *wl_source; + + gpointer _reserved[GST_PADDING]; +}; + +struct _GstVulkanWindowWaylandClass { + /*< private >*/ + GstVulkanWindowClass parent_class; + + /*< private >*/ + gpointer _reserved[GST_PADDING]; +}; + +GType gst_vulkan_window_wayland_get_type (void); + +GstVulkanWindowWayland * gst_vulkan_window_wayland_new (GstVulkanDisplay * display); + +G_END_DECLS + +#endif /* __GST_GL_WINDOW_X11_H__ */ diff --git a/ext/vulkan/wayland/wayland_event_source.c b/ext/vulkan/wayland/wayland_event_source.c new file mode 100644 index 0000000000..e1ae1ef35a --- /dev/null +++ b/ext/vulkan/wayland/wayland_event_source.c @@ -0,0 +1,205 @@ +/* + * GStreamer + * Copyright (C) 2010 Intel Corporation. + * Copyright (C) 2012 Matthew Waters + * Copyright (C) 2016 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Authors: + * Matthew Allum + * Robert Bragg + * Kristian Høgsberg + */ + +/* code originally from clutter's wayland backend found here + * http://git.gnome.org/browse/clutter/tree/clutter/wayland/clutter-event-wayland.c + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "wayland_event_source.h" + +static void +sync_callback (void *data, struct wl_callback *callback, uint32_t serial) +{ + gboolean *done = data; + + *done = TRUE; + wl_callback_destroy (callback); +} + +static const struct wl_callback_listener sync_listener = { + sync_callback +}; + +/* only thread safe iff called on the same thread @queue is being dispatched on */ +gint +gst_vulkan_wl_display_roundtrip_queue (struct wl_display *display, + struct wl_event_queue *queue) +{ + struct wl_callback *callback; + gboolean done = FALSE; + gint ret = 0; + + if (queue) { + /* creating a wl_proxy and setting the queue is racy with the dispatching + * of the default queue */ + while (wl_display_prepare_read_queue (display, queue) != 0) { + if ((ret = wl_display_dispatch_queue_pending (display, queue)) < 0) + return ret; + } + } + if (!(callback = wl_display_sync (display))) { + return -1; + } + wl_callback_add_listener (callback, &sync_listener, &done); + if (queue) { + wl_proxy_set_queue ((struct wl_proxy *) callback, queue); + wl_display_cancel_read (display); + while (!done && ret >= 0) + ret = wl_display_dispatch_queue (display, queue); + } else { + while (!done && ret >= 0) + ret = wl_display_dispatch (display); + } + + if (ret == -1 && !done) + wl_callback_destroy (callback); + + return ret; +} + +typedef struct _WaylandEventSource +{ + GSource source; + GPollFD pfd; + uint32_t mask; + struct wl_display *display; + struct wl_event_queue *queue; + gboolean reading; +} WaylandEventSource; + +static gboolean +wayland_event_source_prepare (GSource * base, gint * timeout) +{ + WaylandEventSource *source = (WaylandEventSource *) base; + + *timeout = -1; + + /* we may be called multiple times for prepare */ + if (source->reading) + wl_display_cancel_read (source->display); + + if (source->queue) { + while (wl_display_prepare_read_queue (source->display, source->queue) != 0) { + if (wl_display_dispatch_queue_pending (source->display, + source->queue) < 0) { + g_critical ("Failed to dispatch pending events\n"); + } + } + } else { + while (wl_display_prepare_read (source->display) != 0) { + if (wl_display_dispatch_pending (source->display) < 0) { + g_critical ("Failed to dispatch pending events\n"); + } + } + } + source->reading = TRUE; + + /* FIXME: this may return EAGAIN if the fd is full */ + if (wl_display_flush (source->display) < 0) + g_critical ("Failed to flush Wayland connection\n"); + + return FALSE; +} + +static gboolean +wayland_event_source_check (GSource * base) +{ + WaylandEventSource *source = (WaylandEventSource *) base; + gboolean retval; + + retval = source->pfd.revents; + + if (source->pfd.revents & G_IO_IN) { + wl_display_read_events (source->display); + } else { + wl_display_cancel_read (source->display); + } + source->reading = FALSE; + + return retval; +} + +static gboolean +wayland_event_source_dispatch (GSource * base, + GSourceFunc callback, gpointer data) +{ + WaylandEventSource *source = (WaylandEventSource *) base; + + if (source->queue) + wl_display_dispatch_queue_pending (source->display, source->queue); + else + wl_display_dispatch_pending (source->display); + source->pfd.revents = 0; + + if (callback) + callback (data); + + return TRUE; +} + +static void +wayland_event_source_finalize (GSource * base) +{ + WaylandEventSource *source = (WaylandEventSource *) base; + + if (source->reading) { + wl_display_cancel_read (source->display); + } + source->reading = FALSE; +} + +static GSourceFuncs wayland_event_source_funcs = { + wayland_event_source_prepare, + wayland_event_source_check, + wayland_event_source_dispatch, + wayland_event_source_finalize +}; + +GSource * +wayland_event_source_new (struct wl_display *display, + struct wl_event_queue *queue) +{ + WaylandEventSource *source; + + source = (WaylandEventSource *) + g_source_new (&wayland_event_source_funcs, sizeof (WaylandEventSource)); + source->display = display; + source->queue = queue; + source->pfd.fd = wl_display_get_fd (display); + source->pfd.events = G_IO_IN | G_IO_ERR; + g_source_add_poll (&source->source, &source->pfd); + + return &source->source; +} diff --git a/ext/vulkan/wayland/wayland_event_source.h b/ext/vulkan/wayland/wayland_event_source.h new file mode 100644 index 0000000000..25ffe7bb48 --- /dev/null +++ b/ext/vulkan/wayland/wayland_event_source.h @@ -0,0 +1,34 @@ +/* + * GStreamer + * Copyright (C) 2016 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __WAYLAND_EVENT_SOURCE_H__ +#define __WAYLAND_EVENT_SOURCE_H__ + +#include + +#include + +GSource * wayland_event_source_new (struct wl_display *display, + struct wl_event_queue *queue); + +G_GNUC_INTERNAL gint gst_vulkan_wl_display_roundtrip_queue (struct wl_display *display, + struct wl_event_queue *queue); + +#endif /* __WAYLAND_EVENT_SOURCE_H__ */