utils: fix gl_create_context() with parent context set.
[profile/ivi/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     get_proc_func = (GLXGetProcAddressProc)
629         dlsym(RTLD_DEFAULT, "glXGetProcAddress");
630     if (!dlerror())
631         return get_proc_func;
632
633     get_proc_func = (GLXGetProcAddressProc)
634         dlsym(RTLD_DEFAULT, "glXGetProcAddressARB");
635     if (!dlerror())
636         return get_proc_func;
637
638     return get_proc_address_default;
639 }
640
641 static inline GLFuncPtr
642 get_proc_address(const char *name)
643 {
644     static GLXGetProcAddressProc get_proc_func = NULL;
645     if (!get_proc_func)
646         get_proc_func = get_proc_address_func();
647     return get_proc_func(name);
648 }
649
650 /**
651  * gl_init_vtable:
652  *
653  * Initializes the global #GLVTable.
654  *
655  * Return value: the #GLVTable filled in with OpenGL extensions, or
656  *   %NULL on error.
657  */
658 static GLVTable gl_vtable_static;
659
660 static GLVTable *
661 gl_init_vtable(void)
662 {
663     GLVTable * const gl_vtable = &gl_vtable_static;
664     const gchar *gl_extensions = (const gchar *)glGetString(GL_EXTENSIONS);
665     gboolean has_extension;
666
667     /* GLX_EXT_texture_from_pixmap */
668     gl_vtable->glx_create_pixmap = (PFNGLXCREATEPIXMAPPROC)
669         get_proc_address("glXCreatePixmap");
670     if (!gl_vtable->glx_create_pixmap)
671         return NULL;
672     gl_vtable->glx_destroy_pixmap = (PFNGLXDESTROYPIXMAPPROC)
673         get_proc_address("glXDestroyPixmap");
674     if (!gl_vtable->glx_destroy_pixmap)
675         return NULL;
676     gl_vtable->glx_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC)
677         get_proc_address("glXBindTexImageEXT");
678     if (!gl_vtable->glx_bind_tex_image)
679         return NULL;
680     gl_vtable->glx_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC)
681         get_proc_address("glXReleaseTexImageEXT");
682     if (!gl_vtable->glx_release_tex_image)
683         return NULL;
684
685     /* GL_ARB_framebuffer_object */
686     has_extension = (
687         find_string("GL_ARB_framebuffer_object", gl_extensions, " ") ||
688         find_string("GL_EXT_framebuffer_object", gl_extensions, " ")
689     );
690     if (has_extension) {
691         gl_vtable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
692             get_proc_address("glGenFramebuffersEXT");
693         if (!gl_vtable->gl_gen_framebuffers)
694             return NULL;
695         gl_vtable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
696             get_proc_address("glDeleteFramebuffersEXT");
697         if (!gl_vtable->gl_delete_framebuffers)
698             return NULL;
699         gl_vtable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
700             get_proc_address("glBindFramebufferEXT");
701         if (!gl_vtable->gl_bind_framebuffer)
702             return NULL;
703         gl_vtable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
704             get_proc_address("glGenRenderbuffersEXT");
705         if (!gl_vtable->gl_gen_renderbuffers)
706             return NULL;
707         gl_vtable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
708             get_proc_address("glDeleteRenderbuffersEXT");
709         if (!gl_vtable->gl_delete_renderbuffers)
710             return NULL;
711         gl_vtable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
712             get_proc_address("glBindRenderbufferEXT");
713         if (!gl_vtable->gl_bind_renderbuffer)
714             return NULL;
715         gl_vtable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
716             get_proc_address("glRenderbufferStorageEXT");
717         if (!gl_vtable->gl_renderbuffer_storage)
718             return NULL;
719         gl_vtable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
720             get_proc_address("glFramebufferRenderbufferEXT");
721         if (!gl_vtable->gl_framebuffer_renderbuffer)
722             return NULL;
723         gl_vtable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
724             get_proc_address("glFramebufferTexture2DEXT");
725         if (!gl_vtable->gl_framebuffer_texture_2d)
726             return NULL;
727         gl_vtable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
728             get_proc_address("glCheckFramebufferStatusEXT");
729         if (!gl_vtable->gl_check_framebuffer_status)
730             return NULL;
731         gl_vtable->has_framebuffer_object = TRUE;
732     }
733
734     /* GL_ARB_fragment_program */
735     has_extension = (
736         find_string("GL_ARB_fragment_program", gl_extensions, " ")
737     );
738     if (has_extension) {
739         gl_vtable->gl_gen_programs = (PFNGLGENPROGRAMSARBPROC)
740             get_proc_address("glGenProgramsARB");
741         if (!gl_vtable->gl_gen_programs)
742             return NULL;
743         gl_vtable->gl_delete_programs = (PFNGLDELETEPROGRAMSARBPROC)
744             get_proc_address("glDeleteProgramsARB");
745         if (!gl_vtable->gl_delete_programs)
746             return NULL;
747         gl_vtable->gl_bind_program = (PFNGLBINDPROGRAMARBPROC)
748             get_proc_address("glBindProgramARB");
749         if (!gl_vtable->gl_bind_program)
750             return NULL;
751         gl_vtable->gl_program_string = (PFNGLPROGRAMSTRINGARBPROC)
752             get_proc_address("glProgramStringARB");
753         if (!gl_vtable->gl_program_string)
754             return NULL;
755         gl_vtable->gl_get_program_iv = (PFNGLGETPROGRAMIVARBPROC)
756             get_proc_address("glGetProgramivARB");
757         if (!gl_vtable->gl_get_program_iv)
758             return NULL;
759         gl_vtable->gl_program_local_parameter_4fv = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)
760             get_proc_address("glProgramLocalParameter4fvARB");
761         if (!gl_vtable->gl_program_local_parameter_4fv)
762             return NULL;
763         gl_vtable->has_fragment_program = TRUE;
764     }
765
766     /* GL_ARB_multitexture */
767     has_extension = (
768         find_string("GL_ARB_multitexture", gl_extensions, " ")
769     );
770     if (has_extension) {
771         gl_vtable->gl_active_texture = (PFNGLACTIVETEXTUREPROC)
772             get_proc_address("glActiveTextureARB");
773         if (!gl_vtable->gl_active_texture)
774             return NULL;
775         gl_vtable->gl_multi_tex_coord_2f = (PFNGLMULTITEXCOORD2FPROC)
776             get_proc_address("glMultiTexCoord2fARB");
777         if (!gl_vtable->gl_multi_tex_coord_2f)
778             return NULL;
779         gl_vtable->has_multitexture = TRUE;
780     }
781     return gl_vtable;
782 }
783
784 /**
785  * gl_get_vtable:
786  *
787  * Retrieves a VTable for OpenGL extensions.
788  *
789  * Return value: VTable for OpenGL extensions
790  */
791 GLVTable *
792 gl_get_vtable(void)
793 {
794     static GStaticMutex mutex          = G_STATIC_MUTEX_INIT;
795     static gboolean     gl_vtable_init = TRUE;
796     static GLVTable    *gl_vtable      = NULL;
797
798     g_static_mutex_lock(&mutex);
799     if (gl_vtable_init) {
800         gl_vtable_init = FALSE;
801         gl_vtable      = gl_init_vtable();
802     }
803     g_static_mutex_unlock(&mutex);
804     return gl_vtable;
805 }
806
807 /**
808  * gl_create_pixmap_object:
809  * @dpy: an X11 #Display
810  * @width: the request width, in pixels
811  * @height: the request height, in pixels
812  *
813  * Creates a #GLPixmapObject of the specified dimensions. This
814  * requires the GLX_EXT_texture_from_pixmap extension.
815  *
816  * Return value: the newly created #GLPixmapObject object
817  */
818 GLPixmapObject *
819 gl_create_pixmap_object(Display *dpy, guint width, guint height)
820 {
821     GLVTable * const    gl_vtable = gl_get_vtable();
822     GLPixmapObject     *pixo;
823     GLXFBConfig        *fbconfig;
824     int                 screen;
825     Window              rootwin;
826     XWindowAttributes   wattr;
827     int                *attr;
828     int                 n_fbconfig_attrs;
829
830     int fbconfig_attrs[32] = {
831         GLX_DRAWABLE_TYPE,      GLX_PIXMAP_BIT,
832         GLX_DOUBLEBUFFER,       GL_FALSE,
833         GLX_RENDER_TYPE,        GLX_RGBA_BIT,
834         GLX_X_RENDERABLE,       GL_TRUE,
835         GLX_Y_INVERTED_EXT,     GL_TRUE,
836         GLX_RED_SIZE,           8,
837         GLX_GREEN_SIZE,         8,
838         GLX_BLUE_SIZE,          8,
839         GL_NONE,
840     };
841
842     int pixmap_attrs[10] = {
843         GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
844         GLX_MIPMAP_TEXTURE_EXT, GL_FALSE,
845         GL_NONE,
846     };
847
848     if (!gl_vtable)
849         return NULL;
850
851     screen  = DefaultScreen(dpy);
852     rootwin = RootWindow(dpy, screen);
853
854     /* XXX: this won't work for different displays */
855     if (!gl_vtable->has_texture_from_pixmap) {
856         const char *glx_extensions = glXQueryExtensionsString(dpy, screen);
857         if (!glx_extensions)
858             return NULL;
859         if (!find_string("GLX_EXT_texture_from_pixmap", glx_extensions, " "))
860             return NULL;
861         gl_vtable->has_texture_from_pixmap = TRUE;
862     }
863
864     pixo = calloc(1, sizeof(*pixo));
865     if (!pixo)
866         return NULL;
867
868     pixo->dpy           = dpy;
869     pixo->width         = width;
870     pixo->height        = height;
871     pixo->pixmap        = None;
872     pixo->glx_pixmap    = None;
873     pixo->is_bound      = FALSE;
874
875     XGetWindowAttributes(dpy, rootwin, &wattr);
876     pixo->pixmap  = XCreatePixmap(dpy, rootwin, width, height, wattr.depth);
877     if (!pixo->pixmap)
878         goto error;
879
880     /* Initialize FBConfig attributes */
881     for (attr = fbconfig_attrs; *attr != GL_NONE; attr += 2)
882         ;
883     *attr++ = GLX_DEPTH_SIZE;                 *attr++ = wattr.depth;
884     if (wattr.depth == 32) {
885     *attr++ = GLX_ALPHA_SIZE;                 *attr++ = 8;
886     *attr++ = GLX_BIND_TO_TEXTURE_RGBA_EXT;   *attr++ = GL_TRUE;
887     }
888     else {
889     *attr++ = GLX_BIND_TO_TEXTURE_RGB_EXT;    *attr++ = GL_TRUE;
890     }
891     *attr++ = GL_NONE;
892
893     fbconfig = glXChooseFBConfig(
894         dpy,
895         screen,
896         fbconfig_attrs, &n_fbconfig_attrs
897     );
898     if (!fbconfig)
899         goto error;
900
901     /* Initialize GLX Pixmap attributes */
902     for (attr = pixmap_attrs; *attr != GL_NONE; attr += 2)
903         ;
904     *attr++ = GLX_TEXTURE_FORMAT_EXT;
905     if (wattr.depth == 32)
906     *attr++ = GLX_TEXTURE_FORMAT_RGBA_EXT;
907     else
908     *attr++ = GLX_TEXTURE_FORMAT_RGB_EXT;
909     *attr++ = GL_NONE;
910
911     x11_trap_errors();
912     pixo->glx_pixmap = gl_vtable->glx_create_pixmap(
913         dpy,
914         fbconfig[0],
915         pixo->pixmap,
916         pixmap_attrs
917     );
918     free(fbconfig);
919     if (x11_untrap_errors() != 0)
920         goto error;
921
922     pixo->target = GL_TEXTURE_2D;
923     glGenTextures(1, &pixo->texture);
924     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
925         goto error;
926     glTexParameteri(pixo->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
927     glTexParameteri(pixo->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
928     gl_unbind_texture(&pixo->old_texture);
929     return pixo;
930
931 error:
932     gl_destroy_pixmap_object(pixo);
933     return NULL;
934 }
935
936 /**
937  * gl_destroy_pixmap_object:
938  * @pixo: a #GLPixmapObject
939  *
940  * Destroys the #GLPixmapObject object.
941  */
942 void
943 gl_destroy_pixmap_object(GLPixmapObject *pixo)
944 {
945     GLVTable * const gl_vtable = gl_get_vtable();
946
947     if (!pixo)
948         return;
949
950     gl_unbind_pixmap_object(pixo);
951
952     if (pixo->texture) {
953         glDeleteTextures(1, &pixo->texture);
954         pixo->texture = 0;
955     }
956
957     if (pixo->glx_pixmap) {
958         gl_vtable->glx_destroy_pixmap(pixo->dpy, pixo->glx_pixmap);
959         pixo->glx_pixmap = None;
960     }
961
962     if (pixo->pixmap) {
963         XFreePixmap(pixo->dpy, pixo->pixmap);
964         pixo->pixmap = None;
965     }
966     free(pixo);
967 }
968
969 /**
970  * gl_bind_pixmap_object:
971  * @pixo: a #GLPixmapObject
972  *
973  * Defines a two-dimensional texture image. The texture image is taken
974  * from the @pixo pixmap and need not be copied. The texture target,
975  * format and size are derived from attributes of the @pixo pixmap.
976  *
977  * Return value: %TRUE on success
978  */
979 gboolean
980 gl_bind_pixmap_object(GLPixmapObject *pixo)
981 {
982     GLVTable * const gl_vtable = gl_get_vtable();
983
984     if (pixo->is_bound)
985         return TRUE;
986
987     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
988         return FALSE;
989
990     x11_trap_errors();
991     gl_vtable->glx_bind_tex_image(
992         pixo->dpy,
993         pixo->glx_pixmap,
994         GLX_FRONT_LEFT_EXT,
995         NULL
996     );
997     XSync(pixo->dpy, False);
998     if (x11_untrap_errors() != 0) {
999         GST_DEBUG("failed to bind pixmap");
1000         return FALSE;
1001     }
1002
1003     pixo->is_bound = TRUE;
1004     return TRUE;
1005 }
1006
1007 /**
1008  * gl_unbind_pixmap_object:
1009  * @pixo: a #GLPixmapObject
1010  *
1011  * Releases a color buffers that is being used as a texture.
1012  *
1013  * Return value: %TRUE on success
1014  */
1015 gboolean
1016 gl_unbind_pixmap_object(GLPixmapObject *pixo)
1017 {
1018     GLVTable * const gl_vtable = gl_get_vtable();
1019
1020     if (!pixo->is_bound)
1021         return TRUE;
1022
1023     x11_trap_errors();
1024     gl_vtable->glx_release_tex_image(
1025         pixo->dpy,
1026         pixo->glx_pixmap,
1027         GLX_FRONT_LEFT_EXT
1028     );
1029     XSync(pixo->dpy, False);
1030     if (x11_untrap_errors() != 0) {
1031         GST_DEBUG("failed to release pixmap");
1032         return FALSE;
1033     }
1034
1035     gl_unbind_texture(&pixo->old_texture);
1036
1037     pixo->is_bound = FALSE;
1038     return TRUE;
1039 }
1040
1041 /**
1042  * gl_create_framebuffer_object:
1043  * @target: the target to which the texture is bound
1044  * @texture: the GL texture to hold the framebuffer
1045  * @width: the requested width, in pixels
1046  * @height: the requested height, in pixels
1047  *
1048  * Creates an FBO with the specified texture and size.
1049  *
1050  * Return value: the newly created #GLFramebufferObject, or %NULL if
1051  *   an error occurred
1052  */
1053 GLFramebufferObject *
1054 gl_create_framebuffer_object(
1055     GLenum target,
1056     GLuint texture,
1057     guint  width,
1058     guint  height
1059 )
1060 {
1061     GLVTable * const gl_vtable = gl_get_vtable();
1062     GLFramebufferObject *fbo;
1063     GLenum status;
1064
1065     if (!gl_vtable || !gl_vtable->has_framebuffer_object)
1066         return NULL;
1067
1068     /* XXX: we only support GL_TEXTURE_2D at this time */
1069     if (target != GL_TEXTURE_2D)
1070         return NULL;
1071
1072     fbo = calloc(1, sizeof(*fbo));
1073     if (!fbo)
1074         return NULL;
1075
1076     fbo->width          = width;
1077     fbo->height         = height;
1078     fbo->fbo            = 0;
1079     fbo->old_fbo        = 0;
1080     fbo->is_bound       = FALSE;
1081
1082     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1083     gl_vtable->gl_gen_framebuffers(1, &fbo->fbo);
1084     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1085     gl_vtable->gl_framebuffer_texture_2d(
1086         GL_FRAMEBUFFER_EXT,
1087         GL_COLOR_ATTACHMENT0_EXT,
1088         target, texture,
1089         0
1090     );
1091
1092     status = gl_vtable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT);
1093     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1094     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
1095         goto error;
1096     return fbo;
1097
1098 error:
1099     gl_destroy_framebuffer_object(fbo);
1100     return NULL;
1101 }
1102
1103 /**
1104  * gl_destroy_framebuffer_object:
1105  * @fbo: a #GLFramebufferObject
1106  *
1107  * Destroys the @fbo object.
1108  */
1109 void
1110 gl_destroy_framebuffer_object(GLFramebufferObject *fbo)
1111 {
1112     GLVTable * const gl_vtable = gl_get_vtable();
1113
1114     if (!fbo)
1115         return;
1116
1117     gl_unbind_framebuffer_object(fbo);
1118
1119     if (fbo->fbo) {
1120         gl_vtable->gl_delete_framebuffers(1, &fbo->fbo);
1121         fbo->fbo = 0;
1122     }
1123     free(fbo);
1124 }
1125
1126 /**
1127  * gl_bind_framebuffer_object:
1128  * @fbo: a #GLFramebufferObject
1129  *
1130  * Binds @fbo object.
1131  *
1132  * Return value: %TRUE on success
1133  */
1134 gboolean
1135 gl_bind_framebuffer_object(GLFramebufferObject *fbo)
1136 {
1137     GLVTable * const gl_vtable = gl_get_vtable();
1138     const guint width  = fbo->width;
1139     const guint height = fbo->height;
1140
1141     const guint attribs = (GL_VIEWPORT_BIT|
1142                            GL_CURRENT_BIT|
1143                            GL_ENABLE_BIT|
1144                            GL_TEXTURE_BIT|
1145                            GL_COLOR_BUFFER_BIT);
1146
1147     if (fbo->is_bound)
1148         return TRUE;
1149
1150     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1151     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1152     glPushAttrib(attribs);
1153     glMatrixMode(GL_PROJECTION);
1154     glPushMatrix();
1155     glLoadIdentity();
1156     glMatrixMode(GL_MODELVIEW);
1157     glPushMatrix();
1158     glLoadIdentity();
1159     glViewport(0, 0, width, height);
1160     glTranslatef(-1.0f, -1.0f, 0.0f);
1161     glScalef(2.0f / width, 2.0f / height, 1.0f);
1162
1163     fbo->is_bound = TRUE;
1164     return TRUE;
1165 }
1166
1167 /**
1168  * gl_unbind_framebuffer_object:
1169  * @fbo: a #GLFramebufferObject
1170  *
1171  * Releases @fbo object.
1172  *
1173  * Return value: %TRUE on success
1174  */
1175 gboolean
1176 gl_unbind_framebuffer_object(GLFramebufferObject *fbo)
1177 {
1178     GLVTable * const gl_vtable = gl_get_vtable();
1179
1180     if (!fbo->is_bound)
1181         return TRUE;
1182
1183     glPopAttrib();
1184     glMatrixMode(GL_PROJECTION);
1185     glPopMatrix();
1186     glMatrixMode(GL_MODELVIEW);
1187     glPopMatrix();
1188     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1189
1190     fbo->is_bound = FALSE;
1191     return TRUE;
1192 }