1 /*
2  *  utils_glx.c - GLX utilities
3  *
4  *  libva-vdpau-driver (C) 2009-2011 Splitted-Desktop Systems
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
19  */
20 
21 #define _GNU_SOURCE 1 /* RTLD_DEFAULT */
22 #include "sysdeps.h"
23 #include <string.h>
24 #include <math.h>
25 #include <dlfcn.h>
26 #include <pthread.h>
27 #include "utils.h"
28 #include "utils_glx.h"
29 #include "utils_x11.h"
30 
31 #define DEBUG 1
32 #include "debug.h"
33 
34 /**
35  * gl_get_error_string:
36  * @error: an OpenGL error enumeration
37  *
38  * Retrieves the string representation the OpenGL @error.
39  *
40  * Return error: the static string representing the OpenGL @error
41  */
42 const char *
gl_get_error_string(GLenum error)43 gl_get_error_string(GLenum error)
44 {
45     static const struct {
46         GLenum val;
47         const char *str;
48     }
49     gl_errors[] = {
50         { GL_NO_ERROR,          "no error" },
51         { GL_INVALID_ENUM,      "invalid enumerant" },
52         { GL_INVALID_VALUE,     "invalid value" },
53         { GL_INVALID_OPERATION, "invalid operation" },
54         { GL_STACK_OVERFLOW,    "stack overflow" },
55         { GL_STACK_UNDERFLOW,   "stack underflow" },
56         { GL_OUT_OF_MEMORY,     "out of memory" },
57 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT
58         { GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "invalid framebuffer operation" },
59 #endif
60         { ~0, NULL }
61     };
62 
63     unsigned int i;
64     for (i = 0; gl_errors[i].str; i++) {
65         if (gl_errors[i].val == error)
66             return gl_errors[i].str;
67     }
68     return "unknown";
69 }
70 
71 /**
72  * gl_purge_errors:
73  *
74  * Purges all OpenGL errors. This function is generally useful to
75  * clear up the pending errors prior to calling gl_check_error().
76  */
77 void
gl_purge_errors(void)78 gl_purge_errors(void)
79 {
80     while (glGetError() != GL_NO_ERROR)
81         ; /* nothing */
82 }
83 
84 /**
85  * gl_check_error:
86  *
87  * Checks whether there is any OpenGL error pending.
88  *
89  * Return value: 1 if an error was encountered
90  */
91 int
gl_check_error(void)92 gl_check_error(void)
93 {
94     GLenum error;
95     int has_errors = 0;
96 
97     while ((error = glGetError()) != GL_NO_ERROR) {
98         D(bug("glError: %s caught", gl_get_error_string(error)));
99         has_errors = 1;
100     }
101     return has_errors;
102 }
103 
104 /**
105  * gl_get_current_color:
106  * @color: the RGBA color components
107  *
108  * This function is a wrapper around glGetFloatv() that does extra error checking.
109  *
110  * Return value: 1 on success
111  */
112 int
gl_get_current_color(float color[4])113 gl_get_current_color(float color[4])
114 {
115     gl_purge_errors();
116     glGetFloatv(GL_CURRENT_COLOR, color);
117     return gl_check_error();
118 }
119 
120 /**
121  * gl_get_param:
122  * @param: the parameter name
123  * @pval: return location for the value
124  *
125  * This function is a wrapper around glGetIntegerv() that does extra
126  * error checking.
127  *
128  * Return value: 1 on success
129  */
130 int
gl_get_param(GLenum param,unsigned int * pval)131 gl_get_param(GLenum param, unsigned int *pval)
132 {
133     GLint val;
134 
135     gl_purge_errors();
136     glGetIntegerv(param, &val);
137     if (gl_check_error())
138         return 0;
139 
140     if (pval)
141         *pval = val;
142     return 1;
143 }
144 
145 /**
146  * gl_get_texture_param:
147  * @target: the target to which the texture is bound
148  * @param: the parameter name
149  * @pval: return location for the value
150  *
151  * This function is a wrapper around glGetTexLevelParameteriv() that
152  * does extra error checking.
153  *
154  * Return value: 1 on success
155  */
156 int
gl_get_texture_param(GLenum target,GLenum param,unsigned int * pval)157 gl_get_texture_param(GLenum target, GLenum param, unsigned int *pval)
158 {
159     GLint val;
160 
161     gl_purge_errors();
162     glGetTexLevelParameteriv(target, 0, param, &val);
163     if (gl_check_error())
164         return 0;
165 
166     if (pval)
167         *pval = val;
168     return 1;
169 }
170 
171 /**
172  * gl_set_bgcolor:
173  * @color: the requested RGB color
174  *
175  * Sets background color to the RGB @color. This basically is a
176  * wrapper around glClearColor().
177  */
178 void
gl_set_bgcolor(uint32_t color)179 gl_set_bgcolor(uint32_t color)
180 {
181     glClearColor(
182         ((color >> 16) & 0xff) / 255.0f,
183         ((color >>  8) & 0xff) / 255.0f,
184         ( color        & 0xff) / 255.0f,
185         1.0f
186     );
187 }
188 
189 /**
190  * gl_set_texture_scaling:
191  * @target: the target to which the texture is currently bound
192  * @scale: scaling algorithm
193  *
194  * Sets scaling algorithm used for the texture currently bound.
195  */
196 void
gl_set_texture_scaling(GLenum target,GLenum scale)197 gl_set_texture_scaling(GLenum target, GLenum scale)
198 {
199     glTexParameteri(target, GL_TEXTURE_MIN_FILTER, scale);
200     glTexParameteri(target, GL_TEXTURE_MAG_FILTER, scale);
201 }
202 
203 /**
204  * gl_perspective:
205  * @fovy: the field of view angle, in degrees, in the y direction
206  * @aspect: the aspect ratio that determines the field of view in the
207  *   x direction.  The aspect ratio is the ratio of x (width) to y
208  *   (height)
209  * @zNear: the distance from the viewer to the near clipping plane
210  *   (always positive)
211  * @zFar: the distance from the viewer to the far clipping plane
212  *   (always positive)
213  *
214  * Specified a viewing frustum into the world coordinate system. This
215  * basically is the Mesa implementation of gluPerspective().
216  */
217 static void
frustum(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble nearval,GLdouble farval)218 frustum(GLdouble left, GLdouble right,
219         GLdouble bottom, GLdouble top,
220         GLdouble nearval, GLdouble farval)
221 {
222     GLdouble x, y, a, b, c, d;
223     GLdouble m[16];
224 
225     x = (2.0 * nearval) / (right - left);
226     y = (2.0 * nearval) / (top - bottom);
227     a = (right + left) / (right - left);
228     b = (top + bottom) / (top - bottom);
229     c = -(farval + nearval) / ( farval - nearval);
230     d = -(2.0 * farval * nearval) / (farval - nearval);
231 
232 #define M(row,col)  m[col*4+row]
233     M(0,0) = x;     M(0,1) = 0.0F;  M(0,2) = a;      M(0,3) = 0.0F;
234     M(1,0) = 0.0F;  M(1,1) = y;     M(1,2) = b;      M(1,3) = 0.0F;
235     M(2,0) = 0.0F;  M(2,1) = 0.0F;  M(2,2) = c;      M(2,3) = d;
236     M(3,0) = 0.0F;  M(3,1) = 0.0F;  M(3,2) = -1.0F;  M(3,3) = 0.0F;
237 #undef M
238 
239     glMultMatrixd(m);
240 }
241 
242 static void
gl_perspective(GLdouble fovy,GLdouble aspect,GLdouble zNear,GLdouble zFar)243 gl_perspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
244 {
245     GLdouble xmin, xmax, ymin, ymax;
246 
247     ymax = zNear * tan(fovy * M_PI / 360.0);
248     ymin = -ymax;
249     xmin = ymin * aspect;
250     xmax = ymax * aspect;
251 
252     /* Don't call glFrustum() because of error semantics (covglu) */
253     frustum(xmin, xmax, ymin, ymax, zNear, zFar);
254 }
255 
256 /**
257  * gl_resize:
258  * @width: the requested width, in pixels
259  * @height: the requested height, in pixels
260  *
261  * Resizes the OpenGL viewport to the specified dimensions, using an
262  * orthogonal projection. (0,0) represents the top-left corner of the
263  * window.
264  */
265 void
gl_resize(unsigned int width,unsigned int height)266 gl_resize(unsigned int width, unsigned int height)
267 {
268 #define FOVY     60.0f
269 #define ASPECT   1.0f
270 #define Z_NEAR   0.1f
271 #define Z_FAR    100.0f
272 #define Z_CAMERA 0.869f
273 
274     glViewport(0, 0, width, height);
275     glMatrixMode(GL_PROJECTION);
276     glLoadIdentity();
277     gl_perspective(FOVY, ASPECT, Z_NEAR, Z_FAR);
278     glMatrixMode(GL_MODELVIEW);
279     glLoadIdentity();
280 
281     glTranslatef(-0.5f, -0.5f, -Z_CAMERA);
282     glScalef(1.0f/width, -1.0f/height, 1.0f/width);
283     glTranslatef(0.0f, -1.0f*height, 0.0f);
284 }
285 
286 /**
287  * gl_create_context:
288  * @dpy: an X11 #Display
289  * @screen: the associated screen of @dpy
290  * @parent: the parent #GLContextState, or %NULL if none is to be used
291  *
292  * Creates a GLX context sharing textures and displays lists with
293  * @parent, if not %NULL.
294  *
295  * Return value: the newly created GLX context
296  */
297 GLContextState *
gl_create_context(Display * dpy,int screen,GLContextState * parent)298 gl_create_context(Display *dpy, int screen, GLContextState *parent)
299 {
300     GLContextState *cs;
301     GLXFBConfig *fbconfigs = NULL;
302     int fbconfig_id, val, n, n_fbconfigs;
303     Status status;
304 
305     static GLint fbconfig_attrs[] = {
306         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
307         GLX_RENDER_TYPE,   GLX_RGBA_BIT,
308         GLX_DOUBLEBUFFER,  True,
309         GLX_RED_SIZE,      8,
310         GLX_GREEN_SIZE,    8,
311         GLX_BLUE_SIZE,     8,
312         None
313     };
314 
315     cs = malloc(sizeof(*cs));
316     if (!cs)
317         goto error;
318 
319     cs->display = dpy;
320     cs->window  = parent ? parent->window : None;
321     cs->visual  = NULL;
322     cs->context = NULL;
323 
324     if (parent && parent->context) {
325         status = glXQueryContext(
326             parent->display,
327             parent->context,
328             GLX_FBCONFIG_ID, &fbconfig_id
329         );
330         if (status != Success)
331             goto error;
332 
333         fbconfigs = glXGetFBConfigs(dpy, screen, &n_fbconfigs);
334         if (!fbconfigs)
335             goto error;
336 
337         /* Find out a GLXFBConfig compatible with the parent context */
338         for (n = 0; n < n_fbconfigs; n++) {
339             status = glXGetFBConfigAttrib(
340                 dpy,
341                 fbconfigs[n],
342                 GLX_FBCONFIG_ID, &val
343             );
344             if (status == Success && val == fbconfig_id)
345                 break;
346         }
347         if (n == n_fbconfigs)
348             goto error;
349     }
350     else {
351         fbconfigs = glXChooseFBConfig(
352             dpy,
353             screen,
354             fbconfig_attrs, &n_fbconfigs
355         );
356         if (!fbconfigs)
357             goto error;
358 
359         /* Select the first one */
360         n = 0;
361     }
362 
363     cs->visual  = glXGetVisualFromFBConfig(dpy, fbconfigs[n]);
364     cs->context = glXCreateNewContext(
365         dpy,
366         fbconfigs[n],
367         GLX_RGBA_TYPE,
368         parent ? parent->context : NULL,
369         True
370     );
371     if (cs->context)
372         goto end;
373 
374 error:
375     gl_destroy_context(cs);
376     cs = NULL;
377 end:
378     if (fbconfigs)
379         XFree(fbconfigs);
380     return cs;
381 }
382 
383 /**
384  * gl_destroy_context:
385  * @cs: a #GLContextState
386  *
387  * Destroys the GLX context @cs
388  */
389 void
gl_destroy_context(GLContextState * cs)390 gl_destroy_context(GLContextState *cs)
391 {
392     if (!cs)
393         return;
394 
395     if (cs->visual) {
396         XFree(cs->visual);
397         cs->visual = NULL;
398     }
399 
400     if (cs->display && cs->context) {
401         if (glXGetCurrentContext() == cs->context)
402             glXMakeCurrent(cs->display, None, NULL);
403         glXDestroyContext(cs->display, cs->context);
404         cs->display = NULL;
405         cs->context = NULL;
406     }
407     free(cs);
408 }
409 
410 /**
411  * gl_init_context:
412  * @cs: a #GLContextState
413  *
414  * Initializes the GLX context @cs with a base environment.
415  */
416 void
gl_init_context(GLContextState * cs)417 gl_init_context(GLContextState *cs)
418 {
419     GLContextState old_cs, tmp_cs;
420 
421     if (!gl_set_current_context(cs, &old_cs))
422         return;
423 
424     glEnable(GL_TEXTURE_2D);
425     glDisable(GL_DEPTH_TEST);
426     glDepthMask(GL_FALSE);
427     glDisable(GL_CULL_FACE);
428     glDrawBuffer(GL_BACK);
429     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
430     glEnable(GL_BLEND);
431     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
432 
433     gl_set_current_context(&old_cs, &tmp_cs);
434 }
435 
436 /**
437  * gl_get_current_context:
438  * @cs: return location to the current #GLContextState
439  *
440  * Retrieves the current GLX context, display and drawable packed into
441  * the #GLContextState struct.
442  */
443 void
gl_get_current_context(GLContextState * cs)444 gl_get_current_context(GLContextState *cs)
445 {
446     cs->display = glXGetCurrentDisplay();
447     cs->window  = glXGetCurrentDrawable();
448     cs->context = glXGetCurrentContext();
449 }
450 
451 /**
452  * gl_set_current_context:
453  * @new_cs: the requested new #GLContextState
454  * @old_cs: return location to the context that was previously current
455  *
456  * Makes the @new_cs GLX context the current GLX rendering context of
457  * the calling thread, replacing the previously current context if
458  * there was one.
459  *
460  * If @old_cs is non %NULL, the previously current GLX context and
461  * window are recorded.
462  *
463  * Return value: 1 on success
464  */
465 int
gl_set_current_context(GLContextState * new_cs,GLContextState * old_cs)466 gl_set_current_context(GLContextState *new_cs, GLContextState *old_cs)
467 {
468     /* If display is NULL, this could be that new_cs was retrieved from
469        gl_get_current_context() with none set previously. If that case,
470        the other fields are also NULL and we don't return an error */
471     if (!new_cs->display)
472         return !new_cs->window && !new_cs->context;
473 
474     if (old_cs) {
475         if (old_cs == new_cs)
476             return 1;
477         gl_get_current_context(old_cs);
478         if (old_cs->display == new_cs->display &&
479             old_cs->window  == new_cs->window  &&
480             old_cs->context == new_cs->context)
481             return 1;
482     }
483     return glXMakeCurrent(new_cs->display, new_cs->window, new_cs->context);
484 }
485 
486 /**
487  * gl_swap_buffers:
488  * @cs: a #GLContextState
489  *
490  * Promotes the contents of the back buffer of the @win window to
491  * become the contents of the front buffer. This simply is wrapper
492  * around glXSwapBuffers().
493  */
494 void
gl_swap_buffers(GLContextState * cs)495 gl_swap_buffers(GLContextState *cs)
496 {
497     glXSwapBuffers(cs->display, cs->window);
498 }
499 
500 /**
501  * gl_create_texture:
502  * @target: the target to which the texture is bound
503  * @format: the format of the pixel data
504  * @width: the requested width, in pixels
505  * @height: the requested height, in pixels
506  *
507  * Creates a texture with the specified dimensions and @format. The
508  * internal format will be automatically derived from @format.
509  *
510  * Return value: the newly created texture name
511  */
512 GLuint
gl_create_texture(GLenum target,GLenum format,unsigned int width,unsigned int height)513 gl_create_texture(GLenum target, GLenum format, unsigned int width, unsigned int height)
514 {
515     GLVTable * const gl_vtable = gl_get_vtable();
516     GLenum internal_format;
517     GLuint texture;
518     unsigned int bytes_per_component;
519 
520     switch (target) {
521     case GL_TEXTURE_2D:
522         if (!gl_vtable->has_texture_non_power_of_two) {
523             D(bug("Unsupported GL_ARB_texture_non_power_of_two extension\n"));
524             return 0;
525         }
526         break;
527     case GL_TEXTURE_RECTANGLE_ARB:
528         if (!gl_vtable->has_texture_rectangle) {
529             D(bug("Unsupported GL_ARB_texture_rectangle extension\n"));
530             return 0;
531         }
532         break;
533     default:
534         D(bug("Unsupported texture target 0x%04x\n", target));
535         return 0;
536     }
537 
538     internal_format = format;
539     switch (format) {
540     case GL_LUMINANCE:
541         bytes_per_component = 1;
542         break;
543     case GL_LUMINANCE_ALPHA:
544         bytes_per_component = 2;
545         break;
546     case GL_RGBA:
547     case GL_BGRA:
548         internal_format = GL_RGBA;
549         bytes_per_component = 4;
550         break;
551     default:
552         bytes_per_component = 0;
553         break;
554     }
555     ASSERT(bytes_per_component > 0);
556 
557     glEnable(target);
558     glGenTextures(1, &texture);
559     glBindTexture(target, texture);
560     gl_set_texture_scaling(target, GL_LINEAR);
561     glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
562     glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
563     glPixelStorei(GL_UNPACK_ALIGNMENT, bytes_per_component);
564     glTexImage2D(
565         target,
566         0,
567         internal_format,
568         width, height,
569         0,
570         format,
571         GL_UNSIGNED_BYTE,
572         NULL
573     );
574     glBindTexture(target, 0);
575     return texture;
576 }
577 
578 /**
579  * get_proc_address:
580  * @name: the name of the OpenGL extension function to lookup
581  *
582  * Returns the specified OpenGL extension function
583  *
584  * Return value: the OpenGL extension matching @name, or %NULL if none
585  *   was found
586  */
587 typedef void (*GLFuncPtr)(void);
588 typedef GLFuncPtr (*GLXGetProcAddressProc)(const char *);
589 
590 static GLFuncPtr
get_proc_address_default(const char * name)591 get_proc_address_default(const char *name)
592 {
593     return NULL;
594 }
595 
596 static GLXGetProcAddressProc
get_proc_address_func(void)597 get_proc_address_func(void)
598 {
599     GLXGetProcAddressProc get_proc_func;
600 
601     dlerror();
602     get_proc_func = (GLXGetProcAddressProc)
603         dlsym(RTLD_DEFAULT, "glXGetProcAddress");
604     if (!dlerror())
605         return get_proc_func;
606 
607     get_proc_func = (GLXGetProcAddressProc)
608         dlsym(RTLD_DEFAULT, "glXGetProcAddressARB");
609     if (!dlerror())
610         return get_proc_func;
611 
612     return get_proc_address_default;
613 }
614 
615 static inline GLFuncPtr
get_proc_address(const char * name)616 get_proc_address(const char *name)
617 {
618     static GLXGetProcAddressProc get_proc_func = NULL;
619     if (!get_proc_func)
620         get_proc_func = get_proc_address_func();
621     return get_proc_func(name);
622 }
623 
624 /**
625  * gl_init_vtable:
626  *
627  * Initializes the global #GLVTable.
628  *
629  * Return value: the #GLVTable filled in with OpenGL extensions, or
630  *   %NULL on error.
631  */
632 static GLVTable gl_vtable_static;
633 
634 static GLVTable *
gl_init_vtable(void)635 gl_init_vtable(void)
636 {
637     GLVTable * const gl_vtable = &gl_vtable_static;
638     const char *gl_extensions = (const char *)glGetString(GL_EXTENSIONS);
639     int has_extension;
640 
641     /* GL_ARB_texture_non_power_of_two */
642     has_extension = (
643         find_string("GL_ARB_texture_non_power_of_two", gl_extensions, " ")
644     );
645     if (has_extension)
646         gl_vtable->has_texture_non_power_of_two = 1;
647 
648     /* GL_ARB_texture_rectangle */
649     has_extension = (
650         find_string("GL_ARB_texture_rectangle", gl_extensions, " ")
651     );
652     if (has_extension)
653         gl_vtable->has_texture_rectangle = 1;
654 
655     /* GLX_EXT_texture_from_pixmap */
656     gl_vtable->glx_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC)
657         get_proc_address("glXBindTexImageEXT");
658     if (!gl_vtable->glx_bind_tex_image)
659         return NULL;
660     gl_vtable->glx_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC)
661         get_proc_address("glXReleaseTexImageEXT");
662     if (!gl_vtable->glx_release_tex_image)
663         return NULL;
664 
665     /* GL_ARB_framebuffer_object */
666     has_extension = (
667         find_string("GL_ARB_framebuffer_object", gl_extensions, " ") ||
668         find_string("GL_EXT_framebuffer_object", gl_extensions, " ")
669     );
670     if (has_extension) {
671         gl_vtable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
672             get_proc_address("glGenFramebuffersEXT");
673         if (!gl_vtable->gl_gen_framebuffers)
674             return NULL;
675         gl_vtable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
676             get_proc_address("glDeleteFramebuffersEXT");
677         if (!gl_vtable->gl_delete_framebuffers)
678             return NULL;
679         gl_vtable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
680             get_proc_address("glBindFramebufferEXT");
681         if (!gl_vtable->gl_bind_framebuffer)
682             return NULL;
683         gl_vtable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
684             get_proc_address("glGenRenderbuffersEXT");
685         if (!gl_vtable->gl_gen_renderbuffers)
686             return NULL;
687         gl_vtable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
688             get_proc_address("glDeleteRenderbuffersEXT");
689         if (!gl_vtable->gl_delete_renderbuffers)
690             return NULL;
691         gl_vtable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
692             get_proc_address("glBindRenderbufferEXT");
693         if (!gl_vtable->gl_bind_renderbuffer)
694             return NULL;
695         gl_vtable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
696             get_proc_address("glRenderbufferStorageEXT");
697         if (!gl_vtable->gl_renderbuffer_storage)
698             return NULL;
699         gl_vtable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
700             get_proc_address("glFramebufferRenderbufferEXT");
701         if (!gl_vtable->gl_framebuffer_renderbuffer)
702             return NULL;
703         gl_vtable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
704             get_proc_address("glFramebufferTexture2DEXT");
705         if (!gl_vtable->gl_framebuffer_texture_2d)
706             return NULL;
707         gl_vtable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
708             get_proc_address("glCheckFramebufferStatusEXT");
709         if (!gl_vtable->gl_check_framebuffer_status)
710             return NULL;
711         gl_vtable->has_framebuffer_object = 1;
712     }
713 
714     /* GL_ARB_fragment_program */
715     has_extension = (
716         find_string("GL_ARB_fragment_program", gl_extensions, " ")
717     );
718     if (has_extension) {
719         gl_vtable->gl_gen_programs = (PFNGLGENPROGRAMSARBPROC)
720             get_proc_address("glGenProgramsARB");
721         if (!gl_vtable->gl_gen_programs)
722             return NULL;
723         gl_vtable->gl_delete_programs = (PFNGLDELETEPROGRAMSARBPROC)
724             get_proc_address("glDeleteProgramsARB");
725         if (!gl_vtable->gl_delete_programs)
726             return NULL;
727         gl_vtable->gl_bind_program = (PFNGLBINDPROGRAMARBPROC)
728             get_proc_address("glBindProgramARB");
729         if (!gl_vtable->gl_bind_program)
730             return NULL;
731         gl_vtable->gl_program_string = (PFNGLPROGRAMSTRINGARBPROC)
732             get_proc_address("glProgramStringARB");
733         if (!gl_vtable->gl_program_string)
734             return NULL;
735         gl_vtable->gl_get_program_iv = (PFNGLGETPROGRAMIVARBPROC)
736             get_proc_address("glGetProgramivARB");
737         if (!gl_vtable->gl_get_program_iv)
738             return NULL;
739         gl_vtable->gl_program_local_parameter_4fv = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)
740             get_proc_address("glProgramLocalParameter4fvARB");
741         if (!gl_vtable->gl_program_local_parameter_4fv)
742             return NULL;
743         gl_vtable->has_fragment_program = 1;
744     }
745 
746     /* GL_ARB_multitexture */
747     has_extension = (
748         find_string("GL_ARB_multitexture", gl_extensions, " ")
749     );
750     if (has_extension) {
751         gl_vtable->gl_active_texture = (PFNGLACTIVETEXTUREPROC)
752             get_proc_address("glActiveTextureARB");
753         if (!gl_vtable->gl_active_texture)
754             return NULL;
755         gl_vtable->gl_multi_tex_coord_2f = (PFNGLMULTITEXCOORD2FPROC)
756             get_proc_address("glMultiTexCoord2fARB");
757         if (!gl_vtable->gl_multi_tex_coord_2f)
758             return NULL;
759         gl_vtable->has_multitexture = 1;
760     }
761 
762     /* GL_NV_vdpau_interop */
763     has_extension = (
764         find_string("GL_NV_vdpau_interop", gl_extensions, " ")
765     );
766     if (has_extension) {
767         gl_vtable->gl_vdpau_init = (PFNGLVDPAUINITNVPROC)
768             get_proc_address("glVDPAUInitNV");
769         if (!gl_vtable->gl_vdpau_init)
770             return NULL;
771         gl_vtable->gl_vdpau_fini = (PFNGLVDPAUFININVPROC)
772             get_proc_address("glVDPAUFiniNV");
773         if (!gl_vtable->gl_vdpau_fini)
774             return NULL;
775         gl_vtable->gl_vdpau_register_video_surface = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)
776             get_proc_address("glVDPAURegisterVideoSurfaceNV");
777         if (!gl_vtable->gl_vdpau_register_video_surface)
778             return NULL;
779         gl_vtable->gl_vdpau_register_output_surface = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)
780             get_proc_address("glVDPAURegisterOutputSurfaceNV");
781         if (!gl_vtable->gl_vdpau_register_output_surface)
782             return NULL;
783         gl_vtable->gl_vdpau_is_surface = (PFNGLVDPAUISSURFACENVPROC)
784             get_proc_address("glVDPAUIsSurfaceNV");
785         if (!gl_vtable->gl_vdpau_is_surface)
786             return NULL;
787         gl_vtable->gl_vdpau_unregister_surface = (PFNGLVDPAUUNREGISTERSURFACENVPROC)
788             get_proc_address("glVDPAUUnregisterSurfaceNV");
789         if (!gl_vtable->gl_vdpau_unregister_surface)
790             return NULL;
791         gl_vtable->gl_vdpau_get_surface_iv = (PFNGLVDPAUGETSURFACEIVNVPROC)
792             get_proc_address("glVDPAUGetSurfaceivNV");
793         if (!gl_vtable->gl_vdpau_get_surface_iv)
794             return NULL;
795         gl_vtable->gl_vdpau_surface_access = (PFNGLVDPAUSURFACEACCESSNVPROC)
796             get_proc_address("glVDPAUSurfaceAccessNV");
797         if (!gl_vtable->gl_vdpau_surface_access)
798             return NULL;
799         gl_vtable->gl_vdpau_map_surfaces = (PFNGLVDPAUMAPSURFACESNVPROC)
800             get_proc_address("glVDPAUMapSurfacesNV");
801         if (!gl_vtable->gl_vdpau_map_surfaces)
802             return NULL;
803         gl_vtable->gl_vdpau_unmap_surfaces = (PFNGLVDPAUUNMAPSURFACESNVPROC)
804             get_proc_address("glVDPAUUnmapSurfacesNV");
805         if (!gl_vtable->gl_vdpau_unmap_surfaces)
806             return NULL;
807         gl_vtable->has_vdpau_interop = 1;
808     }
809     return gl_vtable;
810 }
811 
812 /**
813  * gl_get_vtable:
814  *
815  * Retrieves a VTable for OpenGL extensions.
816  *
817  * Return value: VTable for OpenGL extensions
818  */
819 GLVTable *
gl_get_vtable(void)820 gl_get_vtable(void)
821 {
822     static pthread_mutex_t mutex          = PTHREAD_MUTEX_INITIALIZER;
823     static int             gl_vtable_init = 1;
824     static GLVTable       *gl_vtable      = NULL;
825 
826     pthread_mutex_lock(&mutex);
827     if (gl_vtable_init) {
828         gl_vtable_init = 0;
829         gl_vtable      = gl_init_vtable();
830     }
831     pthread_mutex_unlock(&mutex);
832     return gl_vtable;
833 }
834 
835 /**
836  * gl_create_pixmap_object:
837  * @dpy: an X11 #Display
838  * @target: the target to which the texture is bound
839  * @width: the request width, in pixels
840  * @height: the request height, in pixels
841  *
842  * Creates a #GLPixmapObject of the specified dimensions. This
843  * requires the GLX_EXT_texture_from_pixmap extension.
844  *
845  * Return value: the newly created #GLPixmapObject object
846  */
847 GLPixmapObject *
gl_create_pixmap_object(Display * dpy,GLenum target,unsigned int width,unsigned int height)848 gl_create_pixmap_object(
849     Display     *dpy,
850     GLenum       target,
851     unsigned int width,
852     unsigned int height
853 )
854 {
855     GLVTable * const    gl_vtable = gl_get_vtable();
856     GLPixmapObject     *pixo;
857     GLXFBConfig        *fbconfig;
858     int                 screen;
859     Window              rootwin;
860     XWindowAttributes   wattr;
861     int                *attr;
862     int                 n_fbconfig_attrs;
863 
864     int fbconfig_attrs[32] = {
865         GLX_DRAWABLE_TYPE,      GLX_PIXMAP_BIT,
866         GLX_DOUBLEBUFFER,       GL_FALSE,
867         GLX_RENDER_TYPE,        GLX_RGBA_BIT,
868         GLX_X_RENDERABLE,       GL_TRUE,
869         GLX_Y_INVERTED_EXT,     GL_TRUE,
870         GLX_RED_SIZE,           8,
871         GLX_GREEN_SIZE,         8,
872         GLX_BLUE_SIZE,          8,
873         GL_NONE,
874     };
875 
876     int pixmap_attrs[10] = {
877         GLX_MIPMAP_TEXTURE_EXT, GL_FALSE,
878         GL_NONE,
879     };
880 
881     if (!gl_vtable)
882         return NULL;
883 
884     screen  = DefaultScreen(dpy);
885     rootwin = RootWindow(dpy, screen);
886 
887     /* XXX: this won't work for different displays */
888     if (!gl_vtable->has_texture_from_pixmap) {
889         const char *glx_extensions;
890         int glx_major, glx_minor;
891         glx_extensions = glXQueryExtensionsString(dpy, screen);
892         if (!glx_extensions)
893             return NULL;
894         if (!find_string("GLX_EXT_texture_from_pixmap", glx_extensions, " "))
895             return NULL;
896         if (!glXQueryVersion(dpy, &glx_major, &glx_minor))
897             return NULL;
898         if (glx_major < 1 || (glx_major == 1 && glx_minor < 3)) /* 1.3 */
899             return NULL;
900         gl_vtable->has_texture_from_pixmap = 1;
901     }
902 
903     pixo = calloc(1, sizeof(*pixo));
904     if (!pixo)
905         return NULL;
906 
907     pixo->dpy           = dpy;
908     pixo->target        = target;
909     pixo->width         = width;
910     pixo->height        = height;
911     pixo->pixmap        = None;
912     pixo->glx_pixmap    = None;
913     pixo->is_bound      = 0;
914 
915     XGetWindowAttributes(dpy, rootwin, &wattr);
916     pixo->pixmap  = XCreatePixmap(dpy, rootwin, width, height, wattr.depth);
917     if (!pixo->pixmap)
918         goto error;
919 
920     /* Initialize FBConfig attributes */
921     for (attr = fbconfig_attrs; *attr != GL_NONE; attr += 2)
922         ;
923     *attr++ = GLX_DEPTH_SIZE;                 *attr++ = wattr.depth;
924     if (wattr.depth == 32) {
925     *attr++ = GLX_ALPHA_SIZE;                 *attr++ = 8;
926     *attr++ = GLX_BIND_TO_TEXTURE_RGBA_EXT;   *attr++ = GL_TRUE;
927     }
928     else {
929     *attr++ = GLX_BIND_TO_TEXTURE_RGB_EXT;    *attr++ = GL_TRUE;
930     }
931     *attr++ = GL_NONE;
932 
933     fbconfig = glXChooseFBConfig(
934         dpy,
935         screen,
936         fbconfig_attrs, &n_fbconfig_attrs
937     );
938     if (!fbconfig)
939         goto error;
940 
941     /* Initialize GLX Pixmap attributes */
942     for (attr = pixmap_attrs; *attr != GL_NONE; attr += 2)
943         ;
944     *attr++ = GLX_TEXTURE_TARGET_EXT;
945     switch (target) {
946     case GL_TEXTURE_2D:
947         *attr++ = GLX_TEXTURE_2D_EXT;
948         break;
949     case GL_TEXTURE_RECTANGLE_ARB:
950         *attr++ = GLX_TEXTURE_RECTANGLE_EXT;
951         break;
952     default:
953         goto error;
954     }
955     *attr++ = GLX_TEXTURE_FORMAT_EXT;
956     if (wattr.depth == 32)
957     *attr++ = GLX_TEXTURE_FORMAT_RGBA_EXT;
958     else
959     *attr++ = GLX_TEXTURE_FORMAT_RGB_EXT;
960     *attr++ = GL_NONE;
961 
962     x11_trap_errors();
963     pixo->glx_pixmap = glXCreatePixmap(dpy, fbconfig[0], pixo->pixmap, pixmap_attrs);
964     free(fbconfig);
965     if (x11_untrap_errors() != 0)
966         goto error;
967 
968     glEnable(pixo->target);
969     glGenTextures(1, &pixo->texture);
970     glBindTexture(pixo->target, pixo->texture);
971     gl_set_texture_scaling(pixo->target, GL_LINEAR);
972     glBindTexture(pixo->target, 0);
973     return pixo;
974 
975 error:
976     gl_destroy_pixmap_object(pixo);
977     return NULL;
978 }
979 
980 /**
981  * gl_destroy_pixmap_object:
982  * @pixo: a #GLPixmapObject
983  *
984  * Destroys the #GLPixmapObject object.
985  */
986 void
gl_destroy_pixmap_object(GLPixmapObject * pixo)987 gl_destroy_pixmap_object(GLPixmapObject *pixo)
988 {
989     if (!pixo)
990         return;
991 
992     gl_unbind_pixmap_object(pixo);
993 
994     if (pixo->texture) {
995         glDeleteTextures(1, &pixo->texture);
996         pixo->texture = 0;
997     }
998 
999     if (pixo->glx_pixmap) {
1000         glXDestroyPixmap(pixo->dpy, pixo->glx_pixmap);
1001         pixo->glx_pixmap = None;
1002     }
1003 
1004     if (pixo->pixmap) {
1005         XFreePixmap(pixo->dpy, pixo->pixmap);
1006         pixo->pixmap = None;
1007     }
1008     free(pixo);
1009 }
1010 
1011 /**
1012  * gl_bind_pixmap_object:
1013  * @pixo: a #GLPixmapObject
1014  *
1015  * Defines a two-dimensional texture image. The texture image is taken
1016  * from the @pixo pixmap and need not be copied. The texture target,
1017  * format and size are derived from attributes of the @pixo pixmap.
1018  *
1019  * Return value: 1 on success
1020  */
1021 int
gl_bind_pixmap_object(GLPixmapObject * pixo)1022 gl_bind_pixmap_object(GLPixmapObject *pixo)
1023 {
1024     GLVTable * const gl_vtable = gl_get_vtable();
1025 
1026     if (pixo->is_bound)
1027         return 1;
1028 
1029     glBindTexture(pixo->target, pixo->texture);
1030 
1031     x11_trap_errors();
1032     gl_vtable->glx_bind_tex_image(
1033         pixo->dpy,
1034         pixo->glx_pixmap,
1035         GLX_FRONT_LEFT_EXT,
1036         NULL
1037     );
1038     XSync(pixo->dpy, False);
1039     if (x11_untrap_errors() != 0) {
1040         D(bug("failed to bind pixmap"));
1041         return 0;
1042     }
1043 
1044     pixo->is_bound = 1;
1045     return 1;
1046 }
1047 
1048 /**
1049  * gl_unbind_pixmap_object:
1050  * @pixo: a #GLPixmapObject
1051  *
1052  * Releases a color buffers that is being used as a texture.
1053  *
1054  * Return value: 1 on success
1055  */
1056 int
gl_unbind_pixmap_object(GLPixmapObject * pixo)1057 gl_unbind_pixmap_object(GLPixmapObject *pixo)
1058 {
1059     GLVTable * const gl_vtable = gl_get_vtable();
1060 
1061     if (!pixo->is_bound)
1062         return 1;
1063 
1064     x11_trap_errors();
1065     gl_vtable->glx_release_tex_image(
1066         pixo->dpy,
1067         pixo->glx_pixmap,
1068         GLX_FRONT_LEFT_EXT
1069     );
1070     XSync(pixo->dpy, False);
1071     if (x11_untrap_errors() != 0) {
1072         D(bug("failed to release pixmap"));
1073         return 0;
1074     }
1075 
1076     glBindTexture(pixo->target, 0);
1077 
1078     pixo->is_bound = 0;
1079     return 1;
1080 }
1081 
1082 /**
1083  * gl_create_framebuffer_object:
1084  * @target: the target to which the texture is bound
1085  * @texture: the GL texture to hold the framebuffer
1086  * @width: the requested width, in pixels
1087  * @height: the requested height, in pixels
1088  *
1089  * Creates an FBO with the specified texture and size.
1090  *
1091  * Return value: the newly created #GLFramebufferObject, or %NULL if
1092  *   an error occurred
1093  */
1094 GLFramebufferObject *
gl_create_framebuffer_object(GLenum target,GLuint texture,unsigned int width,unsigned int height)1095 gl_create_framebuffer_object(
1096     GLenum       target,
1097     GLuint       texture,
1098     unsigned int width,
1099     unsigned int height
1100 )
1101 {
1102     GLVTable * const gl_vtable = gl_get_vtable();
1103     GLFramebufferObject *fbo;
1104     GLenum status;
1105 
1106     if (!gl_vtable || !gl_vtable->has_framebuffer_object)
1107         return NULL;
1108 
1109     fbo = calloc(1, sizeof(*fbo));
1110     if (!fbo)
1111         return NULL;
1112 
1113     fbo->width    = width;
1114     fbo->height   = height;
1115     fbo->fbo      = 0;
1116     fbo->old_fbo  = 0;
1117     fbo->is_bound = 0;
1118 
1119     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1120     gl_vtable->gl_gen_framebuffers(1, &fbo->fbo);
1121     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1122     gl_vtable->gl_framebuffer_texture_2d(
1123         GL_FRAMEBUFFER_EXT,
1124         GL_COLOR_ATTACHMENT0_EXT,
1125         target, texture,
1126         0
1127     );
1128 
1129     status = gl_vtable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT);
1130     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1131     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
1132         goto error;
1133     return fbo;
1134 
1135 error:
1136     gl_destroy_framebuffer_object(fbo);
1137     return NULL;
1138 }
1139 
1140 /**
1141  * gl_destroy_framebuffer_object:
1142  * @fbo: a #GLFramebufferObject
1143  *
1144  * Destroys the @fbo object.
1145  */
1146 void
gl_destroy_framebuffer_object(GLFramebufferObject * fbo)1147 gl_destroy_framebuffer_object(GLFramebufferObject *fbo)
1148 {
1149     GLVTable * const gl_vtable = gl_get_vtable();
1150 
1151     if (!fbo)
1152         return;
1153 
1154     gl_unbind_framebuffer_object(fbo);
1155 
1156     if (fbo->fbo) {
1157         gl_vtable->gl_delete_framebuffers(1, &fbo->fbo);
1158         fbo->fbo = 0;
1159     }
1160     free(fbo);
1161 }
1162 
1163 /**
1164  * gl_bind_framebuffer_object:
1165  * @fbo: a #GLFramebufferObject
1166  *
1167  * Binds @fbo object.
1168  *
1169  * Return value: 1 on success
1170  */
1171 int
gl_bind_framebuffer_object(GLFramebufferObject * fbo)1172 gl_bind_framebuffer_object(GLFramebufferObject *fbo)
1173 {
1174     GLVTable * const gl_vtable = gl_get_vtable();
1175     const unsigned int width   = fbo->width;
1176     const unsigned int height  = fbo->height;
1177 
1178     const unsigned int attribs = (GL_VIEWPORT_BIT |
1179                                   GL_CURRENT_BIT  |
1180                                   GL_ENABLE_BIT   |
1181                                   GL_TEXTURE_BIT  |
1182                                   GL_COLOR_BUFFER_BIT);
1183 
1184     if (fbo->is_bound)
1185         return 1;
1186 
1187     gl_get_param(GL_FRAMEBUFFER_BINDING, &fbo->old_fbo);
1188     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->fbo);
1189     glPushAttrib(attribs);
1190     glMatrixMode(GL_PROJECTION);
1191     glPushMatrix();
1192     glLoadIdentity();
1193     glMatrixMode(GL_MODELVIEW);
1194     glPushMatrix();
1195     glLoadIdentity();
1196     glViewport(0, 0, width, height);
1197     glTranslatef(-1.0f, -1.0f, 0.0f);
1198     glScalef(2.0f / width, 2.0f / height, 1.0f);
1199 
1200     fbo->is_bound = 1;
1201     return 1;
1202 }
1203 
1204 /**
1205  * gl_unbind_framebuffer_object:
1206  * @fbo: a #GLFramebufferObject
1207  *
1208  * Releases @fbo object.
1209  *
1210  * Return value: 1 on success
1211  */
1212 int
gl_unbind_framebuffer_object(GLFramebufferObject * fbo)1213 gl_unbind_framebuffer_object(GLFramebufferObject *fbo)
1214 {
1215     GLVTable * const gl_vtable = gl_get_vtable();
1216 
1217     if (!fbo->is_bound)
1218         return 1;
1219 
1220     glPopAttrib();
1221     glMatrixMode(GL_PROJECTION);
1222     glPopMatrix();
1223     glMatrixMode(GL_MODELVIEW);
1224     glPopMatrix();
1225     gl_vtable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo->old_fbo);
1226 
1227     fbo->is_bound = 0;
1228     return 1;
1229 }
1230 
1231 /**
1232  * gl_vdpau_init:
1233  * @device: a #VdpDevice
1234  * @get_proc_address: the #VdpGetProcAddress generated during
1235  *   #VdpDevice creation
1236  *
1237  * Informs the GL context which VDPAU device to interact with.
1238  *
1239  * Return value: 1 on success
1240  */
1241 int
gl_vdpau_init(VdpDevice device,VdpGetProcAddress get_proc_address)1242 gl_vdpau_init(VdpDevice device, VdpGetProcAddress get_proc_address)
1243 {
1244     GLVTable * const gl_vtable = gl_get_vtable();
1245 
1246     if (!gl_vtable || !gl_vtable->has_vdpau_interop)
1247         return 0;
1248 
1249     gl_vtable->gl_vdpau_init((void *)(uintptr_t)device, get_proc_address);
1250 
1251     return 1;
1252 }
1253 
1254 /**
1255  * gl_vdpau_exit:
1256  *
1257  * Disposes the VDPAU/GL interact functionality for the current context.
1258  */
1259 void
gl_vdpau_exit(void)1260 gl_vdpau_exit(void)
1261 {
1262     GLVTable * const gl_vtable = gl_get_vtable();
1263 
1264     if (!gl_vtable || !gl_vtable->has_vdpau_interop)
1265         return;
1266 
1267     gl_vtable->gl_vdpau_fini();
1268 }
1269 
1270 /**
1271  * gl_vdpau_create_video_surface:
1272  * @target: the texture target
1273  * @surface: the VDPAU video surface to wrap
1274  *
1275  * Creates a VDPAU/GL surface from the specified @surface, which is a
1276  * #VdpVideoSurface.
1277  *
1278  * Return value: the newly created #GLVdpSurface, or %NULL if an error
1279  *   occurred
1280  */
1281 GLVdpSurface *
gl_vdpau_create_video_surface(GLenum target,VdpVideoSurface surface)1282 gl_vdpau_create_video_surface(GLenum target, VdpVideoSurface surface)
1283 {
1284     GLVTable * const gl_vtable = gl_get_vtable();
1285     GLVdpSurface *s;
1286     unsigned int i;
1287 
1288     if (!gl_vtable || !gl_vtable->has_vdpau_interop)
1289         return NULL;
1290 
1291     s = calloc(1, sizeof(*s));
1292     if (!s)
1293         return NULL;
1294 
1295     s->target           = target;
1296     s->num_textures     = 4;
1297     s->is_bound         = 0;
1298 
1299     glEnable(s->target);
1300     glGenTextures(s->num_textures, &s->textures[0]);
1301 
1302     s->surface = gl_vtable->gl_vdpau_register_video_surface(
1303         (void *)(uintptr_t)surface,
1304         s->target,
1305         s->num_textures, s->textures
1306     );
1307     if (!s->surface)
1308         goto error;
1309 
1310     for (i = 0; i < s->num_textures; i++) {
1311         glBindTexture(s->target, s->textures[i]);
1312         gl_set_texture_scaling(s->target, GL_LINEAR);
1313         glTexParameteri(s->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1314         glTexParameteri(s->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1315         glBindTexture(s->target, 0);
1316     }
1317 
1318     /* XXX: optimize for reading only */
1319     gl_vtable->gl_vdpau_surface_access(s->surface, GL_READ_ONLY);
1320     return s;
1321 
1322 error:
1323     gl_vdpau_destroy_surface(s);
1324     return NULL;
1325 }
1326 
1327 /**
1328  * gl_vdpau_create_output_surface:
1329  * @target: the texture target
1330  * @surface: the VDPAU output surface to wrap
1331  *
1332  * Creates a VDPAU/GL surface from the specified @surface, which is a
1333  * #VdpOutputSurface.
1334  *
1335  * Return value: the newly created #GLVdpSurface, or %NULL if an error
1336  *   occurred
1337  */
1338 GLVdpSurface *
gl_vdpau_create_output_surface(GLenum target,VdpOutputSurface surface)1339 gl_vdpau_create_output_surface(GLenum target, VdpOutputSurface surface)
1340 {
1341     GLVTable * const gl_vtable = gl_get_vtable();
1342     GLVdpSurface *s;
1343 
1344     if (!gl_vtable || !gl_vtable->has_vdpau_interop)
1345         return NULL;
1346 
1347     s = calloc(1, sizeof(*s));
1348     if (!s)
1349         return NULL;
1350 
1351     s->target           = target;
1352     s->num_textures     = 1;
1353     s->is_bound         = 0;
1354 
1355     glEnable(s->target);
1356     glGenTextures(1, &s->textures[0]);
1357 
1358     s->surface = gl_vtable->gl_vdpau_register_output_surface(
1359         (void *)(uintptr_t)surface,
1360         s->target,
1361         s->num_textures, s->textures
1362     );
1363     if (!s->surface)
1364         goto error;
1365 
1366     glBindTexture(s->target, s->textures[0]);
1367     gl_set_texture_scaling(s->target, GL_LINEAR);
1368     glBindTexture(s->target, 0);
1369 
1370     /* XXX: optimize for reading only */
1371     gl_vtable->gl_vdpau_surface_access(s->surface, GL_READ_ONLY);
1372     return s;
1373 
1374 error:
1375     gl_vdpau_destroy_surface(s);
1376     return NULL;
1377 }
1378 
1379 /**
1380  * gl_vdpau_destroy_surface:
1381  * @s: a #GLVdpSurface
1382  *
1383  * Destroys the @s object.
1384  */
1385 void
gl_vdpau_destroy_surface(GLVdpSurface * s)1386 gl_vdpau_destroy_surface(GLVdpSurface *s)
1387 {
1388     GLVTable * const gl_vtable = gl_get_vtable();
1389     unsigned int i;
1390 
1391     if (!s)
1392         return;
1393 
1394     gl_vdpau_unbind_surface(s);
1395 
1396     if (s->surface) {
1397         gl_vtable->gl_vdpau_unregister_surface(s->surface);
1398         s->surface = 0;
1399     }
1400 
1401     if (s->num_textures > 0) {
1402         glDeleteTextures(s->num_textures, s->textures);
1403         for (i = 0; i < s->num_textures; i++)
1404             s->textures[i] = 0;
1405         s->num_textures = 0;
1406     }
1407     free(s);
1408 }
1409 
1410 /**
1411  * gl_vdpau_bind_surface:
1412  * @s: a #GLVdpSurface
1413  *
1414  * Binds surface @s.
1415  *
1416  * Return value: 1 on success
1417  */
1418 int
gl_vdpau_bind_surface(GLVdpSurface * s)1419 gl_vdpau_bind_surface(GLVdpSurface *s)
1420 {
1421     GLVTable * const gl_vtable = gl_get_vtable();
1422 
1423     if (s->is_bound)
1424         return 1;
1425 
1426     gl_vtable->gl_vdpau_map_surfaces(1, &s->surface);
1427     s->is_bound = 1;
1428     return 1;
1429 }
1430 
1431 /**
1432  * gl_vdpau_unbind_surface:
1433  * @s: a #GLVdpSurface
1434  *
1435  * Releases surface @s.
1436  *
1437  * Return value: 1 on success
1438  */
1439 int
gl_vdpau_unbind_surface(GLVdpSurface * s)1440 gl_vdpau_unbind_surface(GLVdpSurface *s)
1441 {
1442     GLVTable * const gl_vtable = gl_get_vtable();
1443 
1444     if (!s->is_bound)
1445         return 1;
1446 
1447     gl_vtable->gl_vdpau_unmap_surfaces(1, &s->surface);
1448     s->is_bound = 0;
1449     return 1;
1450 }
1451