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
DebugMessageF("error: %s\n", apple_cgl.error_string(error));
191
/* The context creation succeeded, so we can link in the new context. */
195
context_list->previous = ac;
198
ac->next = context_list;
203
apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
204
__func__, (void *) ac, (void *) ac->context_obj);
206
unlock_context_list();
212
apple_glx_destroy_context(void **ptr, Display * dpy)
214
struct apple_glx_context *ac = *ptr;
219
apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
220
__func__, (void *) ac, (void *) ac->context_obj);
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)) {
231
/* Remove ac from the context_list as soon as possible. */
235
ac->previous->next = ac->next;
238
context_list = ac->next;
242
ac->next->previous = ac->previous;
245
unlock_context_list();
248
if (apple_cgl.clear_drawable(ac->context_obj)) {
249
fprintf(stderr, "error: while clearing drawable!\n");
254
* This potentially causes surface_notify_handler to be called in
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.
261
ac->drawable->destroy(ac->drawable);
264
if (apple_cgl.destroy_pixel_format(ac->pixel_format_obj)) {
265
fprintf(stderr, "error: destroying pixel format in %s\n", __func__);
269
if (apple_cgl.destroy_context(ac->context_obj)) {
270
fprintf(stderr, "error: destroying context_obj in %s\n", __func__);
278
apple_glx_garbage_collect_drawables(dpy);
282
/* Return true if an error occurred. */
284
apple_glx_make_current_context(Display * dpy, void *oldptr, void *ptr,
285
GLXDrawable drawable)
287
struct apple_glx_context *oldac = oldptr;
288
struct apple_glx_context *ac = ptr;
289
struct apple_glx_drawable *newagd = NULL;
291
bool same_drawable = false;
294
apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n",
295
__func__, (void *) oldac, (void *) ac, drawable);
297
apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n",
299
(void *) (oldac ? oldac->context_obj : NULL),
300
(void *) (ac ? ac->context_obj : NULL));
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;
311
/* Reset the is_current state of the old context, if non-NULL. */
312
if (oldac && (ac != oldac))
313
oldac->is_current = false;
316
/*Clear the current context for this thread. */
317
apple_cgl.set_current_context(NULL);
320
oldac->is_current = false;
322
if (oldac->drawable) {
323
oldac->drawable->destroy(oldac->drawable);
324
oldac->drawable = NULL;
327
/* Invalidate this to prevent surface recreation. */
328
oldac->last_surface_window = None;
334
if (None == drawable) {
337
/* Clear the current drawable for this context_obj. */
339
if (apple_cgl.set_current_context(ac->context_obj))
342
if (apple_cgl.clear_drawable(ac->context_obj))
346
ac->drawable->destroy(ac->drawable);
350
/* Invalidate this to prevent surface recreation. */
351
ac->last_surface_window = None;
353
apple_glx_diagnostic("%s: drawable is None, error is: %d\n",
359
/* This is an optimisation to avoid searching for the current drawable. */
360
if (ac->drawable && ac->drawable->drawable == drawable) {
361
newagd = ac->drawable;
364
/* Find the drawable if possible, and retain a reference to it. */
366
apple_glx_drawable_find(drawable, APPLE_GLX_DRAWABLE_REFERENCE);
370
* Try to destroy the old drawable, so long as the new one
373
if (ac->drawable && !same_drawable) {
374
ac->drawable->destroy(ac->drawable);
378
if (NULL == newagd) {
379
if (apple_glx_surface_create(dpy, ac->screen, drawable, &newagd))
382
/* The drawable is referenced once by apple_glx_surface_create. */
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.
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.
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
403
newagd->reference(newagd);
405
/* Save the new drawable with the context structure. */
406
ac->drawable = newagd;
409
/* We are reusing an existing drawable structure. */
412
assert(ac->drawable == newagd);
413
/* The drawable_find above retained a reference for us. */
416
ac->drawable = newagd;
421
* Avoid this costly path if this is the same drawable and the
422
* context is already current.
425
if (same_drawable && ac->is_current) {
426
apple_glx_diagnostic("same_drawable and ac->is_current\n");
430
cglerr = apple_cgl.set_current_context(ac->context_obj);
432
if (kCGLNoError != cglerr) {
433
fprintf(stderr, "set current error: %s\n",
434
apple_cgl.error_string(cglerr));
438
ac->is_current = true;
440
assert(NULL != ac->context_obj);
441
assert(NULL != ac->drawable);
443
ac->thread_id = pthread_self();
445
/* This will be set if the pending_destroy code indicates it should be: */
446
ac->last_surface_window = None;
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))
459
fprintf(stderr, "internal error: invalid drawable type: %d\n",
468
apple_glx_is_current_drawable(Display * dpy, void *ptr, GLXDrawable drawable)
470
struct apple_glx_context *ac = ptr;
472
if (ac->drawable && ac->drawable->drawable == drawable) {
475
else if (NULL == ac->drawable && None != ac->last_surface_window) {
476
apple_glx_context_update(dpy, ac);
478
return (ac->drawable && ac->drawable->drawable == drawable);
485
apple_glx_copy_context(void *currentptr, void *srcptr, void *destptr,
486
unsigned long mask, int *errorptr, bool * x11errorptr)
488
struct apple_glx_context *src, *dest;
494
if (src->screen != dest->screen) {
495
*errorptr = BadMatch;
500
if (dest == currentptr || dest->is_current) {
501
*errorptr = BadAccess;
507
* If srcptr is the current context then we should do an implicit glFlush.
509
if (currentptr == srcptr)
512
err = apple_cgl.copy_context(src->context_obj, dest->context_obj,
515
if (kCGLNoError != err) {
516
*errorptr = GLXBadContext;
517
*x11errorptr = false;
525
* The value returned is the total number of contexts set to update.
526
* It's meant for debugging/introspection.
529
apple_glx_context_surface_changed(unsigned int uid, pthread_t caller)
531
struct apple_glx_context *ac;
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) {
540
if (caller == ac->thread_id) {
541
apple_glx_diagnostic("caller is the same thread for uid %u\n",
544
xp_update_gl_context(ac->context_obj);
547
ac->need_update = true;
553
unlock_context_list();
559
apple_glx_context_update(Display * dpy, void *ptr)
561
struct apple_glx_context *ac = ptr;
563
if (NULL == ac->drawable && None != ac->last_surface_window) {
566
/* Attempt to recreate the surface for a destroyed drawable. */
568
apple_glx_make_current_context(dpy, ac, ac, ac->last_surface_window);
570
apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__,
571
failed ? "YES" : "NO");
574
if (ac->need_update) {
575
xp_update_gl_context(ac->context_obj);
576
ac->need_update = false;
578
apple_glx_diagnostic("%s: updating context %p\n", __func__, ptr);
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);
587
struct apple_glx_drawable *d;
589
apple_glx_diagnostic("%s: attempting to destroy drawable %p\n",
591
apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n",
592
__func__, ac->drawable->drawable);
596
ac->last_surface_window = d->drawable;
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.
613
apple_glx_context_uses_stereo(void *ptr)
615
struct apple_glx_context *ac = ptr;
617
return ac->uses_stereo;