apple_glx_context.c revision ad503c41557606d15b0420c824369456f6d20a8f
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
58static 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 */
67static struct apple_glx_context *context_list = NULL;
68
69/* This guards the context_list above. */
70static void
71lock_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
84static void
85unlock_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
98static bool
99is_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 */
123bool
124apple_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
209void
210apple_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. */
281bool
282apple_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("%s: 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
465bool
466apple_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
482bool
483apple_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 */
526int
527apple_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
556void
557apple_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
610bool
611apple_glx_context_uses_stereo(void *ptr)
612{
613   struct apple_glx_context *ac = ptr;
614
615   return ac->uses_stereo;
616}
617