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