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       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