1 /*
2 * Copyright (C) 2009 Splitted-Desktop Systems. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sub license, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial portions
14 * of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
19 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 #define _GNU_SOURCE 1
26 #include "sysdeps.h"
27 #include "va_glx_private.h"
28 #include "va_glx_impl.h"
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <dlfcn.h>
35
va_glx_error_message(const char * format,...)36 static void va_glx_error_message(const char *format, ...)
37 {
38 va_list args;
39 va_start(args, format);
40 fprintf(stderr, "libva-glx error: ");
41 vfprintf(stderr, format, args);
42 va_end(args);
43 }
44
45 // X error trap
46 static int x11_error_code = 0;
47 static int (*old_error_handler)(Display *, XErrorEvent *);
48
error_handler(Display * dpy,XErrorEvent * error)49 static int error_handler(Display *dpy, XErrorEvent *error)
50 {
51 x11_error_code = error->error_code;
52 return 0;
53 }
54
x11_trap_errors(void)55 static void x11_trap_errors(void)
56 {
57 x11_error_code = 0;
58 old_error_handler = XSetErrorHandler(error_handler);
59 }
60
x11_untrap_errors(void)61 static int x11_untrap_errors(void)
62 {
63 XSetErrorHandler(old_error_handler);
64 return x11_error_code;
65 }
66
67 // Returns a string representation of an OpenGL error
gl_get_error_string(GLenum error)68 static const char *gl_get_error_string(GLenum error)
69 {
70 static const struct {
71 GLenum val;
72 const char *str;
73 }
74 gl_errors[] = {
75 { GL_NO_ERROR, "no error" },
76 { GL_INVALID_ENUM, "invalid enumerant" },
77 { GL_INVALID_VALUE, "invalid value" },
78 { GL_INVALID_OPERATION, "invalid operation" },
79 { GL_STACK_OVERFLOW, "stack overflow" },
80 { GL_STACK_UNDERFLOW, "stack underflow" },
81 { GL_OUT_OF_MEMORY, "out of memory" },
82 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT
83 { GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "invalid framebuffer operation" },
84 #endif
85 { ~0, NULL }
86 };
87
88 int i;
89 for (i = 0; gl_errors[i].str; i++) {
90 if (gl_errors[i].val == error)
91 return gl_errors[i].str;
92 }
93 return "unknown";
94 }
95
gl_do_check_error(int report)96 static inline int gl_do_check_error(int report)
97 {
98 GLenum error;
99 int is_error = 0;
100 while ((error = glGetError()) != GL_NO_ERROR) {
101 if (report)
102 va_glx_error_message("glError: %s caught\n",
103 gl_get_error_string(error));
104 is_error = 1;
105 }
106 return is_error;
107 }
108
gl_purge_errors(void)109 static inline void gl_purge_errors(void)
110 {
111 gl_do_check_error(0);
112 }
113
gl_check_error(void)114 static inline int gl_check_error(void)
115 {
116 return gl_do_check_error(1);
117 }
118
119 // glGetTexLevelParameteriv() wrapper
gl_get_texture_param(GLenum param,unsigned int * pval)120 static int gl_get_texture_param(GLenum param, unsigned int *pval)
121 {
122 GLint val;
123
124 gl_purge_errors();
125 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, param, &val);
126 if (gl_check_error())
127 return 0;
128 if (pval)
129 *pval = val;
130 return 1;
131 }
132
133 // Returns the OpenGL VTable
gl_get_vtable(VADriverContextP ctx)134 static inline VAOpenGLVTableP gl_get_vtable(VADriverContextP ctx)
135 {
136 return &VA_DRIVER_CONTEXT_GLX(ctx)->gl_vtable;
137 }
138
139 // Lookup for a GLX function
140 typedef void (*GLFuncPtr)(void);
141 typedef GLFuncPtr(*GLXGetProcAddressProc)(const char *);
142
get_proc_address_default(const char * name)143 static GLFuncPtr get_proc_address_default(const char *name)
144 {
145 return NULL;
146 }
147
get_proc_address_func(void)148 static GLXGetProcAddressProc get_proc_address_func(void)
149 {
150 GLXGetProcAddressProc get_proc_func;
151
152 dlerror();
153 get_proc_func = (GLXGetProcAddressProc)
154 dlsym(RTLD_DEFAULT, "glXGetProcAddress");
155 if (!dlerror())
156 return get_proc_func;
157
158 get_proc_func = (GLXGetProcAddressProc)
159 dlsym(RTLD_DEFAULT, "glXGetProcAddressARB");
160 if (!dlerror())
161 return get_proc_func;
162
163 return get_proc_address_default;
164 }
165
get_proc_address(const char * name)166 static inline GLFuncPtr get_proc_address(const char *name)
167 {
168 static GLXGetProcAddressProc get_proc_func = NULL;
169 if (!get_proc_func)
170 get_proc_func = get_proc_address_func();
171 return get_proc_func(name);
172 }
173
174 // Check for GLX extensions (TFP, FBO)
check_extension(const char * name,const char * ext)175 static int check_extension(const char *name, const char *ext)
176 {
177 const char *end;
178 int name_len, n;
179
180 if (!name || !ext)
181 return 0;
182
183 end = ext + strlen(ext);
184 name_len = strlen(name);
185 while (ext < end) {
186 n = strcspn(ext, " ");
187 if (n == name_len && strncmp(name, ext, n) == 0)
188 return 1;
189 ext += (n + 1);
190 }
191 return 0;
192 }
193
check_extension3(const char * name)194 static int check_extension3(const char *name)
195 {
196 int nbExtensions, i;
197 PFNGLGETSTRINGIPROC glGetStringi = 0;
198
199 glGetStringi = (PFNGLGETSTRINGIPROC) get_proc_address("glGetStringi");
200 if (!glGetStringi)
201 return 0;
202
203
204 glGetIntegerv(GL_NUM_EXTENSIONS, &nbExtensions);
205 for (i = 0; i < nbExtensions; i++) {
206 const GLubyte *strExtension = glGetStringi(GL_EXTENSIONS, i);
207 if (strcmp((const char *) strExtension, name) == 0)
208 return 1;
209 }
210
211 return 0;
212 }
213
check_tfp_extensions(VADriverContextP ctx)214 static int check_tfp_extensions(VADriverContextP ctx)
215 {
216 const char *gl_extensions;
217 const char *glx_extensions;
218
219 gl_extensions = (const char *)glGetString(GL_EXTENSIONS);
220 if (!check_extension("GL_ARB_texture_non_power_of_two", gl_extensions) && !check_extension3("GL_ARB_texture_non_power_of_two"))
221 return 0;
222
223 glx_extensions = glXQueryExtensionsString(ctx->native_dpy, ctx->x11_screen);
224 if (!check_extension("GLX_EXT_texture_from_pixmap", glx_extensions))
225 return 0;
226
227 return 1;
228 }
229
check_fbo_extensions(VADriverContextP ctx)230 static int check_fbo_extensions(VADriverContextP ctx)
231 {
232 const char *gl_extensions;
233
234 gl_extensions = (const char *)glGetString(GL_EXTENSIONS);
235 if (check_extension("GL_ARB_framebuffer_object", gl_extensions) || check_extension3("GL_ARB_framebuffer_object"))
236 return 1;
237 if (check_extension("GL_EXT_framebuffer_object", gl_extensions) || check_extension3("GL_EXT_framebuffer_object"))
238 return 1;
239
240 return 0;
241 }
242
243 // Load GLX extensions
load_tfp_extensions(VADriverContextP ctx)244 static int load_tfp_extensions(VADriverContextP ctx)
245 {
246 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
247
248 pOpenGLVTable->glx_create_pixmap = (PFNGLXCREATEPIXMAPPROC)
249 get_proc_address("glXCreatePixmap");
250 if (!pOpenGLVTable->glx_create_pixmap)
251 return 0;
252 pOpenGLVTable->glx_destroy_pixmap = (PFNGLXDESTROYPIXMAPPROC)
253 get_proc_address("glXDestroyPixmap");
254 if (!pOpenGLVTable->glx_destroy_pixmap)
255 return 0;
256 pOpenGLVTable->glx_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC)
257 get_proc_address("glXBindTexImageEXT");
258 if (!pOpenGLVTable->glx_bind_tex_image)
259 return 0;
260 pOpenGLVTable->glx_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC)
261 get_proc_address("glXReleaseTexImageEXT");
262 if (!pOpenGLVTable->glx_release_tex_image)
263 return 0;
264 return 1;
265 }
266
load_fbo_extensions(VADriverContextP ctx)267 static int load_fbo_extensions(VADriverContextP ctx)
268 {
269 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
270
271 pOpenGLVTable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
272 get_proc_address("glGenFramebuffersEXT");
273 if (!pOpenGLVTable->gl_gen_framebuffers)
274 return 0;
275 pOpenGLVTable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
276 get_proc_address("glDeleteFramebuffersEXT");
277 if (!pOpenGLVTable->gl_delete_framebuffers)
278 return 0;
279 pOpenGLVTable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
280 get_proc_address("glBindFramebufferEXT");
281 if (!pOpenGLVTable->gl_bind_framebuffer)
282 return 0;
283 pOpenGLVTable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
284 get_proc_address("glGenRenderbuffersEXT");
285 if (!pOpenGLVTable->gl_gen_renderbuffers)
286 return 0;
287 pOpenGLVTable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
288 get_proc_address("glDeleteRenderbuffersEXT");
289 if (!pOpenGLVTable->gl_delete_renderbuffers)
290 return 0;
291 pOpenGLVTable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
292 get_proc_address("glBindRenderbufferEXT");
293 if (!pOpenGLVTable->gl_bind_renderbuffer)
294 return 0;
295 pOpenGLVTable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
296 get_proc_address("glRenderbufferStorageEXT");
297 if (!pOpenGLVTable->gl_renderbuffer_storage)
298 return 0;
299 pOpenGLVTable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
300 get_proc_address("glFramebufferRenderbufferEXT");
301 if (!pOpenGLVTable->gl_framebuffer_renderbuffer)
302 return 0;
303 pOpenGLVTable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
304 get_proc_address("glFramebufferTexture2DEXT");
305 if (!pOpenGLVTable->gl_framebuffer_texture_2d)
306 return 0;
307 pOpenGLVTable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
308 get_proc_address("glCheckFramebufferStatusEXT");
309 if (!pOpenGLVTable->gl_check_framebuffer_status)
310 return 0;
311 return 1;
312 }
313
314
315 /* ========================================================================= */
316 /* === VA/GLX helpers === */
317 /* ========================================================================= */
318
319 // OpenGL context state
320 typedef struct OpenGLContextState *OpenGLContextStateP;
321
322 struct OpenGLContextState {
323 Display *display;
324 Window window;
325 GLXContext context;
326 };
327
328 static void
gl_destroy_context(OpenGLContextStateP cs)329 gl_destroy_context(OpenGLContextStateP cs)
330 {
331 if (!cs)
332 return;
333
334 if (cs->display && cs->context) {
335 if (glXGetCurrentContext() == cs->context)
336 glXMakeCurrent(cs->display, None, NULL);
337 glXDestroyContext(cs->display, cs->context);
338 cs->display = NULL;
339 cs->context = NULL;
340 }
341 free(cs);
342 }
343
344 static OpenGLContextStateP
gl_create_context(VADriverContextP ctx,OpenGLContextStateP parent)345 gl_create_context(VADriverContextP ctx, OpenGLContextStateP parent)
346 {
347 OpenGLContextStateP cs;
348 GLXFBConfig *fbconfigs = NULL;
349 int fbconfig_id, val, n, n_fbconfigs;
350 Status status;
351
352 static GLint fbconfig_attrs[] = {
353 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
354 GLX_RENDER_TYPE, GLX_RGBA_BIT,
355 GLX_DOUBLEBUFFER, True,
356 GLX_RED_SIZE, 8,
357 GLX_GREEN_SIZE, 8,
358 GLX_BLUE_SIZE, 8,
359 None
360 };
361
362 cs = malloc(sizeof(*cs));
363 if (!cs)
364 goto error;
365
366 if (parent) {
367 cs->display = parent->display;
368 cs->window = parent->window;
369 } else {
370 cs->display = ctx->native_dpy;
371 cs->window = None;
372 }
373 cs->context = NULL;
374
375 if (parent && parent->context) {
376 status = glXQueryContext(
377 parent->display,
378 parent->context,
379 GLX_FBCONFIG_ID, &fbconfig_id
380 );
381 if (status != Success)
382 goto error;
383
384 if (fbconfig_id == GLX_DONT_CARE)
385 goto choose_fbconfig;
386
387 fbconfigs = glXGetFBConfigs(
388 parent->display,
389 DefaultScreen(parent->display),
390 &n_fbconfigs
391 );
392 if (!fbconfigs)
393 goto error;
394
395 /* Find out a GLXFBConfig compatible with the parent context */
396 for (n = 0; n < n_fbconfigs; n++) {
397 status = glXGetFBConfigAttrib(
398 cs->display,
399 fbconfigs[n],
400 GLX_FBCONFIG_ID, &val
401 );
402 if (status == Success && val == fbconfig_id)
403 break;
404 }
405 if (n == n_fbconfigs)
406 goto error;
407 } else {
408 choose_fbconfig:
409 fbconfigs = glXChooseFBConfig(
410 ctx->native_dpy,
411 ctx->x11_screen,
412 fbconfig_attrs, &n_fbconfigs
413 );
414 if (!fbconfigs)
415 goto error;
416
417 /* Select the first one */
418 n = 0;
419 }
420
421 cs->context = glXCreateNewContext(
422 cs->display,
423 fbconfigs[n],
424 GLX_RGBA_TYPE,
425 parent ? parent->context : NULL,
426 True
427 );
428 if (cs->context)
429 goto end;
430
431 error:
432 gl_destroy_context(cs);
433 cs = NULL;
434 end:
435 if (fbconfigs)
436 XFree(fbconfigs);
437 return cs;
438 }
439
gl_get_current_context(OpenGLContextStateP cs)440 static void gl_get_current_context(OpenGLContextStateP cs)
441 {
442 cs->display = glXGetCurrentDisplay();
443 cs->window = glXGetCurrentDrawable();
444 cs->context = glXGetCurrentContext();
445 }
446
447 static int
gl_set_current_context(OpenGLContextStateP new_cs,OpenGLContextStateP old_cs)448 gl_set_current_context(OpenGLContextStateP new_cs, OpenGLContextStateP old_cs)
449 {
450 /* If display is NULL, this could be that new_cs was retrieved from
451 gl_get_current_context() with none set previously. If that case,
452 the other fields are also NULL and we don't return an error */
453 if (!new_cs->display)
454 return !new_cs->window && !new_cs->context;
455
456 if (old_cs) {
457 if (old_cs == new_cs)
458 return 1;
459 gl_get_current_context(old_cs);
460 if (old_cs->display == new_cs->display &&
461 old_cs->window == new_cs->window &&
462 old_cs->context == new_cs->context)
463 return 1;
464 }
465 return glXMakeCurrent(new_cs->display, new_cs->window, new_cs->context);
466 }
467
468 /** Unique VASurfaceGLX identifier */
469 #define VA_SURFACE_GLX_MAGIC VA_FOURCC('V','A','G','L')
470
471 struct VASurfaceGLX {
472 uint32_t magic; ///< Magic number identifying a VASurfaceGLX
473 GLenum target; ///< GL target to which the texture is bound
474 GLuint texture; ///< GL texture
475 VASurfaceID surface; ///< Associated VA surface
476 unsigned int width;
477 unsigned int height;
478 OpenGLContextStateP gl_context;
479 int is_bound;
480 Pixmap pixmap;
481 GLuint pix_texture;
482 GLXPixmap glx_pixmap;
483 GLuint fbo;
484 };
485
486 // Create Pixmaps for GLX texture-from-pixmap extension
create_tfp_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)487 static int create_tfp_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
488 {
489 VAOpenGLVTableP const pOpenGLVTable = gl_get_vtable(ctx);
490 const unsigned int width = pSurfaceGLX->width;
491 const unsigned int height = pSurfaceGLX->height;
492 Pixmap pixmap = None;
493 GLXFBConfig *fbconfig = NULL;
494 GLXPixmap glx_pixmap = None;
495 Window root_window;
496 XWindowAttributes wattr;
497 int *attrib;
498 int n_fbconfig_attrs;
499
500 root_window = RootWindow(ctx->native_dpy, ctx->x11_screen);
501 XGetWindowAttributes(ctx->native_dpy, root_window, &wattr);
502 if (wattr.depth != 24 && wattr.depth != 32)
503 return 0;
504 pixmap = XCreatePixmap(
505 ctx->native_dpy,
506 root_window,
507 width,
508 height,
509 wattr.depth
510 );
511 if (!pixmap)
512 return 0;
513 pSurfaceGLX->pixmap = pixmap;
514
515 int fbconfig_attrs[32] = {
516 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
517 GLX_DOUBLEBUFFER, GL_TRUE,
518 GLX_RENDER_TYPE, GLX_RGBA_BIT,
519 GLX_X_RENDERABLE, GL_TRUE,
520 GLX_Y_INVERTED_EXT, GL_TRUE,
521 GLX_RED_SIZE, 8,
522 GLX_GREEN_SIZE, 8,
523 GLX_BLUE_SIZE, 8,
524 /*
525 * depth test isn't enabled in the implementaion of VA GLX,
526 * so depth buffer is unnecessary. However to workaround a
527 * bug in older verson of xorg-server, always require a depth
528 * buffer.
529 *
530 * See https://bugs.freedesktop.org/show_bug.cgi?id=76755
531 */
532 GLX_DEPTH_SIZE, 1,
533 GL_NONE,
534 };
535 for (attrib = fbconfig_attrs; *attrib != GL_NONE; attrib += 2)
536 ;
537 if (wattr.depth == 32) {
538 *attrib++ = GLX_ALPHA_SIZE;
539 *attrib++ = 8;
540 *attrib++ = GLX_BIND_TO_TEXTURE_RGBA_EXT;
541 *attrib++ = GL_TRUE;
542 } else {
543 *attrib++ = GLX_BIND_TO_TEXTURE_RGB_EXT;
544 *attrib++ = GL_TRUE;
545 }
546 *attrib++ = GL_NONE;
547
548 fbconfig = glXChooseFBConfig(
549 ctx->native_dpy,
550 ctx->x11_screen,
551 fbconfig_attrs,
552 &n_fbconfig_attrs
553 );
554 if (!fbconfig)
555 return 0;
556
557 int pixmap_attrs[10] = {
558 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
559 GLX_MIPMAP_TEXTURE_EXT, GL_FALSE,
560 GL_NONE,
561 };
562 for (attrib = pixmap_attrs; *attrib != GL_NONE; attrib += 2)
563 ;
564 *attrib++ = GLX_TEXTURE_FORMAT_EXT;
565 if (wattr.depth == 32)
566 *attrib++ = GLX_TEXTURE_FORMAT_RGBA_EXT;
567 else
568 *attrib++ = GLX_TEXTURE_FORMAT_RGB_EXT;
569 *attrib++ = GL_NONE;
570
571 x11_trap_errors();
572 glx_pixmap = pOpenGLVTable->glx_create_pixmap(
573 ctx->native_dpy,
574 fbconfig[0],
575 pixmap,
576 pixmap_attrs
577 );
578 free(fbconfig);
579 if (x11_untrap_errors() != 0)
580 return 0;
581 pSurfaceGLX->glx_pixmap = glx_pixmap;
582
583 glGenTextures(1, &pSurfaceGLX->pix_texture);
584 glBindTexture(GL_TEXTURE_2D, pSurfaceGLX->pix_texture);
585 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
586 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
587 return 1;
588 }
589
590 // Destroy Pixmaps used for TFP
destroy_tfp_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)591 static void destroy_tfp_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
592 {
593 VAOpenGLVTableP const pOpenGLVTable = gl_get_vtable(ctx);
594
595 if (pSurfaceGLX->pix_texture) {
596 glDeleteTextures(1, &pSurfaceGLX->pix_texture);
597 pSurfaceGLX->pix_texture = 0;
598 }
599
600 if (pSurfaceGLX->glx_pixmap) {
601 pOpenGLVTable->glx_destroy_pixmap(ctx->native_dpy, pSurfaceGLX->glx_pixmap);
602 pSurfaceGLX->glx_pixmap = None;
603 }
604
605 if (pSurfaceGLX->pixmap) {
606 XFreePixmap(ctx->native_dpy, pSurfaceGLX->pixmap);
607 pSurfaceGLX->pixmap = None;
608 }
609 }
610
611 // Bind GLX Pixmap to texture
bind_pixmap(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)612 static int bind_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
613 {
614 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
615
616 if (pSurfaceGLX->is_bound)
617 return 1;
618
619 glBindTexture(GL_TEXTURE_2D, pSurfaceGLX->pix_texture);
620
621 x11_trap_errors();
622 pOpenGLVTable->glx_bind_tex_image(
623 ctx->native_dpy,
624 pSurfaceGLX->glx_pixmap,
625 GLX_FRONT_LEFT_EXT,
626 NULL
627 );
628 XSync(ctx->native_dpy, False);
629 if (x11_untrap_errors() != 0) {
630 va_glx_error_message("failed to bind pixmap\n");
631 return 0;
632 }
633
634 pSurfaceGLX->is_bound = 1;
635 return 1;
636 }
637
638 // Release GLX Pixmap from texture
unbind_pixmap(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)639 static int unbind_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
640 {
641 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
642
643 if (!pSurfaceGLX->is_bound)
644 return 1;
645
646 x11_trap_errors();
647 pOpenGLVTable->glx_release_tex_image(
648 ctx->native_dpy,
649 pSurfaceGLX->glx_pixmap,
650 GLX_FRONT_LEFT_EXT
651 );
652 XSync(ctx->native_dpy, False);
653 if (x11_untrap_errors() != 0) {
654 va_glx_error_message("failed to release pixmap\n");
655 return 0;
656 }
657
658 glBindTexture(GL_TEXTURE_2D, 0);
659
660 pSurfaceGLX->is_bound = 0;
661 return 1;
662 }
663
664 // Render GLX Pixmap to texture
render_pixmap(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)665 static void render_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
666 {
667 const unsigned int w = pSurfaceGLX->width;
668 const unsigned int h = pSurfaceGLX->height;
669
670 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
671 glBegin(GL_QUADS);
672 {
673 glTexCoord2f(0.0f, 0.0f);
674 glVertex2i(0, 0);
675 glTexCoord2f(0.0f, 1.0f);
676 glVertex2i(0, h);
677 glTexCoord2f(1.0f, 1.0f);
678 glVertex2i(w, h);
679 glTexCoord2f(1.0f, 0.0f);
680 glVertex2i(w, 0);
681 }
682 glEnd();
683 }
684
685 // Create offscreen surface
create_fbo_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)686 static int create_fbo_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
687 {
688 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
689 GLuint fbo;
690 GLenum status;
691
692 pOpenGLVTable->gl_gen_framebuffers(1, &fbo);
693 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo);
694 pOpenGLVTable->gl_framebuffer_texture_2d(
695 GL_FRAMEBUFFER_EXT,
696 GL_COLOR_ATTACHMENT0_EXT,
697 GL_TEXTURE_2D,
698 pSurfaceGLX->texture,
699 0
700 );
701
702 status = pOpenGLVTable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT);
703 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0);
704 if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
705 return 0;
706
707 pSurfaceGLX->fbo = fbo;
708 return 1;
709 }
710
711 // Destroy offscreen surface
destroy_fbo_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)712 static void destroy_fbo_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
713 {
714 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
715
716 if (pSurfaceGLX->fbo) {
717 pOpenGLVTable->gl_delete_framebuffers(1, &pSurfaceGLX->fbo);
718 pSurfaceGLX->fbo = 0;
719 }
720 }
721
722 // Setup matrices to match the FBO texture dimensions
fbo_enter(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)723 static void fbo_enter(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
724 {
725 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
726 const unsigned int width = pSurfaceGLX->width;
727 const unsigned int height = pSurfaceGLX->height;
728
729 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, pSurfaceGLX->fbo);
730 glPushAttrib(GL_VIEWPORT_BIT);
731 glMatrixMode(GL_PROJECTION);
732 glPushMatrix();
733 glLoadIdentity();
734 glMatrixMode(GL_MODELVIEW);
735 glPushMatrix();
736 glLoadIdentity();
737 glViewport(0, 0, width, height);
738 glTranslatef(-1.0f, -1.0f, 0.0f);
739 glScalef(2.0f / width, 2.0f / height, 1.0f);
740 }
741
742 // Restore original OpenGL matrices
fbo_leave(VADriverContextP ctx)743 static void fbo_leave(VADriverContextP ctx)
744 {
745 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx);
746
747 glPopAttrib();
748 glMatrixMode(GL_PROJECTION);
749 glPopMatrix();
750 glMatrixMode(GL_MODELVIEW);
751 glPopMatrix();
752 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0);
753 }
754
755 // Check internal texture format is supported
is_supported_internal_format(GLenum format)756 static int is_supported_internal_format(GLenum format)
757 {
758 /* XXX: we don't support other textures than RGBA */
759 switch (format) {
760 case 4:
761 case GL_RGBA:
762 case GL_RGBA8:
763 return 1;
764 }
765 return 0;
766 }
767
768 // Destroy VA/GLX surface
769 static void
destroy_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)770 destroy_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
771 {
772 unbind_pixmap(ctx, pSurfaceGLX);
773 destroy_fbo_surface(ctx, pSurfaceGLX);
774 destroy_tfp_surface(ctx, pSurfaceGLX);
775 free(pSurfaceGLX);
776 }
777
778 // Create VA/GLX surface
779 static VASurfaceGLXP
create_surface(VADriverContextP ctx,GLenum target,GLuint texture)780 create_surface(VADriverContextP ctx, GLenum target, GLuint texture)
781 {
782 VASurfaceGLXP pSurfaceGLX = NULL;
783 unsigned int internal_format, border_width, width, height;
784 int is_error = 1;
785
786 pSurfaceGLX = malloc(sizeof(*pSurfaceGLX));
787 if (!pSurfaceGLX)
788 goto end;
789
790 pSurfaceGLX->magic = VA_SURFACE_GLX_MAGIC;
791 pSurfaceGLX->target = target;
792 pSurfaceGLX->texture = texture;
793 pSurfaceGLX->surface = VA_INVALID_SURFACE;
794 pSurfaceGLX->gl_context = NULL;
795 pSurfaceGLX->is_bound = 0;
796 pSurfaceGLX->pixmap = None;
797 pSurfaceGLX->pix_texture = 0;
798 pSurfaceGLX->glx_pixmap = None;
799 pSurfaceGLX->fbo = 0;
800
801 glEnable(target);
802 glBindTexture(target, texture);
803 if (!gl_get_texture_param(GL_TEXTURE_INTERNAL_FORMAT, &internal_format))
804 goto end;
805 if (!is_supported_internal_format(internal_format))
806 goto end;
807
808 /* Check texture dimensions */
809 if (!gl_get_texture_param(GL_TEXTURE_BORDER, &border_width))
810 goto end;
811 if (!gl_get_texture_param(GL_TEXTURE_WIDTH, &width))
812 goto end;
813 if (!gl_get_texture_param(GL_TEXTURE_HEIGHT, &height))
814 goto end;
815
816 width -= 2 * border_width;
817 height -= 2 * border_width;
818 if (width == 0 || height == 0)
819 goto end;
820
821 pSurfaceGLX->width = width;
822 pSurfaceGLX->height = height;
823
824 /* Create TFP objects */
825 if (!create_tfp_surface(ctx, pSurfaceGLX))
826 goto end;
827
828 /* Create FBO objects */
829 if (!create_fbo_surface(ctx, pSurfaceGLX))
830 goto end;
831
832 is_error = 0;
833 end:
834 if (is_error && pSurfaceGLX) {
835 destroy_surface(ctx, pSurfaceGLX);
836 pSurfaceGLX = NULL;
837 }
838 return pSurfaceGLX;
839 }
840
841
842 /* ========================================================================= */
843 /* === VA/GLX implementation from the driver (fordward calls) === */
844 /* ========================================================================= */
845
846 #define INVOKE(ctx, func, args) do { \
847 VADriverVTableGLXP vtable = (ctx)->vtable_glx; \
848 if (!vtable->va##func##GLX) \
849 return VA_STATUS_ERROR_UNIMPLEMENTED; \
850 \
851 VAStatus status = vtable->va##func##GLX args; \
852 if (status != VA_STATUS_SUCCESS) \
853 return status; \
854 } while (0)
855
856 static VAStatus
vaCreateSurfaceGLX_impl_driver(VADriverContextP ctx,GLenum target,GLuint texture,void ** gl_surface)857 vaCreateSurfaceGLX_impl_driver(
858 VADriverContextP ctx,
859 GLenum target,
860 GLuint texture,
861 void **gl_surface
862 )
863 {
864 INVOKE(ctx, CreateSurface, (ctx, target, texture, gl_surface));
865 return VA_STATUS_SUCCESS;
866 }
867
868 static VAStatus
vaDestroySurfaceGLX_impl_driver(VADriverContextP ctx,void * gl_surface)869 vaDestroySurfaceGLX_impl_driver(VADriverContextP ctx, void *gl_surface)
870 {
871 INVOKE(ctx, DestroySurface, (ctx, gl_surface));
872 return VA_STATUS_SUCCESS;
873 }
874
875 static VAStatus
vaCopySurfaceGLX_impl_driver(VADriverContextP ctx,void * gl_surface,VASurfaceID surface,unsigned int flags)876 vaCopySurfaceGLX_impl_driver(
877 VADriverContextP ctx,
878 void *gl_surface,
879 VASurfaceID surface,
880 unsigned int flags
881 )
882 {
883 INVOKE(ctx, CopySurface, (ctx, gl_surface, surface, flags));
884 return VA_STATUS_SUCCESS;
885 }
886
887 #undef INVOKE
888
889
890 /* ========================================================================= */
891 /* === VA/GLX implementation from libVA (generic and suboptimal path) === */
892 /* ========================================================================= */
893
894 #define INIT_SURFACE(surface, surface_arg) do { \
895 surface = (VASurfaceGLXP)(surface_arg); \
896 if (!check_surface(surface)) \
897 return VA_STATUS_ERROR_INVALID_SURFACE; \
898 } while (0)
899
900 // Check VASurfaceGLX is valid
check_surface(VASurfaceGLXP pSurfaceGLX)901 static inline int check_surface(VASurfaceGLXP pSurfaceGLX)
902 {
903 return pSurfaceGLX && pSurfaceGLX->magic == VA_SURFACE_GLX_MAGIC;
904 }
905
906 static VAStatus
vaCreateSurfaceGLX_impl_libva(VADriverContextP ctx,GLenum target,GLuint texture,void ** gl_surface)907 vaCreateSurfaceGLX_impl_libva(
908 VADriverContextP ctx,
909 GLenum target,
910 GLuint texture,
911 void **gl_surface
912 )
913 {
914 VASurfaceGLXP pSurfaceGLX;
915 struct OpenGLContextState old_cs, *new_cs;
916
917 gl_get_current_context(&old_cs);
918 new_cs = gl_create_context(ctx, &old_cs);
919 if (!new_cs)
920 goto error;
921 if (!gl_set_current_context(new_cs, NULL))
922 goto error;
923
924 pSurfaceGLX = create_surface(ctx, target, texture);
925 if (!pSurfaceGLX)
926 goto error;
927
928 pSurfaceGLX->gl_context = new_cs;
929 *gl_surface = pSurfaceGLX;
930
931 gl_set_current_context(&old_cs, NULL);
932 return VA_STATUS_SUCCESS;
933
934 error:
935 if (new_cs)
936 gl_destroy_context(new_cs);
937
938 return VA_STATUS_ERROR_ALLOCATION_FAILED;
939 }
940
941 static VAStatus
vaDestroySurfaceGLX_impl_libva(VADriverContextP ctx,void * gl_surface)942 vaDestroySurfaceGLX_impl_libva(VADriverContextP ctx, void *gl_surface)
943 {
944 VASurfaceGLXP pSurfaceGLX;
945 struct OpenGLContextState old_cs = {0}, *new_cs;
946
947 INIT_SURFACE(pSurfaceGLX, gl_surface);
948
949 new_cs = pSurfaceGLX->gl_context;
950 if (!gl_set_current_context(new_cs, &old_cs))
951 return VA_STATUS_ERROR_OPERATION_FAILED;
952
953 destroy_surface(ctx, pSurfaceGLX);
954
955 gl_destroy_context(new_cs);
956 gl_set_current_context(&old_cs, NULL);
957 return VA_STATUS_SUCCESS;
958 }
959
960 static inline VAStatus
deassociate_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)961 deassociate_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
962 {
963 if (!unbind_pixmap(ctx, pSurfaceGLX))
964 return VA_STATUS_ERROR_OPERATION_FAILED;
965
966 pSurfaceGLX->surface = VA_INVALID_SURFACE;
967 return VA_STATUS_SUCCESS;
968 }
969
970 static VAStatus
associate_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX,VASurfaceID surface,unsigned int flags)971 associate_surface(
972 VADriverContextP ctx,
973 VASurfaceGLXP pSurfaceGLX,
974 VASurfaceID surface,
975 unsigned int flags
976 )
977 {
978 VAStatus status;
979
980 /* XXX: optimise case where we are associating the same VA surface
981 as before an no changed occurred to it */
982 status = deassociate_surface(ctx, pSurfaceGLX);
983 if (status != VA_STATUS_SUCCESS)
984 return status;
985
986 x11_trap_errors();
987 status = ctx->vtable->vaPutSurface(
988 ctx,
989 surface,
990 (void *)pSurfaceGLX->pixmap,
991 0, 0, pSurfaceGLX->width, pSurfaceGLX->height,
992 0, 0, pSurfaceGLX->width, pSurfaceGLX->height,
993 NULL, 0,
994 flags
995 );
996 XSync(ctx->native_dpy, False);
997 if (x11_untrap_errors() != 0)
998 return VA_STATUS_ERROR_OPERATION_FAILED;
999 if (status != VA_STATUS_SUCCESS)
1000 return status;
1001
1002 pSurfaceGLX->surface = surface;
1003 return VA_STATUS_SUCCESS;
1004 }
1005
1006 static inline VAStatus
sync_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)1007 sync_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
1008 {
1009 if (pSurfaceGLX->surface == VA_INVALID_SURFACE)
1010 return VA_STATUS_ERROR_INVALID_SURFACE;
1011
1012 return ctx->vtable->vaSyncSurface(ctx, pSurfaceGLX->surface);
1013 }
1014
1015 static inline VAStatus
begin_render_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)1016 begin_render_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
1017 {
1018 VAStatus status;
1019
1020 status = sync_surface(ctx, pSurfaceGLX);
1021 if (status != VA_STATUS_SUCCESS)
1022 return status;
1023
1024 if (!bind_pixmap(ctx, pSurfaceGLX))
1025 return VA_STATUS_ERROR_OPERATION_FAILED;
1026
1027 return VA_STATUS_SUCCESS;
1028 }
1029
1030 static inline VAStatus
end_render_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX)1031 end_render_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX)
1032 {
1033 if (!unbind_pixmap(ctx, pSurfaceGLX))
1034 return VA_STATUS_ERROR_OPERATION_FAILED;
1035
1036 return VA_STATUS_SUCCESS;
1037 }
1038
1039 static VAStatus
copy_surface(VADriverContextP ctx,VASurfaceGLXP pSurfaceGLX,VASurfaceID surface,unsigned int flags)1040 copy_surface(
1041 VADriverContextP ctx,
1042 VASurfaceGLXP pSurfaceGLX,
1043 VASurfaceID surface,
1044 unsigned int flags
1045 )
1046 {
1047 VAStatus status;
1048
1049 /* Associate VA surface */
1050 status = associate_surface(ctx, pSurfaceGLX, surface, flags);
1051 if (status != VA_STATUS_SUCCESS)
1052 return status;
1053
1054 /* Render to FBO */
1055 fbo_enter(ctx, pSurfaceGLX);
1056 status = begin_render_surface(ctx, pSurfaceGLX);
1057 if (status == VA_STATUS_SUCCESS) {
1058 render_pixmap(ctx, pSurfaceGLX);
1059 status = end_render_surface(ctx, pSurfaceGLX);
1060 }
1061 fbo_leave(ctx);
1062 if (status != VA_STATUS_SUCCESS)
1063 return status;
1064
1065 return deassociate_surface(ctx, pSurfaceGLX);
1066 }
1067
1068 static VAStatus
vaCopySurfaceGLX_impl_libva(VADriverContextP ctx,void * gl_surface,VASurfaceID surface,unsigned int flags)1069 vaCopySurfaceGLX_impl_libva(
1070 VADriverContextP ctx,
1071 void *gl_surface,
1072 VASurfaceID surface,
1073 unsigned int flags
1074 )
1075 {
1076 VASurfaceGLXP pSurfaceGLX;
1077 VAStatus status;
1078 struct OpenGLContextState old_cs = {0};
1079
1080 INIT_SURFACE(pSurfaceGLX, gl_surface);
1081
1082 if (!gl_set_current_context(pSurfaceGLX->gl_context, &old_cs))
1083 return VA_STATUS_ERROR_OPERATION_FAILED;
1084
1085 status = copy_surface(ctx, pSurfaceGLX, surface, flags);
1086
1087 gl_set_current_context(&old_cs, NULL);
1088 return status;
1089 }
1090
1091 #undef INIT_SURFACE
1092
1093
1094 /* ========================================================================= */
1095 /* === Private VA/GLX vtable initialization === */
1096 /* ========================================================================= */
1097
1098 // Initialize GLX driver context
va_glx_init_context(VADriverContextP ctx)1099 VAStatus va_glx_init_context(VADriverContextP ctx)
1100 {
1101 VADriverContextGLXP glx_ctx = VA_DRIVER_CONTEXT_GLX(ctx);
1102 VADriverVTableGLXP vtable = &glx_ctx->vtable;
1103 int glx_major, glx_minor;
1104
1105 if (glx_ctx->is_initialized)
1106 return VA_STATUS_SUCCESS;
1107
1108 if (ctx->vtable_glx && ctx->vtable_glx->vaCopySurfaceGLX) {
1109 vtable->vaCreateSurfaceGLX = vaCreateSurfaceGLX_impl_driver;
1110 vtable->vaDestroySurfaceGLX = vaDestroySurfaceGLX_impl_driver;
1111 vtable->vaCopySurfaceGLX = vaCopySurfaceGLX_impl_driver;
1112 } else {
1113 vtable->vaCreateSurfaceGLX = vaCreateSurfaceGLX_impl_libva;
1114 vtable->vaDestroySurfaceGLX = vaDestroySurfaceGLX_impl_libva;
1115 vtable->vaCopySurfaceGLX = vaCopySurfaceGLX_impl_libva;
1116
1117 if (!glXQueryVersion(ctx->native_dpy, &glx_major, &glx_minor))
1118 return VA_STATUS_ERROR_UNIMPLEMENTED;
1119
1120 if (!check_tfp_extensions(ctx) || !load_tfp_extensions(ctx))
1121 return VA_STATUS_ERROR_UNIMPLEMENTED;
1122
1123 if (!check_fbo_extensions(ctx) || !load_fbo_extensions(ctx))
1124 return VA_STATUS_ERROR_UNIMPLEMENTED;
1125 }
1126
1127 glx_ctx->is_initialized = 1;
1128 return VA_STATUS_SUCCESS;
1129 }
1130