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