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