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