2 Copyright (c) 2008, 2009 Apple Inc.
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:
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
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.
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.
41 // Get the newer glext.h first
45 #include <OpenGL/CGLTypes.h>
46 #include <OpenGL/CGLCurrent.h>
47 #include <OpenGL/OpenGL.h>
49 #include "glxclient.h"
51 #include "apple_glx.h"
52 #include "apple_glx_context.h"
54 #include "apple_visual.h"
55 #include "apple_cgl.h"
56 #include "apple_glx_drawable.h"
58 static pthread_mutex_t context_lock
= PTHREAD_MUTEX_INITIALIZER
;
61 * This should be locked on creation and destruction of the
64 * It's also locked when the surface_notify_handler is searching
65 * for a uid associated with a surface.
67 static struct apple_glx_context
*context_list
= NULL
;
69 /* This guards the context_list above. */
71 lock_context_list(void)
75 err
= pthread_mutex_lock(&context_lock
);
78 fprintf(stderr
, "pthread_mutex_lock failure in %s: %d\n",
85 unlock_context_list(void)
89 err
= pthread_mutex_unlock(&context_lock
);
92 fprintf(stderr
, "pthread_mutex_unlock failure in %s: %d\n",
99 is_context_valid(struct apple_glx_context
*ac
)
101 struct apple_glx_context
*i
;
105 for (i
= context_list
; i
; i
= i
->next
) {
107 unlock_context_list();
112 unlock_context_list();
117 /* This creates an apple_private_context struct.
119 * It's typically called to save the struct in a GLXContext.
121 * This is also where the CGLContextObj is created, and the CGLPixelFormatObj.
124 apple_glx_create_context(void **ptr
, Display
* dpy
, int screen
,
125 const void *mode
, void *sharedContext
,
126 int *errorptr
, bool * x11errorptr
)
128 struct apple_glx_context
*ac
;
129 struct apple_glx_context
*sharedac
= sharedContext
;
134 ac
= malloc(sizeof *ac
);
137 *errorptr
= BadAlloc
;
142 if (sharedac
&& !is_context_valid(sharedac
)) {
143 *errorptr
= GLXBadContext
;
144 *x11errorptr
= false;
149 ac
->context_obj
= NULL
;
150 ac
->pixel_format_obj
= NULL
;
152 ac
->thread_id
= pthread_self();
154 ac
->double_buffered
= false;
155 ac
->uses_stereo
= false;
156 ac
->need_update
= false;
157 ac
->is_current
= false;
158 ac
->made_current
= false;
159 ac
->last_surface_window
= None
;
161 apple_visual_create_pfobj(&ac
->pixel_format_obj
, mode
,
162 &ac
->double_buffered
, &ac
->uses_stereo
,
163 /*offscreen */ false);
165 error
= apple_cgl
.create_context(ac
->pixel_format_obj
,
166 sharedac
? sharedac
->context_obj
: NULL
,
171 (void) apple_cgl
.destroy_pixel_format(ac
->pixel_format_obj
);
175 if (kCGLBadMatch
== error
) {
176 *errorptr
= BadMatch
;
180 *errorptr
= GLXBadContext
;
181 *x11errorptr
= false;
184 if (getenv("LIBGL_DIAGNOSTIC"))
185 fprintf(stderr
, "error: %s\n", apple_cgl
.error_string(error
));
190 /* The context creation succeeded, so we can link in the new context. */
194 context_list
->previous
= ac
;
197 ac
->next
= context_list
;
202 apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
203 __func__
, (void *) ac
, (void *) ac
->context_obj
);
205 unlock_context_list();
211 apple_glx_destroy_context(void **ptr
, Display
* dpy
)
213 struct apple_glx_context
*ac
= *ptr
;
218 apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
219 __func__
, (void *) ac
, (void *) ac
->context_obj
);
221 if (apple_cgl
.get_current_context() == ac
->context_obj
) {
222 apple_glx_diagnostic("%s: context ac->context_obj %p "
223 "is still current!\n", __func__
,
224 (void *) ac
->context_obj
);
225 if (apple_cgl
.set_current_context(NULL
)) {
230 /* Remove ac from the context_list as soon as possible. */
234 ac
->previous
->next
= ac
->next
;
237 context_list
= ac
->next
;
241 ac
->next
->previous
= ac
->previous
;
244 unlock_context_list();
247 if (apple_cgl
.clear_drawable(ac
->context_obj
)) {
248 fprintf(stderr
, "error: while clearing drawable!\n");
253 * This potentially causes surface_notify_handler to be called in
255 * We can NOT have a lock held at this point. It would result in
256 * an abort due to an attempted deadlock. This is why we earlier
257 * removed the ac pointer from the double-linked list.
260 ac
->drawable
->destroy(ac
->drawable
);
263 if (apple_cgl
.destroy_pixel_format(ac
->pixel_format_obj
)) {
264 fprintf(stderr
, "error: destroying pixel format in %s\n", __func__
);
268 if (apple_cgl
.destroy_context(ac
->context_obj
)) {
269 fprintf(stderr
, "error: destroying context_obj in %s\n", __func__
);
277 apple_glx_garbage_collect_drawables(dpy
);
281 /* Return true if an error occured. */
283 apple_glx_make_current_context(Display
* dpy
, void *oldptr
, void *ptr
,
284 GLXDrawable drawable
)
286 struct apple_glx_context
*oldac
= oldptr
;
287 struct apple_glx_context
*ac
= ptr
;
288 struct apple_glx_drawable
*newagd
= NULL
;
290 bool same_drawable
= false;
293 apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n",
294 __func__
, (void *) oldac
, (void *) ac
, drawable
);
296 apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n",
298 (void *) (oldac
? oldac
->context_obj
: NULL
),
299 (void *) (ac
? ac
->context_obj
: NULL
));
302 /* This a common path for GLUT and other apps, so special case it. */
303 if (ac
&& ac
->drawable
&& ac
->drawable
->drawable
== drawable
) {
304 same_drawable
= true;
310 /* Reset the is_current state of the old context, if non-NULL. */
311 if (oldac
&& (ac
!= oldac
))
312 oldac
->is_current
= false;
315 /*Clear the current context for this thread. */
316 apple_cgl
.set_current_context(NULL
);
319 oldac
->is_current
= false;
321 if (oldac
->drawable
) {
322 oldac
->drawable
->destroy(oldac
->drawable
);
323 oldac
->drawable
= NULL
;
326 /* Invalidate this to prevent surface recreation. */
327 oldac
->last_surface_window
= None
;
333 if (None
== drawable
) {
336 /* Clear the current drawable for this context_obj. */
338 if (apple_cgl
.set_current_context(ac
->context_obj
))
341 if (apple_cgl
.clear_drawable(ac
->context_obj
))
345 ac
->drawable
->destroy(ac
->drawable
);
349 /* Invalidate this to prevent surface recreation. */
350 ac
->last_surface_window
= None
;
352 apple_glx_diagnostic("%s: drawable is None, error is: %d\n",
358 /* This is an optimisation to avoid searching for the current drawable. */
359 if (ac
->drawable
&& ac
->drawable
->drawable
== drawable
) {
360 newagd
= ac
->drawable
;
363 /* Find the drawable if possible, and retain a reference to it. */
365 apple_glx_drawable_find(drawable
, APPLE_GLX_DRAWABLE_REFERENCE
);
369 * Try to destroy the old drawable, so long as the new one
372 if (ac
->drawable
&& !same_drawable
) {
373 ac
->drawable
->destroy(ac
->drawable
);
377 if (NULL
== newagd
) {
378 if (apple_glx_surface_create(dpy
, ac
->screen
, drawable
, &newagd
))
381 /* The drawable is referenced once by apple_glx_surface_create. */
384 * FIXME: We actually need 2 references to prevent premature surface
385 * destruction. The problem is that the surface gets destroyed in
386 * the case of the context being reused for another window, and
387 * we then lose the surface contents. Wait for destruction of a
388 * window to destroy a surface.
390 * Note: this may leave around surfaces we don't want around, if
391 * say we are using X for raster drawing after OpenGL rendering,
392 * but it will be compatible with the old libGL's behavior.
394 * Someday the X11 and OpenGL rendering must be unified at some
395 * layer. I suspect we can do that via shared memory and
396 * multiple threads in the X server (1 for each context created
397 * by a client). This would also allow users to render from
398 * multiple clients to the same OpenGL surface. In fact it could
402 newagd
->reference(newagd
);
404 /* Save the new drawable with the context structure. */
405 ac
->drawable
= newagd
;
408 /* We are reusing an existing drawable structure. */
411 assert(ac
->drawable
== newagd
);
412 /* The drawable_find above retained a reference for us. */
415 ac
->drawable
= newagd
;
420 * Avoid this costly path if this is the same drawable and the
421 * context is already current.
424 if (same_drawable
&& ac
->is_current
) {
425 apple_glx_diagnostic("same_drawable and ac->is_current\n");
429 cglerr
= apple_cgl
.set_current_context(ac
->context_obj
);
431 if (kCGLNoError
!= cglerr
) {
432 fprintf(stderr
, "set current error: %s\n",
433 apple_cgl
.error_string(cglerr
));
437 ac
->is_current
= true;
439 assert(NULL
!= ac
->context_obj
);
440 assert(NULL
!= ac
->drawable
);
442 ac
->thread_id
= pthread_self();
444 /* This will be set if the pending_destroy code indicates it should be: */
445 ac
->last_surface_window
= None
;
447 switch (ac
->drawable
->type
) {
448 case APPLE_GLX_DRAWABLE_PBUFFER
:
449 case APPLE_GLX_DRAWABLE_SURFACE
:
450 case APPLE_GLX_DRAWABLE_PIXMAP
:
451 if (ac
->drawable
->callbacks
.make_current
) {
452 if (ac
->drawable
->callbacks
.make_current(ac
, ac
->drawable
))
458 fprintf(stderr
, "internal error: invalid drawable type: %d\n",
467 apple_glx_is_current_drawable(Display
* dpy
, void *ptr
, GLXDrawable drawable
)
469 struct apple_glx_context
*ac
= ptr
;
471 if (ac
->drawable
&& ac
->drawable
->drawable
== drawable
) {
474 else if (NULL
== ac
->drawable
&& None
!= ac
->last_surface_window
) {
475 apple_glx_context_update(dpy
, ac
);
477 return (ac
->drawable
&& ac
->drawable
->drawable
== drawable
);
484 apple_glx_copy_context(void *currentptr
, void *srcptr
, void *destptr
,
485 unsigned long mask
, int *errorptr
, bool * x11errorptr
)
487 struct apple_glx_context
*src
, *dest
;
493 if (src
->screen
!= dest
->screen
) {
494 *errorptr
= BadMatch
;
499 if (dest
== currentptr
|| dest
->is_current
) {
500 *errorptr
= BadAccess
;
506 * If srcptr is the current context then we should do an implicit glFlush.
508 if (currentptr
== srcptr
)
511 err
= apple_cgl
.copy_context(src
->context_obj
, dest
->context_obj
,
514 if (kCGLNoError
!= err
) {
515 *errorptr
= GLXBadContext
;
516 *x11errorptr
= false;
524 * The value returned is the total number of contexts set to update.
525 * It's meant for debugging/introspection.
528 apple_glx_context_surface_changed(unsigned int uid
, pthread_t caller
)
530 struct apple_glx_context
*ac
;
535 for (ac
= context_list
; ac
; ac
= ac
->next
) {
536 if (ac
->drawable
&& APPLE_GLX_DRAWABLE_SURFACE
== ac
->drawable
->type
537 && ac
->drawable
->types
.surface
.uid
== uid
) {
539 if (caller
== ac
->thread_id
) {
540 apple_glx_diagnostic("caller is the same thread for uid %u\n",
543 xp_update_gl_context(ac
->context_obj
);
546 ac
->need_update
= true;
552 unlock_context_list();
558 apple_glx_context_update(Display
* dpy
, void *ptr
)
560 struct apple_glx_context
*ac
= ptr
;
562 if (NULL
== ac
->drawable
&& None
!= ac
->last_surface_window
) {
565 /* Attempt to recreate the surface for a destroyed drawable. */
567 apple_glx_make_current_context(dpy
, ac
, ac
, ac
->last_surface_window
);
569 apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__
,
570 failed
? "YES" : "NO");
573 if (ac
->need_update
) {
574 xp_update_gl_context(ac
->context_obj
);
575 ac
->need_update
= false;
577 apple_glx_diagnostic("%s: updating context %p\n", __func__
, ptr
);
580 if (ac
->drawable
&& APPLE_GLX_DRAWABLE_SURFACE
== ac
->drawable
->type
581 && ac
->drawable
->types
.surface
.pending_destroy
) {
582 apple_glx_diagnostic("%s: clearing drawable %p\n", __func__
, ptr
);
583 apple_cgl
.clear_drawable(ac
->context_obj
);
586 struct apple_glx_drawable
*d
;
588 apple_glx_diagnostic("%s: attempting to destroy drawable %p\n",
590 apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n",
591 __func__
, ac
->drawable
->drawable
);
595 ac
->last_surface_window
= d
->drawable
;
600 * This will destroy the surface drawable if there are
601 * no references to it.
602 * It also subtracts 1 from the reference_count.
603 * If there are references to it, then it's probably made
604 * current in another context.
612 apple_glx_context_uses_stereo(void *ptr
)
614 struct apple_glx_context
*ac
= ptr
;
616 return ac
->uses_stereo
;