Home | History | Annotate | Download | only in glx
      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 
     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 
     48 static int error_handler(Display *dpy, XErrorEvent *error)
     49 {
     50     x11_error_code = error->error_code;
     51     return 0;
     52 }
     53 
     54 static void x11_trap_errors(void)
     55 {
     56     x11_error_code    = 0;
     57     old_error_handler = XSetErrorHandler(error_handler);
     58 }
     59 
     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
     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 
     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 
    108 static inline void gl_purge_errors(void)
    109 {
    110     gl_do_check_error(0);
    111 }
    112 
    113 static inline int gl_check_error(void)
    114 {
    115     return gl_do_check_error(1);
    116 }
    117 
    118 // glGetTexLevelParameteriv() wrapper
    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
    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 
    142 static GLFuncPtr get_proc_address_default(const char *name)
    143 {
    144     return NULL;
    145 }
    146 
    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 
    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)
    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 
    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 
    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
    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 
    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
    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
    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 
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    860 static inline int check_surface(VASurfaceGLXP pSurfaceGLX)
    861 {
    862     return pSurfaceGLX && pSurfaceGLX->magic == VA_SURFACE_GLX_MAGIC;
    863 }
    864 
    865 static VAStatus
    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
    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
    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
    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
    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
    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
    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
    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
   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
   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