1/*
2 * GStreamer
3 * Copyright (C) 2014 Sebastian Dröge <sebastian@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#import <OpenGLES/EAGL.h>
26#import <QuartzCore/QuartzCore.h>
27#import <UIKit/UIKit.h>
28#include <OpenGLES/ES2/gl.h>
29
30#include "gstglcontext_eagl.h"
31#include "../gstglcontext_private.h"
32
33#define GST_CAT_DEFAULT gst_gl_context_debug
34
35static gboolean gst_gl_context_eagl_create_context (GstGLContext * context,
36    GstGLAPI gl_api, GstGLContext * other_context, GError ** error);
37static void gst_gl_context_eagl_destroy_context (GstGLContext * context);
38static gboolean gst_gl_context_eagl_choose_format (GstGLContext * context,
39    GError ** error);
40static guintptr gst_gl_context_eagl_get_gl_context (GstGLContext * window);
41static gboolean gst_gl_context_eagl_activate (GstGLContext * context,
42    gboolean activate);
43static void gst_gl_context_eagl_swap_buffers (GstGLContext * context);
44static GstGLAPI gst_gl_context_eagl_get_gl_api (GstGLContext * context);
45static GstGLPlatform gst_gl_context_eagl_get_gl_platform (GstGLContext *
46    context);
47
48struct _GstGLContextEaglPrivate
49{
50  gpointer eagl_context;
51
52  /* Used if we render to a window */
53  gpointer eagl_layer;
54  GLuint framebuffer;
55  GLuint color_renderbuffer;
56  GLuint depth_renderbuffer;
57};
58
59G_DEFINE_TYPE_WITH_PRIVATE (GstGLContextEagl, gst_gl_context_eagl,
60    GST_TYPE_GL_CONTEXT);
61
62static void
63gst_gl_context_eagl_class_init (GstGLContextEaglClass * klass)
64{
65  GstGLContextClass *context_class;
66
67  context_class = (GstGLContextClass *) klass;
68
69  context_class->destroy_context =
70      GST_DEBUG_FUNCPTR (gst_gl_context_eagl_destroy_context);
71  context_class->create_context =
72      GST_DEBUG_FUNCPTR (gst_gl_context_eagl_create_context);
73  context_class->choose_format =
74      GST_DEBUG_FUNCPTR (gst_gl_context_eagl_choose_format);
75  context_class->get_gl_context =
76      GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_context);
77  context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_eagl_activate);
78  context_class->swap_buffers =
79      GST_DEBUG_FUNCPTR (gst_gl_context_eagl_swap_buffers);
80  context_class->get_gl_api =
81      GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_api);
82  context_class->get_gl_platform =
83      GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_platform);
84}
85
86static void
87gst_gl_context_eagl_init (GstGLContextEagl * context)
88{
89  context->priv = gst_gl_context_eagl_get_instance_private (context);
90}
91
92/* Must be called in the gl thread */
93GstGLContextEagl *
94gst_gl_context_eagl_new (GstGLDisplay * display)
95{
96  GstGLContextEagl *context;
97
98  /* there isn't actually a display type for eagl yet? */
99  context = g_object_new (GST_TYPE_GL_CONTEXT_EAGL, NULL);
100  gst_object_ref_sink (context);
101
102  return context;
103}
104
105void
106gst_gl_context_eagl_resize (GstGLContextEagl * eagl_context)
107{
108  int width, height;
109
110  glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->color_renderbuffer);
111  [GS_GL_CONTEXT_EAGL_CONTEXT(eagl_context) renderbufferStorage:GL_RENDERBUFFER fromDrawable:GS_GL_CONTEXT_EAGL_LAYER(eagl_context)];
112  glGetRenderbufferParameteriv (GL_RENDERBUFFER,
113      GL_RENDERBUFFER_WIDTH, &width);
114  glGetRenderbufferParameteriv (GL_RENDERBUFFER,
115      GL_RENDERBUFFER_HEIGHT, &height);
116  glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->depth_renderbuffer);
117  glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width,
118      height);
119}
120
121static void
122gst_gl_context_eagl_release_layer (GstGLContext * context)
123{
124  GstGLContextEagl *context_eagl;
125
126  context_eagl = GST_GL_CONTEXT_EAGL (context);
127
128  if (context_eagl->priv->eagl_layer) {
129    gst_gl_context_eagl_activate (context, TRUE);
130
131    [GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl) renderbufferStorage: GL_RENDERBUFFER fromDrawable:nil];
132
133    glDeleteFramebuffers (1, &context_eagl->priv->framebuffer);
134    context_eagl->priv->framebuffer = 0;
135
136    glDeleteRenderbuffers (1, &context_eagl->priv->depth_renderbuffer);
137    context_eagl->priv->depth_renderbuffer = 0;
138    glDeleteRenderbuffers (1, &context_eagl->priv->color_renderbuffer);
139    context_eagl->priv->color_renderbuffer = 0;
140
141    context_eagl->priv->eagl_layer = nil;
142    gst_gl_context_eagl_activate (context, FALSE);
143  }
144}
145
146void
147gst_gl_context_eagl_update_layer (GstGLContext * context)
148{
149  GLuint framebuffer;
150  GLuint color_renderbuffer;
151  GLuint depth_renderbuffer;
152  GLint width;
153  GLint height;
154  CAEAGLLayer *eagl_layer;
155  GLenum status;
156  GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context);
157  GstGLContextEaglPrivate *priv = context_eagl->priv;
158  UIView *window_handle = nil;
159  GstGLWindow *window = gst_gl_context_get_window (context);
160  if (window)
161    window_handle = (__bridge UIView *)((void *)gst_gl_window_get_window_handle (window));
162
163  if (!window_handle) {
164    GST_INFO_OBJECT (context, "window handle not set yet, not updating layer");
165    goto out;
166  }
167
168  GST_INFO_OBJECT (context, "updating layer, frame %fx%f",
169      window_handle.frame.size.width, window_handle.frame.size.height);
170
171  if (priv->eagl_layer)
172    gst_gl_context_eagl_release_layer (context);
173
174  eagl_layer = (CAEAGLLayer *)[window_handle layer];
175  [EAGLContext setCurrentContext:GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl)];
176
177  /* Allocate framebuffer */
178  glGenFramebuffers (1, &framebuffer);
179  glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
180  /* Allocate color render buffer */
181  glGenRenderbuffers (1, &color_renderbuffer);
182  glBindRenderbuffer (GL_RENDERBUFFER, color_renderbuffer);
183  [GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl) renderbufferStorage: GL_RENDERBUFFER fromDrawable:eagl_layer];
184  glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
185      GL_RENDERBUFFER, color_renderbuffer);
186  /* Get renderbuffer width/height */
187  glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,
188      &width);
189  glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT,
190      &height);
191  /* allocate depth render buffer */
192  glGenRenderbuffers (1, &depth_renderbuffer);
193  glBindRenderbuffer (GL_RENDERBUFFER, depth_renderbuffer);
194  glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width,
195      height);
196  glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
197      GL_RENDERBUFFER, depth_renderbuffer);
198
199  /* check creation status */
200  status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
201  if (status != GL_FRAMEBUFFER_COMPLETE) {
202    GST_ERROR ("Failed to make complete framebuffer object %x", status);
203    goto out;
204  }
205  glBindRenderbuffer (GL_RENDERBUFFER, 0);
206  glBindFramebuffer (GL_FRAMEBUFFER, 0);
207
208  priv->eagl_layer = (__bridge_retained gpointer)eagl_layer;
209  priv->framebuffer = framebuffer;
210  priv->color_renderbuffer = color_renderbuffer;
211  priv->depth_renderbuffer = depth_renderbuffer;
212
213out:
214  if (window)
215    gst_object_unref (window);
216}
217
218static gboolean
219gst_gl_context_eagl_create_context (GstGLContext * context, GstGLAPI gl_api,
220    GstGLContext * other_context, GError ** error)
221{
222  GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context);
223  GstGLContextEaglPrivate *priv = context_eagl->priv;
224  EAGLSharegroup *share_group;
225
226  if (other_context) {
227    EAGLContext *external_gl_context = (__bridge EAGLContext *)(void *)
228        gst_gl_context_get_gl_context (other_context);
229    share_group = [external_gl_context sharegroup];
230  } else {
231    share_group = nil;
232  }
233
234  priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:share_group];
235  if (!priv->eagl_context) {
236    priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:share_group];
237  }
238  if (!priv->eagl_context) {
239    g_set_error_literal (error, GST_GL_CONTEXT_ERROR,
240        GST_GL_CONTEXT_ERROR_CREATE_CONTEXT,
241        "Failed to create OpenGL ES context");
242    return FALSE;
243  }
244
245  priv->eagl_layer = NULL;
246  priv->framebuffer = 0;
247  priv->color_renderbuffer = 0;
248  priv->depth_renderbuffer = 0;
249
250  GST_INFO_OBJECT (context, "context created, updating layer");
251  gst_gl_context_eagl_update_layer (context);
252
253  return TRUE;
254}
255
256static void
257gst_gl_context_eagl_destroy_context (GstGLContext * context)
258{
259  GstGLContextEagl *context_eagl;
260
261  context_eagl = GST_GL_CONTEXT_EAGL (context);
262
263  if (!context_eagl->priv->eagl_context)
264    return;
265
266  gst_gl_context_eagl_release_layer (context);
267
268  CFRelease(context_eagl->priv->eagl_context);
269  context_eagl->priv->eagl_context = NULL;
270}
271
272static gboolean
273gst_gl_context_eagl_choose_format (GstGLContext * context, GError ** error)
274{
275  GstGLContextEagl *context_eagl;
276  GstGLWindow *window;
277  UIView *window_handle = nil;
278
279  context_eagl = GST_GL_CONTEXT_EAGL (context);
280  window = gst_gl_context_get_window (context);
281
282  if (!window)
283    return TRUE;
284
285  if (window)
286    window_handle = (__bridge UIView *)(void *)gst_gl_window_get_window_handle (window);
287
288  if (!window_handle) {
289    gst_object_unref (window);
290    return TRUE;
291  }
292
293  CAEAGLLayer *eagl_layer;
294  NSDictionary * dict =[NSDictionary dictionaryWithObjectsAndKeys:
295      [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
296      kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
297
298  eagl_layer = (CAEAGLLayer *)[window_handle layer];
299  [eagl_layer setOpaque:YES];
300  [eagl_layer setDrawableProperties:dict];
301
302  gst_object_unref (window);
303
304  return TRUE;
305}
306
307static guintptr
308gst_gl_context_eagl_get_gl_context (GstGLContext * context)
309{
310  return (guintptr) GST_GL_CONTEXT_EAGL (context)->priv->eagl_context;
311}
312
313void
314gst_gl_context_eagl_prepare_draw (GstGLContextEagl * context)
315{
316  if (!context->priv->eagl_layer)
317    return;
318
319  glBindFramebuffer (GL_FRAMEBUFFER, context->priv->framebuffer);
320  glBindRenderbuffer (GL_RENDERBUFFER, context->priv->color_renderbuffer);
321}
322
323void
324gst_gl_context_eagl_finish_draw (GstGLContextEagl * context)
325{
326  if (!context->priv->eagl_layer)
327    return;
328
329  glBindRenderbuffer (GL_RENDERBUFFER, 0);
330  glBindFramebuffer (GL_FRAMEBUFFER, 0);
331}
332
333static void
334gst_gl_context_eagl_swap_buffers (GstGLContext * context)
335{
336  GstGLContextEagl *context_eagl;
337
338  context_eagl = GST_GL_CONTEXT_EAGL (context);
339
340  if (!context_eagl->priv->eagl_layer)
341    return;
342
343  [GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl) presentRenderbuffer:GL_RENDERBUFFER];
344}
345
346static gboolean
347gst_gl_context_eagl_activate (GstGLContext * context, gboolean activate)
348{
349  GstGLContextEagl *context_eagl;
350
351  context_eagl = GST_GL_CONTEXT_EAGL (context);
352
353  if (activate) {
354    EAGLContext *cur_ctx =[EAGLContext currentContext];
355
356    if (cur_ctx == context_eagl->priv->eagl_context) {
357      GST_DEBUG ("Already attached the context to thread %p", g_thread_self ());
358      return TRUE;
359    }
360
361    GST_DEBUG ("Attaching context to thread %p", g_thread_self ());
362    if ([EAGLContext setCurrentContext:GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl)] == NO) {
363      GST_ERROR ("Couldn't make context current");
364      return FALSE;
365    }
366  } else {
367    GST_DEBUG ("Detaching context from thread %p", g_thread_self ());
368    if ([EAGLContext setCurrentContext:nil] == NO) {
369      GST_ERROR ("Couldn't unbind context");
370      return FALSE;
371    }
372  }
373
374  return TRUE;
375}
376
377static GstGLAPI
378gst_gl_context_eagl_get_gl_api (GstGLContext * context)
379{
380  return GST_GL_API_GLES2;
381}
382
383static GstGLPlatform
384gst_gl_context_eagl_get_gl_platform (GstGLContext * context)
385{
386  return GST_GL_PLATFORM_EAGL;
387}
388
389guintptr
390gst_gl_context_eagl_get_current_context (void)
391{
392  return (guintptr) [EAGLContext currentContext];
393}
394