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