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