gst: don't use volatile to mean atomic
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / ios / gstvkwindow_ios.m
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 <QuartzCore/QuartzCore.h>
26
27 #include <gst/gst.h>
28
29 #include <gst/vulkan/vulkan.h>
30
31 #include "gstvkwindow_ios.h"
32 #include "gstvkdisplay_ios.h"
33
34 #include "gstvkios_utils.h"
35
36 #define GET_PRIV(o) gst_vulkan_window_ios_get_instance_private (o)
37
38 #define GST_CAT_DEFAULT gst_vulkan_window_ios_debug
39 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
40
41 static void
42 _init_debug (void)
43 {
44   static gsize _init = 0;
45
46   if (g_once_init_enter (&_init)) {
47     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowios", 0,
48         "Vulkan iOS Window");
49     g_once_init_leave (&_init, 1);
50   }
51 }
52
53 enum
54 {
55   PROP_0,
56 };
57
58 struct _GstVulkanWindowIosPrivate
59 {
60   gpointer internal_view;
61   gpointer internal_layer;
62   gpointer external_view;
63
64   gint preferred_width;
65   gint preferred_height;
66
67   GMutex lock;
68   GCond cond;
69 };
70
71 #define gst_vulkan_window_ios_parent_class parent_class
72 G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowIos, gst_vulkan_window_ios,
73     GST_TYPE_VULKAN_WINDOW, G_ADD_PRIVATE (GstVulkanWindowIos) _init_debug ());
74
75 static VkSurfaceKHR gst_vulkan_window_ios_get_surface (GstVulkanWindow * window,
76     GError ** error);
77 static gboolean gst_vulkan_window_ios_get_presentation_support (GstVulkanWindow
78     * window, GstVulkanDevice * device, guint32 queue_family_idx);
79 static gboolean gst_vulkan_window_ios_open (GstVulkanWindow * window,
80     GError ** error);
81 static void gst_vulkan_window_ios_close (GstVulkanWindow * window);
82 static void gst_vulkan_window_ios_set_window_handle (GstVulkanWindow * window,
83     guintptr window_handle);
84
85 static void
86 gst_vulkan_window_ios_finalize (GObject * object)
87 {
88   GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (object);
89   GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios);
90
91   g_mutex_clear (&priv->lock);
92   g_cond_clear (&priv->cond);
93
94   G_OBJECT_CLASS (parent_class)->finalize (object);
95 }
96
97 static void
98 gst_vulkan_window_ios_class_init (GstVulkanWindowIosClass * klass)
99 {
100   GObjectClass *obj_class = G_OBJECT_CLASS (klass);
101   GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass;
102
103   obj_class->finalize = gst_vulkan_window_ios_finalize;
104
105   window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_ios_open);
106   window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_ios_close);
107   window_class->get_surface = gst_vulkan_window_ios_get_surface;
108   window_class->get_presentation_support =
109       gst_vulkan_window_ios_get_presentation_support;
110   window_class->set_window_handle =
111       gst_vulkan_window_ios_set_window_handle;
112 }
113
114 static void
115 gst_vulkan_window_ios_init (GstVulkanWindowIos * window_ios)
116 {
117   GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios);
118
119   priv->preferred_width = 320;
120   priv->preferred_height = 240;
121
122   g_mutex_init (&priv->lock);
123   g_cond_init (&priv->cond);
124 }
125
126 /* Must be called in the gl thread */
127 GstVulkanWindowIos *
128 gst_vulkan_window_ios_new (GstVulkanDisplay * display)
129 {
130   GstVulkanWindowIos *window;
131
132   _init_debug ();
133
134   if ((gst_vulkan_display_get_handle_type (display) &
135           GST_VULKAN_DISPLAY_TYPE_IOS)
136       == GST_VULKAN_DISPLAY_TYPE_NONE) {
137     GST_INFO ("Wrong display type %u for this window type %u", display->type,
138         GST_VULKAN_DISPLAY_TYPE_IOS);
139     return NULL;
140   }
141
142   window = g_object_new (GST_TYPE_VULKAN_WINDOW_IOS, NULL);
143   gst_object_ref_sink (window);
144
145   return window;
146 }
147
148 static void
149 _create_window (GstVulkanWindowIos * window_ios)
150 {
151   GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios);
152   UIView *external_view = (__bridge UIView *) priv->external_view;
153   CGRect rect = CGRectMake (0, 0, external_view.frame.size.width, external_view.frame.size.height);
154   GstVulkanUIView *view;
155
156   view = [[GstVulkanUIView alloc] initWithFrame:rect];
157   [view setGstWindow:window_ios];
158   view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
159   view.contentMode = UIViewContentModeRedraw;
160
161   g_mutex_lock (&priv->lock);
162
163   priv->internal_view = (__bridge_retained gpointer) view;
164   [external_view addSubview:view];
165   priv->internal_layer = (__bridge_retained gpointer) [view layer];
166
167   g_cond_broadcast (&priv->cond);
168   g_mutex_unlock (&priv->lock);
169 }
170
171 gboolean
172 gst_vulkan_window_ios_create_window (GstVulkanWindowIos * window_ios)
173 {
174   GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios);
175
176   if (!priv->external_view) {
177     GST_WARNING_OBJECT(window_ios, "No external UIView provided");
178     return FALSE;
179   }
180
181   _invoke_on_main ((GstVulkanWindowFunc) _create_window,
182       gst_object_ref (window_ios), gst_object_unref);
183
184   /* XXX: Maybe we need an async create_window/get_surface()? */
185   g_mutex_lock (&priv->lock);
186   while (!priv->internal_view)
187     g_cond_wait (&priv->cond, &priv->lock);
188   g_mutex_unlock (&priv->lock);
189
190   return TRUE;
191 }
192
193 static VkSurfaceKHR
194 gst_vulkan_window_ios_get_surface (GstVulkanWindow * window, GError ** error)
195 {
196   GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window);
197   GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios);
198   VkIOSSurfaceCreateInfoMVK info = { 0, };
199   VkSurfaceKHR ret;
200   VkResult err;
201
202   if (!priv->internal_layer) {
203     g_set_error_literal (error, GST_VULKAN_ERROR,
204         VK_ERROR_INITIALIZATION_FAILED,
205         "No layer to retrieve surface for. Has create_window() been called?");
206     return 0;
207   }
208
209   info.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
210   info.pNext = NULL;
211   info.flags = 0;
212   info.pView = priv->internal_layer;
213
214   if (!window_ios->CreateIOSSurface)
215     window_ios->CreateIOSSurface =
216         gst_vulkan_instance_get_proc_address (window->display->instance,
217         "vkCreateIOSSurfaceMVK");
218   if (!window_ios->CreateIOSSurface) {
219     g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT,
220         "Could not retrieve \"vkCreateIOSSurfaceMVK\" function pointer");
221     return 0;
222   }
223
224   err =
225       window_ios->CreateIOSSurface (window->display->instance->instance, &info,
226       NULL, &ret);
227   if (gst_vulkan_error_to_g_error (err, error, "vkCreateIOSSurfaceMVK") < 0)
228     return 0;
229
230   return ret;
231 }
232
233 static gboolean
234 gst_vulkan_window_ios_get_presentation_support (GstVulkanWindow * window,
235     GstVulkanDevice * device, guint32 queue_family_idx)
236 {
237   return TRUE;
238 }
239
240 static gboolean
241 gst_vulkan_window_ios_open (GstVulkanWindow * window, GError ** error)
242 {
243   GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window);
244
245   if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error))
246     return FALSE;
247
248   return gst_vulkan_window_ios_create_window (window_ios);
249 }
250
251 static void
252 gst_vulkan_window_ios_close (GstVulkanWindow * window)
253 {
254   GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window);
255   GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios);
256   GstVulkanUIView *view = (__bridge GstVulkanUIView *) priv->internal_view;
257
258   [view setGstWindow:NULL];
259   CFBridgingRelease (priv->internal_view);
260   priv->internal_view = NULL;
261   CFBridgingRelease (priv->internal_layer);
262   priv->internal_layer = NULL;
263
264   GST_VULKAN_WINDOW_CLASS (parent_class)->close (window);
265 }
266
267 static void
268 gst_vulkan_window_ios_set_window_handle (GstVulkanWindow * window,
269     guintptr window_handle)
270 {
271   GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window);
272   GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios);
273   gpointer view = (gpointer) window_handle;
274
275   g_return_if_fail (view != NULL);
276
277   if (priv->external_view && priv->external_view != view) {
278     GST_FIXME_OBJECT (window_ios, "View changes are not implemented");
279     return;
280   }
281
282   priv->external_view = view;
283 }
284
285 @implementation GstVulkanUIView {
286     GstVulkanWindowIos * window_ios;
287
288     guint width;
289     guint height;
290 };
291
292 +(Class) layerClass
293 {
294   return [CAMetalLayer class];
295 }
296
297 -(void) setGstWindow:(GstVulkanWindowIos *) window
298 {
299   window_ios = window;
300 }
301
302 -(void) layoutSubViews
303 {
304   [super layoutSubviews];
305   CGSize rect = self.bounds.size;
306   GST_ERROR ("%ix%i", (int) rect.width, (int) rect.height);
307   gboolean resize = self->width != rect.width || self->height != rect.height;
308   self->width = rect.width;
309   self->height = rect.height;
310   if (resize && self->window_ios) {
311     gst_vulkan_window_resize (GST_VULKAN_WINDOW (self->window_ios), rect.width, rect.height);
312   }
313 }
314
315 @end
316
317 void
318 _invoke_on_main (GstVulkanWindowFunc func, gpointer data, GDestroyNotify notify)
319 {
320   if ([NSThread isMainThread]) {
321     func (data);
322     if (notify)
323       notify (data);
324   } else {
325     dispatch_async (dispatch_get_main_queue (), ^{
326       func (data);
327       if (notify)
328         notify (data);
329     });
330   }
331 }
332