• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * GStreamer
3 * Copyright (C) 2019 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#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include <Cocoa/Cocoa.h>
26#include <QuartzCore/QuartzCore.h>
27
28#include <gst/gst.h>
29
30#include <gst/vulkan/vulkan.h>
31
32#include "gstvkwindow_cocoa.h"
33#include "gstvkdisplay_cocoa.h"
34
35#include "gstvkcocoa_utils.h"
36
37#define GET_PRIV(o) gst_vulkan_window_cocoa_get_instance_private (o)
38
39#define GST_CAT_DEFAULT gst_vulkan_window_cocoa_debug
40GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
41
42static void
43_init_debug (void)
44{
45  static gsize _init = 0;
46
47  if (g_once_init_enter (&_init)) {
48    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowmacos", 0,
49        "Vulkan MacOS Window");
50    g_once_init_leave (&_init, 1);
51  }
52}
53
54gboolean gst_vulkan_window_cocoa_handle_event (GstVulkanWindowCocoa * window_cocoa);
55
56enum
57{
58  PROP_0,
59};
60
61struct _GstVulkanWindowCocoaPrivate
62{
63  gpointer internal_win_id;
64  gpointer internal_view;
65
66  gint preferred_width;
67  gint preferred_height;
68
69  gboolean visible;
70};
71
72#define gst_vulkan_window_cocoa_parent_class parent_class
73G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowCocoa, gst_vulkan_window_cocoa,
74    GST_TYPE_VULKAN_WINDOW, G_ADD_PRIVATE (GstVulkanWindowCocoa) _init_debug ());
75
76static VkSurfaceKHR gst_vulkan_window_cocoa_get_surface (GstVulkanWindow * window,
77    GError ** error);
78static gboolean gst_vulkan_window_cocoa_get_presentation_support (GstVulkanWindow
79    * window, GstVulkanDevice * device, guint32 queue_family_idx);
80static gboolean gst_vulkan_window_cocoa_open (GstVulkanWindow * window,
81    GError ** error);
82static void gst_vulkan_window_cocoa_close (GstVulkanWindow * window);
83
84static void
85gst_vulkan_window_cocoa_finalize (GObject * object)
86{
87  G_OBJECT_CLASS (parent_class)->finalize (object);
88}
89
90static void
91gst_vulkan_window_cocoa_class_init (GstVulkanWindowCocoaClass * klass)
92{
93  GObjectClass *obj_class = G_OBJECT_CLASS (klass);
94  GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass;
95
96  obj_class->finalize = gst_vulkan_window_cocoa_finalize;
97
98  window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_cocoa_open);
99  window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_cocoa_close);
100  window_class->get_surface = gst_vulkan_window_cocoa_get_surface;
101  window_class->get_presentation_support =
102      gst_vulkan_window_cocoa_get_presentation_support;
103}
104
105static void
106gst_vulkan_window_cocoa_init (GstVulkanWindowCocoa * window)
107{
108  GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window);
109
110  priv->preferred_width = 320;
111  priv->preferred_height = 240;
112}
113
114/* Must be called in the gl thread */
115GstVulkanWindowCocoa *
116gst_vulkan_window_cocoa_new (GstVulkanDisplay * display)
117{
118  GstVulkanWindowCocoa *window;
119
120  _init_debug ();
121
122  if ((gst_vulkan_display_get_handle_type (display) &
123          GST_VULKAN_DISPLAY_TYPE_COCOA)
124      == GST_VULKAN_DISPLAY_TYPE_NONE) {
125    GST_INFO ("Wrong display type %u for this window type %u", display->type,
126        GST_VULKAN_DISPLAY_TYPE_COCOA);
127    return NULL;
128  }
129
130  window = g_object_new (GST_TYPE_VULKAN_WINDOW_COCOA, NULL);
131  gst_object_ref_sink (window);
132
133  return window;
134}
135
136static void
137_show_window (gpointer data)
138{
139  GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (data);
140  GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
141  GstVulkanNSWindow *internal_win_id = (__bridge GstVulkanNSWindow *)priv->internal_win_id;
142
143  GST_DEBUG_OBJECT (window_cocoa, "showing");
144  [internal_win_id makeMainWindow];
145  [internal_win_id orderFrontRegardless];
146  [internal_win_id setViewsNeedDisplay:YES];
147
148  priv->visible = TRUE;
149}
150
151static void
152gst_vulkan_window_cocoa_show (GstVulkanWindow * window)
153{
154  GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
155  GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
156
157  if (!priv->visible)
158    _invoke_on_main ((GstVulkanWindowFunc) _show_window, gst_object_ref (window),
159        (GDestroyNotify) gst_object_unref);
160}
161
162static void
163gst_vulkan_window_cocoa_hide (GstVulkanWindow * window)
164{
165//  GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
166
167  /* FIXME: implement */
168}
169
170static void
171_create_window (GstVulkanWindowCocoa * window_cocoa)
172{
173  GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
174  NSRect mainRect = [[NSScreen mainScreen] visibleFrame];
175  gint h = priv->preferred_height;
176  gint y = mainRect.size.height > h ? (mainRect.size.height - h) * 0.5 : 0;
177  NSRect rect = NSMakeRect (0, y, priv->preferred_width, priv->preferred_height);
178  GstVulkanNSWindow *internal_win_id;
179  GstVulkanNSView *view;
180
181  view = [[GstVulkanNSView alloc] initWithFrame:rect];
182  view.wantsLayer = YES;
183
184  internal_win_id = [[GstVulkanNSWindow alloc] initWithContentRect:rect styleMask:
185      (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
186      NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable)
187      backing: NSBackingStoreBuffered defer: NO screen: nil gstWin: window_cocoa];
188
189  [internal_win_id setContentView:view];
190
191  priv->internal_win_id = (__bridge_retained gpointer)internal_win_id;
192  priv->internal_view = (__bridge_retained gpointer)view;
193
194  gst_vulkan_window_cocoa_show (GST_VULKAN_WINDOW (window_cocoa));
195}
196
197gboolean
198gst_vulkan_window_cocoa_create_window (GstVulkanWindowCocoa * window_cocoa)
199{
200  _invoke_on_main ((GstVulkanWindowFunc) _create_window,
201      gst_object_ref (window_cocoa), gst_object_unref);
202
203  g_usleep(1000000);
204
205  return TRUE;
206}
207
208static VkSurfaceKHR
209gst_vulkan_window_cocoa_get_surface (GstVulkanWindow * window, GError ** error)
210{
211  GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
212  GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
213  VkMacOSSurfaceCreateInfoMVK info = { 0, };
214  VkSurfaceKHR ret;
215  VkResult err;
216
217  info.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
218  info.pNext = NULL;
219  info.flags = 0;
220  info.pView = priv->internal_view;
221
222  if (!window_cocoa->CreateMacOSSurface)
223    window_cocoa->CreateMacOSSurface =
224        gst_vulkan_instance_get_proc_address (window->display->instance,
225        "vkCreateMacOSSurfaceMVK");
226  if (!window_cocoa->CreateMacOSSurface) {
227    g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT,
228        "Could not retrieve \"vkCreateMacOSSurfaceMVK\" function pointer");
229    return VK_NULL_HANDLE;
230  }
231
232  err =
233      window_cocoa->CreateMacOSSurface (window->display->instance->instance, &info,
234      NULL, &ret);
235  if (gst_vulkan_error_to_g_error (err, error, "vkCreateMacOSSurfaceMVK") < 0)
236    return VK_NULL_HANDLE;
237
238  return ret;
239}
240
241static gboolean
242gst_vulkan_window_cocoa_get_presentation_support (GstVulkanWindow * window,
243    GstVulkanDevice * device, guint32 queue_family_idx)
244{
245  return TRUE;
246}
247
248static gboolean
249gst_vulkan_window_cocoa_open (GstVulkanWindow * window, GError ** error)
250{
251  GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (window);
252
253  if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error))
254    return FALSE;
255
256  return gst_vulkan_window_cocoa_create_window (window_cocoa);
257}
258
259static void
260_close_window (gpointer * data)
261{
262  GstVulkanWindowCocoa *window_cocoa = GST_VULKAN_WINDOW_COCOA (data);
263  GstVulkanWindow *window = GST_VULKAN_WINDOW (window_cocoa);
264  GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
265  GstVulkanNSWindow *internal_win_id =
266      (__bridge GstVulkanNSWindow *) priv->internal_win_id;
267
268  gst_vulkan_window_cocoa_hide (window);
269
270  [[internal_win_id contentView] removeFromSuperview];
271  CFBridgingRelease (priv->internal_win_id);
272  priv->internal_win_id = NULL;
273  CFBridgingRelease (priv->internal_view);
274  priv->internal_view = NULL;
275}
276
277static void
278gst_vulkan_window_cocoa_close (GstVulkanWindow * window)
279{
280  _invoke_on_main ((GstVulkanWindowFunc) _close_window, gst_object_ref (window),
281      (GDestroyNotify) gst_object_unref);
282
283  GST_VULKAN_WINDOW_CLASS (parent_class)->close (window);
284}
285
286@implementation GstVulkanNSWindow
287
288- (id) initWithContentRect: (NSRect) contentRect
289        styleMask: (unsigned int) styleMask
290    backing: (NSBackingStoreType) bufferingType
291    defer: (BOOL) flag screen: (NSScreen *) aScreen
292    gstWin: (GstVulkanWindowCocoa *) cocoa {
293
294  m_isClosed = NO;
295  window_cocoa = cocoa;
296
297  self = [super initWithContentRect: contentRect
298        styleMask: styleMask backing: bufferingType
299        defer: flag screen:aScreen];
300
301  GST_DEBUG ("initializing GstVulkanNSWindow");
302
303  [self setReleasedWhenClosed:NO];
304  [self setTitle:@"Vulkan renderer"];
305  [self setBackgroundColor:[NSColor blackColor]];
306  [self orderOut:self];
307
308  return self;
309}
310
311- (void) setClosed {
312  m_isClosed = YES;
313}
314
315- (BOOL) isClosed {
316  return m_isClosed;
317}
318
319- (BOOL) canBecomeMainWindow {
320  return YES;
321}
322
323- (BOOL) canBecomeKeyWindow {
324  return YES;
325}
326
327- (BOOL) windowShouldClose:(id)sender {
328
329  GstVulkanWindowCocoaPrivate *priv = GET_PRIV (window_cocoa);
330  GstVulkanNSWindow *internal_win_id = (__bridge GstVulkanNSWindow *)priv->internal_win_id;
331  GST_DEBUG ("user clicked the close button");
332  [internal_win_id setClosed];
333  return YES;
334}
335
336@end
337
338
339@implementation GstVulkanNSView
340
341-(BOOL) wantsUpdateLayer
342{
343   return YES;
344}
345
346+(Class) layerClass
347{
348  return [CAMetalLayer class];
349}
350
351-(CALayer*) makeBackingLayer
352{
353  CALayer* layer = [self.class.layerClass layer];
354  CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)];
355  layer.contentsScale = MIN(viewScale.width, viewScale.height);
356  return layer;
357}
358
359@end
360
361void
362_invoke_on_main (GstVulkanWindowFunc func, gpointer data, GDestroyNotify notify)
363{
364  if ([NSThread isMainThread]) {
365    func (data);
366    if (notify)
367      notify (data);
368  } else {
369    dispatch_async (dispatch_get_main_queue (), ^{
370      func (data);
371      if (notify)
372        notify (data);
373    });
374  }
375}
376
377