Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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