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 #include "util/debug.h"
60 static pthread_mutex_t context_lock
= PTHREAD_MUTEX_INITIALIZER
;
63 * This should be locked on creation and destruction of the
66 * It's also locked when the surface_notify_handler is searching
67 * for a uid associated with a surface.
69 static struct apple_glx_context
*context_list
= NULL
;
71 /* This guards the context_list above. */
73 lock_context_list(void)
77 err
= pthread_mutex_lock(&context_lock
);
80 fprintf(stderr
, "pthread_mutex_lock failure in %s: %d\n",
87 unlock_context_list(void)
91 err
= pthread_mutex_unlock(&context_lock
);
94 fprintf(stderr
, "pthread_mutex_unlock failure in %s: %d\n",
101 is_context_valid(struct apple_glx_context
*ac
)
103 struct apple_glx_context
*i
;
107 for (i
= context_list
; i
; i
= i
->next
) {
109 unlock_context_list();
114 unlock_context_list();
119 /* This creates an apple_private_context struct.
121 * It's typically called to save the struct in a GLXContext.
123 * This is also where the CGLContextObj is created, and the CGLPixelFormatObj.
126 apple_glx_create_context(void **ptr
, Display
* dpy
, int screen
,
127 const void *mode
, void *sharedContext
,
128 int *errorptr
, bool * x11errorptr
)
130 struct apple_glx_context
*ac
;
131 struct apple_glx_context
*sharedac
= sharedContext
;
136 ac
= malloc(sizeof *ac
);
139 *errorptr
= BadAlloc
;
144 if (sharedac
&& !is_context_valid(sharedac
)) {
145 *errorptr
= GLXBadContext
;
146 *x11errorptr
= false;
151 ac
->context_obj
= NULL
;
152 ac
->pixel_format_obj
= NULL
;
154 ac
->thread_id
= pthread_self();
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
;
163 apple_visual_create_pfobj(&ac
->pixel_format_obj
, mode
,
164 &ac
->double_buffered
, &ac
->uses_stereo
,
165 /*offscreen */ false);
167 error
= apple_cgl
.create_context(ac
->pixel_format_obj
,
168 sharedac
? sharedac
->context_obj
: NULL
,
173 (void) apple_cgl
.destroy_pixel_format(ac
->pixel_format_obj
);
177 if (kCGLBadMatch
== error
) {
178 *errorptr
= BadMatch
;
182 *errorptr
= GLXBadContext
;
183 *x11errorptr
= false;
186 if (env_var_as_boolean("LIBGL_DIAGNOSTIC", false))
187 fprintf(stderr
, "error: %s\n", apple_cgl
.error_string(error
));
192 /* The context creation succeeded, so we can link in the new context. */
196 context_list
->previous
= ac
;
199 ac
->next
= context_list
;
204 apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
205 __func__
, (void *) ac
, (void *) ac
->context_obj
);
207 unlock_context_list();
213 apple_glx_destroy_context(void **ptr
, Display
* dpy
)
215 struct apple_glx_context
*ac
= *ptr
;
220 apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
221 __func__
, (void *) ac
, (void *) ac
->context_obj
);
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
)) {
232 /* Remove ac from the context_list as soon as possible. */
236 ac
->previous
->next
= ac
->next
;
239 context_list
= ac
->next
;
243 ac
->next
->previous
= ac
->previous
;
246 unlock_context_list();
249 if (apple_cgl
.clear_drawable(ac
->context_obj
)) {
250 fprintf(stderr
, "error: while clearing drawable!\n");
255 * This potentially causes surface_notify_handler to be called in
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.
262 ac
->drawable
->destroy(ac
->drawable
);
265 if (apple_cgl
.destroy_pixel_format(ac
->pixel_format_obj
)) {
266 fprintf(stderr
, "error: destroying pixel format in %s\n", __func__
);
270 if (apple_cgl
.destroy_context(ac
->context_obj
)) {
271 fprintf(stderr
, "error: destroying context_obj in %s\n", __func__
);
279 apple_glx_garbage_collect_drawables(dpy
);
283 /* Return true if an error occurred. */
285 apple_glx_make_current_context(Display
* dpy
, void *oldptr
, void *ptr
,
286 GLXDrawable drawable
)
288 struct apple_glx_context
*oldac
= oldptr
;
289 struct apple_glx_context
*ac
= ptr
;
290 struct apple_glx_drawable
*newagd
= NULL
;
292 bool same_drawable
= false;
295 apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n",
296 __func__
, (void *) oldac
, (void *) ac
, drawable
);
298 apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n",
300 (void *) (oldac
? oldac
->context_obj
: NULL
),
301 (void *) (ac
? ac
->context_obj
: NULL
));
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;
312 /* Reset the is_current state of the old context, if non-NULL. */
313 if (oldac
&& (ac
!= oldac
))
314 oldac
->is_current
= false;
317 /*Clear the current context for this thread. */
318 apple_cgl
.set_current_context(NULL
);
321 oldac
->is_current
= false;
323 if (oldac
->drawable
) {
324 oldac
->drawable
->destroy(oldac
->drawable
);
325 oldac
->drawable
= NULL
;
328 /* Invalidate this to prevent surface recreation. */
329 oldac
->last_surface_window
= None
;
335 if (None
== drawable
) {
338 /* Clear the current drawable for this context_obj. */
340 if (apple_cgl
.set_current_context(ac
->context_obj
))
343 if (apple_cgl
.clear_drawable(ac
->context_obj
))
347 ac
->drawable
->destroy(ac
->drawable
);
351 /* Invalidate this to prevent surface recreation. */
352 ac
->last_surface_window
= None
;
354 apple_glx_diagnostic("%s: drawable is None, error is: %d\n",
360 /* This is an optimisation to avoid searching for the current drawable. */
361 if (ac
->drawable
&& ac
->drawable
->drawable
== drawable
) {
362 newagd
= ac
->drawable
;
365 /* Find the drawable if possible, and retain a reference to it. */
367 apple_glx_drawable_find(drawable
, APPLE_GLX_DRAWABLE_REFERENCE
);
371 * Try to destroy the old drawable, so long as the new one
374 if (ac
->drawable
&& !same_drawable
) {
375 ac
->drawable
->destroy(ac
->drawable
);
379 if (NULL
== newagd
) {
380 if (apple_glx_surface_create(dpy
, ac
->screen
, drawable
, &newagd
))
383 /* The drawable is referenced once by apple_glx_surface_create. */
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.
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.
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
404 newagd
->reference(newagd
);
406 /* Save the new drawable with the context structure. */
407 ac
->drawable
= newagd
;
410 /* We are reusing an existing drawable structure. */
413 assert(ac
->drawable
== newagd
);
414 /* The drawable_find above retained a reference for us. */
417 ac
->drawable
= newagd
;
422 * Avoid this costly path if this is the same drawable and the
423 * context is already current.
426 if (same_drawable
&& ac
->is_current
) {
427 apple_glx_diagnostic("same_drawable and ac->is_current\n");
431 cglerr
= apple_cgl
.set_current_context(ac
->context_obj
);
433 if (kCGLNoError
!= cglerr
) {
434 fprintf(stderr
, "set current error: %s\n",
435 apple_cgl
.error_string(cglerr
));
439 ac
->is_current
= true;
441 assert(NULL
!= ac
->context_obj
);
442 assert(NULL
!= ac
->drawable
);
444 ac
->thread_id
= pthread_self();
446 /* This will be set if the pending_destroy code indicates it should be: */
447 ac
->last_surface_window
= None
;
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
))
460 fprintf(stderr
, "internal error: invalid drawable type: %d\n",
469 apple_glx_is_current_drawable(Display
* dpy
, void *ptr
, GLXDrawable drawable
)
471 struct apple_glx_context
*ac
= ptr
;
473 if (ac
->drawable
&& ac
->drawable
->drawable
== drawable
) {
476 else if (NULL
== ac
->drawable
&& None
!= ac
->last_surface_window
) {
477 apple_glx_context_update(dpy
, ac
);
479 return (ac
->drawable
&& ac
->drawable
->drawable
== drawable
);
486 apple_glx_copy_context(void *currentptr
, void *srcptr
, void *destptr
,
487 unsigned long mask
, int *errorptr
, bool * x11errorptr
)
489 struct apple_glx_context
*src
, *dest
;
495 if (src
->screen
!= dest
->screen
) {
496 *errorptr
= BadMatch
;
501 if (dest
== currentptr
|| dest
->is_current
) {
502 *errorptr
= BadAccess
;
508 * If srcptr is the current context then we should do an implicit glFlush.
510 if (currentptr
== srcptr
)
513 err
= apple_cgl
.copy_context(src
->context_obj
, dest
->context_obj
,
516 if (kCGLNoError
!= err
) {
517 *errorptr
= GLXBadContext
;
518 *x11errorptr
= false;
526 * The value returned is the total number of contexts set to update.
527 * It's meant for debugging/introspection.
530 apple_glx_context_surface_changed(unsigned int uid
, pthread_t caller
)
532 struct apple_glx_context
*ac
;
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
) {
541 if (caller
== ac
->thread_id
) {
542 apple_glx_diagnostic("caller is the same thread for uid %u\n",
545 xp_update_gl_context(ac
->context_obj
);
548 ac
->need_update
= true;
554 unlock_context_list();
560 apple_glx_context_update(Display
* dpy
, void *ptr
)
562 struct apple_glx_context
*ac
= ptr
;
564 if (NULL
== ac
->drawable
&& None
!= ac
->last_surface_window
) {
567 /* Attempt to recreate the surface for a destroyed drawable. */
569 apple_glx_make_current_context(dpy
, ac
, ac
, ac
->last_surface_window
);
571 apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__
,
572 failed
? "YES" : "NO");
575 if (ac
->need_update
) {
576 xp_update_gl_context(ac
->context_obj
);
577 ac
->need_update
= false;
579 apple_glx_diagnostic("%s: updating context %p\n", __func__
, ptr
);
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
);
588 struct apple_glx_drawable
*d
;
590 apple_glx_diagnostic("%s: attempting to destroy drawable %p\n",
592 apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n",
593 __func__
, ac
->drawable
->drawable
);
597 ac
->last_surface_window
= d
->drawable
;
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.
614 apple_glx_context_uses_stereo(void *ptr
)
616 struct apple_glx_context
*ac
= ptr
;
618 return ac
->uses_stereo
;