libs: use glib >= 2.32 semantics for mutexes.
[vaapi:gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiutils_glx.c
1 /*
2  *  gstvaapiutils_glx.c - GLX utilties
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-2012 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 #define _GNU_SOURCE 1 /* RTLD_DEFAULT */
24 #include "sysdeps.h"
25 #include <string.h>
26 #include <math.h>
27 #include <dlfcn.h>
28 #include "gstvaapiutils_glx.h"
29 #include "gstvaapiutils_x11.h"
30
31 #define DEBUG 1
32 #include "gstvaapidebug.h"
33
34 /** Lookup for substring NAME in string EXT using SEP as separators */
35 static gboolean
36 find_string(const char *name, const char *ext, const char *sep)
37 {
38     const char *end;
39     int name_len, n;
40
41     if (!name || !ext)
42         return FALSE;
43
44     end = ext + strlen(ext);
45     name_len = strlen(name);
46     while (ext < end) {
47         n = strcspn(ext, sep);
48         if (n == name_len && strncmp(name, ext, n) == 0)
49             return TRUE;
50         ext += (n + 1);
51     }
52     return FALSE;
53 }
54
55 /**
56  * gl_get_error_string:
57  * @error: an OpenGL error enumeration
58  *
59  * Retrieves the string representation the OpenGL @error.
60  *
61  * Return error: the static string representing the OpenGL @error
62  */
63 const char *
64 gl_get_error_string(GLenum error)
65 {
66     switch (error) {
67 #define MAP(id, str) \
68         case id: return str " (" #id ")"
69         MAP(GL_NO_ERROR,                "no error");
70         MAP(GL_INVALID_ENUM,            "invalid enumerant");
71         MAP(GL_INVALID_VALUE,           "invalid value");
72         MAP(GL_INVALID_OPERATION,       "invalid operation");
73         MAP(GL_STACK_OVERFLOW,          "stack overflow");
74         MAP(GL_STACK_UNDERFLOW,         "stack underflow");
75         MAP(GL_OUT_OF_MEMORY,           "out of memory");
76 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT
77         MAP(GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
78             "invalid framebuffer operation");
79 #endif
80 #undef MAP
81     default: break;
82     };
83     return "<unknown>";
84 }
85
86 /**
87  * gl_purge_errors:
88  *
89  * Purges all OpenGL errors. This function is generally useful to
90  * clear up the pending errors prior to calling gl_check_error().
91  */
92 void
93 gl_purge_errors(void)
94 {
95     while (glGetError() != GL_NO_ERROR)
96         ; /* nothing */
97 }
98
99 /**
100  * gl_check_error:
101  *
102  * Checks whether there is any OpenGL error pending.
103  *
104  * Return value: %TRUE if an error was encountered
105  */
106 gboolean
107 gl_check_error(void)
108 {
109     GLenum error;
110     gboolean has_errors = FALSE;
111
112     while ((error = glGetError()) != GL_NO_ERROR) {
113         GST_DEBUG("glError: %s caught", gl_get_error_string(error));
114         has_errors = TRUE;
115     }
116     return has_errors;
117 }
118
119 /**
120  * gl_get_param:
121  * @param: the parameter name
122  * @pval: return location for the value
123  *
124  * This function is a wrapper around glGetIntegerv() that does extra
125  * error checking.
126  *
127  * Return value: %TRUE on success
128  */
129 gboolean
130 gl_get_param(GLenum param, guint *pval)
131 {
132     GLint val;
133
134     gl_purge_errors();
135     glGetIntegerv(param, &val);
136     if (gl_check_error())
137         return FALSE;
138
139     if (pval)
140         *pval = val;
141     return TRUE;
142 }
143
144 /**
145  * gl_get_texture_param:
146  * @target: the target to which the texture is bound
147  * @param: the parameter name
148  * @pval: return location for the value
149  *
150  * This function is a wrapper around glGetTexLevelParameteriv() that
151  * does extra error checking.
152  *
153  * Return value: %TRUE on success
154  */
155 gboolean
156 gl_get_texture_param(GLenum target, GLenum param, guint *pval)
157 {
158     GLint val;
159
160     gl_purge_errors();
161     glGetTexLevelParameteriv(target, 0, param, &val);
162     if (gl_check_error())
163         return FALSE;
164
165     if (pval)
166         *pval = val;
167     return TRUE;
168 }
169
170 /**
171  * gl_get_texture_binding:
172  * @target: a texture target
173  *
174  * Determines the texture binding type for the specified target.
175  *
176  * Return value: texture binding type for @target
177  */
178 static GLenum
179 gl_get_texture_binding(GLenum target)
180 {
181     GLenum binding;
182
183     switch (target) {
184     case GL_TEXTURE_1D:
185         binding = GL_TEXTURE_BINDING_1D;
186         break;
187     case GL_TEXTURE_2D:
188         binding = GL_TEXTURE_BINDING_2D;
189         break;
190     case GL_TEXTURE_3D:
191         binding = GL_TEXTURE_BINDING_3D;
192         break;
193     case GL_TEXTURE_RECTANGLE_ARB:
194         binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
195         break;
196     default:
197         binding = 0;
198         break;
199     }
200     return binding;
201 }
202
203 /**
204  * gl_set_bgcolor:
205  * @color: the requested RGB color
206  *
207  * Sets background color to the RGB @color. This basically is a
208  * wrapper around glClearColor().
209  */
210 void
211 gl_set_bgcolor(guint32 color)
212 {
213     glClearColor(
214         ((color >> 16) & 0xff) / 255.0f,
215         ((color >>  8) & 0xff) / 255.0f,
216         ( color        & 0xff) / 255.0f,
217         1.0f
218     );
219 }
220
221 /**
222  * gl_perspective:
223  * @fovy: the field of view angle, in degrees, in the y direction
224  * @aspect: the aspect ratio that determines the field of view in the
225  *   x direction.  The aspect ratio is the ratio of x (width) to y
226  *   (height)
227  * @zNear: the distance from the viewer to the near clipping plane
228  *   (always positive)
229  * @zFar: the distance from the viewer to the far clipping plane
230  *   (always positive)
231  *
232  * Specified a viewing frustum into the world coordinate system. This
233  * basically is the Mesa implementation of gluPerspective().
234  */
235 static void
236 gl_perspective(GLdouble fovy, GLdouble aspect, GLdouble near_val, GLdouble far_val)
237 {
238     GLdouble left, right, top, bottom;
239
240     /* Source (Q 9.085):
241        <http://www.opengl.org/resources/faq/technical/transformations.htm> */
242     top    = tan(fovy * M_PI / 360.0) * near_val;
243     bottom = -top;
244     left   = aspect * bottom;
245     right  = aspect * top;
246     glFrustum(left, right, bottom, top, near_val, far_val);
247 }
248
249 /**
250  * gl_resize:
251  * @width: the requested width, in pixels
252  * @height: the requested height, in pixels
253  *
254  * Resizes the OpenGL viewport to the specified dimensions, using an
255  * orthogonal projection. (0,0) represents the top-left corner of the
256  * window.
257  */
258 void
259 gl_resize(guint width, guint height)
260 {
261 #define FOVY     60.0f
262 #define ASPECT   1.0f
263 #define Z_NEAR   0.1f
264 #define Z_FAR    100.0f
265 #define Z_CAMERA 0.869f
266
267     glViewport(0, 0, width, height);
268     glMatrixMode(GL_PROJECTION);
269     glLoadIdentity();
270     gl_perspective(FOVY, ASPECT, Z_NEAR, Z_FAR);
271     glMatrixMode(GL_MODELVIEW);
272     glLoadIdentity();
273
274     glTranslatef(-0.5f, -0.5f, -Z_CAMERA);
275     glScalef(1.0f/width, -1.0f/height, 1.0f/width);
276     glTranslatef(0.0f, -1.0f*height, 0.0f);
277 }
278
279 /**
280  * gl_create_context:
281  * @dpy: an X11 #Display
282  * @screen: the associated screen of @dpy
283  * @parent: the parent #GLContextState, or %NULL if none is to be used
284  *
285  * Creates a GLX context sharing textures and displays lists with
286  * @parent, if not %NULL.
287  *
288  * Return value: the newly created GLX context
289  */
290 GLContextState *
291 gl_create_context(Display *dpy, int screen, GLContextState *parent)
292 {
293     GLContextState *cs;
294     GLXFBConfig *fbconfigs = NULL;
295     int fbconfig_id, val, n, n_fbconfigs;
296     Status status;
297
298     static GLint fbconfig_attrs[] = {
299         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
300         GLX_RENDER_TYPE,   GLX_RGBA_BIT,
301         GLX_DOUBLEBUFFER,  True,
302         GLX_RED_SIZE,      8,
303         GLX_GREEN_SIZE,    8, 
304         GLX_BLUE_SIZE,     8,
305         None
306     };
307
308     cs = malloc(sizeof(*cs));
309     if (!cs)
310         goto error;
311
312     if (parent) {
313         cs->display     = parent->display;
314         cs->window      = parent->window;
315         screen          = DefaultScreen(parent->display);
316     }
317     else {
318         cs->display     = dpy;
319         cs->window      = None;
320     }
321     cs->visual          = NULL;
322     cs->context         = NULL;
323     cs->swapped_buffers = FALSE;
324
325     if (parent && parent->context) {
326         status = glXQueryContext(
327             parent->display,
328             parent->context,
329             GLX_FBCONFIG_ID, &fbconfig_id
330         );
331         if (status != Success)
332             goto error;
333
334         if (fbconfig_id == GLX_DONT_CARE)
335             goto choose_fbconfig;
336
337         fbconfigs = glXGetFBConfigs(parent->display, screen, &n_fbconfigs);
338         if (!fbconfigs)
339             goto error;
340
341         /* Find out a GLXFBConfig compatible with the parent context */
342         for (n = 0; n < n_fbconfigs; n++) {
343             status = glXGetFBConfigAttrib(
344                 parent->display,
345                 fbconfigs[n],
346                 GLX_FBCONFIG_ID, &val
347             );
348             if (status == Success && val == fbconfig_id)
349                 break;
350         }
351         if (n == n_fbconfigs)
352             goto error;
353     }
354     else {
355     choose_fbconfig:
356         fbconfigs = glXChooseFBConfig(
357             cs->display,
358             screen,
359             fbconfig_attrs, &n_fbconfigs
360         );
361         if (!fbconfigs)
362             goto error;
363
364         /* Select the first one */
365         n = 0;
366     }
367
368     cs->visual  = glXGetVisualFromFBConfig(cs->display, fbconfigs[n]);
369     cs->context = glXCreateNewContext(
370         cs->display,
371         fbconfigs[n],
372         GLX_RGBA_TYPE,
373         parent ? parent->context : NULL,
374         True
375     );
376     if (cs->context)
377         goto end;
378
379 error:
380     gl_destroy_context(cs);
381     cs = NULL;
382 end:
383     if (fbconfigs)
384         XFree(fbconfigs);
385     return cs;
386 }
387
388 /**
389  * gl_destroy_context:
390  * @cs: a #GLContextState
391  *
392  * Destroys the GLX context @cs
393  */
394 void
395 gl_destroy_context(GLContextState *cs)
396 {
397     if (!cs)
398         return;
399
400     if (cs->visual) {
401         XFree(cs->visual);
402         cs->visual = NULL;
403     }
404
405     if (cs->display && cs->context) {
406         if (glXGetCurrentContext() == cs->context) {
407             /* XXX: if buffers were never swapped, the application
408                will crash later with the NVIDIA driver */
409             if (!cs->swapped_buffers)
410                 gl_swap_buffers(cs);
411             glXMakeCurrent(cs->display, None, NULL);
412         }
413         glXDestroyContext(cs->display, cs->context);
414         cs->display = NULL;
415         cs->context = NULL;
416     }
417     free(cs);
418 }
419
420 /**
421  * gl_get_current_context:
422  * @cs: return location to the current #GLContextState
423  *
424  * Retrieves the current GLX context, display and drawable packed into
425  * the #GLContextState struct.
426  */
427 void
428 gl_get_current_context(GLContextState *cs)
429 {
430     cs->display = glXGetCurrentDisplay();
431     cs->window  = glXGetCurrentDrawable();
432     cs->context = glXGetCurrentContext();
433 }
434
435 /**
436  * gl_set_current_context:
437  * @new_cs: the requested new #GLContextState
438  * @old_cs: return location to the context that was previously current
439  *
440  * Makes the @new_cs GLX context the current GLX rendering context of
441  * the calling thread, replacing the previously current context if
442  * there was one.
443  *
444  * If @old_cs is non %NULL, the previously current GLX context and
445  * window are recorded.
446  *
447  * Return value: %TRUE on success
448  */
449 gboolean
450 gl_set_current_context(GLContextState *new_cs, GLContextState *old_cs)
451 {
452     /* If display is NULL, this could be that new_cs was retrieved from
453        gl_get_current_context() with none set previously. If that case,
454        the other fields are also NULL and we don't return an error */
455     if (!new_cs->display)
456         return !new_cs->window && !new_cs->context;
457
458     if (old_cs) {
459         if (old_cs == new_cs)
460             return TRUE;
461         gl_get_current_context(old_cs);
462         if (old_cs->display == new_cs->display &&
463             old_cs->window  == new_cs->window  &&
464             old_cs->context == new_cs->context)
465             return TRUE;
466     }
467     return glXMakeCurrent(new_cs->display, new_cs->window, new_cs->context);
468 }
469
470 /**
471  * gl_swap_buffers:
472  * @cs: a #GLContextState
473  *
474  * Promotes the contents of the back buffer of the @win window to
475  * become the contents of the front buffer. This simply is wrapper
476  * around glXSwapBuffers().
477  */
478 void
479 gl_swap_buffers(GLContextState *cs)
480 {
481     glXSwapBuffers(cs->display, cs->window);
482     cs->swapped_buffers = TRUE;
483 }
484
485 /**
486  * gl_bind_texture:
487  * @ts: a #GLTextureState
488  * @target: the target to which the texture is bound
489  * @texture: the name of a texture
490  *
491  * Binds @texture to the specified @target, while recording the
492  * previous state in @ts.
493  *
494  * Return value: %TRUE on success
495  */
496 gboolean
497 gl_bind_texture(GLTextureState *ts, GLenum target, GLuint texture)
498 {
499     GLenum binding;
500
501     ts->target = target;
502
503     if (glIsEnabled(target)) {
504         binding = gl_get_texture_binding(target);
505         if (!binding)
506             return FALSE;
507         if (!gl_get_param(binding, &ts->old_texture))
508             return FALSE;
509         ts->was_enabled = TRUE;
510         ts->was_bound   = texture == ts->old_texture;
511         if (ts->was_bound)
512             return TRUE;
513     }
514     else {
515         glEnable(target);
516         ts->old_texture = 0;
517         ts->was_enabled = FALSE;
518         ts->was_bound   = FALSE;
519     }
520
521     gl_purge_errors();
522     glBindTexture(target, texture);
523     if (gl_check_error())
524         return FALSE;
525     return TRUE;
526 }
527
528 /**
529  * gl_unbind_texture:
530  * @ts: a #GLTextureState
531  *
532  * Rebinds the texture that was previously bound and recorded in @ts.
533  */
534 void
535 gl_unbind_texture(GLTextureState *ts)
536 {
537     if (!ts->was_bound && ts->old_texture)
538         glBindTexture(ts->target, ts->old_texture);
539     if (!ts->was_enabled)
540         glDisable(ts->target);
541 }
542
543 /**
544  * gl_create_texture:
545  * @target: the target to which the texture is bound
546  * @format: the format of the pixel data
547  * @width: the requested width, in pixels
548  * @height: the requested height, in pixels
549  *
550  * Creates a texture with the specified dimensions and @format. The
551  * internal format will be automatically derived from @format.
552  *
553  * Return value: the newly created texture name
554  */
555 GLuint
556 gl_create_texture(GLenum target, GLenum format, guint width, guint height)
557 {
558     GLenum internal_format;
559     GLuint texture;
560     GLTextureState ts;
561     guint bytes_per_component;
562
563     internal_format = format;
564     switch (format) {
565     case GL_LUMINANCE:
566         bytes_per_component = 1;
567         break;
568     case GL_LUMINANCE_ALPHA:
569         bytes_per_component = 2;
570         break;
571     case GL_RGBA:
572     case GL_BGRA:
573         internal_format = GL_RGBA;
574         bytes_per_component = 4;
575         break;
576     default:
577         bytes_per_component = 0;
578         break;
579     }
580     g_assert(bytes_per_component > 0);
581
582     glGenTextures(1, &texture);
583     if (!gl_bind_texture(&ts, target, texture))
584         return 0;
585     glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
586     glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
587     glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
588     glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
589     glPixelStorei(GL_UNPACK_ALIGNMENT, bytes_per_component);
590     glTexImage2D(
591         target,
592         0,
593         internal_format,
594         width, height,
595         0,
596         format,
597         GL_UNSIGNED_BYTE,
598         NULL
599     );
600     gl_unbind_texture(&ts);
601     return texture;
602 }
603
604 /**
605  * get_proc_address:
606  * @name: the name of the OpenGL extension function to lookup
607  *
608  * Returns the specified OpenGL extension function
609  *
610  * Return value: the OpenGL extension matching @name, or %NULL if none
611  *   was found
612  */
613 typedef void (*GLFuncPtr)(void);
614 typedef GLFuncPtr (*GLXGetProcAddressProc)(const char *);
615
616 static GLFuncPtr
617 get_proc_address_default(const char *name)
618 {
619     return NULL;
620 }
621
622 static GLXGetProcAddressProc
623 get_proc_address_func(void)
624 {
625     GLXGetProcAddressProc get_proc_func;
626
627     dlerror();
628     *(void **)(&get_proc_func) = dlsym(RTLD_DEFAULT, "glXGetProcAddress");
629     if (!dlerror())
630         return get_proc_func;
631
632     *(void **)(&get_proc_func) = dlsym(RTLD_DEFAULT, "glXGetProcAddressARB");
633     if (!dlerror())
634         return get_proc_func;
635
636     return get_proc_address_default;
637 }
638
639 static inline GLFuncPtr
640 get_proc_address(const char *name)
641 {
642     static GLXGetProcAddressProc get_proc_func = NULL;
643     if (!get_proc_func)
644         get_proc_func = get_proc_address_func();
645     return get_proc_func(name);
646 }
647
648 /**
649  * gl_init_vtable:
650  *
651  * Initializes the global #GLVTable.
652  *
653  * Return value: the #GLVTable filled in with OpenGL extensions, or
654  *   %NULL on error.
655  */
656 static GLVTable gl_vtable_static;
657
658 static GLVTable *
659 gl_init_vtable(void)
660 {
661     GLVTable * const gl_vtable = &gl_vtable_static;
662     const gchar *gl_extensions = (const gchar *)glGetString(GL_EXTENSIONS);
663     gboolean has_extension;
664
665     /* GLX_EXT_texture_from_pixmap */
666     gl_vtable->glx_create_pixmap = (PFNGLXCREATEPIXMAPPROC)
667         get_proc_address("glXCreatePixmap");
668     if (!gl_vtable->glx_create_pixmap)
669         return NULL;
670     gl_vtable->glx_destroy_pixmap = (PFNGLXDESTROYPIXMAPPROC)
671         get_proc_address("glXDestroyPixmap");
672     if (!gl_vtable->glx_destroy_pixmap)
673         return NULL;
674     gl_vtable->glx_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC)
675         get_proc_address("glXBindTexImageEXT");
676     if (!gl_vtable->glx_bind_tex_image)
677         return NULL;
678     gl_vtable->glx_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC)
679         get_proc_address("glXReleaseTexImageEXT");
680     if (!gl_vtable->glx_release_tex_image)
681         return NULL;
682
683     /* GL_ARB_framebuffer_object */
684     has_extension = (
685         find_string("GL_ARB_framebuffer_object", gl_extensions, " ") ||
686         find_string("GL_EXT_framebuffer_object", gl_extensions, " ")
687     );
688     if (has_extension) {
689         gl_vtable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
690             get_proc_address("glGenFramebuffersEXT");
691         if (!gl_vtable->gl_gen_framebuffers)
692             return NULL;
693         gl_vtable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
694             get_proc_address("glDeleteFramebuffersEXT");
695         if (!gl_vtable->gl_delete_framebuffers)
696             return NULL;
697         gl_vtable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
698             get_proc_address("glBindFramebufferEXT");
699         if (!gl_vtable->gl_bind_framebuffer)
700             return NULL;
701         gl_vtable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
702             get_proc_address("glGenRenderbuffersEXT");
703         if (!gl_vtable->gl_gen_renderbuffers)
704             return NULL;
705         gl_vtable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
706             get_proc_address("glDeleteRenderbuffersEXT");
707         if (!gl_vtable->gl_delete_renderbuffers)
708             return NULL;
709         gl_vtable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
710             get_proc_address("glBindRenderbufferEXT");
711         if (!gl_vtable->gl_bind_renderbuffer)
712             return NULL;
713         gl_vtable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
714             get_proc_address("glRenderbufferStorageEXT");
715         if (!gl_vtable->gl_renderbuffer_storage)
716             return NULL;
717         gl_vtable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
718             get_proc_address("glFramebufferRenderbufferEXT");
719         if (!gl_vtable->gl_framebuffer_renderbuffer)
720             return NULL;
721         gl_vtable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
722             get_proc_address("glFramebufferTexture2DEXT");
723         if (!gl_vtable->gl_framebuffer_texture_2d)
724             return NULL;
725         gl_vtable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
726             get_proc_address("glCheckFramebufferStatusEXT");
727         if (!gl_vtable->gl_check_framebuffer_status)
728             return NULL;
729         gl_vtable->has_framebuffer_object = TRUE;
730     }
731     return gl_vtable;
732 }
733
734 /**
735  * gl_get_vtable:
736  *
737  * Retrieves a VTable for OpenGL extensions.
738  *
739  * Return value: VTable for OpenGL extensions
740  */
741 GLVTable *
742 gl_get_vtable(void)
743 {
744     static gsize        gl_vtable_init = FALSE;
745     static GLVTable    *gl_vtable      = NULL;
746
747     if (g_once_init_enter(&gl_vtable_init)) {
748         gl_vtable = gl_init_vtable();
749         g_once_init_leave(&gl_vtable_init, TRUE);
750     }
751     return gl_vtable;
752 }
753
754 /**
755  * gl_create_pixmap_object:
756  * @dpy: an X11 #Display
757  * @width: the request width, in pixels
758  * @height: the request height, in pixels
759  *
760  * Creates a #GLPixmapObject of the specified dimensions. This
761  * requires the GLX_EXT_texture_from_pixmap extension.
762  *
763  * Return value: the newly created #GLPixmapObject object
764  */
765 GLPixmapObject *
766 gl_create_pixmap_object(Display *dpy, guint width, guint height)
767 {
768     GLVTable * const    gl_vtable = gl_get_vtable();
769     GLPixmapObject     *pixo;
770     GLXFBConfig        *fbconfig;
771     int                 screen;
772     Window              rootwin;
773     XWindowAttributes   wattr;
774     int                *attr;
775     int                 n_fbconfig_attrs;
776
777     int fbconfig_attrs[32] = {
778         GLX_DRAWABLE_TYPE,      GLX_PIXMAP_BIT,
779         GLX_DOUBLEBUFFER,       GL_FALSE,
780         GLX_RENDER_TYPE,        GLX_RGBA_BIT,
781         GLX_X_RENDERABLE,       GL_TRUE,
782         GLX_Y_INVERTED_EXT,     GL_TRUE,
783         GLX_RED_SIZE,           8,
784         GLX_GREEN_SIZE,         8,
785         GLX_BLUE_SIZE,          8,
786         GL_NONE,
787     };
788
789     int pixmap_attrs[10] = {
790         GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
791         GLX_MIPMAP_TEXTURE_EXT, GL_FALSE,
792         GL_NONE,
793     };
794
795     if (!gl_vtable)
796         return NULL;
797
798     screen  = DefaultScreen(dpy);
799     rootwin = RootWindow(dpy, screen);
800
801     /* XXX: this won't work for different displays */
802     if (!gl_vtable->has_texture_from_pixmap) {
803         const char *glx_extensions = glXQueryExtensionsString(dpy, screen);
804         if (!glx_extensions)
805             return NULL;
806         if (!find_string("GLX_EXT_texture_from_pixmap", glx_extensions, " "))
807             return NULL;
808         gl_vtable->has_texture_from_pixmap = TRUE;
809     }
810
811     pixo = calloc(1, sizeof(*pixo));
812     if (!pixo)
813         return NULL;
814
815     pixo->dpy           = dpy;
816     pixo->width         = width;
817     pixo->height        = height;
818     pixo->pixmap        = None;
819     pixo->glx_pixmap    = None;
820     pixo->is_bound      = FALSE;
821
822     XGetWindowAttributes(dpy, rootwin, &wattr);
823     pixo->pixmap  = XCreatePixmap(dpy, rootwin, width, height, wattr.depth);
824     if (!pixo->pixmap)
825         goto error;
826
827     /* Initialize FBConfig attributes */
828     for (attr = fbconfig_attrs; *attr != GL_NONE; attr += 2)
829         ;
830     *attr++ = GLX_DEPTH_SIZE;                 *attr++ = wattr.depth;
831     if (wattr.depth == 32) {
832     *attr++ = GLX_ALPHA_SIZE;                 *attr++ = 8;
833     *attr++ = GLX_BIND_TO_TEXTURE_RGBA_EXT;   *attr++ = GL_TRUE;
834     }
835     else {
836     *attr++ = GLX_BIND_TO_TEXTURE_RGB_EXT;    *attr++ = GL_TRUE;
837     }
838     *attr++ = GL_NONE;
839
840     fbconfig = glXChooseFBConfig(
841         dpy,
842         screen,
843         fbconfig_attrs, &n_fbconfig_attrs
844     );
845     if (!fbconfig)
846         goto error;
847
848     /* Initialize GLX Pixmap attributes */
849     for (attr = pixmap_attrs; *attr != GL_NONE; attr += 2)
850         ;
851     *attr++ = GLX_TEXTURE_FORMAT_EXT;
852     if (wattr.depth == 32)
853     *attr++ = GLX_TEXTURE_FORMAT_RGBA_EXT;
854     else
855     *attr++ = GLX_TEXTURE_FORMAT_RGB_EXT;
856     *attr++ = GL_NONE;
857
858     x11_trap_errors();
859     pixo->glx_pixmap = gl_vtable->glx_create_pixmap(
860         dpy,
861         fbconfig[0],
862         pixo->pixmap,
863         pixmap_attrs
864     );
865     free(fbconfig);
866     if (x11_untrap_errors() != 0)
867         goto error;
868
869     pixo->target = GL_TEXTURE_2D;
870     glGenTextures(1, &pixo->texture);
871     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
872         goto error;
873     glTexParameteri(pixo->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
874     glTexParameteri(pixo->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
875     gl_unbind_texture(&pixo->old_texture);
876     return pixo;
877
878 error:
879     gl_destroy_pixmap_object(pixo);
880     return NULL;
881 }
882
883 /**
884  * gl_destroy_pixmap_object:
885  * @pixo: a #GLPixmapObject
886  *
887  * Destroys the #GLPixmapObject object.
888  */
889 void
890 gl_destroy_pixmap_object(GLPixmapObject *pixo)
891 {
892     GLVTable * const gl_vtable = gl_get_vtable();
893
894     if (!pixo)
895         return;
896
897     gl_unbind_pixmap_object(pixo);
898
899     if (pixo->texture) {
900         glDeleteTextures(1, &pixo->texture);
901         pixo->texture = 0;
902     }
903
904     if (pixo->glx_pixmap) {
905         gl_vtable->glx_destroy_pixmap(pixo->dpy, pixo->glx_pixmap);
906         pixo->glx_pixmap = None;
907     }
908
909     if (pixo->pixmap) {
910         XFreePixmap(pixo->dpy, pixo->pixmap);
911         pixo->pixmap = None;
912     }
913     free(pixo);
914 }
915
916 /**
917  * gl_bind_pixmap_object:
918  * @pixo: a #GLPixmapObject
919  *
920  * Defines a two-dimensional texture image. The texture image is taken
921  * from the @pixo pixmap and need not be copied. The texture target,
922  * format and size are derived from attributes of the @pixo pixmap.
923  *
924  * Return value: %TRUE on success
925  */
926 gboolean
927 gl_bind_pixmap_object(GLPixmapObject *pixo)
928 {
929     GLVTable * const gl_vtable = gl_get_vtable();
930
931     if (pixo->is_bound)
932         return TRUE;
933
934     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
935         return FALSE;
936
937     x11_trap_errors();
938     gl_vtable->glx_bind_tex_image(
939         pixo->dpy,
940         pixo->glx_pixmap,
941         GLX_FRONT_LEFT_EXT,
942         NULL
943     );
944     XSync(pixo->dpy, False);
945     if (x11_untrap_errors() != 0) {
946         GST_DEBUG("failed to bind pixmap");
947         return FALSE;
948     }
949
950     pixo->is_bound = TRUE;
951     return TRUE;
952 }
953
954 /**
955  * gl_unbind_pixmap_object:
956  * @pixo: a #GLPixmapObject
957  *
958  * Releases a color buffers that is being used as a texture.
959  *
960  * Return value: %TRUE on success
961  */
962 gboolean
963 gl_unbind_pixmap_object(GLPixmapObject *pixo)
964 {
965     GLVTable * const gl_vtable = gl_get_vtable();
966
967     if (!pixo->is_bound)
968         return TRUE;
969
970     x11_trap_errors();
971     gl_vtable->glx_release_tex_image(
972         pixo->dpy,
973         pixo->glx_pixmap,
974         GLX_FRONT_LEFT_EXT
975     );
976     XSync(pixo->dpy, False);
977     if (x11_untrap_errors() != 0) {
978         GST_DEBUG("failed to release pixmap");
979         return FALSE;
980     }
981
982     gl_unbind_texture(&pixo->old_texture);
983
984     pixo->is_bound = FALSE;
985     return TRUE;
986 }
987
988 /**
989  * gl_create_framebuffer_object:
990  * @target: the target to which the texture is bound
991  * @texture: the GL texture to hold the framebuffer
992  * @width: the requested width, in pixels
993  * @height: the requested height, in pixels
994  *
995  * Creates an FBO with the specified texture and size.
996  *
997  * Return value: the newly created #GLFramebufferObject, or %NULL if
998  *   an error occurred
999  */
1000 GLFramebufferObject *
1001 gl_create_framebuffer_object(
1002     GLenum target,
1003     GLuint texture,
1004     guint  width,
1005     guint  height
1006 )
1007 {
1008     GLVTable * const gl_vtable = gl_get_vtable();
1009     GLFramebufferObject *fbo;
1010     GLenum status;
1011
1012     if (!gl_vtable || !gl_vtable->has_framebuffer_object)
1013         return NULL;
1014
1015     /* XXX: we only support GL_TEXTURE_2D at this time */
1016     if (target != GL_TEXTURE_2D)
1017         return NULL;
1018
1019     fbo = calloc(1, sizeof(*fbo));
1020     if (!fbo)
1021         return NULL;
1022
1023     fbo->width          = width;
1024     fbo->height         = height;
1025     fbo->fbo            = 0;
1026     fbo->old_fbo        = 0;
1027     fbo->is_bound       = FALSE;
1028
1029     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1030     gl_vtable->gl_gen_framebuffers(1, &fbo->fbo);
1031     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1032     gl_vtable->gl_framebuffer_texture_2d(
1033         GL_FRAMEBUFFER_EXT,
1034         GL_COLOR_ATTACHMENT0_EXT,
1035         target, texture,
1036         0
1037     );
1038
1039     status = gl_vtable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT);
1040     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1041     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
1042         goto error;
1043     return fbo;
1044
1045 error:
1046     gl_destroy_framebuffer_object(fbo);
1047     return NULL;
1048 }
1049
1050 /**
1051  * gl_destroy_framebuffer_object:
1052  * @fbo: a #GLFramebufferObject
1053  *
1054  * Destroys the @fbo object.
1055  */
1056 void
1057 gl_destroy_framebuffer_object(GLFramebufferObject *fbo)
1058 {
1059     GLVTable * const gl_vtable = gl_get_vtable();
1060
1061     if (!fbo)
1062         return;
1063
1064     gl_unbind_framebuffer_object(fbo);
1065
1066     if (fbo->fbo) {
1067         gl_vtable->gl_delete_framebuffers(1, &fbo->fbo);
1068         fbo->fbo = 0;
1069     }
1070     free(fbo);
1071 }
1072
1073 /**
1074  * gl_bind_framebuffer_object:
1075  * @fbo: a #GLFramebufferObject
1076  *
1077  * Binds @fbo object.
1078  *
1079  * Return value: %TRUE on success
1080  */
1081 gboolean
1082 gl_bind_framebuffer_object(GLFramebufferObject *fbo)
1083 {
1084     GLVTable * const gl_vtable = gl_get_vtable();
1085     const guint width  = fbo->width;
1086     const guint height = fbo->height;
1087
1088     const guint attribs = (GL_VIEWPORT_BIT|
1089                            GL_CURRENT_BIT|
1090                            GL_ENABLE_BIT|
1091                            GL_TEXTURE_BIT|
1092                            GL_COLOR_BUFFER_BIT);
1093
1094     if (fbo->is_bound)
1095         return TRUE;
1096
1097     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1098     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1099     glPushAttrib(attribs);
1100     glMatrixMode(GL_PROJECTION);
1101     glPushMatrix();
1102     glLoadIdentity();
1103     glMatrixMode(GL_MODELVIEW);
1104     glPushMatrix();
1105     glLoadIdentity();
1106     glViewport(0, 0, width, height);
1107     glTranslatef(-1.0f, -1.0f, 0.0f);
1108     glScalef(2.0f / width, 2.0f / height, 1.0f);
1109
1110     fbo->is_bound = TRUE;
1111     return TRUE;
1112 }
1113
1114 /**
1115  * gl_unbind_framebuffer_object:
1116  * @fbo: a #GLFramebufferObject
1117  *
1118  * Releases @fbo object.
1119  *
1120  * Return value: %TRUE on success
1121  */
1122 gboolean
1123 gl_unbind_framebuffer_object(GLFramebufferObject *fbo)
1124 {
1125     GLVTable * const gl_vtable = gl_get_vtable();
1126
1127     if (!fbo->is_bound)
1128         return TRUE;
1129
1130     glPopAttrib();
1131     glMatrixMode(GL_PROJECTION);
1132     glPopMatrix();
1133     glMatrixMode(GL_MODELVIEW);
1134     glPopMatrix();
1135     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1136
1137     fbo->is_bound = FALSE;
1138     return TRUE;
1139 }