apple_glx_context.c revision b8e80941
1/* 2 Copyright (c) 2008, 2009 Apple Inc. 3 4 Permission is hereby granted, free of charge, to any person 5 obtaining a copy of this software and associated documentation files 6 (the "Software"), to deal in the Software without restriction, 7 including without limitation the rights to use, copy, modify, merge, 8 publish, distribute, sublicense, and/or sell copies of the Software, 9 and to permit persons to whom the Software is furnished to do so, 10 subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be 13 included in all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 19 HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 DEALINGS IN THE SOFTWARE. 23 24 Except as contained in this notice, the name(s) of the above 25 copyright holders shall not be used in advertising or otherwise to 26 promote the sale, use or other dealings in this Software without 27 prior written authorization. 28*/ 29 30#include <stdbool.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <limits.h> 34#include <assert.h> 35#include <pthread.h> 36 37#include <fcntl.h> 38#include <sys/mman.h> 39#include <unistd.h> 40 41// Get the newer glext.h first 42#include <GL/gl.h> 43#include <GL/glext.h> 44 45#include <OpenGL/CGLTypes.h> 46#include <OpenGL/CGLCurrent.h> 47#include <OpenGL/OpenGL.h> 48 49#include "glxclient.h" 50 51#include "apple_glx.h" 52#include "apple_glx_context.h" 53#include "appledri.h" 54#include "apple_visual.h" 55#include "apple_cgl.h" 56#include "apple_glx_drawable.h" 57 58#include "util/debug.h" 59 60static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER; 61 62/* 63 * This should be locked on creation and destruction of the 64 * apple_glx_contexts. 65 * 66 * It's also locked when the surface_notify_handler is searching 67 * for a uid associated with a surface. 68 */ 69static struct apple_glx_context *context_list = NULL; 70 71/* This guards the context_list above. */ 72static void 73lock_context_list(void) 74{ 75 int err; 76 77 err = pthread_mutex_lock(&context_lock); 78 79 if (err) { 80 fprintf(stderr, "pthread_mutex_lock failure in %s: %d\n", 81 __func__, err); 82 abort(); 83 } 84} 85 86static void 87unlock_context_list(void) 88{ 89 int err; 90 91 err = pthread_mutex_unlock(&context_lock); 92 93 if (err) { 94 fprintf(stderr, "pthread_mutex_unlock failure in %s: %d\n", 95 __func__, err); 96 abort(); 97 } 98} 99 100static bool 101is_context_valid(struct apple_glx_context *ac) 102{ 103 struct apple_glx_context *i; 104 105 lock_context_list(); 106 107 for (i = context_list; i; i = i->next) { 108 if (ac == i) { 109 unlock_context_list(); 110 return true; 111 } 112 } 113 114 unlock_context_list(); 115 116 return false; 117} 118 119/* This creates an apple_private_context struct. 120 * 121 * It's typically called to save the struct in a GLXContext. 122 * 123 * This is also where the CGLContextObj is created, and the CGLPixelFormatObj. 124 */ 125bool 126apple_glx_create_context(void **ptr, Display * dpy, int screen, 127 const void *mode, void *sharedContext, 128 int *errorptr, bool * x11errorptr) 129{ 130 struct apple_glx_context *ac; 131 struct apple_glx_context *sharedac = sharedContext; 132 CGLError error; 133 134 *ptr = NULL; 135 136 ac = malloc(sizeof *ac); 137 138 if (NULL == ac) { 139 *errorptr = BadAlloc; 140 *x11errorptr = true; 141 return true; 142 } 143 144 if (sharedac && !is_context_valid(sharedac)) { 145 *errorptr = GLXBadContext; 146 *x11errorptr = false; 147 free(ac); 148 return true; 149 } 150 151 ac->context_obj = NULL; 152 ac->pixel_format_obj = NULL; 153 ac->drawable = NULL; 154 ac->thread_id = pthread_self(); 155 ac->screen = screen; 156 ac->double_buffered = false; 157 ac->uses_stereo = false; 158 ac->need_update = false; 159 ac->is_current = false; 160 ac->made_current = false; 161 ac->last_surface_window = None; 162 163 apple_visual_create_pfobj(&ac->pixel_format_obj, mode, 164 &ac->double_buffered, &ac->uses_stereo, 165 /*offscreen */ false); 166 167 error = apple_cgl.create_context(ac->pixel_format_obj, 168 sharedac ? sharedac->context_obj : NULL, 169 &ac->context_obj); 170 171 172 if (error) { 173 (void) apple_cgl.destroy_pixel_format(ac->pixel_format_obj); 174 175 free(ac); 176 177 if (kCGLBadMatch == error) { 178 *errorptr = BadMatch; 179 *x11errorptr = true; 180 } 181 else { 182 *errorptr = GLXBadContext; 183 *x11errorptr = false; 184 } 185 186 if (env_var_as_boolean("LIBGL_DIAGNOSTIC", false)) 187 fprintf(stderr, "error: %s\n", apple_cgl.error_string(error)); 188 189 return true; 190 } 191 192 /* The context creation succeeded, so we can link in the new context. */ 193 lock_context_list(); 194 195 if (context_list) 196 context_list->previous = ac; 197 198 ac->previous = NULL; 199 ac->next = context_list; 200 context_list = ac; 201 202 *ptr = ac; 203 204 apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n", 205 __func__, (void *) ac, (void *) ac->context_obj); 206 207 unlock_context_list(); 208 209 return false; 210} 211 212void 213apple_glx_destroy_context(void **ptr, Display * dpy) 214{ 215 struct apple_glx_context *ac = *ptr; 216 217 if (NULL == ac) 218 return; 219 220 apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n", 221 __func__, (void *) ac, (void *) ac->context_obj); 222 223 if (apple_cgl.get_current_context() == ac->context_obj) { 224 apple_glx_diagnostic("%s: context ac->context_obj %p " 225 "is still current!\n", __func__, 226 (void *) ac->context_obj); 227 if (apple_cgl.set_current_context(NULL)) { 228 abort(); 229 } 230 } 231 232 /* Remove ac from the context_list as soon as possible. */ 233 lock_context_list(); 234 235 if (ac->previous) { 236 ac->previous->next = ac->next; 237 } 238 else { 239 context_list = ac->next; 240 } 241 242 if (ac->next) { 243 ac->next->previous = ac->previous; 244 } 245 246 unlock_context_list(); 247 248 249 if (apple_cgl.clear_drawable(ac->context_obj)) { 250 fprintf(stderr, "error: while clearing drawable!\n"); 251 abort(); 252 } 253 254 /* 255 * This potentially causes surface_notify_handler to be called in 256 * apple_glx.c... 257 * We can NOT have a lock held at this point. It would result in 258 * an abort due to an attempted deadlock. This is why we earlier 259 * removed the ac pointer from the double-linked list. 260 */ 261 if (ac->drawable) { 262 ac->drawable->destroy(ac->drawable); 263 } 264 265 if (apple_cgl.destroy_pixel_format(ac->pixel_format_obj)) { 266 fprintf(stderr, "error: destroying pixel format in %s\n", __func__); 267 abort(); 268 } 269 270 if (apple_cgl.destroy_context(ac->context_obj)) { 271 fprintf(stderr, "error: destroying context_obj in %s\n", __func__); 272 abort(); 273 } 274 275 free(ac); 276 277 *ptr = NULL; 278 279 apple_glx_garbage_collect_drawables(dpy); 280} 281 282 283/* Return true if an error occurred. */ 284bool 285apple_glx_make_current_context(Display * dpy, void *oldptr, void *ptr, 286 GLXDrawable drawable) 287{ 288 struct apple_glx_context *oldac = oldptr; 289 struct apple_glx_context *ac = ptr; 290 struct apple_glx_drawable *newagd = NULL; 291 CGLError cglerr; 292 bool same_drawable = false; 293 294#if 0 295 apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n", 296 __func__, (void *) oldac, (void *) ac, drawable); 297 298 apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n", 299 __func__, 300 (void *) (oldac ? oldac->context_obj : NULL), 301 (void *) (ac ? ac->context_obj : NULL)); 302#endif 303 304 /* This a common path for GLUT and other apps, so special case it. */ 305 if (ac && ac->drawable && ac->drawable->drawable == drawable) { 306 same_drawable = true; 307 308 if (ac->is_current) 309 return false; 310 } 311 312 /* Reset the is_current state of the old context, if non-NULL. */ 313 if (oldac && (ac != oldac)) 314 oldac->is_current = false; 315 316 if (NULL == ac) { 317 /*Clear the current context for this thread. */ 318 apple_cgl.set_current_context(NULL); 319 320 if (oldac) { 321 oldac->is_current = false; 322 323 if (oldac->drawable) { 324 oldac->drawable->destroy(oldac->drawable); 325 oldac->drawable = NULL; 326 } 327 328 /* Invalidate this to prevent surface recreation. */ 329 oldac->last_surface_window = None; 330 } 331 332 return false; 333 } 334 335 if (None == drawable) { 336 bool error = false; 337 338 /* Clear the current drawable for this context_obj. */ 339 340 if (apple_cgl.set_current_context(ac->context_obj)) 341 error = true; 342 343 if (apple_cgl.clear_drawable(ac->context_obj)) 344 error = true; 345 346 if (ac->drawable) { 347 ac->drawable->destroy(ac->drawable); 348 ac->drawable = NULL; 349 } 350 351 /* Invalidate this to prevent surface recreation. */ 352 ac->last_surface_window = None; 353 354 apple_glx_diagnostic("%s: drawable is None, error is: %d\n", 355 __func__, error); 356 357 return error; 358 } 359 360 /* This is an optimisation to avoid searching for the current drawable. */ 361 if (ac->drawable && ac->drawable->drawable == drawable) { 362 newagd = ac->drawable; 363 } 364 else { 365 /* Find the drawable if possible, and retain a reference to it. */ 366 newagd = 367 apple_glx_drawable_find(drawable, APPLE_GLX_DRAWABLE_REFERENCE); 368 } 369 370 /* 371 * Try to destroy the old drawable, so long as the new one 372 * isn't the old. 373 */ 374 if (ac->drawable && !same_drawable) { 375 ac->drawable->destroy(ac->drawable); 376 ac->drawable = NULL; 377 } 378 379 if (NULL == newagd) { 380 if (apple_glx_surface_create(dpy, ac->screen, drawable, &newagd)) 381 return true; 382 383 /* The drawable is referenced once by apple_glx_surface_create. */ 384 385 /* 386 * FIXME: We actually need 2 references to prevent premature surface 387 * destruction. The problem is that the surface gets destroyed in 388 * the case of the context being reused for another window, and 389 * we then lose the surface contents. Wait for destruction of a 390 * window to destroy a surface. 391 * 392 * Note: this may leave around surfaces we don't want around, if 393 * say we are using X for raster drawing after OpenGL rendering, 394 * but it will be compatible with the old libGL's behavior. 395 * 396 * Someday the X11 and OpenGL rendering must be unified at some 397 * layer. I suspect we can do that via shared memory and 398 * multiple threads in the X server (1 for each context created 399 * by a client). This would also allow users to render from 400 * multiple clients to the same OpenGL surface. In fact it could 401 * all be OpenGL. 402 * 403 */ 404 newagd->reference(newagd); 405 406 /* Save the new drawable with the context structure. */ 407 ac->drawable = newagd; 408 } 409 else { 410 /* We are reusing an existing drawable structure. */ 411 412 if (same_drawable) { 413 assert(ac->drawable == newagd); 414 /* The drawable_find above retained a reference for us. */ 415 } 416 else { 417 ac->drawable = newagd; 418 } 419 } 420 421 /* 422 * Avoid this costly path if this is the same drawable and the 423 * context is already current. 424 */ 425 426 if (same_drawable && ac->is_current) { 427 apple_glx_diagnostic("same_drawable and ac->is_current\n"); 428 return false; 429 } 430 431 cglerr = apple_cgl.set_current_context(ac->context_obj); 432 433 if (kCGLNoError != cglerr) { 434 fprintf(stderr, "set current error: %s\n", 435 apple_cgl.error_string(cglerr)); 436 return true; 437 } 438 439 ac->is_current = true; 440 441 assert(NULL != ac->context_obj); 442 assert(NULL != ac->drawable); 443 444 ac->thread_id = pthread_self(); 445 446 /* This will be set if the pending_destroy code indicates it should be: */ 447 ac->last_surface_window = None; 448 449 switch (ac->drawable->type) { 450 case APPLE_GLX_DRAWABLE_PBUFFER: 451 case APPLE_GLX_DRAWABLE_SURFACE: 452 case APPLE_GLX_DRAWABLE_PIXMAP: 453 if (ac->drawable->callbacks.make_current) { 454 if (ac->drawable->callbacks.make_current(ac, ac->drawable)) 455 return true; 456 } 457 break; 458 459 default: 460 fprintf(stderr, "internal error: invalid drawable type: %d\n", 461 ac->drawable->type); 462 abort(); 463 } 464 465 return false; 466} 467 468bool 469apple_glx_is_current_drawable(Display * dpy, void *ptr, GLXDrawable drawable) 470{ 471 struct apple_glx_context *ac = ptr; 472 473 if (ac->drawable && ac->drawable->drawable == drawable) { 474 return true; 475 } 476 else if (NULL == ac->drawable && None != ac->last_surface_window) { 477 apple_glx_context_update(dpy, ac); 478 479 return (ac->drawable && ac->drawable->drawable == drawable); 480 } 481 482 return false; 483} 484 485bool 486apple_glx_copy_context(void *currentptr, void *srcptr, void *destptr, 487 unsigned long mask, int *errorptr, bool * x11errorptr) 488{ 489 struct apple_glx_context *src, *dest; 490 CGLError err; 491 492 src = srcptr; 493 dest = destptr; 494 495 if (src->screen != dest->screen) { 496 *errorptr = BadMatch; 497 *x11errorptr = true; 498 return true; 499 } 500 501 if (dest == currentptr || dest->is_current) { 502 *errorptr = BadAccess; 503 *x11errorptr = true; 504 return true; 505 } 506 507 /* 508 * If srcptr is the current context then we should do an implicit glFlush. 509 */ 510 if (currentptr == srcptr) 511 glFlush(); 512 513 err = apple_cgl.copy_context(src->context_obj, dest->context_obj, 514 (GLbitfield) mask); 515 516 if (kCGLNoError != err) { 517 *errorptr = GLXBadContext; 518 *x11errorptr = false; 519 return true; 520 } 521 522 return false; 523} 524 525/* 526 * The value returned is the total number of contexts set to update. 527 * It's meant for debugging/introspection. 528 */ 529int 530apple_glx_context_surface_changed(unsigned int uid, pthread_t caller) 531{ 532 struct apple_glx_context *ac; 533 int updated = 0; 534 535 lock_context_list(); 536 537 for (ac = context_list; ac; ac = ac->next) { 538 if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type 539 && ac->drawable->types.surface.uid == uid) { 540 541 if (caller == ac->thread_id) { 542 apple_glx_diagnostic("caller is the same thread for uid %u\n", 543 uid); 544 545 xp_update_gl_context(ac->context_obj); 546 } 547 else { 548 ac->need_update = true; 549 ++updated; 550 } 551 } 552 } 553 554 unlock_context_list(); 555 556 return updated; 557} 558 559void 560apple_glx_context_update(Display * dpy, void *ptr) 561{ 562 struct apple_glx_context *ac = ptr; 563 564 if (NULL == ac->drawable && None != ac->last_surface_window) { 565 bool failed; 566 567 /* Attempt to recreate the surface for a destroyed drawable. */ 568 failed = 569 apple_glx_make_current_context(dpy, ac, ac, ac->last_surface_window); 570 571 apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__, 572 failed ? "YES" : "NO"); 573 } 574 575 if (ac->need_update) { 576 xp_update_gl_context(ac->context_obj); 577 ac->need_update = false; 578 579 apple_glx_diagnostic("%s: updating context %p\n", __func__, ptr); 580 } 581 582 if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type 583 && ac->drawable->types.surface.pending_destroy) { 584 apple_glx_diagnostic("%s: clearing drawable %p\n", __func__, ptr); 585 apple_cgl.clear_drawable(ac->context_obj); 586 587 if (ac->drawable) { 588 struct apple_glx_drawable *d; 589 590 apple_glx_diagnostic("%s: attempting to destroy drawable %p\n", 591 __func__, ptr); 592 apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n", 593 __func__, ac->drawable->drawable); 594 595 d = ac->drawable; 596 597 ac->last_surface_window = d->drawable; 598 599 ac->drawable = NULL; 600 601 /* 602 * This will destroy the surface drawable if there are 603 * no references to it. 604 * It also subtracts 1 from the reference_count. 605 * If there are references to it, then it's probably made 606 * current in another context. 607 */ 608 d->destroy(d); 609 } 610 } 611} 612 613bool 614apple_glx_context_uses_stereo(void *ptr) 615{ 616 struct apple_glx_context *ac = ptr; 617 618 return ac->uses_stereo; 619} 620