From 012d6b1d98b759826a77eec6cd62648b78bca9ca Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 25 Mar 2019 17:50:13 +1100 Subject: vulkan: Add Cocoa window implementation --- ext/vulkan/cocoa/vkcocoa_utils.h | 55 ++++++ ext/vulkan/cocoa/vkdisplay_cocoa.h | 67 +++++++ ext/vulkan/cocoa/vkdisplay_cocoa.m | 245 ++++++++++++++++++++++++ ext/vulkan/cocoa/vkwindow_cocoa.h | 82 ++++++++ ext/vulkan/cocoa/vkwindow_cocoa.m | 373 +++++++++++++++++++++++++++++++++++++ ext/vulkan/meson.build | 28 +++ ext/vulkan/vkapi.h | 6 + ext/vulkan/vkconfig.h.meson | 1 + ext/vulkan/vkdisplay.c | 10 + ext/vulkan/vkdisplay.h | 1 + ext/vulkan/vkwindow.c | 7 + 11 files changed, 875 insertions(+) create mode 100644 ext/vulkan/cocoa/vkcocoa_utils.h create mode 100644 ext/vulkan/cocoa/vkdisplay_cocoa.h create mode 100644 ext/vulkan/cocoa/vkdisplay_cocoa.m create mode 100644 ext/vulkan/cocoa/vkwindow_cocoa.h create mode 100644 ext/vulkan/cocoa/vkwindow_cocoa.m diff --git a/ext/vulkan/cocoa/vkcocoa_utils.h b/ext/vulkan/cocoa/vkcocoa_utils.h new file mode 100644 index 000000000..2007a36b6 --- /dev/null +++ b/ext/vulkan/cocoa/vkcocoa_utils.h @@ -0,0 +1,55 @@ +/* + * GStreamer + * Copyright (C) 2019 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 __VULKAN_COCOA_UTILS_H__ +#define __VULKAN_COCOA_UTILS_H__ + +#include +#include + +#include "vkwindow_cocoa.h" + +G_BEGIN_DECLS + +@interface GstVulkanNSView : NSView +@end + +@interface GstVulkanNSWindow: NSWindow { + BOOL m_isClosed; + GstVulkanWindowCocoa *window_cocoa; +} +- (id)initWithContentRect:(NSRect)contentRect + styleMask: (unsigned int) styleMask + backing: (NSBackingStoreType) bufferingType + defer: (BOOL) flag screen: (NSScreen *) aScreen + gstWin: (GstVulkanWindowCocoa *) window; +- (void) setClosed; +- (BOOL) isClosed; +- (BOOL) canBecomeMainWindow; +- (BOOL) canBecomeKeyWindow; +@end + +typedef void (*GstVulkanWindowFunc) (gpointer data); + +void _invoke_on_main (GstVulkanWindowFunc func, gpointer data, GDestroyNotify notify); + +G_END_DECLS + +#endif diff --git a/ext/vulkan/cocoa/vkdisplay_cocoa.h b/ext/vulkan/cocoa/vkdisplay_cocoa.h new file mode 100644 index 000000000..e3ab7c088 --- /dev/null +++ b/ext/vulkan/cocoa/vkdisplay_cocoa.h @@ -0,0 +1,67 @@ +/* + * GStreamer + * Copyright (C) 2019 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_VULKAN_DISPLAY_COCOA_H__ +#define __GST_VULKAN_DISPLAY_COCOA_H__ + +#include + +#include +#ifndef VK_USE_PLATFORM_MACOS_MVK +#error "VK_USE_PLATFORM_MACOS_MVK not defined before including this header" +#error "Either include vkapi.h or define VK_USE_PLATFORM_MACOS_MVK before including this header" +#endif +#include + +G_BEGIN_DECLS + +GType gst_vulkan_display_cocoa_get_type (void); + +#define GST_TYPE_VULKAN_DISPLAY_COCOA (gst_vulkan_display_cocoa_get_type()) +#define GST_VULKAN_DISPLAY_COCOA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VULKAN_DISPLAY_COCOA,GstVulkanDisplayCocoa)) +#define GST_VULKAN_DISPLAY_COCOA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VULKAN_DISPLAY_COCOA,GstVulkanDisplayCocoaClass)) +#define GST_IS_VULKAN_DISPLAY_COCOA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VULKAN_DISPLAY_COCOA)) +#define GST_IS_VULKAN_DISPLAY_COCOA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VULKAN_DISPLAY_COCOA)) +#define GST_VULKAN_DISPLAY_COCOA_CAST(obj) ((GstVulkanDisplayCocoa*)(obj)) + +typedef struct _GstVulkanDisplayCocoa GstVulkanDisplayCocoa; +typedef struct _GstVulkanDisplayCocoaClass GstVulkanDisplayCocoaClass; + +/** + * GstVulkanDisplayCocoa: + * + * the contents of a #GstVulkanDisplayCocoa are private and should only be accessed + * through the provided API + */ +struct _GstVulkanDisplayCocoa +{ + GstVulkanDisplay parent; +}; + +struct _GstVulkanDisplayCocoaClass +{ + GstVulkanDisplayClass object_class; +}; + +GstVulkanDisplayCocoa * gst_vulkan_display_cocoa_new (void); + +G_END_DECLS + +#endif /* __GST_VULKAN_DISPLAY_COCOA_H__ */ diff --git a/ext/vulkan/cocoa/vkdisplay_cocoa.m b/ext/vulkan/cocoa/vkdisplay_cocoa.m new file mode 100644 index 000000000..4d84235cd --- /dev/null +++ b/ext/vulkan/cocoa/vkdisplay_cocoa.m @@ -0,0 +1,245 @@ +/* + * GStreamer + * Copyright (C) 2019 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 "vkdisplay_cocoa.h" +#include "vkcocoa_utils.h" + +#define GST_CAT_DEFAULT gst_vulkan_display_debug +GST_DEBUG_CATEGORY_STATIC (gst_vulkan_display_debug); + +G_DEFINE_TYPE (GstVulkanDisplayCocoa, gst_vulkan_display_cocoa, + GST_TYPE_VULKAN_DISPLAY); + +static void gst_vulkan_display_cocoa_finalize (GObject * object); +static gpointer gst_vulkan_display_cocoa_get_handle (GstVulkanDisplay * display); + +/* Define this if the GLib patch from + * https://bugzilla.gnome.org/show_bug.cgi?id=741450 + * is used + */ +#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION + +static GstVulkanDisplayCocoa *singleton = NULL; +static gint nsapp_source_id = 0; +static GMutex nsapp_lock; +static GCond nsapp_cond; + +static gboolean +gst_vulkan_display_cocoa_nsapp_iteration (gpointer data) +{ + NSEvent *event = nil; + + if (![NSThread isMainThread]) { + GST_WARNING ("NSApp iteration not running in the main thread"); + return FALSE; + } + + while ((event = ([NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate dateWithTimeIntervalSinceNow:0.05] + inMode:NSDefaultRunLoopMode dequeue:YES])) != nil) { + [NSApp sendEvent:event]; + } + + return TRUE; +} + +static void +gst_vulkan_display_cocoa_open_and_attach_source (gpointer data) +{ + if ([NSThread isMainThread]) { + /* The sharedApplication class method initializes + * the display environment and connects your program + * to the window server and the display server. + * It has to be done in the main thread. + */ + [NSApplication sharedApplication]; + + GST_DEBUG ("Custom NSApp initialization done"); + + nsapp_source_id = g_timeout_add (60, gst_vulkan_display_cocoa_nsapp_iteration, + NULL); + + GST_DEBUG ("NSApp iteration loop attached, id %d", nsapp_source_id); + } +} + +static gboolean +gst_vulkan_display_cocoa_init_nsapp (gpointer data) +{ + g_mutex_lock (&nsapp_lock); + + gst_vulkan_display_cocoa_open_and_attach_source (data); + + g_cond_signal (&nsapp_cond); + g_mutex_unlock (&nsapp_lock); + + return FALSE; +} + +static GstVulkanDisplayCocoa * +gst_vulkan_display_cocoa_setup_nsapp (gpointer data) +{ + GMainContext *context = g_main_context_default (); + gint delta_ms = 0; + + g_mutex_lock (&nsapp_lock); + + if (singleton) { + GST_DEBUG ("Get existing display"); + singleton = gst_object_ref (singleton); + g_mutex_unlock (&nsapp_lock); + return singleton; + } + + if (NSApp != nil && !singleton) { + GstVulkanDisplayCocoa *ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_COCOA, NULL); + gst_object_ref_sink (ret); + g_mutex_unlock (&nsapp_lock); + return ret; + } + + /* All application have to start with [NSApplication sharedApplication] + * so if NSApp is nil here let's assume this is a debugging application + * that runs a glib main loop. */ + g_assert (NSApp == nil); + + GST_DEBUG ("The application has not initialized NSApp"); + + if ([NSThread isMainThread]) { + + GST_DEBUG ("Setting up NSApp from the main thread"); + if (g_main_context_is_owner (context)) { + GST_DEBUG ("The main thread own the context"); + gst_vulkan_display_cocoa_open_and_attach_source (data); + } else if (g_main_context_acquire (context)) { + GST_DEBUG ("The main loop should be shortly running in the main thread"); + gst_vulkan_display_cocoa_open_and_attach_source (data); + g_main_context_release (context); + } else { + GST_WARNING ("Main loop running in another thread"); + } + } else { + + GST_DEBUG ("Setting up NSApp not from the main thread"); + + if (g_main_context_is_owner (context)) { + GST_WARNING ("Default context not own by the main thread"); + delta_ms = -1; + } else if (g_main_context_acquire (context)) { + GST_DEBUG ("The main loop should be shortly running in the main thread"); + delta_ms = 1000; + g_main_context_release (context); + } else { + GST_DEBUG ("Main loop running in main thread"); + delta_ms = 500; + } + + if (delta_ms > 0) { + gint64 end_time = g_get_monotonic_time () + delta_ms * 1000;; + g_idle_add_full (G_PRIORITY_HIGH, gst_vulkan_display_cocoa_init_nsapp, data, NULL); + g_cond_wait_until (&nsapp_cond, &nsapp_lock, end_time); + } + } + + if (NSApp == nil) { + GST_ERROR ("Custom NSApp initialization failed"); + } else { + GST_DEBUG ("Create display"); + singleton = g_object_new (GST_TYPE_VULKAN_DISPLAY_COCOA, NULL); + gst_object_ref_sink (singleton); + } + + g_mutex_unlock (&nsapp_lock); + + return singleton; +} + +#endif +static void +gst_vulkan_display_cocoa_class_init (GstVulkanDisplayCocoaClass * klass) +{ + GST_VULKAN_DISPLAY_CLASS (klass)->get_handle = + GST_DEBUG_FUNCPTR (gst_vulkan_display_cocoa_get_handle); + + G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_cocoa_finalize; +} + +static void +gst_vulkan_display_cocoa_init (GstVulkanDisplayCocoa * display_cocoa) +{ + GstVulkanDisplay *display = (GstVulkanDisplay *) display_cocoa; + + display->type = GST_VULKAN_DISPLAY_TYPE_COCOA; +} + +static void +gst_vulkan_display_cocoa_finalize (GObject * object) +{ +#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION + g_mutex_lock (&nsapp_lock); + if (singleton) { + GST_DEBUG ("Destroy display"); + singleton = NULL; + if (nsapp_source_id) { + GST_DEBUG ("Remove NSApp loop iteration, id %d", nsapp_source_id); + g_source_remove (nsapp_source_id); + } + nsapp_source_id = 0; + g_mutex_unlock (&nsapp_lock); + } + g_mutex_unlock (&nsapp_lock); +#endif + + G_OBJECT_CLASS (gst_vulkan_display_cocoa_parent_class)->finalize (object); +} + +/** + * gst_vulkan_display_cocoa_new: + * + * Create a new #GstVulkanDisplayCocoa. + * + * Returns: (transfer full): a new #GstVulkanDisplayCocoa or %NULL + */ +GstVulkanDisplayCocoa * +gst_vulkan_display_cocoa_new (void) +{ + GstVulkanDisplayCocoa *ret; + + GST_DEBUG_CATEGORY_GET (gst_vulkan_display_debug, "vulkandisplay"); + +#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION + ret = gst_vulkan_display_cocoa_setup_nsapp (NULL); +#else + ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_COCOA, NULL); + gst_object_ref_sink (ret); +#endif + + return ret; +} + +static gpointer +gst_vulkan_display_cocoa_get_handle (GstVulkanDisplay * display) +{ + return (gpointer) (__bridge gpointer) NSApp; +} diff --git a/ext/vulkan/cocoa/vkwindow_cocoa.h b/ext/vulkan/cocoa/vkwindow_cocoa.h new file mode 100644 index 000000000..0efa09f91 --- /dev/null +++ b/ext/vulkan/cocoa/vkwindow_cocoa.h @@ -0,0 +1,82 @@ +/* + * GStreamer + * Copyright (C) 2019 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_VULKAN_WINDOW_COCOA_H__ +#define __GST_VULKAN_WINDOW_COCOA_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VULKAN_WINDOW_COCOA (gst_vulkan_window_cocoa_get_type()) +#define GST_VULKAN_WINDOW_COCOA(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_WINDOW_COCOA, GstVulkanWindowCocoa)) +#define GST_VULKAN_WINDOW_COCOA_CLASS(k) (G_TYPE_CHECK_CLASS((k), GST_TYPE_VULKAN_WINDOW_COCOA, GstVulkanWindowCocoaClass)) +#define GST_IS_VULKAN_WINDOW_COCOA(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_WINDOW_COCOA)) +#define GST_IS_VULKAN_WINDOW_COCOA_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_WINDOW_COCOA)) +#define GST_VULKAN_WINDOW_COCOA_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_WINDOW_COCOA, GstVulkanWindowCocoaClass)) + +typedef struct _GstVulkanWindowCocoa GstVulkanWindowCocoa; +typedef struct _GstVulkanWindowCocoaPrivate GstVulkanWindowCocoaPrivate; +typedef struct _GstVulkanWindowCocoaClass GstVulkanWindowCocoaClass; + +/** + * GstVulkanWindowCocoa: + * + * Opaque #GstVulkanWindowCocoa object + */ +struct _GstVulkanWindowCocoa +{ + /*< private >*/ + GstVulkanWindow parent; + + gpointer view; + + gint visible :1; + + PFN_vkCreateMacOSSurfaceMVK CreateMacOSSurface; + + /*< private >*/ + GstVulkanWindowCocoaPrivate *priv; + + gpointer _reserved[GST_PADDING]; +}; + +/** + * GstVulkanWindowCocoaClass: + * + * Opaque #GstVulkanWindowCocoaClass object + */ +struct _GstVulkanWindowCocoaClass { + /*< private >*/ + GstVulkanWindowClass parent_class; + + /*< private >*/ + gpointer _reserved[GST_PADDING_LARGE]; +}; + +GType gst_vulkan_window_cocoa_get_type (void); + +GstVulkanWindowCocoa * gst_vulkan_window_cocoa_new (GstVulkanDisplay * display); + +gboolean gst_vulkan_window_cocoa_create_window (GstVulkanWindowCocoa * window_cocoa); + +G_END_DECLS + +#endif /* __GST_VULKAN_WINDOW_COCOA_H__ */ diff --git a/ext/vulkan/cocoa/vkwindow_cocoa.m b/ext/vulkan/cocoa/vkwindow_cocoa.m new file mode 100644 index 000000000..edfb1a1bd --- /dev/null +++ b/ext/vulkan/cocoa/vkwindow_cocoa.m @@ -0,0 +1,373 @@ +/* + * GStreamer + * Copyright (C) 2019 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 +#include + +#include + +#include "vkwindow_cocoa.h" +#include "vkdisplay_cocoa.h" + +#include "vkcocoa_utils.h" + +#define GST_VULKAN_WINDOW_COCOA_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_VULKAN_WINDOW_COCOA, GstVulkanWindowCocoaPrivate)) + +#define GST_CAT_DEFAULT gst_vulkan_window_cocoa_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static void +_init_debug (void) +{ + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowmacos", 0, + "Vulkan MacOS Window"); + g_once_init_leave (&_init, 1); + } +} + +gboolean gst_vulkan_window_cocoa_handle_event (GstVulkanWindowCocoa * window_cocoa); + +enum +{ + PROP_0, +}; + +struct _GstVulkanWindowCocoaPrivate +{ + gpointer internal_win_id; + gpointer internal_view; + + gint preferred_width; + gint preferred_height; + + gboolean visible; +}; + +#define gst_vulkan_window_cocoa_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowCocoa, gst_vulkan_window_cocoa, + GST_TYPE_VULKAN_WINDOW, G_ADD_PRIVATE (GstVulkanWindowCocoa) _init_debug ()); + +static VkSurfaceKHR gst_vulkan_window_cocoa_get_surface (GstVulkanWindow * window, + GError ** error); +static gboolean gst_vulkan_window_cocoa_get_presentation_support (GstVulkanWindow + * window, GstVulkanDevice * device, guint32 queue_family_idx); +static gboolean gst_vulkan_window_cocoa_open (GstVulkanWindow * window, + GError ** error); +static void gst_vulkan_window_cocoa_close (GstVulkanWindow * window); + +static void +gst_vulkan_window_cocoa_finalize (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_vulkan_window_cocoa_class_init (GstVulkanWindowCocoaClass * klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass; + + obj_class->finalize = gst_vulkan_window_cocoa_finalize; + + window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_cocoa_open); + window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_cocoa_close); + window_class->get_surface = gst_vulkan_window_cocoa_get_surface; + window_class->get_presentation_support = + gst_vulkan_window_cocoa_get_presentation_support; +} + +static void +gst_vulkan_window_cocoa_init (GstVulkanWindowCocoa * window) +{ + window->priv = gst_vulkan_window_cocoa_get_instance_private (window); + + window->priv->preferred_width = 320; + window->priv->preferred_height = 240; +} + +/* Must be called in the gl thread */ +GstVulkanWindowCocoa * +gst_vulkan_window_cocoa_new (GstVulkanDisplay * display) +{ + GstVulkanWindowCocoa *window; + + _init_debug (); + + if ((gst_vulkan_display_get_handle_type (display) & + GST_VULKAN_DISPLAY_TYPE_COCOA) + == GST_VULKAN_DISPLAY_TYPE_NONE) { + GST_INFO ("Wrong display type %u for this window type %u", display->type, + GST_VULKAN_DISPLAY_TYPE_COCOA); + return NULL; + } + + window = g_object_new (GST_TYPE_VULKAN_WINDOW_COCOA, NULL); + gst_object_ref_sink (window); + + return window; +} + +static void +_show_window (gpointer data) +{ + GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (data); + GstVulkanWindowCocoaPrivate *priv = window_cocoa->priv; + GstVulkanNSWindow *internal_win_id = (__bridge GstVulkanNSWindow *)priv->internal_win_id; + + GST_DEBUG_OBJECT (window_cocoa, "showing"); + [internal_win_id makeMainWindow]; + [internal_win_id orderFrontRegardless]; + [internal_win_id setViewsNeedDisplay:YES]; + + priv->visible = TRUE; +} + +static void +gst_vulkan_window_cocoa_show (GstVulkanWindow * window) +{ + GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window); + GstVulkanWindowCocoaPrivate *priv = window_cocoa->priv; + + if (!priv->visible) + _invoke_on_main ((GstVulkanWindowFunc) _show_window, gst_object_ref (window), + (GDestroyNotify) gst_object_unref); +} + +static void +gst_vulkan_window_cocoa_hide (GstVulkanWindow * window) +{ +// GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window); + + /* FIXME: implement */ +} + +static void +_create_window (GstVulkanWindowCocoa * window_cocoa) +{ + GstVulkanWindowCocoaPrivate *priv = window_cocoa->priv; + NSRect mainRect = [[NSScreen mainScreen] visibleFrame]; + gint h = priv->preferred_height; + gint y = mainRect.size.height > h ? (mainRect.size.height - h) * 0.5 : 0; + NSRect rect = NSMakeRect (0, y, priv->preferred_width, priv->preferred_height); + GstVulkanNSWindow *internal_win_id; + GstVulkanNSView *view; + + view = [[GstVulkanNSView alloc] initWithFrame:rect]; + view.wantsLayer = YES; + + internal_win_id = [[GstVulkanNSWindow alloc] initWithContentRect:rect styleMask: + (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | + NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable) + backing: NSBackingStoreBuffered defer: NO screen: nil gstWin: window_cocoa]; + + [internal_win_id setContentView:view]; + + priv->internal_win_id = (__bridge_retained gpointer)internal_win_id; + priv->internal_view = (__bridge_retained gpointer)view; + + gst_vulkan_window_cocoa_show (GST_VULKAN_WINDOW (window_cocoa)); +} + +gboolean +gst_vulkan_window_cocoa_create_window (GstVulkanWindowCocoa * window_cocoa) +{ + _invoke_on_main ((GstVulkanWindowFunc) _create_window, + gst_object_ref (window_cocoa), gst_object_unref); + + g_usleep(1000000); + + return TRUE; +} + +static VkSurfaceKHR +gst_vulkan_window_cocoa_get_surface (GstVulkanWindow * window, GError ** error) +{ + GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window); + VkMacOSSurfaceCreateInfoMVK info = { 0, }; + VkSurfaceKHR ret; + VkResult err; + + info.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + info.pNext = NULL; + info.flags = 0; + info.pView = window_cocoa->priv->internal_view; + + if (!window_cocoa->CreateMacOSSurface) + window_cocoa->CreateMacOSSurface = + gst_vulkan_instance_get_proc_address (window->display->instance, + "vkCreateMacOSSurfaceMVK"); + if (!window_cocoa->CreateMacOSSurface) { + g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT, + "Could not retrieve \"vkCreateMacOSSurfaceMVK\" function pointer"); + return NULL; + } + + err = + window_cocoa->CreateMacOSSurface (window->display->instance->instance, &info, + NULL, &ret); + if (gst_vulkan_error_to_g_error (err, error, "vkCreateMacOSSurfaceMVK") < 0) + return NULL; + + return ret; +} + +static gboolean +gst_vulkan_window_cocoa_get_presentation_support (GstVulkanWindow * window, + GstVulkanDevice * device, guint32 queue_family_idx) +{ + return TRUE; +} + +static gboolean +gst_vulkan_window_cocoa_open (GstVulkanWindow * window, GError ** error) +{ + GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window); + + if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error)) + return FALSE; + + return gst_vulkan_window_cocoa_create_window (window_cocoa); +} + +static void +_close_window (gpointer * data) +{ + GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (data); + GstVulkanWindow *window = GST_VULKAN_WINDOW (window_cocoa); + GstVulkanNSWindow *internal_win_id = + (__bridge GstVulkanNSWindow *) window_cocoa->priv->internal_win_id; + + gst_vulkan_window_cocoa_hide (window); + + [[internal_win_id contentView] removeFromSuperview]; + CFBridgingRelease (window_cocoa->priv->internal_win_id); + window_cocoa->priv->internal_win_id = NULL; + CFBridgingRelease (window_cocoa->priv->internal_view); + window_cocoa->priv->internal_view = NULL; +} + +static void +gst_vulkan_window_cocoa_close (GstVulkanWindow * window) +{ + _invoke_on_main ((GstVulkanWindowFunc) _close_window, gst_object_ref (window), + (GDestroyNotify) gst_object_unref); + + GST_VULKAN_WINDOW_CLASS (parent_class)->close (window); +} + +@implementation GstVulkanNSWindow + +- (id) initWithContentRect: (NSRect) contentRect + styleMask: (unsigned int) styleMask + backing: (NSBackingStoreType) bufferingType + defer: (BOOL) flag screen: (NSScreen *) aScreen + gstWin: (GstVulkanWindowCocoa *) cocoa { + + m_isClosed = NO; + window_cocoa = cocoa; + + self = [super initWithContentRect: contentRect + styleMask: styleMask backing: bufferingType + defer: flag screen:aScreen]; + + GST_DEBUG ("initializing GstVulkanNSWindow"); + + [self setReleasedWhenClosed:NO]; + [self setTitle:@"Vulkan renderer"]; + [self setBackgroundColor:[NSColor blackColor]]; + [self orderOut:self]; + + return self; +} + +- (void) setClosed { + m_isClosed = YES; +} + +- (BOOL) isClosed { + return m_isClosed; +} + +- (BOOL) canBecomeMainWindow { + return YES; +} + +- (BOOL) canBecomeKeyWindow { + return YES; +} + +- (BOOL) windowShouldClose:(id)sender { + + GstVulkanWindowCocoaPrivate *priv = window_cocoa->priv; + GstVulkanNSWindow *internal_win_id = (__bridge GstVulkanNSWindow *)priv->internal_win_id; + GST_DEBUG ("user clicked the close button\n"); + [internal_win_id setClosed]; + return YES; +} + +@end + + +@implementation GstVulkanNSView + +-(BOOL) wantsUpdateLayer +{ + return YES; +} + ++(Class) layerClass +{ + return [CAMetalLayer class]; +} + +-(CALayer*) makeBackingLayer +{ + CALayer* layer = [self.class.layerClass layer]; + CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)]; + layer.contentsScale = MIN(viewScale.width, viewScale.height); + return layer; +} + +@end + +void +_invoke_on_main (GstVulkanWindowFunc func, gpointer data, GDestroyNotify notify) +{ + if ([NSThread isMainThread]) { + func (data); + if (notify) + notify (data); + } else { + dispatch_async (dispatch_get_main_queue (), ^{ + func (data); + if (notify) + notify (data); + }); + } +} + diff --git a/ext/vulkan/meson.build b/ext/vulkan/meson.build index 8c9844cd1..4b64f78e9 100644 --- a/ext/vulkan/meson.build +++ b/ext/vulkan/meson.build @@ -18,6 +18,7 @@ vulkan_sources = [ 'vkwindow.c', ] +vulkan_objc_args = [] vulkan_defines = [] optional_deps = [] if get_option('vulkan').disabled() @@ -61,6 +62,32 @@ if vulkan_dep.found() and has_vulkan_header vkconf.set10('GST_VULKAN_HAVE_WINDOW_WAYLAND', 1) endif + if ['darwin', 'ios'].contains(host_system) + objc = meson.get_compiler('objc') + if not objc.has_argument('-fobjc-arc') + error('ARC is required for building') + endif + + vulkan_objc_args += ['-fobjc-arc'] + endif + + if host_system == 'darwin' + foundation_dep = dependency('appleframeworks', modules : ['Foundation'], required : get_option('vulkan')) + quartzcore_dep = dependency('appleframeworks', modules : ['QuartzCore'], required : get_option('vulkan')) + corefoundation_dep = dependency('appleframeworks', modules : ['CoreFoundation'], required : get_option('vulkan')) + cocoa_dep = dependency('appleframeworks', modules : ['Cocoa'], required : get_option('vulkan')) + + if foundation_dep.found() and quartzcore_dep.found() and corefoundation_dep.found() and cocoa_dep.found() + vulkan_sources += [ + 'cocoa/vkdisplay_cocoa.m', + 'cocoa/vkwindow_cocoa.m', + ] + optional_deps += [foundation_dep, corefoundation_dep, quartzcore_dep, cocoa_dep] + have_vulkan_windowing = true + vkconf.set10('GST_VULKAN_HAVE_WINDOW_COCOA', 1) + endif + endif + if have_vulkan_windowing configure_file(input : 'vkconfig.h.meson', output : 'vkconfig.h', @@ -69,6 +96,7 @@ if vulkan_dep.found() and has_vulkan_header gstvulkan = library('gstvulkan', vulkan_sources, c_args : gst_plugins_bad_args + vulkan_defines, + objc_args : gst_plugins_bad_args + vulkan_defines + vulkan_objc_args, link_args : noseh_link_args, include_directories : [configinc], dependencies : [vulkan_dep, gstvideo_dep, gstbase_dep] + optional_deps, diff --git a/ext/vulkan/vkapi.h b/ext/vulkan/vkapi.h index a37c29d0f..2df3e2028 100644 --- a/ext/vulkan/vkapi.h +++ b/ext/vulkan/vkapi.h @@ -40,6 +40,12 @@ #endif #endif +#if GST_VULKAN_HAVE_WINDOW_COCOA +#ifndef VK_USE_PLATFORM_MACOS_MVK +#define VK_USE_PLATFORM_MACOS_MVK +#endif +#endif + #include #endif /* _VK_H_ */ diff --git a/ext/vulkan/vkconfig.h.meson b/ext/vulkan/vkconfig.h.meson index bd58da98a..1ed97cab0 100644 --- a/ext/vulkan/vkconfig.h.meson +++ b/ext/vulkan/vkconfig.h.meson @@ -12,6 +12,7 @@ G_BEGIN_DECLS #mesondefine GST_VULKAN_HAVE_WINDOW_XCB #mesondefine GST_VULKAN_HAVE_WINDOW_WAYLAND +#mesondefine GST_VULKAN_HAVE_WINDOW_COCOA G_END_DECLS diff --git a/ext/vulkan/vkdisplay.c b/ext/vulkan/vkdisplay.c index c11437963..a8f5096cd 100644 --- a/ext/vulkan/vkdisplay.c +++ b/ext/vulkan/vkdisplay.c @@ -36,6 +36,9 @@ #if GST_VULKAN_HAVE_WINDOW_WAYLAND #include "wayland/vkdisplay_wayland.h" #endif +#if GST_VULKAN_HAVE_WINDOW_COCOA +#include "cocoa/vkdisplay_cocoa.h" +#endif GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT); #define GST_CAT_DEFAULT gst_vulkan_display_debug @@ -426,6 +429,9 @@ gst_vulkan_display_choose_type (GstVulkanInstance * instance) #if GST_VULKAN_HAVE_WINDOW_WAYLAND CHOOSE_WINSYS (wayland, WAYLAND); #endif +#if GST_VULKAN_HAVE_WINDOW_COCOA + CHOOSE_WINSYS (cocoa, COCOA); +#endif #undef CHOOSE_WINSYS @@ -453,6 +459,10 @@ gst_vulkan_display_type_to_extension_string (GstVulkanDisplayType type) if (type & GST_VULKAN_DISPLAY_TYPE_WAYLAND) return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; #endif +#if GST_VULKAN_HAVE_WINDOW_COCOA + if (type & GST_VULKAN_DISPLAY_TYPE_COCOA) + return VK_MVK_MACOS_SURFACE_EXTENSION_NAME; +#endif return NULL; } diff --git a/ext/vulkan/vkdisplay.h b/ext/vulkan/vkdisplay.h index b0bbb4fdc..89aa16947 100644 --- a/ext/vulkan/vkdisplay.h +++ b/ext/vulkan/vkdisplay.h @@ -49,6 +49,7 @@ enum _GstVulkanDisplayType GST_VULKAN_DISPLAY_TYPE_WAYLAND = (1 << 2), GST_VULKAN_DISPLAY_TYPE_MIR = (1 << 3), GST_VULKAN_DISPLAY_TYPE_WIN32 = (1 << 4), + GST_VULKAN_DISPLAY_TYPE_COCOA = (1 << 5), GST_VULKAN_DISPLAY_TYPE_ANY = G_MAXUINT32 }; diff --git a/ext/vulkan/vkwindow.c b/ext/vulkan/vkwindow.c index 3a1d579c5..102e96d25 100644 --- a/ext/vulkan/vkwindow.c +++ b/ext/vulkan/vkwindow.c @@ -46,6 +46,9 @@ #if GST_VULKAN_HAVE_WINDOW_WAYLAND #include "wayland/vkwindow_wayland.h" #endif +#if GST_VULKAN_HAVE_WINDOW_COCOA +#include "cocoa/vkwindow_cocoa.h" +#endif #define GST_CAT_DEFAULT gst_vulkan_window_debug GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); @@ -183,6 +186,10 @@ gst_vulkan_window_new (GstVulkanDisplay * display) #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 GST_VULKAN_HAVE_WINDOW_COCOA + if (!window && (!user_choice || g_strstr_len (user_choice, 5, "cocoa"))) + window = GST_VULKAN_WINDOW (gst_vulkan_window_cocoa_new (display)); #endif if (!window) { /* subclass returned a NULL window */ -- cgit v1.2.1