apple_glx_context.c revision b8e80941
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
60static 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 */
69static struct apple_glx_context *context_list = NULL;
70
71/* This guards the context_list above. */
72static void
73lock_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
86static void
87unlock_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
100static bool
101is_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 */
125bool
126apple_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
212void
213apple_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. */
284bool
285apple_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
468bool
469apple_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
485bool
486apple_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 */
529int
530apple_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
559void
560apple_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
613bool
614apple_glx_context_uses_stereo(void *ptr)
615{
616   struct apple_glx_context *ac = ptr;
617
618   return ac->uses_stereo;
619}
620