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