/* * 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 "sysdeps.h" #include "va_glx_private.h" #include "va_glx_impl.h" #include #include #include #include #include #include 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_extension3(const char *name) { int nbExtensions, i; PFNGLGETSTRINGIPROC glGetStringi = 0; glGetStringi = (PFNGLGETSTRINGIPROC) get_proc_address("glGetStringi"); if(!glGetStringi) return 0; glGetIntegerv(GL_NUM_EXTENSIONS, &nbExtensions); for(i = 0; i < nbExtensions; i++) { const GLubyte *strExtension = glGetStringi(GL_EXTENSIONS, i); if(strcmp((const char *) strExtension, name) == 0) return 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) && !check_extension3("GL_ARB_texture_non_power_of_two")) 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) || check_extension3("GL_ARB_framebuffer_object")) return 1; if (check_extension("GL_EXT_framebuffer_object", gl_extensions) || check_extension3("GL_EXT_framebuffer_object")) 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; if (parent) { cs->display = parent->display; cs->window = parent->window; } else { cs->display = ctx->native_dpy; cs->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( parent->display, DefaultScreen(parent->display), &n_fbconfigs ); if (!fbconfigs) goto error; /* Find out a GLXFBConfig compatible with the parent context */ for (n = 0; n < n_fbconfigs; n++) { status = glXGetFBConfigAttrib( cs->display, 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( cs->display, 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, /* * depth test isn't enabled in the implementaion of VA GLX, * so depth buffer is unnecessary. However to workaround a * bug in older verson of xorg-server, always require a depth * buffer. * * See https://bugs.freedesktop.org/show_bug.cgi?id=76755 */ GLX_DEPTH_SIZE, 1, GL_NONE, }; for (attrib = fbconfig_attrs; *attrib != GL_NONE; attrib += 2) ; 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) goto error; if (!gl_set_current_context(new_cs, NULL)) goto error; pSurfaceGLX = create_surface(ctx, target, texture); if (!pSurfaceGLX) goto error; pSurfaceGLX->gl_context = new_cs; *gl_surface = pSurfaceGLX; gl_set_current_context(&old_cs, NULL); return VA_STATUS_SUCCESS; error: if (new_cs) gl_destroy_context(new_cs); return VA_STATUS_ERROR_ALLOCATION_FAILED; } static VAStatus vaDestroySurfaceGLX_impl_libva(VADriverContextP ctx, void *gl_surface) { VASurfaceGLXP pSurfaceGLX; struct OpenGLContextState old_cs = {0}, *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 = {0}; 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; }