• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
lock_context_list(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
unlock_context_list(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
is_context_valid(struct apple_glx_context * ac)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
apple_glx_create_context(void ** ptr,Display * dpy,int screen,const void * mode,void * sharedContext,int * errorptr,bool * x11errorptr)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
apple_glx_destroy_context(void ** ptr,Display * dpy)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
apple_glx_make_current_context(Display * dpy,void * oldptr,void * ptr,GLXDrawable drawable)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
apple_glx_is_current_drawable(Display * dpy,void * ptr,GLXDrawable drawable)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
apple_glx_copy_context(void * currentptr,void * srcptr,void * destptr,unsigned long mask,int * errorptr,bool * x11errorptr)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
apple_glx_context_surface_changed(unsigned int uid,pthread_t caller)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
apple_glx_context_update(Display * dpy,void * ptr)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
apple_glx_context_uses_stereo(void * ptr)612 apple_glx_context_uses_stereo(void *ptr)
613 {
614    struct apple_glx_context *ac = ptr;
615 
616    return ac->uses_stereo;
617 }
618