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