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