1 /* 2 * Copyright (C) 2009 Splitted-Desktop Systems. All Rights Reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sub license, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the 13 * next paragraph) shall be included in all copies or substantial portions 14 * of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 19 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR 20 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 #define _GNU_SOURCE 1 26 #include "va_glx_private.h" 27 #include "va_glx_impl.h" 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <stdarg.h> 31 #include <string.h> 32 #include <assert.h> 33 #include <dlfcn.h> 34 35 static void va_glx_error_message(const char *format, ...) 36 { 37 va_list args; 38 va_start(args, format); 39 fprintf(stderr, "libva-glx error: "); 40 vfprintf(stderr, format, args); 41 va_end(args); 42 } 43 44 // X error trap 45 static int x11_error_code = 0; 46 static int (*old_error_handler)(Display *, XErrorEvent *); 47 48 static int error_handler(Display *dpy, XErrorEvent *error) 49 { 50 x11_error_code = error->error_code; 51 return 0; 52 } 53 54 static void x11_trap_errors(void) 55 { 56 x11_error_code = 0; 57 old_error_handler = XSetErrorHandler(error_handler); 58 } 59 60 static int x11_untrap_errors(void) 61 { 62 XSetErrorHandler(old_error_handler); 63 return x11_error_code; 64 } 65 66 // Returns a string representation of an OpenGL error 67 static const char *gl_get_error_string(GLenum error) 68 { 69 static const struct { 70 GLenum val; 71 const char *str; 72 } 73 gl_errors[] = { 74 { GL_NO_ERROR, "no error" }, 75 { GL_INVALID_ENUM, "invalid enumerant" }, 76 { GL_INVALID_VALUE, "invalid value" }, 77 { GL_INVALID_OPERATION, "invalid operation" }, 78 { GL_STACK_OVERFLOW, "stack overflow" }, 79 { GL_STACK_UNDERFLOW, "stack underflow" }, 80 { GL_OUT_OF_MEMORY, "out of memory" }, 81 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT 82 { GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "invalid framebuffer operation" }, 83 #endif 84 { ~0, NULL } 85 }; 86 87 int i; 88 for (i = 0; gl_errors[i].str; i++) { 89 if (gl_errors[i].val == error) 90 return gl_errors[i].str; 91 } 92 return "unknown"; 93 } 94 95 static inline int gl_do_check_error(int report) 96 { 97 GLenum error; 98 int is_error = 0; 99 while ((error = glGetError()) != GL_NO_ERROR) { 100 if (report) 101 va_glx_error_message("glError: %s caught\n", 102 gl_get_error_string(error)); 103 is_error = 1; 104 } 105 return is_error; 106 } 107 108 static inline void gl_purge_errors(void) 109 { 110 gl_do_check_error(0); 111 } 112 113 static inline int gl_check_error(void) 114 { 115 return gl_do_check_error(1); 116 } 117 118 // glGetTexLevelParameteriv() wrapper 119 static int gl_get_texture_param(GLenum param, unsigned int *pval) 120 { 121 GLint val; 122 123 gl_purge_errors(); 124 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, param, &val); 125 if (gl_check_error()) 126 return 0; 127 if (pval) 128 *pval = val; 129 return 1; 130 } 131 132 // Returns the OpenGL VTable 133 static inline VAOpenGLVTableP gl_get_vtable(VADriverContextP ctx) 134 { 135 return &VA_DRIVER_CONTEXT_GLX(ctx)->gl_vtable; 136 } 137 138 // Lookup for a GLX function 139 typedef void (*GLFuncPtr)(void); 140 typedef GLFuncPtr (*GLXGetProcAddressProc)(const char *); 141 142 static GLFuncPtr get_proc_address_default(const char *name) 143 { 144 return NULL; 145 } 146 147 static GLXGetProcAddressProc get_proc_address_func(void) 148 { 149 GLXGetProcAddressProc get_proc_func; 150 151 dlerror(); 152 get_proc_func = (GLXGetProcAddressProc) 153 dlsym(RTLD_DEFAULT, "glXGetProcAddress"); 154 if (!dlerror() && get_proc_func) 155 return get_proc_func; 156 157 get_proc_func = (GLXGetProcAddressProc) 158 dlsym(RTLD_DEFAULT, "glXGetProcAddressARB"); 159 if (!dlerror() && get_proc_func) 160 return get_proc_func; 161 162 return get_proc_address_default; 163 } 164 165 static inline GLFuncPtr get_proc_address(const char *name) 166 { 167 static GLXGetProcAddressProc get_proc_func = NULL; 168 if (!get_proc_func) 169 get_proc_func = get_proc_address_func(); 170 return get_proc_func(name); 171 } 172 173 // Check for GLX extensions (TFP, FBO) 174 static int check_extension(const char *name, const char *ext) 175 { 176 const char *end; 177 int name_len, n; 178 179 if (!name || !ext) 180 return 0; 181 182 end = ext + strlen(ext); 183 name_len = strlen(name); 184 while (ext < end) { 185 n = strcspn(ext, " "); 186 if (n == name_len && strncmp(name, ext, n) == 0) 187 return 1; 188 ext += (n + 1); 189 } 190 return 0; 191 } 192 193 static int check_tfp_extensions(VADriverContextP ctx) 194 { 195 const char *gl_extensions; 196 const char *glx_extensions; 197 198 gl_extensions = (const char *)glGetString(GL_EXTENSIONS); 199 if (!check_extension("GL_ARB_texture_non_power_of_two", gl_extensions)) 200 return 0; 201 202 glx_extensions = glXQueryExtensionsString(ctx->native_dpy, ctx->x11_screen); 203 if (!check_extension("GLX_EXT_texture_from_pixmap", glx_extensions)) 204 return 0; 205 return 1; 206 } 207 208 static int check_fbo_extensions(VADriverContextP ctx) 209 { 210 const char *gl_extensions; 211 212 gl_extensions = (const char *)glGetString(GL_EXTENSIONS); 213 if (check_extension("GL_ARB_framebuffer_object", gl_extensions)) 214 return 1; 215 if (check_extension("GL_EXT_framebuffer_object", gl_extensions)) 216 return 1; 217 return 0; 218 } 219 220 // Load GLX extensions 221 static int load_tfp_extensions(VADriverContextP ctx) 222 { 223 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 224 225 pOpenGLVTable->glx_create_pixmap = (PFNGLXCREATEPIXMAPPROC) 226 get_proc_address("glXCreatePixmap"); 227 if (!pOpenGLVTable->glx_create_pixmap) 228 return 0; 229 pOpenGLVTable->glx_destroy_pixmap = (PFNGLXDESTROYPIXMAPPROC) 230 get_proc_address("glXDestroyPixmap"); 231 if (!pOpenGLVTable->glx_destroy_pixmap) 232 return 0; 233 pOpenGLVTable->glx_bind_tex_image = (PFNGLXBINDTEXIMAGEEXTPROC) 234 get_proc_address("glXBindTexImageEXT"); 235 if (!pOpenGLVTable->glx_bind_tex_image) 236 return 0; 237 pOpenGLVTable->glx_release_tex_image = (PFNGLXRELEASETEXIMAGEEXTPROC) 238 get_proc_address("glXReleaseTexImageEXT"); 239 if (!pOpenGLVTable->glx_release_tex_image) 240 return 0; 241 return 1; 242 } 243 244 static int load_fbo_extensions(VADriverContextP ctx) 245 { 246 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 247 248 pOpenGLVTable->gl_gen_framebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) 249 get_proc_address("glGenFramebuffersEXT"); 250 if (!pOpenGLVTable->gl_gen_framebuffers) 251 return 0; 252 pOpenGLVTable->gl_delete_framebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC) 253 get_proc_address("glDeleteFramebuffersEXT"); 254 if (!pOpenGLVTable->gl_delete_framebuffers) 255 return 0; 256 pOpenGLVTable->gl_bind_framebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC) 257 get_proc_address("glBindFramebufferEXT"); 258 if (!pOpenGLVTable->gl_bind_framebuffer) 259 return 0; 260 pOpenGLVTable->gl_gen_renderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC) 261 get_proc_address("glGenRenderbuffersEXT"); 262 if (!pOpenGLVTable->gl_gen_renderbuffers) 263 return 0; 264 pOpenGLVTable->gl_delete_renderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC) 265 get_proc_address("glDeleteRenderbuffersEXT"); 266 if (!pOpenGLVTable->gl_delete_renderbuffers) 267 return 0; 268 pOpenGLVTable->gl_bind_renderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC) 269 get_proc_address("glBindRenderbufferEXT"); 270 if (!pOpenGLVTable->gl_bind_renderbuffer) 271 return 0; 272 pOpenGLVTable->gl_renderbuffer_storage = (PFNGLRENDERBUFFERSTORAGEEXTPROC) 273 get_proc_address("glRenderbufferStorageEXT"); 274 if (!pOpenGLVTable->gl_renderbuffer_storage) 275 return 0; 276 pOpenGLVTable->gl_framebuffer_renderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) 277 get_proc_address("glFramebufferRenderbufferEXT"); 278 if (!pOpenGLVTable->gl_framebuffer_renderbuffer) 279 return 0; 280 pOpenGLVTable->gl_framebuffer_texture_2d = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) 281 get_proc_address("glFramebufferTexture2DEXT"); 282 if (!pOpenGLVTable->gl_framebuffer_texture_2d) 283 return 0; 284 pOpenGLVTable->gl_check_framebuffer_status = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) 285 get_proc_address("glCheckFramebufferStatusEXT"); 286 if (!pOpenGLVTable->gl_check_framebuffer_status) 287 return 0; 288 return 1; 289 } 290 291 292 /* ========================================================================= */ 293 /* === VA/GLX helpers === */ 294 /* ========================================================================= */ 295 296 // OpenGL context state 297 typedef struct OpenGLContextState *OpenGLContextStateP; 298 299 struct OpenGLContextState { 300 Display *display; 301 Window window; 302 GLXContext context; 303 }; 304 305 static void 306 gl_destroy_context(OpenGLContextStateP cs) 307 { 308 if (!cs) 309 return; 310 311 if (cs->display && cs->context) { 312 if (glXGetCurrentContext() == cs->context) 313 glXMakeCurrent(cs->display, None, NULL); 314 glXDestroyContext(cs->display, cs->context); 315 cs->display = NULL; 316 cs->context = NULL; 317 } 318 free(cs); 319 } 320 321 static OpenGLContextStateP 322 gl_create_context(VADriverContextP ctx, OpenGLContextStateP parent) 323 { 324 OpenGLContextStateP cs; 325 GLXFBConfig *fbconfigs = NULL; 326 int fbconfig_id, val, n, n_fbconfigs; 327 Status status; 328 329 static GLint fbconfig_attrs[] = { 330 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 331 GLX_RENDER_TYPE, GLX_RGBA_BIT, 332 GLX_DOUBLEBUFFER, True, 333 GLX_RED_SIZE, 8, 334 GLX_GREEN_SIZE, 8, 335 GLX_BLUE_SIZE, 8, 336 None 337 }; 338 339 cs = malloc(sizeof(*cs)); 340 if (!cs) 341 goto error; 342 343 cs->display = ctx->native_dpy; 344 cs->window = parent ? parent->window : None; 345 cs->context = NULL; 346 347 if (parent && parent->context) { 348 status = glXQueryContext( 349 parent->display, 350 parent->context, 351 GLX_FBCONFIG_ID, &fbconfig_id 352 ); 353 if (status != Success) 354 goto error; 355 356 if (fbconfig_id == GLX_DONT_CARE) 357 goto choose_fbconfig; 358 359 fbconfigs = glXGetFBConfigs( 360 ctx->native_dpy, 361 ctx->x11_screen, 362 &n_fbconfigs 363 ); 364 if (!fbconfigs) 365 goto error; 366 367 /* Find out a GLXFBConfig compatible with the parent context */ 368 for (n = 0; n < n_fbconfigs; n++) { 369 status = glXGetFBConfigAttrib( 370 ctx->native_dpy, 371 fbconfigs[n], 372 GLX_FBCONFIG_ID, &val 373 ); 374 if (status == Success && val == fbconfig_id) 375 break; 376 } 377 if (n == n_fbconfigs) 378 goto error; 379 } 380 else { 381 choose_fbconfig: 382 fbconfigs = glXChooseFBConfig( 383 ctx->native_dpy, 384 ctx->x11_screen, 385 fbconfig_attrs, &n_fbconfigs 386 ); 387 if (!fbconfigs) 388 goto error; 389 390 /* Select the first one */ 391 n = 0; 392 } 393 394 cs->context = glXCreateNewContext( 395 ctx->native_dpy, 396 fbconfigs[n], 397 GLX_RGBA_TYPE, 398 parent ? parent->context : NULL, 399 True 400 ); 401 if (cs->context) 402 goto end; 403 404 error: 405 gl_destroy_context(cs); 406 cs = NULL; 407 end: 408 if (fbconfigs) 409 XFree(fbconfigs); 410 return cs; 411 } 412 413 static void gl_get_current_context(OpenGLContextStateP cs) 414 { 415 cs->display = glXGetCurrentDisplay(); 416 cs->window = glXGetCurrentDrawable(); 417 cs->context = glXGetCurrentContext(); 418 } 419 420 static int 421 gl_set_current_context(OpenGLContextStateP new_cs, OpenGLContextStateP old_cs) 422 { 423 /* If display is NULL, this could be that new_cs was retrieved from 424 gl_get_current_context() with none set previously. If that case, 425 the other fields are also NULL and we don't return an error */ 426 if (!new_cs->display) 427 return !new_cs->window && !new_cs->context; 428 429 if (old_cs) { 430 if (old_cs == new_cs) 431 return 1; 432 gl_get_current_context(old_cs); 433 if (old_cs->display == new_cs->display && 434 old_cs->window == new_cs->window && 435 old_cs->context == new_cs->context) 436 return 1; 437 } 438 return glXMakeCurrent(new_cs->display, new_cs->window, new_cs->context); 439 } 440 441 /** Unique VASurfaceGLX identifier */ 442 #define VA_SURFACE_GLX_MAGIC VA_FOURCC('V','A','G','L') 443 444 struct VASurfaceGLX { 445 uint32_t magic; ///< Magic number identifying a VASurfaceGLX 446 GLenum target; ///< GL target to which the texture is bound 447 GLuint texture; ///< GL texture 448 VASurfaceID surface; ///< Associated VA surface 449 unsigned int width; 450 unsigned int height; 451 OpenGLContextStateP gl_context; 452 int is_bound; 453 Pixmap pixmap; 454 GLuint pix_texture; 455 GLXPixmap glx_pixmap; 456 GLuint fbo; 457 }; 458 459 // Create Pixmaps for GLX texture-from-pixmap extension 460 static int create_tfp_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 461 { 462 VAOpenGLVTableP const pOpenGLVTable = gl_get_vtable(ctx); 463 const unsigned int width = pSurfaceGLX->width; 464 const unsigned int height = pSurfaceGLX->height; 465 Pixmap pixmap = None; 466 GLXFBConfig *fbconfig = NULL; 467 GLXPixmap glx_pixmap = None; 468 Window root_window; 469 XWindowAttributes wattr; 470 int *attrib; 471 int n_fbconfig_attrs; 472 473 root_window = RootWindow(ctx->native_dpy, ctx->x11_screen); 474 XGetWindowAttributes(ctx->native_dpy, root_window, &wattr); 475 if (wattr.depth != 24 && wattr.depth != 32) 476 return 0; 477 pixmap = XCreatePixmap( 478 ctx->native_dpy, 479 root_window, 480 width, 481 height, 482 wattr.depth 483 ); 484 if (!pixmap) 485 return 0; 486 pSurfaceGLX->pixmap = pixmap; 487 488 int fbconfig_attrs[32] = { 489 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, 490 GLX_DOUBLEBUFFER, GL_TRUE, 491 GLX_RENDER_TYPE, GLX_RGBA_BIT, 492 GLX_X_RENDERABLE, GL_TRUE, 493 GLX_Y_INVERTED_EXT, GL_TRUE, 494 GLX_RED_SIZE, 8, 495 GLX_GREEN_SIZE, 8, 496 GLX_BLUE_SIZE, 8, 497 GL_NONE, 498 }; 499 for (attrib = fbconfig_attrs; *attrib != GL_NONE; attrib += 2) 500 ; 501 *attrib++ = GLX_DEPTH_SIZE; *attrib++ = wattr.depth; 502 if (wattr.depth == 32) { 503 *attrib++ = GLX_ALPHA_SIZE; *attrib++ = 8; 504 *attrib++ = GLX_BIND_TO_TEXTURE_RGBA_EXT; *attrib++ = GL_TRUE; 505 } 506 else { 507 *attrib++ = GLX_BIND_TO_TEXTURE_RGB_EXT; *attrib++ = GL_TRUE; 508 } 509 *attrib++ = GL_NONE; 510 511 fbconfig = glXChooseFBConfig( 512 ctx->native_dpy, 513 ctx->x11_screen, 514 fbconfig_attrs, 515 &n_fbconfig_attrs 516 ); 517 if (!fbconfig) 518 return 0; 519 520 int pixmap_attrs[10] = { 521 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, 522 GLX_MIPMAP_TEXTURE_EXT, GL_FALSE, 523 GL_NONE, 524 }; 525 for (attrib = pixmap_attrs; *attrib != GL_NONE; attrib += 2) 526 ; 527 *attrib++ = GLX_TEXTURE_FORMAT_EXT; 528 if (wattr.depth == 32) 529 *attrib++ = GLX_TEXTURE_FORMAT_RGBA_EXT; 530 else 531 *attrib++ = GLX_TEXTURE_FORMAT_RGB_EXT; 532 *attrib++ = GL_NONE; 533 534 x11_trap_errors(); 535 glx_pixmap = pOpenGLVTable->glx_create_pixmap( 536 ctx->native_dpy, 537 fbconfig[0], 538 pixmap, 539 pixmap_attrs 540 ); 541 free(fbconfig); 542 if (x11_untrap_errors() != 0) 543 return 0; 544 pSurfaceGLX->glx_pixmap = glx_pixmap; 545 546 glGenTextures(1, &pSurfaceGLX->pix_texture); 547 glBindTexture(GL_TEXTURE_2D, pSurfaceGLX->pix_texture); 548 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 549 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 550 return 1; 551 } 552 553 // Destroy Pixmaps used for TFP 554 static void destroy_tfp_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 555 { 556 VAOpenGLVTableP const pOpenGLVTable = gl_get_vtable(ctx); 557 558 if (pSurfaceGLX->pix_texture) { 559 glDeleteTextures(1, &pSurfaceGLX->pix_texture); 560 pSurfaceGLX->pix_texture = 0; 561 } 562 563 if (pSurfaceGLX->glx_pixmap) { 564 pOpenGLVTable->glx_destroy_pixmap(ctx->native_dpy, pSurfaceGLX->glx_pixmap); 565 pSurfaceGLX->glx_pixmap = None; 566 } 567 568 if (pSurfaceGLX->pixmap) { 569 XFreePixmap(ctx->native_dpy, pSurfaceGLX->pixmap); 570 pSurfaceGLX->pixmap = None; 571 } 572 } 573 574 // Bind GLX Pixmap to texture 575 static int bind_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 576 { 577 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 578 579 if (pSurfaceGLX->is_bound) 580 return 1; 581 582 glBindTexture(GL_TEXTURE_2D, pSurfaceGLX->pix_texture); 583 584 x11_trap_errors(); 585 pOpenGLVTable->glx_bind_tex_image( 586 ctx->native_dpy, 587 pSurfaceGLX->glx_pixmap, 588 GLX_FRONT_LEFT_EXT, 589 NULL 590 ); 591 XSync(ctx->native_dpy, False); 592 if (x11_untrap_errors() != 0) { 593 va_glx_error_message("failed to bind pixmap\n"); 594 return 0; 595 } 596 597 pSurfaceGLX->is_bound = 1; 598 return 1; 599 } 600 601 // Release GLX Pixmap from texture 602 static int unbind_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 603 { 604 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 605 606 if (!pSurfaceGLX->is_bound) 607 return 1; 608 609 x11_trap_errors(); 610 pOpenGLVTable->glx_release_tex_image( 611 ctx->native_dpy, 612 pSurfaceGLX->glx_pixmap, 613 GLX_FRONT_LEFT_EXT 614 ); 615 XSync(ctx->native_dpy, False); 616 if (x11_untrap_errors() != 0) { 617 va_glx_error_message("failed to release pixmap\n"); 618 return 0; 619 } 620 621 glBindTexture(GL_TEXTURE_2D, 0); 622 623 pSurfaceGLX->is_bound = 0; 624 return 1; 625 } 626 627 // Render GLX Pixmap to texture 628 static void render_pixmap(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 629 { 630 const unsigned int w = pSurfaceGLX->width; 631 const unsigned int h = pSurfaceGLX->height; 632 633 glColor4f(1.0f, 1.0f, 1.0f, 1.0f); 634 glBegin(GL_QUADS); 635 { 636 glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); 637 glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h); 638 glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); 639 glTexCoord2f(1.0f, 0.0f); glVertex2i(w, 0); 640 } 641 glEnd(); 642 } 643 644 // Create offscreen surface 645 static int create_fbo_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 646 { 647 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 648 GLuint fbo; 649 GLenum status; 650 651 pOpenGLVTable->gl_gen_framebuffers(1, &fbo); 652 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, fbo); 653 pOpenGLVTable->gl_framebuffer_texture_2d( 654 GL_FRAMEBUFFER_EXT, 655 GL_COLOR_ATTACHMENT0_EXT, 656 GL_TEXTURE_2D, 657 pSurfaceGLX->texture, 658 0 659 ); 660 661 status = pOpenGLVTable->gl_check_framebuffer_status(GL_DRAW_FRAMEBUFFER_EXT); 662 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0); 663 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) 664 return 0; 665 666 pSurfaceGLX->fbo = fbo; 667 return 1; 668 } 669 670 // Destroy offscreen surface 671 static void destroy_fbo_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 672 { 673 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 674 675 if (pSurfaceGLX->fbo) { 676 pOpenGLVTable->gl_delete_framebuffers(1, &pSurfaceGLX->fbo); 677 pSurfaceGLX->fbo = 0; 678 } 679 } 680 681 // Setup matrices to match the FBO texture dimensions 682 static void fbo_enter(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 683 { 684 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 685 const unsigned int width = pSurfaceGLX->width; 686 const unsigned int height = pSurfaceGLX->height; 687 688 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, pSurfaceGLX->fbo); 689 glPushAttrib(GL_VIEWPORT_BIT); 690 glMatrixMode(GL_PROJECTION); 691 glPushMatrix(); 692 glLoadIdentity(); 693 glMatrixMode(GL_MODELVIEW); 694 glPushMatrix(); 695 glLoadIdentity(); 696 glViewport(0, 0, width, height); 697 glTranslatef(-1.0f, -1.0f, 0.0f); 698 glScalef(2.0f / width, 2.0f / height, 1.0f); 699 } 700 701 // Restore original OpenGL matrices 702 static void fbo_leave(VADriverContextP ctx) 703 { 704 VAOpenGLVTableP pOpenGLVTable = gl_get_vtable(ctx); 705 706 glPopAttrib(); 707 glMatrixMode(GL_PROJECTION); 708 glPopMatrix(); 709 glMatrixMode(GL_MODELVIEW); 710 glPopMatrix(); 711 pOpenGLVTable->gl_bind_framebuffer(GL_FRAMEBUFFER_EXT, 0); 712 } 713 714 // Check internal texture format is supported 715 static int is_supported_internal_format(GLenum format) 716 { 717 /* XXX: we don't support other textures than RGBA */ 718 switch (format) { 719 case 4: 720 case GL_RGBA: 721 case GL_RGBA8: 722 return 1; 723 } 724 return 0; 725 } 726 727 // Destroy VA/GLX surface 728 static void 729 destroy_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 730 { 731 unbind_pixmap(ctx, pSurfaceGLX); 732 destroy_fbo_surface(ctx, pSurfaceGLX); 733 destroy_tfp_surface(ctx, pSurfaceGLX); 734 free(pSurfaceGLX); 735 } 736 737 // Create VA/GLX surface 738 static VASurfaceGLXP 739 create_surface(VADriverContextP ctx, GLenum target, GLuint texture) 740 { 741 VASurfaceGLXP pSurfaceGLX = NULL; 742 unsigned int internal_format, border_width, width, height; 743 int is_error = 1; 744 745 pSurfaceGLX = malloc(sizeof(*pSurfaceGLX)); 746 if (!pSurfaceGLX) 747 goto end; 748 749 pSurfaceGLX->magic = VA_SURFACE_GLX_MAGIC; 750 pSurfaceGLX->target = target; 751 pSurfaceGLX->texture = texture; 752 pSurfaceGLX->surface = VA_INVALID_SURFACE; 753 pSurfaceGLX->gl_context = NULL; 754 pSurfaceGLX->is_bound = 0; 755 pSurfaceGLX->pixmap = None; 756 pSurfaceGLX->pix_texture = 0; 757 pSurfaceGLX->glx_pixmap = None; 758 pSurfaceGLX->fbo = 0; 759 760 glEnable(target); 761 glBindTexture(target, texture); 762 if (!gl_get_texture_param(GL_TEXTURE_INTERNAL_FORMAT, &internal_format)) 763 goto end; 764 if (!is_supported_internal_format(internal_format)) 765 goto end; 766 767 /* Check texture dimensions */ 768 if (!gl_get_texture_param(GL_TEXTURE_BORDER, &border_width)) 769 goto end; 770 if (!gl_get_texture_param(GL_TEXTURE_WIDTH, &width)) 771 goto end; 772 if (!gl_get_texture_param(GL_TEXTURE_HEIGHT, &height)) 773 goto end; 774 775 width -= 2 * border_width; 776 height -= 2 * border_width; 777 if (width == 0 || height == 0) 778 goto end; 779 780 pSurfaceGLX->width = width; 781 pSurfaceGLX->height = height; 782 783 /* Create TFP objects */ 784 if (!create_tfp_surface(ctx, pSurfaceGLX)) 785 goto end; 786 787 /* Create FBO objects */ 788 if (!create_fbo_surface(ctx, pSurfaceGLX)) 789 goto end; 790 791 is_error = 0; 792 end: 793 if (is_error && pSurfaceGLX) { 794 destroy_surface(ctx, pSurfaceGLX); 795 pSurfaceGLX = NULL; 796 } 797 return pSurfaceGLX; 798 } 799 800 801 /* ========================================================================= */ 802 /* === VA/GLX implementation from the driver (fordward calls) === */ 803 /* ========================================================================= */ 804 805 #define INVOKE(ctx, func, args) do { \ 806 VADriverVTableGLXP vtable = (ctx)->vtable_glx; \ 807 if (!vtable->va##func##GLX) \ 808 return VA_STATUS_ERROR_UNIMPLEMENTED; \ 809 \ 810 VAStatus status = vtable->va##func##GLX args; \ 811 if (status != VA_STATUS_SUCCESS) \ 812 return status; \ 813 } while (0) 814 815 static VAStatus 816 vaCreateSurfaceGLX_impl_driver( 817 VADriverContextP ctx, 818 GLenum target, 819 GLuint texture, 820 void **gl_surface 821 ) 822 { 823 INVOKE(ctx, CreateSurface, (ctx, target, texture, gl_surface)); 824 return VA_STATUS_SUCCESS; 825 } 826 827 static VAStatus 828 vaDestroySurfaceGLX_impl_driver(VADriverContextP ctx, void *gl_surface) 829 { 830 INVOKE(ctx, DestroySurface, (ctx, gl_surface)); 831 return VA_STATUS_SUCCESS; 832 } 833 834 static VAStatus 835 vaCopySurfaceGLX_impl_driver( 836 VADriverContextP ctx, 837 void *gl_surface, 838 VASurfaceID surface, 839 unsigned int flags 840 ) 841 { 842 INVOKE(ctx, CopySurface, (ctx, gl_surface, surface, flags)); 843 return VA_STATUS_SUCCESS; 844 } 845 846 #undef INVOKE 847 848 849 /* ========================================================================= */ 850 /* === VA/GLX implementation from libVA (generic and suboptimal path) === */ 851 /* ========================================================================= */ 852 853 #define INIT_SURFACE(surface, surface_arg) do { \ 854 surface = (VASurfaceGLXP)(surface_arg); \ 855 if (!check_surface(surface)) \ 856 return VA_STATUS_ERROR_INVALID_SURFACE; \ 857 } while (0) 858 859 // Check VASurfaceGLX is valid 860 static inline int check_surface(VASurfaceGLXP pSurfaceGLX) 861 { 862 return pSurfaceGLX && pSurfaceGLX->magic == VA_SURFACE_GLX_MAGIC; 863 } 864 865 static VAStatus 866 vaCreateSurfaceGLX_impl_libva( 867 VADriverContextP ctx, 868 GLenum target, 869 GLuint texture, 870 void **gl_surface 871 ) 872 { 873 VASurfaceGLXP pSurfaceGLX; 874 struct OpenGLContextState old_cs, *new_cs; 875 876 gl_get_current_context(&old_cs); 877 new_cs = gl_create_context(ctx, &old_cs); 878 if (!new_cs) 879 goto error; 880 if (!gl_set_current_context(new_cs, NULL)) 881 goto error; 882 883 pSurfaceGLX = create_surface(ctx, target, texture); 884 if (!pSurfaceGLX) 885 goto error; 886 887 pSurfaceGLX->gl_context = new_cs; 888 *gl_surface = pSurfaceGLX; 889 890 gl_set_current_context(&old_cs, NULL); 891 return VA_STATUS_SUCCESS; 892 893 error: 894 if (new_cs) 895 gl_destroy_context(new_cs); 896 897 return VA_STATUS_ERROR_ALLOCATION_FAILED; 898 } 899 900 static VAStatus 901 vaDestroySurfaceGLX_impl_libva(VADriverContextP ctx, void *gl_surface) 902 { 903 VASurfaceGLXP pSurfaceGLX; 904 struct OpenGLContextState old_cs, *new_cs; 905 906 INIT_SURFACE(pSurfaceGLX, gl_surface); 907 908 new_cs = pSurfaceGLX->gl_context; 909 if (!gl_set_current_context(new_cs, &old_cs)) 910 return VA_STATUS_ERROR_OPERATION_FAILED; 911 912 destroy_surface(ctx, pSurfaceGLX); 913 914 gl_destroy_context(new_cs); 915 gl_set_current_context(&old_cs, NULL); 916 return VA_STATUS_SUCCESS; 917 } 918 919 static inline VAStatus 920 deassociate_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 921 { 922 if (!unbind_pixmap(ctx, pSurfaceGLX)) 923 return VA_STATUS_ERROR_OPERATION_FAILED; 924 925 pSurfaceGLX->surface = VA_INVALID_SURFACE; 926 return VA_STATUS_SUCCESS; 927 } 928 929 static VAStatus 930 associate_surface( 931 VADriverContextP ctx, 932 VASurfaceGLXP pSurfaceGLX, 933 VASurfaceID surface, 934 unsigned int flags 935 ) 936 { 937 VAStatus status; 938 939 /* XXX: optimise case where we are associating the same VA surface 940 as before an no changed occurred to it */ 941 status = deassociate_surface(ctx, pSurfaceGLX); 942 if (status != VA_STATUS_SUCCESS) 943 return status; 944 945 x11_trap_errors(); 946 status = ctx->vtable->vaPutSurface( 947 ctx, 948 surface, 949 (void *)pSurfaceGLX->pixmap, 950 0, 0, pSurfaceGLX->width, pSurfaceGLX->height, 951 0, 0, pSurfaceGLX->width, pSurfaceGLX->height, 952 NULL, 0, 953 flags 954 ); 955 XSync(ctx->native_dpy, False); 956 if (x11_untrap_errors() != 0) 957 return VA_STATUS_ERROR_OPERATION_FAILED; 958 if (status != VA_STATUS_SUCCESS) 959 return status; 960 961 pSurfaceGLX->surface = surface; 962 return VA_STATUS_SUCCESS; 963 } 964 965 static inline VAStatus 966 sync_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 967 { 968 if (pSurfaceGLX->surface == VA_INVALID_SURFACE) 969 return VA_STATUS_ERROR_INVALID_SURFACE; 970 971 return ctx->vtable->vaSyncSurface(ctx, pSurfaceGLX->surface); 972 } 973 974 static inline VAStatus 975 begin_render_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 976 { 977 VAStatus status; 978 979 status = sync_surface(ctx, pSurfaceGLX); 980 if (status != VA_STATUS_SUCCESS) 981 return status; 982 983 if (!bind_pixmap(ctx, pSurfaceGLX)) 984 return VA_STATUS_ERROR_OPERATION_FAILED; 985 986 return VA_STATUS_SUCCESS; 987 } 988 989 static inline VAStatus 990 end_render_surface(VADriverContextP ctx, VASurfaceGLXP pSurfaceGLX) 991 { 992 if (!unbind_pixmap(ctx, pSurfaceGLX)) 993 return VA_STATUS_ERROR_OPERATION_FAILED; 994 995 return VA_STATUS_SUCCESS; 996 } 997 998 static VAStatus 999 copy_surface( 1000 VADriverContextP ctx, 1001 VASurfaceGLXP pSurfaceGLX, 1002 VASurfaceID surface, 1003 unsigned int flags 1004 ) 1005 { 1006 VAStatus status; 1007 1008 /* Associate VA surface */ 1009 status = associate_surface(ctx, pSurfaceGLX, surface, flags); 1010 if (status != VA_STATUS_SUCCESS) 1011 return status; 1012 1013 /* Render to FBO */ 1014 fbo_enter(ctx, pSurfaceGLX); 1015 status = begin_render_surface(ctx, pSurfaceGLX); 1016 if (status == VA_STATUS_SUCCESS) { 1017 render_pixmap(ctx, pSurfaceGLX); 1018 status = end_render_surface(ctx, pSurfaceGLX); 1019 } 1020 fbo_leave(ctx); 1021 if (status != VA_STATUS_SUCCESS) 1022 return status; 1023 1024 return deassociate_surface(ctx, pSurfaceGLX); 1025 } 1026 1027 static VAStatus 1028 vaCopySurfaceGLX_impl_libva( 1029 VADriverContextP ctx, 1030 void *gl_surface, 1031 VASurfaceID surface, 1032 unsigned int flags 1033 ) 1034 { 1035 VASurfaceGLXP pSurfaceGLX; 1036 VAStatus status; 1037 struct OpenGLContextState old_cs; 1038 1039 INIT_SURFACE(pSurfaceGLX, gl_surface); 1040 1041 if (!gl_set_current_context(pSurfaceGLX->gl_context, &old_cs)) 1042 return VA_STATUS_ERROR_OPERATION_FAILED; 1043 1044 status = copy_surface(ctx, pSurfaceGLX, surface, flags); 1045 1046 gl_set_current_context(&old_cs, NULL); 1047 return status; 1048 } 1049 1050 #undef INIT_SURFACE 1051 1052 1053 /* ========================================================================= */ 1054 /* === Private VA/GLX vtable initialization === */ 1055 /* ========================================================================= */ 1056 1057 // Initialize GLX driver context 1058 VAStatus va_glx_init_context(VADriverContextP ctx) 1059 { 1060 VADriverContextGLXP glx_ctx = VA_DRIVER_CONTEXT_GLX(ctx); 1061 VADriverVTableGLXP vtable = &glx_ctx->vtable; 1062 int glx_major, glx_minor; 1063 1064 if (glx_ctx->is_initialized) 1065 return VA_STATUS_SUCCESS; 1066 1067 if (ctx->vtable_glx && ctx->vtable_glx->vaCopySurfaceGLX) { 1068 vtable->vaCreateSurfaceGLX = vaCreateSurfaceGLX_impl_driver; 1069 vtable->vaDestroySurfaceGLX = vaDestroySurfaceGLX_impl_driver; 1070 vtable->vaCopySurfaceGLX = vaCopySurfaceGLX_impl_driver; 1071 } 1072 else { 1073 vtable->vaCreateSurfaceGLX = vaCreateSurfaceGLX_impl_libva; 1074 vtable->vaDestroySurfaceGLX = vaDestroySurfaceGLX_impl_libva; 1075 vtable->vaCopySurfaceGLX = vaCopySurfaceGLX_impl_libva; 1076 1077 if (!glXQueryVersion(ctx->native_dpy, &glx_major, &glx_minor)) 1078 return VA_STATUS_ERROR_UNIMPLEMENTED; 1079 1080 if (!check_tfp_extensions(ctx) || !load_tfp_extensions(ctx)) 1081 return VA_STATUS_ERROR_UNIMPLEMENTED; 1082 1083 if (!check_fbo_extensions(ctx) || !load_fbo_extensions(ctx)) 1084 return VA_STATUS_ERROR_UNIMPLEMENTED; 1085 } 1086 1087 glx_ctx->is_initialized = 1; 1088 return VA_STATUS_SUCCESS; 1089 } 1090