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