Home | History | Annotate | Download | only in apple
      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