Fix the typecast warnings:
[vaapi:sree-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     g_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 GStaticMutex mutex          = G_STATIC_MUTEX_INIT;
745     static gboolean     gl_vtable_init = TRUE;
746     static GLVTable    *gl_vtable      = NULL;
747
748     g_static_mutex_lock(&mutex);
749     if (gl_vtable_init) {
750         gl_vtable_init = FALSE;
751         gl_vtable      = gl_init_vtable();
752     }
753     g_static_mutex_unlock(&mutex);
754     return gl_vtable;
755 }
756
757 /**
758  * gl_create_pixmap_object:
759  * @dpy: an X11 #Display
760  * @width: the request width, in pixels
761  * @height: the request height, in pixels
762  *
763  * Creates a #GLPixmapObject of the specified dimensions. This
764  * requires the GLX_EXT_texture_from_pixmap extension.
765  *
766  * Return value: the newly created #GLPixmapObject object
767  */
768 GLPixmapObject *
769 gl_create_pixmap_object(Display *dpy, guint width, guint height)
770 {
771     GLVTable * const    gl_vtable = gl_get_vtable();
772     GLPixmapObject     *pixo;
773     GLXFBConfig        *fbconfig;
774     int                 screen;
775     Window              rootwin;
776     XWindowAttributes   wattr;
777     int                *attr;
778     int                 n_fbconfig_attrs;
779
780     int fbconfig_attrs[32] = {
781         GLX_DRAWABLE_TYPE,      GLX_PIXMAP_BIT,
782         GLX_DOUBLEBUFFER,       GL_FALSE,
783         GLX_RENDER_TYPE,        GLX_RGBA_BIT,
784         GLX_X_RENDERABLE,       GL_TRUE,
785         GLX_Y_INVERTED_EXT,     GL_TRUE,
786         GLX_RED_SIZE,           8,
787         GLX_GREEN_SIZE,         8,
788         GLX_BLUE_SIZE,          8,
789         GL_NONE,
790     };
791
792     int pixmap_attrs[10] = {
793         GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
794         GLX_MIPMAP_TEXTURE_EXT, GL_FALSE,
795         GL_NONE,
796     };
797
798     if (!gl_vtable)
799         return NULL;
800
801     screen  = DefaultScreen(dpy);
802     rootwin = RootWindow(dpy, screen);
803
804     /* XXX: this won't work for different displays */
805     if (!gl_vtable->has_texture_from_pixmap) {
806         const char *glx_extensions = glXQueryExtensionsString(dpy, screen);
807         if (!glx_extensions)
808             return NULL;
809         if (!find_string("GLX_EXT_texture_from_pixmap", glx_extensions, " "))
810             return NULL;
811         gl_vtable->has_texture_from_pixmap = TRUE;
812     }
813
814     pixo = calloc(1, sizeof(*pixo));
815     if (!pixo)
816         return NULL;
817
818     pixo->dpy           = dpy;
819     pixo->width         = width;
820     pixo->height        = height;
821     pixo->pixmap        = None;
822     pixo->glx_pixmap    = None;
823     pixo->is_bound      = FALSE;
824
825     XGetWindowAttributes(dpy, rootwin, &wattr);
826     pixo->pixmap  = XCreatePixmap(dpy, rootwin, width, height, wattr.depth);
827     if (!pixo->pixmap)
828         goto error;
829
830     /* Initialize FBConfig attributes */
831     for (attr = fbconfig_attrs; *attr != GL_NONE; attr += 2)
832         ;
833     *attr++ = GLX_DEPTH_SIZE;                 *attr++ = wattr.depth;
834     if (wattr.depth == 32) {
835     *attr++ = GLX_ALPHA_SIZE;                 *attr++ = 8;
836     *attr++ = GLX_BIND_TO_TEXTURE_RGBA_EXT;   *attr++ = GL_TRUE;
837     }
838     else {
839     *attr++ = GLX_BIND_TO_TEXTURE_RGB_EXT;    *attr++ = GL_TRUE;
840     }
841     *attr++ = GL_NONE;
842
843     fbconfig = glXChooseFBConfig(
844         dpy,
845         screen,
846         fbconfig_attrs, &n_fbconfig_attrs
847     );
848     if (!fbconfig)
849         goto error;
850
851     /* Initialize GLX Pixmap attributes */
852     for (attr = pixmap_attrs; *attr != GL_NONE; attr += 2)
853         ;
854     *attr++ = GLX_TEXTURE_FORMAT_EXT;
855     if (wattr.depth == 32)
856     *attr++ = GLX_TEXTURE_FORMAT_RGBA_EXT;
857     else
858     *attr++ = GLX_TEXTURE_FORMAT_RGB_EXT;
859     *attr++ = GL_NONE;
860
861     x11_trap_errors();
862     pixo->glx_pixmap = gl_vtable->glx_create_pixmap(
863         dpy,
864         fbconfig[0],
865         pixo->pixmap,
866         pixmap_attrs
867     );
868     g_free(fbconfig);
869     if (x11_untrap_errors() != 0)
870         goto error;
871
872     pixo->target = GL_TEXTURE_2D;
873     glGenTextures(1, &pixo->texture);
874     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
875         goto error;
876     glTexParameteri(pixo->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
877     glTexParameteri(pixo->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
878     gl_unbind_texture(&pixo->old_texture);
879     return pixo;
880
881 error:
882     gl_destroy_pixmap_object(pixo);
883     return NULL;
884 }
885
886 /**
887  * gl_destroy_pixmap_object:
888  * @pixo: a #GLPixmapObject
889  *
890  * Destroys the #GLPixmapObject object.
891  */
892 void
893 gl_destroy_pixmap_object(GLPixmapObject *pixo)
894 {
895     GLVTable * const gl_vtable = gl_get_vtable();
896
897     if (!pixo)
898         return;
899
900     gl_unbind_pixmap_object(pixo);
901
902     if (pixo->texture) {
903         glDeleteTextures(1, &pixo->texture);
904         pixo->texture = 0;
905     }
906
907     if (pixo->glx_pixmap) {
908         gl_vtable->glx_destroy_pixmap(pixo->dpy, pixo->glx_pixmap);
909         pixo->glx_pixmap = None;
910     }
911
912     if (pixo->pixmap) {
913         XFreePixmap(pixo->dpy, pixo->pixmap);
914         pixo->pixmap = None;
915     }
916     g_free(pixo);
917 }
918
919 /**
920  * gl_bind_pixmap_object:
921  * @pixo: a #GLPixmapObject
922  *
923  * Defines a two-dimensional texture image. The texture image is taken
924  * from the @pixo pixmap and need not be copied. The texture target,
925  * format and size are derived from attributes of the @pixo pixmap.
926  *
927  * Return value: %TRUE on success
928  */
929 gboolean
930 gl_bind_pixmap_object(GLPixmapObject *pixo)
931 {
932     GLVTable * const gl_vtable = gl_get_vtable();
933
934     if (pixo->is_bound)
935         return TRUE;
936
937     if (!gl_bind_texture(&pixo->old_texture, pixo->target, pixo->texture))
938         return FALSE;
939
940     x11_trap_errors();
941     gl_vtable->glx_bind_tex_image(
942         pixo->dpy,
943         pixo->glx_pixmap,
944         GLX_FRONT_LEFT_EXT,
945         NULL
946     );
947     XSync(pixo->dpy, False);
948     if (x11_untrap_errors() != 0) {
949         GST_DEBUG("failed to bind pixmap");
950         return FALSE;
951     }
952
953     pixo->is_bound = TRUE;
954     return TRUE;
955 }
956
957 /**
958  * gl_unbind_pixmap_object:
959  * @pixo: a #GLPixmapObject
960  *
961  * Releases a color buffers that is being used as a texture.
962  *
963  * Return value: %TRUE on success
964  */
965 gboolean
966 gl_unbind_pixmap_object(GLPixmapObject *pixo)
967 {
968     GLVTable * const gl_vtable = gl_get_vtable();
969
970     if (!pixo->is_bound)
971         return TRUE;
972
973     x11_trap_errors();
974     gl_vtable->glx_release_tex_image(
975         pixo->dpy,
976         pixo->glx_pixmap,
977         GLX_FRONT_LEFT_EXT
978     );
979     XSync(pixo->dpy, False);
980     if (x11_untrap_errors() != 0) {
981         GST_DEBUG("failed to release pixmap");
982         return FALSE;
983     }
984
985     gl_unbind_texture(&pixo->old_texture);
986
987     pixo->is_bound = FALSE;
988     return TRUE;
989 }
990
991 /**
992  * gl_create_framebuffer_object:
993  * @target: the target to which the texture is bound
994  * @texture: the GL texture to hold the framebuffer
995  * @width: the requested width, in pixels
996  * @height: the requested height, in pixels
997  *
998  * Creates an FBO with the specified texture and size.
999  *
1000  * Return value: the newly created #GLFramebufferObject, or %NULL if
1001  *   an error occurred
1002  */
1003 GLFramebufferObject *
1004 gl_create_framebuffer_object(
1005     GLenum target,
1006     GLuint texture,
1007     guint  width,
1008     guint  height
1009 )
1010 {
1011     GLVTable * const gl_vtable = gl_get_vtable();
1012     GLFramebufferObject *fbo;
1013     GLenum status;
1014
1015     if (!gl_vtable || !gl_vtable->has_framebuffer_object)
1016         return NULL;
1017
1018     /* XXX: we only support GL_TEXTURE_2D at this time */
1019     if (target != GL_TEXTURE_2D)
1020         return NULL;
1021
1022     fbo = calloc(1, sizeof(*fbo));
1023     if (!fbo)
1024         return NULL;
1025
1026     fbo->width          = width;
1027     fbo->height         = height;
1028     fbo->fbo            = 0;
1029     fbo->old_fbo        = 0;
1030     fbo->is_bound       = FALSE;
1031
1032     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1033     gl_vtable->gl_gen_framebuffers(1, &fbo->fbo);
1034     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1035     gl_vtable->gl_framebuffer_texture_2d(
1036         GL_FRAMEBUFFER_EXT,
1037         GL_COLOR_ATTACHMENT0_EXT,
1038         target, texture,
1039         0
1040     );
1041
1042     status = gl_vtable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT);
1043     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1044     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
1045         goto error;
1046     return fbo;
1047
1048 error:
1049     gl_destroy_framebuffer_object(fbo);
1050     return NULL;
1051 }
1052
1053 /**
1054  * gl_destroy_framebuffer_object:
1055  * @fbo: a #GLFramebufferObject
1056  *
1057  * Destroys the @fbo object.
1058  */
1059 void
1060 gl_destroy_framebuffer_object(GLFramebufferObject *fbo)
1061 {
1062     GLVTable * const gl_vtable = gl_get_vtable();
1063
1064     if (!fbo)
1065         return;
1066
1067     gl_unbind_framebuffer_object(fbo);
1068
1069     if (fbo->fbo) {
1070         gl_vtable->gl_delete_framebuffers(1, &fbo->fbo);
1071         fbo->fbo = 0;
1072     }
1073     g_free(fbo);
1074 }
1075
1076 /**
1077  * gl_bind_framebuffer_object:
1078  * @fbo: a #GLFramebufferObject
1079  *
1080  * Binds @fbo object.
1081  *
1082  * Return value: %TRUE on success
1083  */
1084 gboolean
1085 gl_bind_framebuffer_object(GLFramebufferObject *fbo)
1086 {
1087     GLVTable * const gl_vtable = gl_get_vtable();
1088     const guint width  = fbo->width;
1089     const guint height = fbo->height;
1090
1091     const guint attribs = (GL_VIEWPORT_BIT|
1092                            GL_CURRENT_BIT|
1093                            GL_ENABLE_BIT|
1094                            GL_TEXTURE_BIT|
1095                            GL_COLOR_BUFFER_BIT);
1096
1097     if (fbo->is_bound)
1098         return TRUE;
1099
1100     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1101     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1102     glPushAttrib(attribs);
1103     glMatrixMode(GL_PROJECTION);
1104     glPushMatrix();
1105     glLoadIdentity();
1106     glMatrixMode(GL_MODELVIEW);
1107     glPushMatrix();
1108     glLoadIdentity();
1109     glViewport(0, 0, width, height);
1110     glTranslatef(-1.0f, -1.0f, 0.0f);
1111     glScalef(2.0f / width, 2.0f / height, 1.0f);
1112
1113     fbo->is_bound = TRUE;
1114     return TRUE;
1115 }
1116
1117 /**
1118  * gl_unbind_framebuffer_object:
1119  * @fbo: a #GLFramebufferObject
1120  *
1121  * Releases @fbo object.
1122  *
1123  * Return value: %TRUE on success
1124  */
1125 gboolean
1126 gl_unbind_framebuffer_object(GLFramebufferObject *fbo)
1127 {
1128     GLVTable * const gl_vtable = gl_get_vtable();
1129
1130     if (!fbo->is_bound)
1131         return TRUE;
1132
1133     glPopAttrib();
1134     glMatrixMode(GL_PROJECTION);
1135     glPopMatrix();
1136     glMatrixMode(GL_MODELVIEW);
1137     glPopMatrix();
1138     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1139
1140     fbo->is_bound = FALSE;
1141     return TRUE;
1142 }