gl: rename platform cocoa to cgl
[platform/upstream/gstreamer.git] / gst-libs / gst / gl / gstglcontext.c
1 /*
2  * GStreamer
3  * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.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 /**
22  * SECTION:gstglcontext
23  * @short_description: OpenGL context abstraction
24  * @title: GstGLContext
25  * @see_also: #GstGLDisplay, #GstGLWindow
26  *
27  * #GstGLContext wraps an OpenGL context object in a uniform API.  As a result
28  * of the limitation on OpenGL context, this object is not thread safe unless
29  * specified and must only be activated in a single thread.
30  */
31
32 #if HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #if defined(ANDROID) || defined(__ANDROID__)
37 /* Avoid a linker error with _isoc99_sscanf() when building a shared library
38  * for android
39  */
40 #define _GNU_SOURCE
41 #endif
42
43 #ifndef GL_NUM_EXTENSIONS
44 #define GL_NUM_EXTENSIONS 0x0000821d
45 #endif
46
47 #include <gmodule.h>
48
49 #include "gl.h"
50 #include "gstglcontext.h"
51
52 #if GST_GL_HAVE_PLATFORM_GLX
53 #include "x11/gstglcontext_glx.h"
54 #endif
55 #if GST_GL_HAVE_PLATFORM_EGL
56 #include "egl/gstglcontext_egl.h"
57 #endif
58 #if GST_GL_HAVE_PLATFORM_CGL
59 #include "cocoa/gstglcontext_cocoa.h"
60 #endif
61 #if GST_GL_HAVE_PLATFORM_WGL
62 #include "win32/gstglcontext_wgl.h"
63 #endif
64 #if GST_GL_HAVE_PLATFORM_EAGL
65 #include "eagl/gstglcontext_eagl.h"
66 #endif
67
68 static GModule *module_self;
69
70 #if GST_GL_HAVE_OPENGL
71 static GOnce module_opengl_gonce = G_ONCE_INIT;
72 static GModule *module_opengl;
73
74 static gpointer
75 load_opengl_module (gpointer user_data)
76 {
77 #ifdef GST_GL_LIBGL_MODULE_NAME
78   module_opengl = g_module_open (GST_GL_LIBGL_MODULE_NAME, G_MODULE_BIND_LAZY);
79 #else
80   /* This automatically handles the suffix and even .la files */
81   module_opengl = g_module_open ("libGL", G_MODULE_BIND_LAZY);
82
83   /* On Linux the .so is only in -dev packages, try with a real soname
84    * Proper compilers will optimize away the strcmp */
85   if (!module_opengl && strcmp (G_MODULE_SUFFIX, "so") == 0)
86     module_opengl = g_module_open ("libGL.so.1", G_MODULE_BIND_LAZY);
87 #endif
88
89   return NULL;
90 }
91 #endif
92
93 #if GST_GL_HAVE_GLES2
94 static GOnce module_gles2_gonce = G_ONCE_INIT;
95 static GModule *module_gles2;
96
97 static gpointer
98 load_gles2_module (gpointer user_data)
99 {
100 #ifdef GST_GL_LIBGLESV2_MODULE_NAME
101   module_gles2 =
102       g_module_open (GST_GL_LIBGLESV2_MODULE_NAME, G_MODULE_BIND_LAZY);
103 #else
104   /* This automatically handles the suffix and even .la files */
105   module_gles2 = g_module_open ("libGLESv2", G_MODULE_BIND_LAZY);
106
107   /* On Linux the .so is only in -dev packages, try with a real soname
108    * Proper compilers will optimize away the strcmp */
109   if (!module_gles2 && strcmp (G_MODULE_SUFFIX, "so") == 0)
110     module_gles2 = g_module_open ("libGLESv2.so.2", G_MODULE_BIND_LAZY);
111 #endif
112
113   return NULL;
114 }
115 #endif
116
117 #if GST_GL_HAVE_GLES3
118 #error "Add module loading support for GLES3"
119 #endif
120
121 #define GST_CAT_DEFAULT gst_gl_context_debug
122 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
123
124 #define gst_gl_context_parent_class parent_class
125 G_DEFINE_ABSTRACT_TYPE (GstGLContext, gst_gl_context, GST_TYPE_OBJECT);
126
127 #define GST_GL_CONTEXT_GET_PRIVATE(o) \
128   (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_CONTEXT, GstGLContextPrivate))
129
130 static gpointer gst_gl_context_create_thread (GstGLContext * context);
131 static void gst_gl_context_finalize (GObject * object);
132
133 struct _GstGLContextPrivate
134 {
135   GstGLDisplay *display;
136
137   GThread *gl_thread;
138
139   /* conditions */
140   GMutex render_lock;
141   GCond create_cond;
142   GCond destroy_cond;
143
144   gboolean created;
145   gboolean alive;
146
147   GstGLContext *other_context;
148   GError **error;
149
150   gint gl_major;
151   gint gl_minor;
152
153   gchar *gl_exts;
154 };
155
156 typedef struct
157 {
158   GstGLContext parent;
159
160   guintptr handle;
161   GstGLPlatform platform;
162   GstGLAPI available_apis;
163 } GstGLWrappedContext;
164
165 typedef struct
166 {
167   GstGLContextClass parent;
168 } GstGLWrappedContextClass;
169
170 #define GST_GL_TYPE_WRAPPED_CONTEXT (gst_gl_wrapped_context_get_type())
171 GType gst_gl_wrapped_context_get_type (void);
172 G_DEFINE_TYPE (GstGLWrappedContext, gst_gl_wrapped_context,
173     GST_GL_TYPE_CONTEXT);
174
175 #define GST_GL_WRAPPED_CONTEXT(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_GL_TYPE_WRAPPED_CONTEXT, GstGLWrappedContext))
176 #define GST_GL_WRAPPED_CONTEXT_CLASS(k)     (G_TYPE_CHECK_CLASS((k), GST_GL_TYPE_CONTEXT, GstGLContextClass))
177 #define GST_GL_IS_WRAPPED_CONTEXT(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_GL_TYPE_WRAPPED_CONTEXT))
178 #define GST_GL_IS_WRAPPED_CONTEXT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_GL_TYPE_WRAPPED_CONTEXT))
179 #define GST_GL_WRAPPED_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_GL_TYPE_WRAPPED_CONTEXT, GstGLWrappedContextClass))
180
181 GQuark
182 gst_gl_context_error_quark (void)
183 {
184   return g_quark_from_static_string ("gst-gl-context-error-quark");
185 }
186
187 static void
188 _ensure_window (GstGLContext * context)
189 {
190   GstGLWindow *window;
191
192   if (context->window)
193     return;
194
195   window = gst_gl_window_new (context->priv->display);
196
197   gst_gl_context_set_window (context, window);
198
199   gst_object_unref (window);
200 }
201
202 static void
203 gst_gl_context_init (GstGLContext * context)
204 {
205   context->priv = GST_GL_CONTEXT_GET_PRIVATE (context);
206
207   context->window = NULL;
208   context->gl_vtable = g_slice_alloc0 (sizeof (GstGLFuncs));
209
210   g_mutex_init (&context->priv->render_lock);
211
212   g_cond_init (&context->priv->create_cond);
213   g_cond_init (&context->priv->destroy_cond);
214   context->priv->created = FALSE;
215 }
216
217 static void
218 gst_gl_context_class_init (GstGLContextClass * klass)
219 {
220   g_type_class_add_private (klass, sizeof (GstGLContextPrivate));
221
222   module_self = g_module_open (NULL, G_MODULE_BIND_LAZY);
223
224   klass->get_proc_address =
225       GST_DEBUG_FUNCPTR (gst_gl_context_default_get_proc_address);
226
227   G_OBJECT_CLASS (klass)->finalize = gst_gl_context_finalize;
228 }
229
230 static void
231 _init_debug (void)
232 {
233   static volatile gsize _init = 0;
234
235   if (g_once_init_enter (&_init)) {
236     GST_DEBUG_CATEGORY_INIT (gst_gl_context_debug, "glcontext", 0,
237         "glcontext element");
238     g_once_init_leave (&_init, 1);
239   }
240 }
241
242 /**
243  * gst_gl_context_new:
244  * @display: a #GstGLDisplay
245  *
246  * Create a new #GstGLContext with the specified @display
247  *
248  * Returns: a new #GstGLContext
249  */
250 GstGLContext *
251 gst_gl_context_new (GstGLDisplay * display)
252 {
253   GstGLContext *context = NULL;
254   const gchar *user_choice;
255
256   _init_debug ();
257
258   user_choice = g_getenv ("GST_GL_PLATFORM");
259   GST_INFO ("creating a context, user choice:%s", user_choice);
260 #if GST_GL_HAVE_PLATFORM_EGL
261   if (!context && (!user_choice || g_strstr_len (user_choice, 7, "egl")))
262     context = GST_GL_CONTEXT (gst_gl_context_egl_new ());
263 #endif
264 #if GST_GL_HAVE_PLATFORM_CGL
265   if (!context && (!user_choice || g_strstr_len (user_choice, 5, "cgl")))
266     context = GST_GL_CONTEXT (gst_gl_context_cocoa_new ());
267 #endif
268 #if GST_GL_HAVE_PLATFORM_GLX
269   if (!context && (!user_choice || g_strstr_len (user_choice, 3, "glx")))
270     context = GST_GL_CONTEXT (gst_gl_context_glx_new ());
271 #endif
272 #if GST_GL_HAVE_PLATFORM_WGL
273   if (!context && (!user_choice || g_strstr_len (user_choice, 3, "wgl"))) {
274     context = GST_GL_CONTEXT (gst_gl_context_wgl_new ());
275   }
276 #endif
277 #if GST_GL_HAVE_PLATFORM_EAGL
278   if (!context && (!user_choice || g_strstr_len (user_choice, 5, "eagl")))
279     context = GST_GL_CONTEXT (gst_gl_context_eagl_new ());
280 #endif
281
282   if (!context) {
283     /* subclass returned a NULL context */
284     GST_WARNING ("Could not create context. user specified %s",
285         user_choice ? user_choice : "(null)");
286
287     return NULL;
288   }
289
290   context->priv->display = gst_object_ref (display);
291
292   return context;
293 }
294
295 /**
296  * gst_gl_context_new_wrapped:
297  * @display: a #GstGLDisplay
298  * @handle: the OpenGL context to wrap
299  * @context_type: a #GstGLPlatform specifying the type of context in @handle
300  * @available_apis: a #GstGLAPI containing the available OpenGL apis in @handle
301  *
302  * Wraps an existing OpenGL context into a #GstGLContext.  
303  *
304  * Returns: a #GstGLContext wrapping @handle
305  */
306 GstGLContext *
307 gst_gl_context_new_wrapped (GstGLDisplay * display, guintptr handle,
308     GstGLPlatform context_type, GstGLAPI available_apis)
309 {
310   GstGLContext *context;
311   GstGLWrappedContext *context_wrap = NULL;
312
313   _init_debug ();
314
315   context_wrap = g_object_new (GST_GL_TYPE_WRAPPED_CONTEXT, NULL);
316
317   if (!context_wrap) {
318     /* subclass returned a NULL context */
319     GST_ERROR ("Could not wrap existing context");
320
321     return NULL;
322   }
323
324   context = (GstGLContext *) context_wrap;
325
326   context->priv->display = gst_object_ref (display);
327   context_wrap->handle = handle;
328   context_wrap->platform = context_type;
329   context_wrap->available_apis = available_apis;
330
331   return context;
332 }
333
334 static void
335 gst_gl_context_finalize (GObject * object)
336 {
337   GstGLContext *context = GST_GL_CONTEXT (object);
338
339   if (context->window) {
340     gst_gl_window_set_resize_callback (context->window, NULL, NULL, NULL);
341     gst_gl_window_set_draw_callback (context->window, NULL, NULL, NULL);
342
343     if (context->priv->alive) {
344       g_mutex_lock (&context->priv->render_lock);
345       GST_INFO ("send quit gl window loop");
346       gst_gl_window_quit (context->window);
347       while (context->priv->alive) {
348         g_cond_wait (&context->priv->destroy_cond, &context->priv->render_lock);
349       }
350       g_mutex_unlock (&context->priv->render_lock);
351     }
352
353     gst_gl_window_set_close_callback (context->window, NULL, NULL, NULL);
354
355     if (context->priv->gl_thread) {
356       gpointer ret = g_thread_join (context->priv->gl_thread);
357       GST_INFO ("gl thread joined");
358       if (ret != NULL)
359         GST_ERROR ("gl thread returned a non-null pointer");
360       context->priv->gl_thread = NULL;
361     }
362
363     gst_object_unref (context->window);
364   }
365
366   gst_object_unref (context->priv->display);
367
368   if (context->gl_vtable) {
369     g_slice_free (GstGLFuncs, context->gl_vtable);
370     context->gl_vtable = NULL;
371   }
372
373   g_mutex_clear (&context->priv->render_lock);
374
375   g_cond_clear (&context->priv->destroy_cond);
376   g_cond_clear (&context->priv->create_cond);
377
378   g_free (context->priv->gl_exts);
379
380   G_OBJECT_CLASS (gst_gl_context_parent_class)->finalize (object);
381 }
382
383 /**
384  * gst_gl_context_activate:
385  * @context: a #GstGLContext
386  * @activate: %TRUE to activate, %FALSE to deactivate
387  *
388  * (De)activate the OpenGL context represented by this @context.
389  *
390  * In OpenGL terms, calls eglMakeCurrent or similar with this context and the
391  * currently set window.  See gst_gl_context_set_window() for details.
392  *
393  * Returns: Whether the activation succeeded
394  */
395 gboolean
396 gst_gl_context_activate (GstGLContext * context, gboolean activate)
397 {
398   GstGLContextClass *context_class;
399   gboolean result;
400
401   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), FALSE);
402   context_class = GST_GL_CONTEXT_GET_CLASS (context);
403   g_return_val_if_fail (context_class->activate != NULL, FALSE);
404
405   result = context_class->activate (context, activate);
406
407   return result;
408 }
409
410 /**
411  * gst_gl_context_get_gl_api:
412  * @context: a #GstGLContext
413  *
414  * Get the currently enabled OpenGL api.
415  *
416  * The currently available API may be limited by the #GstGLDisplay in use and/or
417  * the #GstGLWindow chosen.
418  *
419  * Returns: the currently available OpenGL api
420  */
421 GstGLAPI
422 gst_gl_context_get_gl_api (GstGLContext * context)
423 {
424   GstGLContextClass *context_class;
425
426   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), GST_GL_API_NONE);
427   context_class = GST_GL_CONTEXT_GET_CLASS (context);
428   g_return_val_if_fail (context_class->get_gl_api != NULL, GST_GL_API_NONE);
429
430   return context_class->get_gl_api (context);
431 }
432
433 /**
434  * gst_gl_context_get_proc_address:
435  * @context: a #GstGLContext
436  * @name: an opengl function name
437  *
438  * Get a function pointer to a specified opengl function, @name.  If the the
439  * specific function does not exist, NULL is returned instead.
440  *
441  * Platform specfic functions (names starting 'egl', 'glX', 'wgl', etc) can also
442  * be retreived using this method.
443  *
444  * Returns: a function pointer or NULL
445  */
446 gpointer
447 gst_gl_context_get_proc_address (GstGLContext * context, const gchar * name)
448 {
449   gpointer ret;
450   GstGLContextClass *context_class;
451
452   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), NULL);
453   g_return_val_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context), NULL);
454   context_class = GST_GL_CONTEXT_GET_CLASS (context);
455   g_return_val_if_fail (context_class->get_proc_address != NULL, NULL);
456
457   ret = context_class->get_proc_address (context, name);
458
459   return ret;
460 }
461
462 gpointer
463 gst_gl_context_default_get_proc_address (GstGLContext * context,
464     const gchar * name)
465 {
466   gpointer ret = NULL;
467   GstGLAPI gl_api = gst_gl_context_get_gl_api (context);
468
469   /* First try to load symbol from the selected GL API for this context */
470 #if GST_GL_HAVE_GLES2
471   if (!ret && (gl_api & GST_GL_API_GLES2)) {
472     g_once (&module_gles2_gonce, load_gles2_module, NULL);
473     if (module_gles2)
474       g_module_symbol (module_gles2, name, &ret);
475   }
476 #endif
477
478 #if GST_GL_HAVE_OPENGL
479   if (!ret && (gl_api & (GST_GL_API_OPENGL | GST_GL_API_OPENGL3))) {
480     g_once (&module_opengl_gonce, load_opengl_module, NULL);
481     if (module_opengl)
482       g_module_symbol (module_opengl, name, &ret);
483   }
484 #endif
485
486   /* Otherwise fall back to the current module */
487   if (!ret)
488     g_module_symbol (module_self, name, &ret);
489
490   return ret;
491 }
492
493 /**
494  * gst_gl_context_set_window:
495  * @context: a #GstGLContext
496  * @window: (transfer full): a #GstGLWindow
497  *
498  * Set's the current window on @context to @window.  The window can only be
499  * changed before gst_gl_context_create() has been called and the @window is not
500  * already running.
501  *
502  * Returns: Whether the window was successfully updated
503  */
504 gboolean
505 gst_gl_context_set_window (GstGLContext * context, GstGLWindow * window)
506 {
507   g_return_val_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context), FALSE);
508
509   /* we can't change the window while we are running */
510   if (context->priv->alive)
511     return FALSE;
512
513   if (window) {
514     if (gst_gl_window_is_running (window))
515       return FALSE;
516
517     g_weak_ref_set (&window->context_ref, context);
518   }
519
520   if (context->window)
521     gst_object_unref (context->window);
522
523   context->window = window ? gst_object_ref (window) : NULL;
524
525   return TRUE;
526 }
527
528 /**
529  * gst_gl_context_get_window:
530  * @context: a #GstGLContext
531  *
532  * Returns: the currently set window
533  */
534 GstGLWindow *
535 gst_gl_context_get_window (GstGLContext * context)
536 {
537   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), NULL);
538
539   if (GST_GL_IS_WRAPPED_CONTEXT (context))
540     return NULL;
541
542   _ensure_window (context);
543
544   return gst_object_ref (context->window);
545 }
546
547 /**
548  * gst_gl_context_create:
549  * @context: a #GstGLContext:
550  * @other_context: (allow-none): a #GstGLContext to share OpenGL objects with
551  * @error: (allow-none): a #GError
552  *
553  * Creates an OpenGL context in the current thread with the specified
554  * @other_context as a context to share shareable OpenGL objects with.  See the
555  * OpenGL specification for what is shared between contexts.
556  *
557  * If an error occurs, and @error is not %NULL, then error will contain details
558  * of the error and %FALSE will be returned.
559  *
560  * Should only be called once.
561  *
562  * Returns: whether the context could successfully be created
563  */
564 gboolean
565 gst_gl_context_create (GstGLContext * context,
566     GstGLContext * other_context, GError ** error)
567 {
568   gboolean alive = FALSE;
569
570   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), FALSE);
571   g_return_val_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context), FALSE);
572   _ensure_window (context);
573
574   g_mutex_lock (&context->priv->render_lock);
575
576   if (!context->priv->created) {
577     context->priv->other_context = other_context;
578     context->priv->error = error;
579
580     context->priv->gl_thread = g_thread_new ("gstglcontext",
581         (GThreadFunc) gst_gl_context_create_thread, context);
582
583     g_cond_wait (&context->priv->create_cond, &context->priv->render_lock);
584
585     context->priv->created = TRUE;
586
587     GST_INFO ("gl thread created");
588   }
589
590   alive = context->priv->alive;
591
592   g_mutex_unlock (&context->priv->render_lock);
593
594   return alive;
595 }
596
597 static gboolean
598 _create_context_gles2 (GstGLContext * context, gint * gl_major, gint * gl_minor,
599     GError ** error)
600 {
601   const GstGLFuncs *gl;
602   GLenum gl_err = GL_NO_ERROR;
603
604   gl = context->gl_vtable;
605
606   GST_INFO ("GL_VERSION: %s", gl->GetString (GL_VERSION));
607   GST_INFO ("GL_SHADING_LANGUAGE_VERSION: %s",
608       gl->GetString (GL_SHADING_LANGUAGE_VERSION));
609   GST_INFO ("GL_VENDOR: %s", gl->GetString (GL_VENDOR));
610   GST_INFO ("GL_RENDERER: %s", gl->GetString (GL_RENDERER));
611
612   gl_err = gl->GetError ();
613   if (gl_err != GL_NO_ERROR) {
614     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
615         "glGetString error: 0x%x", gl_err);
616     return FALSE;
617   }
618 #if GST_GL_HAVE_GLES2
619   if (!GL_ES_VERSION_2_0) {
620     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_OLD_LIBS,
621         "OpenGL|ES >= 2.0 is required");
622     return FALSE;
623   }
624 #endif
625
626   if (gl_major)
627     *gl_major = 2;
628   if (gl_minor)
629     *gl_minor = 0;
630
631   return TRUE;
632 }
633
634 static gboolean
635 _create_context_opengl (GstGLContext * context, gint * gl_major,
636     gint * gl_minor, GError ** error)
637 {
638   const GstGLFuncs *gl;
639   guint maj, min;
640   GLenum gl_err = GL_NO_ERROR;
641   GString *opengl_version = NULL;
642
643   gl = context->gl_vtable;
644
645   GST_INFO ("GL_VERSION: %s", gl->GetString (GL_VERSION));
646   GST_INFO ("GL_SHADING_LANGUAGE_VERSION: %s",
647       gl->GetString (GL_SHADING_LANGUAGE_VERSION));
648   GST_INFO ("GL_VENDOR: %s", gl->GetString (GL_VENDOR));
649   GST_INFO ("GL_RENDERER: %s", gl->GetString (GL_RENDERER));
650
651   gl_err = gl->GetError ();
652   if (gl_err != GL_NO_ERROR) {
653     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
654         "glGetString error: 0x%x", gl_err);
655     return FALSE;
656   }
657   opengl_version =
658       g_string_truncate (g_string_new ((gchar *) gl->GetString (GL_VERSION)),
659       3);
660
661   sscanf (opengl_version->str, "%d.%d", &maj, &min);
662
663   g_string_free (opengl_version, TRUE);
664
665   /* OpenGL > 1.2.0 */
666   if ((maj < 1) || (maj < 2 && maj >= 1 && min < 2)) {
667     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_OLD_LIBS,
668         "OpenGL >= 1.2.0 required, found %u.%u", maj, min);
669     return FALSE;
670   }
671
672   if (gl_major)
673     *gl_major = maj;
674   if (gl_minor)
675     *gl_minor = min;
676
677   return TRUE;
678 }
679
680 static GstGLAPI
681 _compiled_api (void)
682 {
683   GstGLAPI ret = GST_GL_API_NONE;
684
685 #if GST_GL_HAVE_OPENGL
686   ret |= GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
687 #endif
688 #if GST_GL_HAVE_GLES2
689   ret |= GST_GL_API_GLES2;
690 #endif
691
692   return ret;
693 }
694
695 static void
696 _unlock_create_thread (GstGLContext * context)
697 {
698   g_mutex_unlock (&context->priv->render_lock);
699 }
700
701 static GString *
702 _build_extension_string (GstGLContext * context)
703 {
704   const GstGLFuncs *gl = context->gl_vtable;
705   GString *ext_g_str = g_string_sized_new (1024);
706   const gchar *ext_const_c_str = NULL;
707   int i, n;
708
709   gl->GetIntegerv (GL_NUM_EXTENSIONS, &n);
710
711   for (i = 0; i < n; i++) {
712     ext_const_c_str = (const gchar *) gl->GetStringi (GL_EXTENSIONS, i);
713     if (ext_const_c_str)
714       g_string_append_printf (ext_g_str, "%s ", ext_const_c_str);
715   }
716
717   return ext_g_str;
718 }
719
720 //gboolean
721 //gst_gl_context_create (GstGLContext * context, GstGLContext * other_context, GError ** error)
722 static gpointer
723 gst_gl_context_create_thread (GstGLContext * context)
724 {
725   GstGLContextClass *context_class;
726   GstGLWindowClass *window_class;
727   GstGLFuncs *gl;
728   gboolean ret = FALSE;
729   GstGLAPI compiled_api, user_api, gl_api;
730   gchar *api_string;
731   gchar *compiled_api_s;
732   gchar *user_api_string;
733   const gchar *user_choice;
734   GError **error;
735   GstGLContext *other_context;
736   GString *ext_g_str = NULL;
737   const gchar *ext_const_c_str = NULL;
738
739   g_mutex_lock (&context->priv->render_lock);
740
741   error = context->priv->error;
742   other_context = context->priv->other_context;
743
744   context_class = GST_GL_CONTEXT_GET_CLASS (context);
745   window_class = GST_GL_WINDOW_GET_CLASS (context->window);
746
747   if (window_class->open) {
748     if (!window_class->open (context->window, error)) {
749       g_assert (error == NULL || *error != NULL);
750       goto failure;
751     }
752   }
753
754   gl = context->gl_vtable;
755   compiled_api = _compiled_api ();
756
757   user_choice = g_getenv ("GST_GL_API");
758
759   user_api = gst_gl_api_from_string (user_choice);
760   user_api_string = gst_gl_api_to_string (user_api);
761
762   compiled_api_s = gst_gl_api_to_string (compiled_api);
763
764   if ((user_api & compiled_api) == GST_GL_API_NONE) {
765     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_WRONG_API,
766         "Cannot create context with the user requested api (%s).  "
767         "We have support for (%s)", user_api_string, compiled_api_s);
768     g_free (user_api_string);
769     g_free (compiled_api_s);
770     goto failure;
771   }
772
773   if (context_class->choose_format &&
774       !context_class->choose_format (context, error)) {
775     g_assert (error == NULL || *error != NULL);
776     g_free (compiled_api_s);
777     g_free (user_api_string);
778     goto failure;
779   }
780
781   GST_INFO ("Attempting to create opengl context. user chosen api(s) (%s), "
782       "compiled api support (%s)", user_api_string, compiled_api_s);
783
784   if (!context_class->create_context (context, compiled_api & user_api,
785           other_context, error)) {
786     g_assert (error == NULL || *error != NULL);
787     g_free (compiled_api_s);
788     g_free (user_api_string);
789     goto failure;
790   }
791   GST_INFO ("created context");
792
793   if (!context_class->activate (context, TRUE)) {
794     g_set_error (error, GST_GL_CONTEXT_ERROR,
795         GST_GL_CONTEXT_ERROR_RESOURCE_UNAVAILABLE,
796         "Failed to activate the GL Context");
797     g_free (compiled_api_s);
798     g_free (user_api_string);
799     goto failure;
800   }
801
802   gl_api = gst_gl_context_get_gl_api (context);
803   g_assert (gl_api != GST_GL_API_NONE && gl_api != GST_GL_API_ANY);
804
805   api_string = gst_gl_api_to_string (gl_api);
806   GST_INFO ("available GL APIs: %s", api_string);
807
808   if (((compiled_api & gl_api) & user_api) == GST_GL_API_NONE) {
809     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_WRONG_API,
810         "failed to create context, context "
811         "could not provide correct api. user (%s), compiled (%s), context (%s)",
812         user_api_string, compiled_api_s, api_string);
813     g_free (api_string);
814     g_free (compiled_api_s);
815     g_free (user_api_string);
816     goto failure;
817   }
818
819   g_free (api_string);
820   g_free (compiled_api_s);
821   g_free (user_api_string);
822
823   gl->GetError = gst_gl_context_get_proc_address (context, "glGetError");
824   gl->GetString = gst_gl_context_get_proc_address (context, "glGetString");
825   gl->GetStringi = gst_gl_context_get_proc_address (context, "glGetStringi");
826   gl->GetIntegerv = gst_gl_context_get_proc_address (context, "glGetIntegerv");
827
828   if (!gl->GetError || !gl->GetString) {
829     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
830         "could not GetProcAddress core opengl functions");
831     goto failure;
832   }
833
834   /* gl api specific code */
835   if (!ret && gl_api & GST_GL_API_OPENGL)
836     ret = _create_context_opengl (context, &context->priv->gl_major,
837         &context->priv->gl_minor, error);
838   if (!ret && gl_api & GST_GL_API_GLES2)
839     ret =
840         _create_context_gles2 (context, &context->priv->gl_major,
841         &context->priv->gl_minor, error);
842
843   if (!ret) {
844     g_assert (error == NULL || *error != NULL);
845     goto failure;
846   }
847
848   /* GL core contexts and GLES3 */
849   if (gl->GetIntegerv && gl->GetStringi && context->priv->gl_major >= 3)
850     ext_g_str = _build_extension_string (context);
851
852   if (ext_g_str && ext_g_str->len) {
853     GST_DEBUG_OBJECT (context, "GL_EXTENSIONS: %s", ext_g_str->str);
854     _gst_gl_feature_check_ext_functions (context, context->priv->gl_major,
855         context->priv->gl_minor, ext_g_str->str);
856
857     context->priv->gl_exts = g_string_free (ext_g_str, FALSE);
858   } else {
859     ext_const_c_str = (const gchar *) gl->GetString (GL_EXTENSIONS);
860     if (!ext_const_c_str)
861       ext_const_c_str = "";
862
863     GST_DEBUG_OBJECT (context, "GL_EXTENSIONS: %s", ext_const_c_str);
864     _gst_gl_feature_check_ext_functions (context, context->priv->gl_major,
865         context->priv->gl_minor, ext_const_c_str);
866
867     context->priv->gl_exts = g_strdup (ext_const_c_str);
868   }
869
870   context->priv->alive = TRUE;
871
872   g_cond_signal (&context->priv->create_cond);
873
874 //  g_mutex_unlock (&context->priv->render_lock);
875   gst_gl_window_send_message_async (context->window,
876       (GstGLWindowCB) _unlock_create_thread, context, NULL);
877
878   gst_gl_window_run (context->window);
879
880   GST_INFO ("loop exited\n");
881
882   g_mutex_lock (&context->priv->render_lock);
883
884   context->priv->alive = FALSE;
885
886   context_class->activate (context, FALSE);
887
888   context_class->destroy_context (context);
889
890   /* User supplied callback */
891   if (context->window->close)
892     context->window->close (context->window->close_data);
893
894   /* window specific shutdown */
895   if (window_class->close) {
896     window_class->close (context->window);
897   }
898
899   g_cond_signal (&context->priv->destroy_cond);
900
901   g_mutex_unlock (&context->priv->render_lock);
902
903   return NULL;
904
905 failure:
906   {
907     g_cond_signal (&context->priv->create_cond);
908     g_mutex_unlock (&context->priv->render_lock);
909     return NULL;
910   }
911 }
912
913 /**
914  * gst_gl_context_get_gl_context:
915  * @context: a #GstGLContext:
916  *
917  * Gets the backing OpenGL context used by @context.
918  *
919  * Returns: The platform specific backing OpenGL context
920  */
921 guintptr
922 gst_gl_context_get_gl_context (GstGLContext * context)
923 {
924   GstGLContextClass *context_class;
925   guintptr result;
926
927   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), 0);
928   context_class = GST_GL_CONTEXT_GET_CLASS (context);
929   g_return_val_if_fail (context_class->get_gl_context != NULL, 0);
930
931   result = context_class->get_gl_context (context);
932
933   return result;
934 }
935
936 /**
937  * gst_gl_context_get_gl_platform:
938  * @context: a #GstGLContext:
939  *
940  * Gets the OpenGL platform that used by @context.
941  *
942  * Returns: The platform specific backing OpenGL context
943  */
944 GstGLPlatform
945 gst_gl_context_get_gl_platform (GstGLContext * context)
946 {
947   GstGLContextClass *context_class;
948
949   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), 0);
950   context_class = GST_GL_CONTEXT_GET_CLASS (context);
951   g_return_val_if_fail (context_class->get_gl_platform != NULL, 0);
952
953   return context_class->get_gl_platform (context);
954 }
955
956 /**
957  * gst_gl_context_get_display:
958  * @context: a #GstGLContext:
959  *
960  * Returns: the #GstGLDisplay associated with this @context
961  */
962 GstGLDisplay *
963 gst_gl_context_get_display (GstGLContext * context)
964 {
965   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), NULL);
966
967   return gst_object_ref (context->priv->display);
968 }
969
970 typedef struct
971 {
972   GstGLContext *context;
973   GstGLContextThreadFunc func;
974   gpointer data;
975 } RunGenericData;
976
977 static void
978 _gst_gl_context_thread_run_generic (RunGenericData * data)
979 {
980   GST_TRACE ("running function:%p data:%p", data->func, data->data);
981
982   data->func (data->context, data->data);
983 }
984
985 /**
986  * gst_gl_context_thread_add:
987  * @context: a #GstGLContext
988  * @func: a #GstGLContextThreadFunc
989  * @data: (closure): user data to call @func with
990  *
991  * Execute @func in the OpenGL thread of @context with @data
992  *
993  * MT-safe
994  */
995 void
996 gst_gl_context_thread_add (GstGLContext * context,
997     GstGLContextThreadFunc func, gpointer data)
998 {
999   GstGLWindow *window;
1000   RunGenericData rdata;
1001
1002   g_return_if_fail (GST_GL_IS_CONTEXT (context));
1003   g_return_if_fail (func != NULL);
1004   g_return_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context));
1005
1006   rdata.context = context;
1007   rdata.data = data;
1008   rdata.func = func;
1009
1010   window = gst_gl_context_get_window (context);
1011
1012   gst_gl_window_send_message (window,
1013       GST_GL_WINDOW_CB (_gst_gl_context_thread_run_generic), &rdata);
1014
1015   gst_object_unref (window);
1016 }
1017
1018 /**
1019  * gst_gl_context_get_gl_version:
1020  * @context: a #GstGLContext
1021  * @maj: (out): resulting major version
1022  * @min: (out): resulting minor version
1023  *
1024  * Returns the OpenGL version implemented by @context.  See
1025  * gst_gl_context_get_gl_api() for retreiving the OpenGL api implemented by
1026  * @context.
1027  */
1028 void
1029 gst_gl_context_get_gl_version (GstGLContext * context, gint * maj, gint * min)
1030 {
1031   g_return_if_fail (GST_GL_IS_CONTEXT (context));
1032   g_return_if_fail (maj == NULL && min == NULL);
1033
1034   if (maj)
1035     *maj = context->priv->gl_major;
1036
1037   if (min)
1038     *min = context->priv->gl_minor;
1039 }
1040
1041 /**
1042  * gst_gl_context_check_feature:
1043  * @context: a #GstGLContext
1044  * @feature: a platform specific feature
1045  *
1046  * Some features require that the context be created before it is possible to
1047  * determine their existence and so will fail if that is not the case.
1048  *
1049  * Returns: Whether @feature is supported by @context
1050  */
1051 gboolean
1052 gst_gl_context_check_feature (GstGLContext * context, const gchar * feature)
1053 {
1054   GstGLContextClass *context_class;
1055
1056   g_return_val_if_fail (GST_GL_IS_CONTEXT (context), FALSE);
1057   g_return_val_if_fail (feature != NULL, FALSE);
1058
1059   context_class = GST_GL_CONTEXT_GET_CLASS (context);
1060
1061   if (g_strstr_len (feature, 3, "GL_"))
1062     return gst_gl_check_extension (feature, context->priv->gl_exts);
1063
1064   if (!context_class->check_feature)
1065     return FALSE;
1066
1067   return context_class->check_feature (context, feature);
1068 }
1069
1070 static GstGLAPI
1071 gst_gl_wrapped_context_get_gl_api (GstGLContext * context)
1072 {
1073   GstGLWrappedContext *context_wrap = GST_GL_WRAPPED_CONTEXT (context);
1074
1075   return context_wrap->available_apis;
1076 }
1077
1078 static guintptr
1079 gst_gl_wrapped_context_get_gl_context (GstGLContext * context)
1080 {
1081   GstGLWrappedContext *context_wrap = GST_GL_WRAPPED_CONTEXT (context);
1082
1083   return context_wrap->handle;
1084 }
1085
1086 static GstGLPlatform
1087 gst_gl_wrapped_context_get_gl_platform (GstGLContext * context)
1088 {
1089   GstGLWrappedContext *context_wrap = GST_GL_WRAPPED_CONTEXT (context);
1090
1091   return context_wrap->platform;
1092 }
1093
1094 static gboolean
1095 gst_gl_wrapped_context_activate (GstGLContext * context, gboolean activate)
1096 {
1097   g_assert_not_reached ();
1098
1099   return FALSE;
1100 }
1101
1102 static void
1103 gst_gl_wrapped_context_class_init (GstGLWrappedContextClass * klass)
1104 {
1105   GstGLContextClass *context_class = (GstGLContextClass *) klass;
1106
1107   context_class->get_gl_context =
1108       GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_get_gl_context);
1109   context_class->get_gl_api =
1110       GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_get_gl_api);
1111   context_class->get_gl_platform =
1112       GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_get_gl_platform);
1113   context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_activate);
1114 }
1115
1116 static void
1117 gst_gl_wrapped_context_init (GstGLWrappedContext * context)
1118 {
1119 }