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       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
apple_glx_destroy_context(void ** ptr,Display * dpy)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
apple_glx_make_current_context(Display * dpy,void * oldptr,void * ptr,GLXDrawable drawable)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
apple_glx_is_current_drawable(Display * dpy,void * ptr,GLXDrawable drawable)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
apple_glx_copy_context(void * currentptr,void * srcptr,void * destptr,unsigned long mask,int * errorptr,bool * x11errorptr)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
apple_glx_context_surface_changed(unsigned int uid,pthread_t caller)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
apple_glx_context_update(Display * dpy,void * ptr)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
apple_glx_context_uses_stereo(void * ptr)611 apple_glx_context_uses_stereo(void *ptr)
612 {
613    struct apple_glx_context *ac = ptr;
614 
615    return ac->uses_stereo;
616 }
617