legal: fix year for some copyright notices.
[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 "config.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     cs->display         = dpy;
313     cs->window          = parent ? parent->window : None;
314     cs->visual          = NULL;
315     cs->context         = NULL;
316     cs->swapped_buffers = FALSE;
317
318     if (parent && parent->context) {
319         status = glXQueryContext(
320             parent->display,
321             parent->context,
322             GLX_FBCONFIG_ID, &fbconfig_id
323         );
324         if (status != Success)
325             goto error;
326
327         if (fbconfig_id == GLX_DONT_CARE)
328             goto choose_fbconfig;
329
330         fbconfigs = glXGetFBConfigs(dpy, screen, &n_fbconfigs);
331         if (!fbconfigs)
332             goto error;
333
334         /* Find out a GLXFBConfig compatible with the parent context */
335         for (n = 0; n < n_fbconfigs; n++) {
336             status = glXGetFBConfigAttrib(
337                 dpy,
338                 fbconfigs[n],
339                 GLX_FBCONFIG_ID, &val
340             );
341             if (status == Success && val == fbconfig_id)
342                 break;
343         }
344         if (n == n_fbconfigs)
345             goto error;
346     }
347     else {
348     choose_fbconfig:
349         fbconfigs = glXChooseFBConfig(
350             dpy,
351             screen,
352             fbconfig_attrs, &n_fbconfigs
353         );
354         if (!fbconfigs)
355             goto error;
356
357         /* Select the first one */
358         n = 0;
359     }
360
361     cs->visual  = glXGetVisualFromFBConfig(dpy, fbconfigs[n]);
362     cs->context = glXCreateNewContext(
363         dpy,
364         fbconfigs[n],
365         GLX_RGBA_TYPE,
366         parent ? parent->context : NULL,
367         True
368     );
369     if (cs->context)
370         goto end;
371
372 error:
373     gl_destroy_context(cs);
374     cs = NULL;
375 end:
376     if (fbconfigs)
377         XFree(fbconfigs);
378     return cs;
379 }
380
381 /**
382  * gl_destroy_context:
383  * @cs: a #GLContextState
384  *
385  * Destroys the GLX context @cs
386  */
387 void
388 gl_destroy_context(GLContextState *cs)
389 {
390     if (!cs)
391         return;
392
393     if (cs->visual) {
394         XFree(cs->visual);
395         cs->visual = NULL;
396     }
397
398     if (cs->display && cs->context) {
399         if (glXGetCurrentContext() == cs->context) {
400             /* XXX: if buffers were never swapped, the application
401                will crash later with the NVIDIA driver */
402             if (!cs->swapped_buffers)
403                 gl_swap_buffers(cs);
404             glXMakeCurrent(cs->display, None, NULL);
405         }
406         glXDestroyContext(cs->display, cs->context);
407         cs->display = NULL;
408         cs->context = NULL;
409     }
410     free(cs);
411 }
412
413 /**
414  * gl_get_current_context:
415  * @cs: return location to the current #GLContextState
416  *
417  * Retrieves the current GLX context, display and drawable packed into
418  * the #GLContextState struct.
419  */
420 void
421 gl_get_current_context(GLContextState *cs)
422 {
423     cs->display = glXGetCurrentDisplay();
424     cs->window  = glXGetCurrentDrawable();
425     cs->context = glXGetCurrentContext();
426 }
427
428 /**
429  * gl_set_current_context:
430  * @new_cs: the requested new #GLContextState
431  * @old_cs: return location to the context that was previously current
432  *
433  * Makes the @new_cs GLX context the current GLX rendering context of
434  * the calling thread, replacing the previously current context if
435  * there was one.
436  *
437  * If @old_cs is non %NULL, the previously current GLX context and
438  * window are recorded.
439  *
440  * Return value: %TRUE on success
441  */
442 gboolean
443 gl_set_current_context(GLContextState *new_cs, GLContextState *old_cs)
444 {
445     /* If display is NULL, this could be that new_cs was retrieved from
446        gl_get_current_context() with none set previously. If that case,
447        the other fields are also NULL and we don't return an error */
448     if (!new_cs->display)
449         return !new_cs->window && !new_cs->context;
450
451     if (old_cs) {
452         if (old_cs == new_cs)
453             return TRUE;
454         gl_get_current_context(old_cs);
455         if (old_cs->display == new_cs->display &&
456             old_cs->window  == new_cs->window  &&
457             old_cs->context == new_cs->context)
458             return TRUE;
459     }
460     return glXMakeCurrent(new_cs->display, new_cs->window, new_cs->context);
461 }
462
463 /**
464  * gl_swap_buffers:
465  * @cs: a #GLContextState
466  *
467  * Promotes the contents of the back buffer of the @win window to
468  * become the contents of the front buffer. This simply is wrapper
469  * around glXSwapBuffers().
470  */
471 void
472 gl_swap_buffers(GLContextState *cs)
473 {
474     glXSwapBuffers(cs->display, cs->window);
475     cs->swapped_buffers = TRUE;
476 }
477
478 /**
479  * gl_bind_texture:
480  * @ts: a #GLTextureState
481  * @target: the target to which the texture is bound
482  * @texture: the name of a texture
483  *
484  * Binds @texture to the specified @target, while recording the
485  * previous state in @ts.
486  *
487  * Return value: %TRUE on success
488  */
489 gboolean
490 gl_bind_texture(GLTextureState *ts, GLenum target, GLuint texture)
491 {
492     GLenum binding;
493
494     ts->target = target;
495
496     if (glIsEnabled(target)) {
497         binding = gl_get_texture_binding(target);
498         if (!binding)
499             return FALSE;
500         if (!gl_get_param(binding, &ts->old_texture))
501             return FALSE;
502         ts->was_enabled = TRUE;
503         ts->was_bound   = texture == ts->old_texture;
504         if (ts->was_bound)
505             return TRUE;
506     }
507     else {
508         glEnable(target);
509         ts->old_texture = 0;
510         ts->was_enabled = FALSE;
511         ts->was_bound   = FALSE;
512     }
513
514     gl_purge_errors();
515     glBindTexture(target, texture);
516     if (gl_check_error())
517         return FALSE;
518     return TRUE;
519 }
520
521 /**
522  * gl_unbind_texture:
523  * @ts: a #GLTextureState
524  *
525  * Rebinds the texture that was previously bound and recorded in @ts.
526  */
527 void
528 gl_unbind_texture(GLTextureState *ts)
529 {
530     if (!ts->was_bound && ts->old_texture)
531         glBindTexture(ts->target, ts->old_texture);
532     if (!ts->was_enabled)
533         glDisable(ts->target);
534 }
535
536 /**
537  * gl_create_texture:
538  * @target: the target to which the texture is bound
539  * @format: the format of the pixel data
540  * @width: the requested width, in pixels
541  * @height: the requested height, in pixels
542  *
543  * Creates a texture with the specified dimensions and @format. The
544  * internal format will be automatically derived from @format.
545  *
546  * Return value: the newly created texture name
547  */
548 GLuint
549 gl_create_texture(GLenum target, GLenum format, guint width, guint height)
550 {
551     GLenum internal_format;
552     GLuint texture;
553     GLTextureState ts;
554     guint bytes_per_component;
555
556     internal_format = format;
557     switch (format) {
558     case GL_LUMINANCE:
559         bytes_per_component = 1;
560         break;
561     case GL_LUMINANCE_ALPHA:
562         bytes_per_component = 2;
563         break;
564     case GL_RGBA:
565     case GL_BGRA:
566         internal_format = GL_RGBA;
567         bytes_per_component = 4;
568         break;
569     default:
570         bytes_per_component = 0;
571         break;
572     }
573     g_assert(bytes_per_component > 0);
574
575     glGenTextures(1, &texture);
576     if (!gl_bind_texture(&ts, target, texture))
577         return 0;
578     glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
579     glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
580     glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
581     glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
582     glPixelStorei(GL_UNPACK_ALIGNMENT, bytes_per_component);
583     glTexImage2D(
584         target,
585         0,
586         internal_format,
587         width, height,
588         0,
589         format,
590         GL_UNSIGNED_BYTE,
591         NULL
592     );
593     gl_unbind_texture(&ts);
594     return texture;
595 }
596
597 /**
598  * get_proc_address:
599  * @name: the name of the OpenGL extension function to lookup
600  *
601  * Returns the specified OpenGL extension function
602  *
603  * Return value: the OpenGL extension matching @name, or %NULL if none
604  *   was found
605  */
606 typedef void (*GLFuncPtr)(void);
607 typedef GLFuncPtr (*GLXGetProcAddressProc)(const char *);
608
609 static GLFuncPtr
610 get_proc_address_default(const char *name)
611 {
612     return NULL;
613 }
614
615 static GLXGetProcAddressProc
616 get_proc_address_func(void)
617 {
618     GLXGetProcAddressProc get_proc_func;
619
620     dlerror();
621     get_proc_func = (GLXGetProcAddressProc)
622         dlsym(RTLD_DEFAULT, "glXGetProcAddress");
623     if (!dlerror())
624         return get_proc_func;
625
626     get_proc_func = (GLXGetProcAddressProc)
627         dlsym(RTLD_DEFAULT, "glXGetProcAddressARB");
628     if (!dlerror())
629         return get_proc_func;
630
631     return get_proc_address_default;
632 }
633
634 static inline GLFuncPtr
635 get_proc_address(const char *name)
636 {
637     static GLXGetProcAddressProc get_proc_func = NULL;
638     if (!get_proc_func)
639         get_proc_func = get_proc_address_func();
640     return get_proc_func(name);
641 }
642
643 /**
644  * gl_init_vtable:
645  *
646  * Initializes the global #GLVTable.
647  *
648  * Return value: the #GLVTable filled in with OpenGL extensions, or
649  *   %NULL on error.
650  */
651 static GLVTable gl_vtable_static;
652
653 static GLVTable *
654 gl_init_vtable(void)
655 {
656     GLVTable * const gl_vtable = &gl_vtable_static;
657     const gchar *gl_extensions = (const gchar *)glGetString(GL_EXTENSIONS);
658     gboolean has_extension;
659
660     /* GLX_EXT_texture_from_pixmap */
661     gl_vtable->glx_create_pixmap = (PFNGLXCREATEPIXMAPPROC)
662         get_proc_address("glXCreatePixmap");
663     if (!gl_vtable->glx_create_pixmap)
664         return NULL;
665     gl_vtable->glx_destroy_pixmap = (PFNGLXDESTROYPIXMAPPROC)
666         get_proc_address("glXDestroyPixmap");
667     if (!gl_vtable->glx_destroy_pixmap)
668         return NULL;
669     gl_vtable->glx_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC)
670         get_proc_address("glXBindTexImageEXT");
671     if (!gl_vtable->glx_bind_tex_image)
672         return NULL;
673     gl_vtable->glx_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC)
674         get_proc_address("glXReleaseTexImageEXT");
675     if (!gl_vtable->glx_release_tex_image)
676         return NULL;
677
678     /* GL_ARB_framebuffer_object */
679     has_extension = (
680         find_string("GL_ARB_framebuffer_object", gl_extensions, " ") ||
681         find_string("GL_EXT_framebuffer_object", gl_extensions, " ")
682     );
683     if (has_extension) {
684         gl_vtable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
685             get_proc_address("glGenFramebuffersEXT");
686         if (!gl_vtable->gl_gen_framebuffers)
687             return NULL;
688         gl_vtable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
689             get_proc_address("glDeleteFramebuffersEXT");
690         if (!gl_vtable->gl_delete_framebuffers)
691             return NULL;
692         gl_vtable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
693             get_proc_address("glBindFramebufferEXT");
694         if (!gl_vtable->gl_bind_framebuffer)
695             return NULL;
696         gl_vtable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
697             get_proc_address("glGenRenderbuffersEXT");
698         if (!gl_vtable->gl_gen_renderbuffers)
699             return NULL;
700         gl_vtable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
701             get_proc_address("glDeleteRenderbuffersEXT");
702         if (!gl_vtable->gl_delete_renderbuffers)
703             return NULL;
704         gl_vtable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
705             get_proc_address("glBindRenderbufferEXT");
706         if (!gl_vtable->gl_bind_renderbuffer)
707             return NULL;
708         gl_vtable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
709             get_proc_address("glRenderbufferStorageEXT");
710         if (!gl_vtable->gl_renderbuffer_storage)
711             return NULL;
712         gl_vtable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
713             get_proc_address("glFramebufferRenderbufferEXT");
714         if (!gl_vtable->gl_framebuffer_renderbuffer)
715             return NULL;
716         gl_vtable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
717             get_proc_address("glFramebufferTexture2DEXT");
718         if (!gl_vtable->gl_framebuffer_texture_2d)
719             return NULL;
720         gl_vtable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
721             get_proc_address("glCheckFramebufferStatusEXT");
722         if (!gl_vtable->gl_check_framebuffer_status)
723             return NULL;
724         gl_vtable->has_framebuffer_object = TRUE;
725     }
726
727     /* GL_ARB_fragment_program */
728     has_extension = (
729         find_string("GL_ARB_fragment_program", gl_extensions, " ")
730     );
731     if (has_extension) {
732         gl_vtable->gl_gen_programs = (PFNGLGENPROGRAMSARBPROC)
733             get_proc_address("glGenProgramsARB");
734         if (!gl_vtable->gl_gen_programs)
735             return NULL;
736         gl_vtable->gl_delete_programs = (PFNGLDELETEPROGRAMSARBPROC)
737             get_proc_address("glDeleteProgramsARB");
738         if (!gl_vtable->gl_delete_programs)
739             return NULL;
740         gl_vtable->gl_bind_program = (PFNGLBINDPROGRAMARBPROC)
741             get_proc_address("glBindProgramARB");
742         if (!gl_vtable->gl_bind_program)
743             return NULL;
744         gl_vtable->gl_program_string = (PFNGLPROGRAMSTRINGARBPROC)
745             get_proc_address("glProgramStringARB");
746         if (!gl_vtable->gl_program_string)
747             return NULL;
748         gl_vtable->gl_get_program_iv = (PFNGLGETPROGRAMIVARBPROC)
749             get_proc_address("glGetProgramivARB");
750         if (!gl_vtable->gl_get_program_iv)
751             return NULL;
752         gl_vtable->gl_program_local_parameter_4fv = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)
753             get_proc_address("glProgramLocalParameter4fvARB");
754         if (!gl_vtable->gl_program_local_parameter_4fv)
755             return NULL;
756         gl_vtable->has_fragment_program = TRUE;
757     }
758
759     /* GL_ARB_multitexture */
760     has_extension = (
761         find_string("GL_ARB_multitexture", gl_extensions, " ")
762     );
763     if (has_extension) {
764         gl_vtable->gl_active_texture = (PFNGLACTIVETEXTUREPROC)
765             get_proc_address("glActiveTextureARB");
766         if (!gl_vtable->gl_active_texture)
767             return NULL;
768         gl_vtable->gl_multi_tex_coord_2f = (PFNGLMULTITEXCOORD2FPROC)
769             get_proc_address("glMultiTexCoord2fARB");
770         if (!gl_vtable->gl_multi_tex_coord_2f)
771             return NULL;
772         gl_vtable->has_multitexture = TRUE;
773     }
774     return gl_vtable;
775 }
776
777 /**
778  * gl_get_vtable:
779  *
780  * Retrieves a VTable for OpenGL extensions.
781  *
782  * Return value: VTable for OpenGL extensions
783  */
784 GLVTable *
785 gl_get_vtable(void)
786 {
787     static GStaticMutex mutex          = G_STATIC_MUTEX_INIT;
788     static gboolean     gl_vtable_init = TRUE;
789     static GLVTable    *gl_vtable      = NULL;
790
791     g_static_mutex_lock(&mutex);
792     if (gl_vtable_init) {
793         gl_vtable_init = FALSE;
794         gl_vtable      = gl_init_vtable();
795     }
796     g_static_mutex_unlock(&mutex);
797     return gl_vtable;
798 }
799
800 /**
801  * gl_create_pixmap_object:
802  * @dpy: an X11 #Display
803  * @width: the request width, in pixels
804  * @height: the request height, in pixels
805  *
806  * Creates a #GLPixmapObject of the specified dimensions. This
807  * requires the GLX_EXT_texture_from_pixmap extension.
808  *
809  * Return value: the newly created #GLPixmapObject object
810  */
811 GLPixmapObject *
812 gl_create_pixmap_object(Display *dpy, guint width, guint height)
813 {
814     GLVTable * const    gl_vtable = gl_get_vtable();
815     GLPixmapObject     *pixo;
816     GLXFBConfig        *fbconfig;
817     int                 screen;
818     Window              rootwin;
819     XWindowAttributes   wattr;
820     int                *attr;
821     int                 n_fbconfig_attrs;
822
823     int fbconfig_attrs[32] = {
824         GLX_DRAWABLE_TYPE,      GLX_PIXMAP_BIT,
825         GLX_DOUBLEBUFFER,       GL_FALSE,
826         GLX_RENDER_TYPE,        GLX_RGBA_BIT,
827         GLX_X_RENDERABLE,       GL_TRUE,
828         GLX_Y_INVERTED_EXT,     GL_TRUE,
829         GLX_RED_SIZE,           8,
830         GLX_GREEN_SIZE,         8,
831         GLX_BLUE_SIZE,          8,
832         GL_NONE,
833     };
834
835     int pixmap_attrs[10] = {
836         GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
837         GLX_MIPMAP_TEXTURE_EXT, GL_FALSE,
838         GL_NONE,
839     };
840
841     if (!gl_vtable)
842         return NULL;
843
844     screen  = DefaultScreen(dpy);
845     rootwin = RootWindow(dpy, screen);
846
847     /* XXX: this won't work for different displays */
848     if (!gl_vtable->has_texture_from_pixmap) {
849         const char *glx_extensions = glXQueryExtensionsString(dpy, screen);
850         if (!glx_extensions)
851             return NULL;
852         if (!find_string("GLX_EXT_texture_from_pixmap", glx_extensions, " "))
853             return NULL;
854         gl_vtable->has_texture_from_pixmap = TRUE;
855     }
856
857     pixo = calloc(1, sizeof(*pixo));
858     if (!pixo)
859         return NULL;
860
861     pixo->dpy           = dpy;
862     pixo->width         = width;
863     pixo->height        = height;
864     pixo->pixmap        = None;
865     pixo->glx_pixmap    = None;
866     pixo->is_bound      = FALSE;
867
868     XGetWindowAttributes(dpy, rootwin, &wattr);
869     pixo->pixmap  = XCreatePixmap(dpy, rootwin, width, height, wattr.depth);
870     if (!pixo->pixmap)
871         goto error;
872
873     /* Initialize FBConfig attributes */
874     for (attr = fbconfig_attrs; *attr != GL_NONE; attr += 2)
875         ;
876     *attr++ = GLX_DEPTH_SIZE;                 *attr++ = wattr.depth;
877     if (wattr.depth == 32) {
878     *attr++ = GLX_ALPHA_SIZE;                 *attr++ = 8;
879     *attr++ = GLX_BIND_TO_TEXTURE_RGBA_EXT;   *attr++ = GL_TRUE;
880     }
881     else {
882     *attr++ = GLX_BIND_TO_TEXTURE_RGB_EXT;    *attr++ = GL_TRUE;
883     }
884     *attr++ = GL_NONE;
885
886     fbconfig = glXChooseFBConfig(
887         dpy,
888         screen,
889         fbconfig_attrs, &n_fbconfig_attrs
890     );
891     if (!fbconfig)
892         goto error;
893
894     /* Initialize GLX Pixmap attributes */
895     for (attr = pixmap_attrs; *attr != GL_NONE; attr += 2)
896         ;
897     *attr++ = GLX_TEXTURE_FORMAT_EXT;
898     if (wattr.depth == 32)
899     *attr++ = GLX_TEXTURE_FORMAT_RGBA_EXT;
900     else
901     *attr++ = GLX_TEXTURE_FORMAT_RGB_EXT;
902     *attr++ = GL_NONE;
903
904     x11_trap_errors();
905     pixo->glx_pixmap = gl_vtable->glx_create_pixmap(
906         dpy,
907         fbconfig[0],
908         pixo->pixmap,
909         pixmap_attrs
910     );
911     free(fbconfig);
912     if (x11_untrap_errors() != 0)
913         goto error;
914
915     pixo->target = GL_TEXTURE_2D;
916     glGenTextures(1, &pixo->texture);
917     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
918         goto error;
919     glTexParameteri(pixo->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
920     glTexParameteri(pixo->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
921     gl_unbind_texture(&pixo->old_texture);
922     return pixo;
923
924 error:
925     gl_destroy_pixmap_object(pixo);
926     return NULL;
927 }
928
929 /**
930  * gl_destroy_pixmap_object:
931  * @pixo: a #GLPixmapObject
932  *
933  * Destroys the #GLPixmapObject object.
934  */
935 void
936 gl_destroy_pixmap_object(GLPixmapObject *pixo)
937 {
938     GLVTable * const gl_vtable = gl_get_vtable();
939
940     if (!pixo)
941         return;
942
943     gl_unbind_pixmap_object(pixo);
944
945     if (pixo->texture) {
946         glDeleteTextures(1, &pixo->texture);
947         pixo->texture = 0;
948     }
949
950     if (pixo->glx_pixmap) {
951         gl_vtable->glx_destroy_pixmap(pixo->dpy, pixo->glx_pixmap);
952         pixo->glx_pixmap = None;
953     }
954
955     if (pixo->pixmap) {
956         XFreePixmap(pixo->dpy, pixo->pixmap);
957         pixo->pixmap = None;
958     }
959     free(pixo);
960 }
961
962 /**
963  * gl_bind_pixmap_object:
964  * @pixo: a #GLPixmapObject
965  *
966  * Defines a two-dimensional texture image. The texture image is taken
967  * from the @pixo pixmap and need not be copied. The texture target,
968  * format and size are derived from attributes of the @pixo pixmap.
969  *
970  * Return value: %TRUE on success
971  */
972 gboolean
973 gl_bind_pixmap_object(GLPixmapObject *pixo)
974 {
975     GLVTable * const gl_vtable = gl_get_vtable();
976
977     if (pixo->is_bound)
978         return TRUE;
979
980     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
981         return FALSE;
982
983     x11_trap_errors();
984     gl_vtable->glx_bind_tex_image(
985         pixo->dpy,
986         pixo->glx_pixmap,
987         GLX_FRONT_LEFT_EXT,
988         NULL
989     );
990     XSync(pixo->dpy, False);
991     if (x11_untrap_errors() != 0) {
992         GST_DEBUG("failed to bind pixmap");
993         return FALSE;
994     }
995
996     pixo->is_bound = TRUE;
997     return TRUE;
998 }
999
1000 /**
1001  * gl_unbind_pixmap_object:
1002  * @pixo: a #GLPixmapObject
1003  *
1004  * Releases a color buffers that is being used as a texture.
1005  *
1006  * Return value: %TRUE on success
1007  */
1008 gboolean
1009 gl_unbind_pixmap_object(GLPixmapObject *pixo)
1010 {
1011     GLVTable * const gl_vtable = gl_get_vtable();
1012
1013     if (!pixo->is_bound)
1014         return TRUE;
1015
1016     x11_trap_errors();
1017     gl_vtable->glx_release_tex_image(
1018         pixo->dpy,
1019         pixo->glx_pixmap,
1020         GLX_FRONT_LEFT_EXT
1021     );
1022     XSync(pixo->dpy, False);
1023     if (x11_untrap_errors() != 0) {
1024         GST_DEBUG("failed to release pixmap");
1025         return FALSE;
1026     }
1027
1028     gl_unbind_texture(&pixo->old_texture);
1029
1030     pixo->is_bound = FALSE;
1031     return TRUE;
1032 }
1033
1034 /**
1035  * gl_create_framebuffer_object:
1036  * @target: the target to which the texture is bound
1037  * @texture: the GL texture to hold the framebuffer
1038  * @width: the requested width, in pixels
1039  * @height: the requested height, in pixels
1040  *
1041  * Creates an FBO with the specified texture and size.
1042  *
1043  * Return value: the newly created #GLFramebufferObject, or %NULL if
1044  *   an error occurred
1045  */
1046 GLFramebufferObject *
1047 gl_create_framebuffer_object(
1048     GLenum target,
1049     GLuint texture,
1050     guint  width,
1051     guint  height
1052 )
1053 {
1054     GLVTable * const gl_vtable = gl_get_vtable();
1055     GLFramebufferObject *fbo;
1056     GLenum status;
1057
1058     if (!gl_vtable || !gl_vtable->has_framebuffer_object)
1059         return NULL;
1060
1061     /* XXX: we only support GL_TEXTURE_2D at this time */
1062     if (target != GL_TEXTURE_2D)
1063         return NULL;
1064
1065     fbo = calloc(1, sizeof(*fbo));
1066     if (!fbo)
1067         return NULL;
1068
1069     fbo->width          = width;
1070     fbo->height         = height;
1071     fbo->fbo            = 0;
1072     fbo->old_fbo        = 0;
1073     fbo->is_bound       = FALSE;
1074
1075     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1076     gl_vtable->gl_gen_framebuffers(1, &fbo->fbo);
1077     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1078     gl_vtable->gl_framebuffer_texture_2d(
1079         GL_FRAMEBUFFER_EXT,
1080         GL_COLOR_ATTACHMENT0_EXT,
1081         target, texture,
1082         0
1083     );
1084
1085     status = gl_vtable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT);
1086     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1087     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
1088         goto error;
1089     return fbo;
1090
1091 error:
1092     gl_destroy_framebuffer_object(fbo);
1093     return NULL;
1094 }
1095
1096 /**
1097  * gl_destroy_framebuffer_object:
1098  * @fbo: a #GLFramebufferObject
1099  *
1100  * Destroys the @fbo object.
1101  */
1102 void
1103 gl_destroy_framebuffer_object(GLFramebufferObject *fbo)
1104 {
1105     GLVTable * const gl_vtable = gl_get_vtable();
1106
1107     if (!fbo)
1108         return;
1109
1110     gl_unbind_framebuffer_object(fbo);
1111
1112     if (fbo->fbo) {
1113         gl_vtable->gl_delete_framebuffers(1, &fbo->fbo);
1114         fbo->fbo = 0;
1115     }
1116     free(fbo);
1117 }
1118
1119 /**
1120  * gl_bind_framebuffer_object:
1121  * @fbo: a #GLFramebufferObject
1122  *
1123  * Binds @fbo object.
1124  *
1125  * Return value: %TRUE on success
1126  */
1127 gboolean
1128 gl_bind_framebuffer_object(GLFramebufferObject *fbo)
1129 {
1130     GLVTable * const gl_vtable = gl_get_vtable();
1131     const guint width  = fbo->width;
1132     const guint height = fbo->height;
1133
1134     const guint attribs = (GL_VIEWPORT_BIT|
1135                            GL_CURRENT_BIT|
1136                            GL_ENABLE_BIT|
1137                            GL_TEXTURE_BIT|
1138                            GL_COLOR_BUFFER_BIT);
1139
1140     if (fbo->is_bound)
1141         return TRUE;
1142
1143     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1144     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1145     glPushAttrib(attribs);
1146     glMatrixMode(GL_PROJECTION);
1147     glPushMatrix();
1148     glLoadIdentity();
1149     glMatrixMode(GL_MODELVIEW);
1150     glPushMatrix();
1151     glLoadIdentity();
1152     glViewport(0, 0, width, height);
1153     glTranslatef(-1.0f, -1.0f, 0.0f);
1154     glScalef(2.0f / width, 2.0f / height, 1.0f);
1155
1156     fbo->is_bound = TRUE;
1157     return TRUE;
1158 }
1159
1160 /**
1161  * gl_unbind_framebuffer_object:
1162  * @fbo: a #GLFramebufferObject
1163  *
1164  * Releases @fbo object.
1165  *
1166  * Return value: %TRUE on success
1167  */
1168 gboolean
1169 gl_unbind_framebuffer_object(GLFramebufferObject *fbo)
1170 {
1171     GLVTable * const gl_vtable = gl_get_vtable();
1172
1173     if (!fbo->is_bound)
1174         return TRUE;
1175
1176     glPopAttrib();
1177     glMatrixMode(GL_PROJECTION);
1178     glPopMatrix();
1179     glMatrixMode(GL_MODELVIEW);
1180     glPopMatrix();
1181     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1182
1183     fbo->is_bound = FALSE;
1184     return TRUE;
1185 }