Index: third_party/libva/va/glx/va_glx_impl.c |
diff --git a/third_party/libva/va/glx/va_glx_impl.c b/third_party/libva/va/glx/va_glx_impl.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..049be09c1c8b6e43f390ebf81c6c19ac75dece62 |
--- /dev/null |
+++ b/third_party/libva/va/glx/va_glx_impl.c |
@@ -0,0 +1,1083 @@ |
+/* |
+ * Copyright (C) 2009 Splitted-Desktop Systems. All Rights Reserved. |
+ * |
+ * Permission is hereby granted, free of charge, to any person obtaining a |
+ * copy of this software and associated documentation files (the |
+ * "Software"), to deal in the Software without restriction, including |
+ * without limitation the rights to use, copy, modify, merge, publish, |
+ * distribute, sub license, and/or sell copies of the Software, and to |
+ * permit persons to whom the Software is furnished to do so, subject to |
+ * the following conditions: |
+ * |
+ * The above copyright notice and this permission notice (including the |
+ * next paragraph) shall be included in all copies or substantial portions |
+ * of the Software. |
+ * |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. |
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR |
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+ */ |
+ |
+#define _GNU_SOURCE 1 |
+#include "va_glx_private.h" |
+#include "va_glx_impl.h" |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <stdarg.h> |
+#include <string.h> |
+#include <assert.h> |
+#include <dlfcn.h> |
+ |
+static void va_glx_error_message(const char *format, ...) |
+{ |
+ va_list args; |
+ va_start(args, format); |
+ fprintf(stderr, "libva-glx error: "); |
+ vfprintf(stderr, format, args); |
+ va_end(args); |
+} |
+ |
+// X error trap |
+static int x11_error_code = 0; |
+static int (*old_error_handler)(Display *, XErrorEvent *); |
+ |
+static int error_handler(Display *dpy, XErrorEvent *error) |
+{ |
+ x11_error_code = error->error_code; |
+ return 0; |
+} |
+ |
+static void x11_trap_errors(void) |
+{ |
+ x11_error_code = 0; |
+ old_error_handler = XSetErrorHandler(error_handler); |
+} |
+ |
+static int x11_untrap_errors(void) |
+{ |
+ XSetErrorHandler(old_error_handler); |
+ return x11_error_code; |
+} |
+ |
+// Returns a string representation of an OpenGL error |
+static const char *gl_get_error_string(GLenum error) |
+{ |
+ static const struct { |
+ GLenum val; |
+ const char *str; |
+ } |
+ gl_errors[] = { |
+ { GL_NO_ERROR, "no error" }, |
+ { GL_INVALID_ENUM, "invalid enumerant" }, |
+ { GL_INVALID_VALUE, "invalid value" }, |
+ { GL_INVALID_OPERATION, "invalid operation" }, |
+ { GL_STACK_OVERFLOW, "stack overflow" }, |
+ { GL_STACK_UNDERFLOW, "stack underflow" }, |
+ { GL_OUT_OF_MEMORY, "out of memory" }, |
+#ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT |
+ { GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "invalid framebuffer operation" }, |
+#endif |
+ { ~0, NULL } |
+ }; |
+ |
+ int i; |
+ for (i = 0; gl_errors[i].str; i++) { |
+ if (gl_errors[i].val == error) |
+ return gl_errors[i].str; |
+ } |
+ return "unknown"; |
+} |
+ |
+static inline int gl_do_check_error(int report) |
+{ |
+ GLenum error; |
+ int is_error = 0; |
+ while ((error = glGetError()) != GL_NO_ERROR) { |
+ if (report) |
+ va_glx_error_message("glError: %s caught\n", |
+ gl_get_error_string(error)); |
+ is_error = 1; |
+ } |
+ return is_error; |
+} |
+ |
+static inline void gl_purge_errors(void) |
+{ |
+ gl_do_check_error(0); |
+} |
+ |
+static inline int gl_check_error(void) |
+{ |
+ return gl_do_check_error(1); |
+} |
+ |
+// glGetTexLevelParameteriv() wrapper |
+static int gl_get_texture_param(GLenum param, unsigned int *pval) |
+{ |
+ GLint val; |
+ |
+ gl_purge_errors(); |
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, param, &val); |
+ if (gl_check_error()) |
+ return 0; |
+ if (pval) |
+ *pval = val; |
+ return 1; |
+} |
+ |
+// Returns the OpenGL VTable |
+static inline VAOpenGLVTableP gl_get_vtable(VADriverContextP ctx) |
+{ |
+ return &VA_DRIVER_CONTEXT_GLX(ctx)->gl_vtable; |
+} |
+ |
+// Lookup for a GLX function |
+typedef void (*GLFuncPtr)(void); |
+typedef GLFuncPtr (*GLXGetProcAddressProc)(const char *); |
+ |
+static GLFuncPtr get_proc_address_default(const char *name) |
+{ |
+ return NULL; |
+} |
+ |
+static GLXGetProcAddressProc get_proc_address_func(void) |
+{ |
+ GLXGetProcAddressProc get_proc_func; |
+ |
+ dlerror(); |
+ get_proc_func = (GLXGetProcAddressProc) |
+ dlsym(RTLD_DEFAULT, "glXGetProcAddress"); |
+ if (!dlerror()) |
+ return get_proc_func; |
+ |
+ get_proc_func = (GLXGetProcAddressProc) |
+ dlsym(RTLD_DEFAULT, "glXGetProcAddressARB"); |
+ if (!dlerror()) |
+ return get_proc_func; |
+ |
+ return get_proc_address_default; |
+} |
+ |
+static inline GLFuncPtr get_proc_address(const char *name) |
+{ |
+ static GLXGetProcAddressProc get_proc_func = NULL; |
+ if (!get_proc_func) |
+ get_proc_func = get_proc_address_func(); |
+ return get_proc_func(name); |
+} |
+ |
+// Check for GLX extensions (TFP, FBO) |
+static int check_extension(const char *name, const char *ext) |
+{ |
+ const char *end; |
+ int name_len, n; |
+ |
+ if (!name || !ext) |
+ return 0; |
+ |
+ end = ext + strlen(ext); |
+ name_len = strlen(name); |
+ while (ext < end) { |
+ n = strcspn(ext, " "); |
+ if (n == name_len && strncmp(name, ext, n) == 0) |
+ return 1; |
+ ext += (n + 1); |
+ } |
+ return 0; |
+} |
+ |
+static int check_tfp_extensions(VADriverContextP ctx) |
+{ |
+ const char *gl_extensions; |
+ const char *glx_extensions; |
+ |
+ gl_extensions = (const char *)glGetString(GL_EXTENSIONS); |
+ if (!check_extension("GL_ARB_texture_non_power_of_two", gl_extensions)) |
+ return 0; |
+ |
+ glx_extensions = glXQueryExtensionsString(ctx->native_dpy, ctx->x11_screen); |
+ if (!check_extension("GLX_EXT_texture_from_pixmap", glx_extensions)) |
+ return 0; |
+ return 1; |
+} |
+ |
+static int check_fbo_extensions(VADriverContextP ctx) |
+{ |
+ const char *gl_extensions; |
+ |
+ gl_extensions = (const char *)glGetString(GL_EXTENSIONS); |
+ if (check_extension("GL_ARB_framebuffer_object", gl_extensions)) |
+ return 1; |
+ if (check_extension("GL_EXT_framebuffer_object", gl_extensions)) |
+ return 1; |
+ return 0; |
+} |
+ |
+// Load GLX extensions |
+static int load_tfp_extensions(VADriverContextP ctx) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ |
+ pOpenGLVTable->glx_create_pixmap = (PFNGLXCREATEPIXMAPPROC) |
+ get_proc_address("glXCreatePixmap"); |
+ if (!pOpenGLVTable->glx_create_pixmap) |
+ return 0; |
+ pOpenGLVTable->glx_destroy_pixmap = (PFNGLXDESTROYPIXMAPPROC) |
+ get_proc_address("glXDestroyPixmap"); |
+ if (!pOpenGLVTable->glx_destroy_pixmap) |
+ return 0; |
+ pOpenGLVTable->glx_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC) |
+ get_proc_address("glXBindTexImageEXT"); |
+ if (!pOpenGLVTable->glx_bind_tex_image) |
+ return 0; |
+ pOpenGLVTable->glx_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC) |
+ get_proc_address("glXReleaseTexImageEXT"); |
+ if (!pOpenGLVTable->glx_release_tex_image) |
+ return 0; |
+ return 1; |
+} |
+ |
+static int load_fbo_extensions(VADriverContextP ctx) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ |
+ pOpenGLVTable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) |
+ get_proc_address("glGenFramebuffersEXT"); |
+ if (!pOpenGLVTable->gl_gen_framebuffers) |
+ return 0; |
+ pOpenGLVTable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC) |
+ get_proc_address("glDeleteFramebuffersEXT"); |
+ if (!pOpenGLVTable->gl_delete_framebuffers) |
+ return 0; |
+ pOpenGLVTable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC) |
+ get_proc_address("glBindFramebufferEXT"); |
+ if (!pOpenGLVTable->gl_bind_framebuffer) |
+ return 0; |
+ pOpenGLVTable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC) |
+ get_proc_address("glGenRenderbuffersEXT"); |
+ if (!pOpenGLVTable->gl_gen_renderbuffers) |
+ return 0; |
+ pOpenGLVTable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC) |
+ get_proc_address("glDeleteRenderbuffersEXT"); |
+ if (!pOpenGLVTable->gl_delete_renderbuffers) |
+ return 0; |
+ pOpenGLVTable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC) |
+ get_proc_address("glBindRenderbufferEXT"); |
+ if (!pOpenGLVTable->gl_bind_renderbuffer) |
+ return 0; |
+ pOpenGLVTable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC) |
+ get_proc_address("glRenderbufferStorageEXT"); |
+ if (!pOpenGLVTable->gl_renderbuffer_storage) |
+ return 0; |
+ pOpenGLVTable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) |
+ get_proc_address("glFramebufferRenderbufferEXT"); |
+ if (!pOpenGLVTable->gl_framebuffer_renderbuffer) |
+ return 0; |
+ pOpenGLVTable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) |
+ get_proc_address("glFramebufferTexture2DEXT"); |
+ if (!pOpenGLVTable->gl_framebuffer_texture_2d) |
+ return 0; |
+ pOpenGLVTable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) |
+ get_proc_address("glCheckFramebufferStatusEXT"); |
+ if (!pOpenGLVTable->gl_check_framebuffer_status) |
+ return 0; |
+ return 1; |
+} |
+ |
+ |
+/* ========================================================================= */ |
+/* === VA/GLX helpers === */ |
+/* ========================================================================= */ |
+ |
+// OpenGL context state |
+typedef struct OpenGLContextState *OpenGLContextStateP; |
+ |
+struct OpenGLContextState { |
+ Display *display; |
+ Window window; |
+ GLXContext context; |
+}; |
+ |
+static void |
+gl_destroy_context(OpenGLContextStateP cs) |
+{ |
+ if (!cs) |
+ return; |
+ |
+ if (cs->display && cs->context) { |
+ if (glXGetCurrentContext() == cs->context) |
+ glXMakeCurrent(cs->display, None, NULL); |
+ glXDestroyContext(cs->display, cs->context); |
+ cs->display = NULL; |
+ cs->context = NULL; |
+ } |
+ free(cs); |
+} |
+ |
+static OpenGLContextStateP |
+gl_create_context(VADriverContextP ctx, OpenGLContextStateP parent) |
+{ |
+ OpenGLContextStateP cs; |
+ GLXFBConfig *fbconfigs = NULL; |
+ int fbconfig_id, val, n, n_fbconfigs; |
+ Status status; |
+ |
+ static GLint fbconfig_attrs[] = { |
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, |
+ GLX_RENDER_TYPE, GLX_RGBA_BIT, |
+ GLX_DOUBLEBUFFER, True, |
+ GLX_RED_SIZE, 8, |
+ GLX_GREEN_SIZE, 8, |
+ GLX_BLUE_SIZE, 8, |
+ None |
+ }; |
+ |
+ cs = malloc(sizeof(*cs)); |
+ if (!cs) |
+ goto error; |
+ |
+ cs->display = ctx->native_dpy; |
+ cs->window = parent ? parent->window : None; |
+ cs->context = NULL; |
+ |
+ if (parent && parent->context) { |
+ status = glXQueryContext( |
+ parent->display, |
+ parent->context, |
+ GLX_FBCONFIG_ID, &fbconfig_id |
+ ); |
+ if (status != Success) |
+ goto error; |
+ |
+ if (fbconfig_id == GLX_DONT_CARE) |
+ goto choose_fbconfig; |
+ |
+ fbconfigs = glXGetFBConfigs( |
+ ctx->native_dpy, |
+ ctx->x11_screen, |
+ &n_fbconfigs |
+ ); |
+ if (!fbconfigs) |
+ goto error; |
+ |
+ /* Find out a GLXFBConfig compatible with the parent context */ |
+ for (n = 0; n < n_fbconfigs; n++) { |
+ status = glXGetFBConfigAttrib( |
+ ctx->native_dpy, |
+ fbconfigs[n], |
+ GLX_FBCONFIG_ID, &val |
+ ); |
+ if (status == Success && val == fbconfig_id) |
+ break; |
+ } |
+ if (n == n_fbconfigs) |
+ goto error; |
+ } |
+ else { |
+ choose_fbconfig: |
+ fbconfigs = glXChooseFBConfig( |
+ ctx->native_dpy, |
+ ctx->x11_screen, |
+ fbconfig_attrs, &n_fbconfigs |
+ ); |
+ if (!fbconfigs) |
+ goto error; |
+ |
+ /* Select the first one */ |
+ n = 0; |
+ } |
+ |
+ cs->context = glXCreateNewContext( |
+ ctx->native_dpy, |
+ fbconfigs[n], |
+ GLX_RGBA_TYPE, |
+ parent ? parent->context : NULL, |
+ True |
+ ); |
+ if (cs->context) |
+ goto end; |
+ |
+error: |
+ gl_destroy_context(cs); |
+ cs = NULL; |
+end: |
+ if (fbconfigs) |
+ XFree(fbconfigs); |
+ return cs; |
+} |
+ |
+static void gl_get_current_context(OpenGLContextStateP cs) |
+{ |
+ cs->display = glXGetCurrentDisplay(); |
+ cs->window = glXGetCurrentDrawable(); |
+ cs->context = glXGetCurrentContext(); |
+} |
+ |
+static int |
+gl_set_current_context(OpenGLContextStateP new_cs, OpenGLContextStateP old_cs) |
+{ |
+ /* If display is NULL, this could be that new_cs was retrieved from |
+ gl_get_current_context() with none set previously. If that case, |
+ the other fields are also NULL and we don't return an error */ |
+ if (!new_cs->display) |
+ return !new_cs->window && !new_cs->context; |
+ |
+ if (old_cs) { |
+ if (old_cs == new_cs) |
+ return 1; |
+ gl_get_current_context(old_cs); |
+ if (old_cs->display == new_cs->display && |
+ old_cs->window == new_cs->window && |
+ old_cs->context == new_cs->context) |
+ return 1; |
+ } |
+ return glXMakeCurrent(new_cs->display, new_cs->window, new_cs->context); |
+} |
+ |
+/** Unique VASurfaceGLX identifier */ |
+#define VA_SURFACE_GLX_MAGIC VA_FOURCC('V','A','G','L') |
+ |
+struct VASurfaceGLX { |
+ uint32_t magic; ///< Magic number identifying a VASurfaceGLX |
+ GLenum target; ///< GL target to which the texture is bound |
+ GLuint texture; ///< GL texture |
+ VASurfaceID surface; ///< Associated VA surface |
+ unsigned int width; |
+ unsigned int height; |
+ OpenGLContextStateP gl_context; |
+ int is_bound; |
+ Pixmap pixmap; |
+ GLuint pix_texture; |
+ GLXPixmap glx_pixmap; |
+ GLuint fbo; |
+}; |
+ |
+// Create Pixmaps for GLX texture-from-pixmap extension |
+static int create_tfp_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAOpenGLVTableP const pOpenGLVTable = gl_get_vtable(ctx); |
+ const unsigned int width = pSurfaceGLX->width; |
+ const unsigned int height = pSurfaceGLX->height; |
+ Pixmap pixmap = None; |
+ GLXFBConfig *fbconfig = NULL; |
+ GLXPixmap glx_pixmap = None; |
+ Window root_window; |
+ XWindowAttributes wattr; |
+ int *attrib; |
+ int n_fbconfig_attrs; |
+ |
+ root_window = RootWindow(ctx->native_dpy, ctx->x11_screen); |
+ XGetWindowAttributes(ctx->native_dpy, root_window, &wattr); |
+ if (wattr.depth != 24 && wattr.depth != 32) |
+ return 0; |
+ pixmap = XCreatePixmap( |
+ ctx->native_dpy, |
+ root_window, |
+ width, |
+ height, |
+ wattr.depth |
+ ); |
+ if (!pixmap) |
+ return 0; |
+ pSurfaceGLX->pixmap = pixmap; |
+ |
+ int fbconfig_attrs[32] = { |
+ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, |
+ GLX_DOUBLEBUFFER, GL_TRUE, |
+ GLX_RENDER_TYPE, GLX_RGBA_BIT, |
+ GLX_X_RENDERABLE, GL_TRUE, |
+ GLX_Y_INVERTED_EXT, GL_TRUE, |
+ GLX_RED_SIZE, 8, |
+ GLX_GREEN_SIZE, 8, |
+ GLX_BLUE_SIZE, 8, |
+ GL_NONE, |
+ }; |
+ for (attrib = fbconfig_attrs; *attrib != GL_NONE; attrib += 2) |
+ ; |
+ *attrib++ = GLX_DEPTH_SIZE; *attrib++ = wattr.depth; |
+ if (wattr.depth == 32) { |
+ *attrib++ = GLX_ALPHA_SIZE; *attrib++ = 8; |
+ *attrib++ = GLX_BIND_TO_TEXTURE_RGBA_EXT; *attrib++ = GL_TRUE; |
+ } |
+ else { |
+ *attrib++ = GLX_BIND_TO_TEXTURE_RGB_EXT; *attrib++ = GL_TRUE; |
+ } |
+ *attrib++ = GL_NONE; |
+ |
+ fbconfig = glXChooseFBConfig( |
+ ctx->native_dpy, |
+ ctx->x11_screen, |
+ fbconfig_attrs, |
+ &n_fbconfig_attrs |
+ ); |
+ if (!fbconfig) |
+ return 0; |
+ |
+ int pixmap_attrs[10] = { |
+ GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, |
+ GLX_MIPMAP_TEXTURE_EXT, GL_FALSE, |
+ GL_NONE, |
+ }; |
+ for (attrib = pixmap_attrs; *attrib != GL_NONE; attrib += 2) |
+ ; |
+ *attrib++ = GLX_TEXTURE_FORMAT_EXT; |
+ if (wattr.depth == 32) |
+ *attrib++ = GLX_TEXTURE_FORMAT_RGBA_EXT; |
+ else |
+ *attrib++ = GLX_TEXTURE_FORMAT_RGB_EXT; |
+ *attrib++ = GL_NONE; |
+ |
+ x11_trap_errors(); |
+ glx_pixmap = pOpenGLVTable->glx_create_pixmap( |
+ ctx->native_dpy, |
+ fbconfig[0], |
+ pixmap, |
+ pixmap_attrs |
+ ); |
+ free(fbconfig); |
+ if (x11_untrap_errors() != 0) |
+ return 0; |
+ pSurfaceGLX->glx_pixmap = glx_pixmap; |
+ |
+ glGenTextures(1, &pSurfaceGLX->pix_texture); |
+ glBindTexture(GL_TEXTURE_2D, pSurfaceGLX->pix_texture); |
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
+ return 1; |
+} |
+ |
+// Destroy Pixmaps used for TFP |
+static void destroy_tfp_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAOpenGLVTableP const pOpenGLVTable = gl_get_vtable(ctx); |
+ |
+ if (pSurfaceGLX->pix_texture) { |
+ glDeleteTextures(1, &pSurfaceGLX->pix_texture); |
+ pSurfaceGLX->pix_texture = 0; |
+ } |
+ |
+ if (pSurfaceGLX->glx_pixmap) { |
+ pOpenGLVTable->glx_destroy_pixmap(ctx->native_dpy, pSurfaceGLX->glx_pixmap); |
+ pSurfaceGLX->glx_pixmap = None; |
+ } |
+ |
+ if (pSurfaceGLX->pixmap) { |
+ XFreePixmap(ctx->native_dpy, pSurfaceGLX->pixmap); |
+ pSurfaceGLX->pixmap = None; |
+ } |
+} |
+ |
+// Bind GLX Pixmap to texture |
+static int bind_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ |
+ if (pSurfaceGLX->is_bound) |
+ return 1; |
+ |
+ glBindTexture(GL_TEXTURE_2D, pSurfaceGLX->pix_texture); |
+ |
+ x11_trap_errors(); |
+ pOpenGLVTable->glx_bind_tex_image( |
+ ctx->native_dpy, |
+ pSurfaceGLX->glx_pixmap, |
+ GLX_FRONT_LEFT_EXT, |
+ NULL |
+ ); |
+ XSync(ctx->native_dpy, False); |
+ if (x11_untrap_errors() != 0) { |
+ va_glx_error_message("failed to bind pixmap\n"); |
+ return 0; |
+ } |
+ |
+ pSurfaceGLX->is_bound = 1; |
+ return 1; |
+} |
+ |
+// Release GLX Pixmap from texture |
+static int unbind_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ |
+ if (!pSurfaceGLX->is_bound) |
+ return 1; |
+ |
+ x11_trap_errors(); |
+ pOpenGLVTable->glx_release_tex_image( |
+ ctx->native_dpy, |
+ pSurfaceGLX->glx_pixmap, |
+ GLX_FRONT_LEFT_EXT |
+ ); |
+ XSync(ctx->native_dpy, False); |
+ if (x11_untrap_errors() != 0) { |
+ va_glx_error_message("failed to release pixmap\n"); |
+ return 0; |
+ } |
+ |
+ glBindTexture(GL_TEXTURE_2D, 0); |
+ |
+ pSurfaceGLX->is_bound = 0; |
+ return 1; |
+} |
+ |
+// Render GLX Pixmap to texture |
+static void render_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ const unsigned int w = pSurfaceGLX->width; |
+ const unsigned int h = pSurfaceGLX->height; |
+ |
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f); |
+ glBegin(GL_QUADS); |
+ { |
+ glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); |
+ glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h); |
+ glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); |
+ glTexCoord2f(1.0f, 0.0f); glVertex2i(w, 0); |
+ } |
+ glEnd(); |
+} |
+ |
+// Create offscreen surface |
+static int create_fbo_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ GLuint fbo; |
+ GLenum status; |
+ |
+ pOpenGLVTable->gl_gen_framebuffers(1, &fbo); |
+ pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo); |
+ pOpenGLVTable->gl_framebuffer_texture_2d( |
+ GL_FRAMEBUFFER_EXT, |
+ GL_COLOR_ATTACHMENT0_EXT, |
+ GL_TEXTURE_2D, |
+ pSurfaceGLX->texture, |
+ 0 |
+ ); |
+ |
+ status = pOpenGLVTable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT); |
+ pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0); |
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT) |
+ return 0; |
+ |
+ pSurfaceGLX->fbo = fbo; |
+ return 1; |
+} |
+ |
+// Destroy offscreen surface |
+static void destroy_fbo_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ |
+ if (pSurfaceGLX->fbo) { |
+ pOpenGLVTable->gl_delete_framebuffers(1, &pSurfaceGLX->fbo); |
+ pSurfaceGLX->fbo = 0; |
+ } |
+} |
+ |
+// Setup matrices to match the FBO texture dimensions |
+static void fbo_enter(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ const unsigned int width = pSurfaceGLX->width; |
+ const unsigned int height = pSurfaceGLX->height; |
+ |
+ pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, pSurfaceGLX->fbo); |
+ glPushAttrib(GL_VIEWPORT_BIT); |
+ glMatrixMode(GL_PROJECTION); |
+ glPushMatrix(); |
+ glLoadIdentity(); |
+ glMatrixMode(GL_MODELVIEW); |
+ glPushMatrix(); |
+ glLoadIdentity(); |
+ glViewport(0, 0, width, height); |
+ glTranslatef(-1.0f, -1.0f, 0.0f); |
+ glScalef(2.0f / width, 2.0f / height, 1.0f); |
+} |
+ |
+// Restore original OpenGL matrices |
+static void fbo_leave(VADriverContextP ctx) |
+{ |
+ VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); |
+ |
+ glPopAttrib(); |
+ glMatrixMode(GL_PROJECTION); |
+ glPopMatrix(); |
+ glMatrixMode(GL_MODELVIEW); |
+ glPopMatrix(); |
+ pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0); |
+} |
+ |
+// Check internal texture format is supported |
+static int is_supported_internal_format(GLenum format) |
+{ |
+ /* XXX: we don't support other textures than RGBA */ |
+ switch (format) { |
+ case 4: |
+ case GL_RGBA: |
+ case GL_RGBA8: |
+ return 1; |
+ } |
+ return 0; |
+} |
+ |
+// Destroy VA/GLX surface |
+static void |
+destroy_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ unbind_pixmap(ctx, pSurfaceGLX); |
+ destroy_fbo_surface(ctx, pSurfaceGLX); |
+ destroy_tfp_surface(ctx, pSurfaceGLX); |
+ free(pSurfaceGLX); |
+} |
+ |
+// Create VA/GLX surface |
+static VASurfaceGLXP |
+create_surface(VADriverContextP ctx, GLenum target, GLuint texture) |
+{ |
+ VASurfaceGLXP pSurfaceGLX = NULL; |
+ unsigned int internal_format, border_width, width, height; |
+ int is_error = 1; |
+ |
+ pSurfaceGLX = malloc(sizeof(*pSurfaceGLX)); |
+ if (!pSurfaceGLX) |
+ goto end; |
+ |
+ pSurfaceGLX->magic = VA_SURFACE_GLX_MAGIC; |
+ pSurfaceGLX->target = target; |
+ pSurfaceGLX->texture = texture; |
+ pSurfaceGLX->surface = VA_INVALID_SURFACE; |
+ pSurfaceGLX->gl_context = NULL; |
+ pSurfaceGLX->is_bound = 0; |
+ pSurfaceGLX->pixmap = None; |
+ pSurfaceGLX->pix_texture = 0; |
+ pSurfaceGLX->glx_pixmap = None; |
+ pSurfaceGLX->fbo = 0; |
+ |
+ glEnable(target); |
+ glBindTexture(target, texture); |
+ if (!gl_get_texture_param(GL_TEXTURE_INTERNAL_FORMAT, &internal_format)) |
+ goto end; |
+ if (!is_supported_internal_format(internal_format)) |
+ goto end; |
+ |
+ /* Check texture dimensions */ |
+ if (!gl_get_texture_param(GL_TEXTURE_BORDER, &border_width)) |
+ goto end; |
+ if (!gl_get_texture_param(GL_TEXTURE_WIDTH, &width)) |
+ goto end; |
+ if (!gl_get_texture_param(GL_TEXTURE_HEIGHT, &height)) |
+ goto end; |
+ |
+ width -= 2 * border_width; |
+ height -= 2 * border_width; |
+ if (width == 0 || height == 0) |
+ goto end; |
+ |
+ pSurfaceGLX->width = width; |
+ pSurfaceGLX->height = height; |
+ |
+ /* Create TFP objects */ |
+ if (!create_tfp_surface(ctx, pSurfaceGLX)) |
+ goto end; |
+ |
+ /* Create FBO objects */ |
+ if (!create_fbo_surface(ctx, pSurfaceGLX)) |
+ goto end; |
+ |
+ is_error = 0; |
+end: |
+ if (is_error && pSurfaceGLX) { |
+ destroy_surface(ctx, pSurfaceGLX); |
+ pSurfaceGLX = NULL; |
+ } |
+ return pSurfaceGLX; |
+} |
+ |
+ |
+/* ========================================================================= */ |
+/* === VA/GLX implementation from the driver (fordward calls) === */ |
+/* ========================================================================= */ |
+ |
+#define INVOKE(ctx, func, args) do { \ |
+ VADriverVTableGLXP vtable = (ctx)->vtable_glx; \ |
+ if (!vtable->va##func##GLX) \ |
+ return VA_STATUS_ERROR_UNIMPLEMENTED; \ |
+ \ |
+ VAStatus status = vtable->va##func##GLX args; \ |
+ if (status != VA_STATUS_SUCCESS) \ |
+ return status; \ |
+ } while (0) |
+ |
+static VAStatus |
+vaCreateSurfaceGLX_impl_driver( |
+ VADriverContextP ctx, |
+ GLenum target, |
+ GLuint texture, |
+ void **gl_surface |
+) |
+{ |
+ INVOKE(ctx, CreateSurface, (ctx, target, texture, gl_surface)); |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static VAStatus |
+vaDestroySurfaceGLX_impl_driver(VADriverContextP ctx, void *gl_surface) |
+{ |
+ INVOKE(ctx, DestroySurface, (ctx, gl_surface)); |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static VAStatus |
+vaCopySurfaceGLX_impl_driver( |
+ VADriverContextP ctx, |
+ void *gl_surface, |
+ VASurfaceID surface, |
+ unsigned int flags |
+) |
+{ |
+ INVOKE(ctx, CopySurface, (ctx, gl_surface, surface, flags)); |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+#undef INVOKE |
+ |
+ |
+/* ========================================================================= */ |
+/* === VA/GLX implementation from libVA (generic and suboptimal path) === */ |
+/* ========================================================================= */ |
+ |
+#define INIT_SURFACE(surface, surface_arg) do { \ |
+ surface = (VASurfaceGLXP)(surface_arg); \ |
+ if (!check_surface(surface)) \ |
+ return VA_STATUS_ERROR_INVALID_SURFACE; \ |
+ } while (0) |
+ |
+// Check VASurfaceGLX is valid |
+static inline int check_surface(VASurfaceGLXP pSurfaceGLX) |
+{ |
+ return pSurfaceGLX && pSurfaceGLX->magic == VA_SURFACE_GLX_MAGIC; |
+} |
+ |
+static VAStatus |
+vaCreateSurfaceGLX_impl_libva( |
+ VADriverContextP ctx, |
+ GLenum target, |
+ GLuint texture, |
+ void **gl_surface |
+) |
+{ |
+ VASurfaceGLXP pSurfaceGLX; |
+ struct OpenGLContextState old_cs, *new_cs; |
+ |
+ gl_get_current_context(&old_cs); |
+ new_cs = gl_create_context(ctx, &old_cs); |
+ if (!new_cs) |
+ return VA_STATUS_ERROR_ALLOCATION_FAILED; |
+ if (!gl_set_current_context(new_cs, NULL)) |
+ return VA_STATUS_ERROR_OPERATION_FAILED; |
+ |
+ pSurfaceGLX = create_surface(ctx, target, texture); |
+ if (!pSurfaceGLX) |
+ return VA_STATUS_ERROR_ALLOCATION_FAILED; |
+ |
+ pSurfaceGLX->gl_context = new_cs; |
+ *gl_surface = pSurfaceGLX; |
+ |
+ gl_set_current_context(&old_cs, NULL); |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static VAStatus |
+vaDestroySurfaceGLX_impl_libva(VADriverContextP ctx, void *gl_surface) |
+{ |
+ VASurfaceGLXP pSurfaceGLX; |
+ struct OpenGLContextState old_cs, *new_cs; |
+ |
+ INIT_SURFACE(pSurfaceGLX, gl_surface); |
+ |
+ new_cs = pSurfaceGLX->gl_context; |
+ if (!gl_set_current_context(new_cs, &old_cs)) |
+ return VA_STATUS_ERROR_OPERATION_FAILED; |
+ |
+ destroy_surface(ctx, pSurfaceGLX); |
+ |
+ gl_destroy_context(new_cs); |
+ gl_set_current_context(&old_cs, NULL); |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static inline VAStatus |
+deassociate_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ if (!unbind_pixmap(ctx, pSurfaceGLX)) |
+ return VA_STATUS_ERROR_OPERATION_FAILED; |
+ |
+ pSurfaceGLX->surface = VA_INVALID_SURFACE; |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static VAStatus |
+associate_surface( |
+ VADriverContextP ctx, |
+ VASurfaceGLXP pSurfaceGLX, |
+ VASurfaceID surface, |
+ unsigned int flags |
+) |
+{ |
+ VAStatus status; |
+ |
+ /* XXX: optimise case where we are associating the same VA surface |
+ as before an no changed occurred to it */ |
+ status = deassociate_surface(ctx, pSurfaceGLX); |
+ if (status != VA_STATUS_SUCCESS) |
+ return status; |
+ |
+ x11_trap_errors(); |
+ status = ctx->vtable->vaPutSurface( |
+ ctx, |
+ surface, |
+ (void *)pSurfaceGLX->pixmap, |
+ 0, 0, pSurfaceGLX->width, pSurfaceGLX->height, |
+ 0, 0, pSurfaceGLX->width, pSurfaceGLX->height, |
+ NULL, 0, |
+ flags |
+ ); |
+ XSync(ctx->native_dpy, False); |
+ if (x11_untrap_errors() != 0) |
+ return VA_STATUS_ERROR_OPERATION_FAILED; |
+ if (status != VA_STATUS_SUCCESS) |
+ return status; |
+ |
+ pSurfaceGLX->surface = surface; |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static inline VAStatus |
+sync_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ if (pSurfaceGLX->surface == VA_INVALID_SURFACE) |
+ return VA_STATUS_ERROR_INVALID_SURFACE; |
+ |
+ return ctx->vtable->vaSyncSurface(ctx, pSurfaceGLX->surface); |
+} |
+ |
+static inline VAStatus |
+begin_render_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ VAStatus status; |
+ |
+ status = sync_surface(ctx, pSurfaceGLX); |
+ if (status != VA_STATUS_SUCCESS) |
+ return status; |
+ |
+ if (!bind_pixmap(ctx, pSurfaceGLX)) |
+ return VA_STATUS_ERROR_OPERATION_FAILED; |
+ |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static inline VAStatus |
+end_render_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) |
+{ |
+ if (!unbind_pixmap(ctx, pSurfaceGLX)) |
+ return VA_STATUS_ERROR_OPERATION_FAILED; |
+ |
+ return VA_STATUS_SUCCESS; |
+} |
+ |
+static VAStatus |
+copy_surface( |
+ VADriverContextP ctx, |
+ VASurfaceGLXP pSurfaceGLX, |
+ VASurfaceID surface, |
+ unsigned int flags |
+) |
+{ |
+ VAStatus status; |
+ |
+ /* Associate VA surface */ |
+ status = associate_surface(ctx, pSurfaceGLX, surface, flags); |
+ if (status != VA_STATUS_SUCCESS) |
+ return status; |
+ |
+ /* Render to FBO */ |
+ fbo_enter(ctx, pSurfaceGLX); |
+ status = begin_render_surface(ctx, pSurfaceGLX); |
+ if (status == VA_STATUS_SUCCESS) { |
+ render_pixmap(ctx, pSurfaceGLX); |
+ status = end_render_surface(ctx, pSurfaceGLX); |
+ } |
+ fbo_leave(ctx); |
+ if (status != VA_STATUS_SUCCESS) |
+ return status; |
+ |
+ return deassociate_surface(ctx, pSurfaceGLX); |
+} |
+ |
+static VAStatus |
+vaCopySurfaceGLX_impl_libva( |
+ VADriverContextP ctx, |
+ void *gl_surface, |
+ VASurfaceID surface, |
+ unsigned int flags |
+) |
+{ |
+ VASurfaceGLXP pSurfaceGLX; |
+ VAStatus status; |
+ struct OpenGLContextState old_cs; |
+ |
+ INIT_SURFACE(pSurfaceGLX, gl_surface); |
+ |
+ if (!gl_set_current_context(pSurfaceGLX->gl_context, &old_cs)) |
+ return VA_STATUS_ERROR_OPERATION_FAILED; |
+ |
+ status = copy_surface(ctx, pSurfaceGLX, surface, flags); |
+ |
+ gl_set_current_context(&old_cs, NULL); |
+ return status; |
+} |
+ |
+#undef INIT_SURFACE |
+ |
+ |
+/* ========================================================================= */ |
+/* === Private VA/GLX vtable initialization === */ |
+/* ========================================================================= */ |
+ |
+// Initialize GLX driver context |
+VAStatus va_glx_init_context(VADriverContextP ctx) |
+{ |
+ VADriverContextGLXP glx_ctx = VA_DRIVER_CONTEXT_GLX(ctx); |
+ VADriverVTableGLXP vtable = &glx_ctx->vtable; |
+ int glx_major, glx_minor; |
+ |
+ if (glx_ctx->is_initialized) |
+ return VA_STATUS_SUCCESS; |
+ |
+ if (ctx->vtable_glx && ctx->vtable_glx->vaCopySurfaceGLX) { |
+ vtable->vaCreateSurfaceGLX = vaCreateSurfaceGLX_impl_driver; |
+ vtable->vaDestroySurfaceGLX = vaDestroySurfaceGLX_impl_driver; |
+ vtable->vaCopySurfaceGLX = vaCopySurfaceGLX_impl_driver; |
+ } |
+ else { |
+ vtable->vaCreateSurfaceGLX = vaCreateSurfaceGLX_impl_libva; |
+ vtable->vaDestroySurfaceGLX = vaDestroySurfaceGLX_impl_libva; |
+ vtable->vaCopySurfaceGLX = vaCopySurfaceGLX_impl_libva; |
+ |
+ if (!glXQueryVersion(ctx->native_dpy, &glx_major, &glx_minor)) |
+ return VA_STATUS_ERROR_UNIMPLEMENTED; |
+ |
+ if (!check_tfp_extensions(ctx) || !load_tfp_extensions(ctx)) |
+ return VA_STATUS_ERROR_UNIMPLEMENTED; |
+ |
+ if (!check_fbo_extensions(ctx) || !load_fbo_extensions(ctx)) |
+ return VA_STATUS_ERROR_UNIMPLEMENTED; |
+ } |
+ |
+ glx_ctx->is_initialized = 1; |
+ return VA_STATUS_SUCCESS; |
+} |