diff options
Diffstat (limited to 'src/glx/apple/apple_glx_context.c')
-rw-r--r-- | src/glx/apple/apple_glx_context.c | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/src/glx/apple/apple_glx_context.c b/src/glx/apple/apple_glx_context.c new file mode 100644 index 0000000000..c58d05a59a --- /dev/null +++ b/src/glx/apple/apple_glx_context.c @@ -0,0 +1,616 @@ +/* + Copyright (c) 2008, 2009 Apple Inc. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT + HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name(s) of the above + copyright holders shall not be used in advertising or otherwise to + promote the sale, use or other dealings in this Software without + prior written authorization. +*/ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <assert.h> +#include <pthread.h> + +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> + +// Get the newer glext.h first +#include <GL/gl.h> +#include <GL/glext.h> + +#include <OpenGL/CGLTypes.h> +#include <OpenGL/CGLCurrent.h> +#include <OpenGL/OpenGL.h> + +#include "glxclient.h" + +#include "apple_glx.h" +#include "apple_glx_context.h" +#include "appledri.h" +#include "apple_visual.h" +#include "apple_cgl.h" +#include "apple_glx_drawable.h" + +static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * This should be locked on creation and destruction of the + * apple_glx_contexts. + * + * It's also locked when the surface_notify_handler is searching + * for a uid associated with a surface. + */ +static struct apple_glx_context *context_list = NULL; + +/* This guards the context_list above. */ +static void +lock_context_list(void) +{ + int err; + + err = pthread_mutex_lock(&context_lock); + + if (err) { + fprintf(stderr, "pthread_mutex_lock failure in %s: %d\n", + __func__, err); + abort(); + } +} + +static void +unlock_context_list(void) +{ + int err; + + err = pthread_mutex_unlock(&context_lock); + + if (err) { + fprintf(stderr, "pthread_mutex_unlock failure in %s: %d\n", + __func__, err); + abort(); + } +} + +static bool +is_context_valid(struct apple_glx_context *ac) +{ + struct apple_glx_context *i; + + lock_context_list(); + + for (i = context_list; i; i = i->next) { + if (ac == i) { + unlock_context_list(); + return true; + } + } + + unlock_context_list(); + + return false; +} + +/* This creates an apple_private_context struct. + * + * It's typically called to save the struct in a GLXContext. + * + * This is also where the CGLContextObj is created, and the CGLPixelFormatObj. + */ +bool +apple_glx_create_context(void **ptr, Display * dpy, int screen, + const void *mode, void *sharedContext, + int *errorptr, bool * x11errorptr) +{ + struct apple_glx_context *ac; + struct apple_glx_context *sharedac = sharedContext; + CGLError error; + + *ptr = NULL; + + ac = malloc(sizeof *ac); + + if (NULL == ac) { + *errorptr = BadAlloc; + *x11errorptr = true; + return true; + } + + if (sharedac && !is_context_valid(sharedac)) { + *errorptr = GLXBadContext; + *x11errorptr = false; + return true; + } + + ac->context_obj = NULL; + ac->pixel_format_obj = NULL; + ac->drawable = NULL; + ac->thread_id = pthread_self(); + ac->screen = screen; + ac->double_buffered = false; + ac->uses_stereo = false; + ac->need_update = false; + ac->is_current = false; + ac->made_current = false; + ac->last_surface_window = None; + + apple_visual_create_pfobj(&ac->pixel_format_obj, mode, + &ac->double_buffered, &ac->uses_stereo, + /*offscreen */ false); + + error = apple_cgl.create_context(ac->pixel_format_obj, + sharedac ? sharedac->context_obj : NULL, + &ac->context_obj); + + + if (error) { + (void) apple_cgl.destroy_pixel_format(ac->pixel_format_obj); + + free(ac); + + if (kCGLBadMatch == error) { + *errorptr = BadMatch; + *x11errorptr = true; + } + else { + *errorptr = GLXBadContext; + *x11errorptr = false; + } + + if (getenv("LIBGL_DIAGNOSTIC")) + fprintf(stderr, "error: %s\n", apple_cgl.error_string(error)); + + return true; + } + + /* The context creation succeeded, so we can link in the new context. */ + lock_context_list(); + + if (context_list) + context_list->previous = ac; + + ac->previous = NULL; + ac->next = context_list; + context_list = ac; + + *ptr = ac; + + apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n", + __func__, (void *) ac, (void *) ac->context_obj); + + unlock_context_list(); + + return false; +} + +void +apple_glx_destroy_context(void **ptr, Display * dpy) +{ + struct apple_glx_context *ac = *ptr; + + if (NULL == ac) + return; + + apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n", + __func__, (void *) ac, (void *) ac->context_obj); + + if (apple_cgl.get_current_context() == ac->context_obj) { + apple_glx_diagnostic("%s: context ac->context_obj %p " + "is still current!\n", __func__, + (void *) ac->context_obj); + if (apple_cgl.set_current_context(NULL)) { + abort(); + } + } + + /* Remove ac from the context_list as soon as possible. */ + lock_context_list(); + + if (ac->previous) { + ac->previous->next = ac->next; + } + else { + context_list = ac->next; + } + + if (ac->next) { + ac->next->previous = ac->previous; + } + + unlock_context_list(); + + + if (apple_cgl.clear_drawable(ac->context_obj)) { + fprintf(stderr, "error: while clearing drawable!\n"); + abort(); + } + + /* + * This potentially causes surface_notify_handler to be called in + * apple_glx.c... + * We can NOT have a lock held at this point. It would result in + * an abort due to an attempted deadlock. This is why we earlier + * removed the ac pointer from the double-linked list. + */ + if (ac->drawable) { + ac->drawable->destroy(ac->drawable); + } + + if (apple_cgl.destroy_pixel_format(ac->pixel_format_obj)) { + fprintf(stderr, "error: destroying pixel format in %s\n", __func__); + abort(); + } + + if (apple_cgl.destroy_context(ac->context_obj)) { + fprintf(stderr, "error: destroying context_obj in %s\n", __func__); + abort(); + } + + free(ac); + + *ptr = NULL; + + apple_glx_garbage_collect_drawables(dpy); +} + + +/* Return true if an error occured. */ +bool +apple_glx_make_current_context(Display * dpy, void *oldptr, void *ptr, + GLXDrawable drawable) +{ + struct apple_glx_context *oldac = oldptr; + struct apple_glx_context *ac = ptr; + struct apple_glx_drawable *newagd = NULL; + CGLError cglerr; + bool same_drawable = false; + +#if 0 + apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n", + __func__, (void *) oldac, (void *) ac, drawable); + + apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n", + __func__, + (void *) (oldac ? oldac->context_obj : NULL), + (void *) (ac ? ac->context_obj : NULL)); +#endif + + /* This a common path for GLUT and other apps, so special case it. */ + if (ac && ac->drawable && ac->drawable->drawable == drawable) { + same_drawable = true; + + if (ac->is_current) + return false; + } + + /* Reset the is_current state of the old context, if non-NULL. */ + if (oldac && (ac != oldac)) + oldac->is_current = false; + + if (NULL == ac) { + /*Clear the current context for this thread. */ + apple_cgl.set_current_context(NULL); + + if (oldac) { + oldac->is_current = false; + + if (oldac->drawable) { + oldac->drawable->destroy(oldac->drawable); + oldac->drawable = NULL; + } + + /* Invalidate this to prevent surface recreation. */ + oldac->last_surface_window = None; + } + + return false; + } + + if (None == drawable) { + bool error = false; + + /* Clear the current drawable for this context_obj. */ + + if (apple_cgl.set_current_context(ac->context_obj)) + error = true; + + if (apple_cgl.clear_drawable(ac->context_obj)) + error = true; + + if (ac->drawable) { + ac->drawable->destroy(ac->drawable); + ac->drawable = NULL; + } + + /* Invalidate this to prevent surface recreation. */ + ac->last_surface_window = None; + + apple_glx_diagnostic("%s: drawable is None, error is: %d\n", + __func__, error); + + return error; + } + + /* This is an optimisation to avoid searching for the current drawable. */ + if (ac->drawable && ac->drawable->drawable == drawable) { + newagd = ac->drawable; + } + else { + /* Find the drawable if possible, and retain a reference to it. */ + newagd = + apple_glx_drawable_find(drawable, APPLE_GLX_DRAWABLE_REFERENCE); + } + + /* + * Try to destroy the old drawable, so long as the new one + * isn't the old. + */ + if (ac->drawable && !same_drawable) { + ac->drawable->destroy(ac->drawable); + ac->drawable = NULL; + } + + if (NULL == newagd) { + if (apple_glx_surface_create(dpy, ac->screen, drawable, &newagd)) + return true; + + /* The drawable is referenced once by apple_glx_surface_create. */ + + /* + * FIXME: We actually need 2 references to prevent premature surface + * destruction. The problem is that the surface gets destroyed in + * the case of the context being reused for another window, and + * we then lose the surface contents. Wait for destruction of a + * window to destroy a surface. + * + * Note: this may leave around surfaces we don't want around, if + * say we are using X for raster drawing after OpenGL rendering, + * but it will be compatible with the old libGL's behavior. + * + * Someday the X11 and OpenGL rendering must be unified at some + * layer. I suspect we can do that via shared memory and + * multiple threads in the X server (1 for each context created + * by a client). This would also allow users to render from + * multiple clients to the same OpenGL surface. In fact it could + * all be OpenGL. + * + */ + newagd->reference(newagd); + + /* Save the new drawable with the context structure. */ + ac->drawable = newagd; + } + else { + /* We are reusing an existing drawable structure. */ + + if (same_drawable) { + assert(ac->drawable == newagd); + /* The drawable_find above retained a reference for us. */ + } + else { + ac->drawable = newagd; + } + } + + /* + * Avoid this costly path if this is the same drawable and the + * context is already current. + */ + + if (same_drawable && ac->is_current) { + apple_glx_diagnostic("%s: same_drawable and ac->is_current\n"); + return false; + } + + cglerr = apple_cgl.set_current_context(ac->context_obj); + + if (kCGLNoError != cglerr) { + fprintf(stderr, "set current error: %s\n", + apple_cgl.error_string(cglerr)); + return true; + } + + ac->is_current = true; + + assert(NULL != ac->context_obj); + assert(NULL != ac->drawable); + + ac->thread_id = pthread_self(); + + /* This will be set if the pending_destroy code indicates it should be: */ + ac->last_surface_window = None; + + switch (ac->drawable->type) { + case APPLE_GLX_DRAWABLE_PBUFFER: + case APPLE_GLX_DRAWABLE_SURFACE: + case APPLE_GLX_DRAWABLE_PIXMAP: + if (ac->drawable->callbacks.make_current) { + if (ac->drawable->callbacks.make_current(ac, ac->drawable)) + return true; + } + break; + + default: + fprintf(stderr, "internal error: invalid drawable type: %d\n", + ac->drawable->type); + abort(); + } + + return false; +} + +bool +apple_glx_is_current_drawable(Display * dpy, void *ptr, GLXDrawable drawable) +{ + struct apple_glx_context *ac = ptr; + + if (ac->drawable && ac->drawable->drawable == drawable) { + return true; + } + else if (NULL == ac->drawable && None != ac->last_surface_window) { + apple_glx_context_update(dpy, ac); + + return (ac->drawable && ac->drawable->drawable == drawable); + } + + return false; +} + +bool +apple_glx_copy_context(void *currentptr, void *srcptr, void *destptr, + unsigned long mask, int *errorptr, bool * x11errorptr) +{ + struct apple_glx_context *src, *dest; + CGLError err; + + src = srcptr; + dest = destptr; + + if (src->screen != dest->screen) { + *errorptr = BadMatch; + *x11errorptr = true; + return true; + } + + if (dest == currentptr || dest->is_current) { + *errorptr = BadAccess; + *x11errorptr = true; + return true; + } + + /* + * If srcptr is the current context then we should do an implicit glFlush. + */ + if (currentptr == srcptr) + glFlush(); + + err = apple_cgl.copy_context(src->context_obj, dest->context_obj, + (GLbitfield) mask); + + if (kCGLNoError != err) { + *errorptr = GLXBadContext; + *x11errorptr = false; + return true; + } + + return false; +} + +/* + * The value returned is the total number of contexts set to update. + * It's meant for debugging/introspection. + */ +int +apple_glx_context_surface_changed(unsigned int uid, pthread_t caller) +{ + struct apple_glx_context *ac; + int updated = 0; + + lock_context_list(); + + for (ac = context_list; ac; ac = ac->next) { + if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type + && ac->drawable->types.surface.uid == uid) { + + if (caller == ac->thread_id) { + apple_glx_diagnostic("caller is the same thread for uid %u\n", + uid); + + xp_update_gl_context(ac->context_obj); + } + else { + ac->need_update = true; + ++updated; + } + } + } + + unlock_context_list(); + + return updated; +} + +void +apple_glx_context_update(Display * dpy, void *ptr) +{ + struct apple_glx_context *ac = ptr; + + if (NULL == ac->drawable && None != ac->last_surface_window) { + bool failed; + + /* Attempt to recreate the surface for a destroyed drawable. */ + failed = + apple_glx_make_current_context(dpy, ac, ac, ac->last_surface_window); + + apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__, + failed ? "YES" : "NO"); + } + + if (ac->need_update) { + xp_update_gl_context(ac->context_obj); + ac->need_update = false; + + apple_glx_diagnostic("%s: updating context %p\n", __func__, ptr); + } + + if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type + && ac->drawable->types.surface.pending_destroy) { + apple_glx_diagnostic("%s: clearing drawable %p\n", __func__, ptr); + apple_cgl.clear_drawable(ac->context_obj); + + if (ac->drawable) { + struct apple_glx_drawable *d; + + apple_glx_diagnostic("%s: attempting to destroy drawable %p\n", + __func__, ptr); + apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n", + __func__, ac->drawable->drawable); + + d = ac->drawable; + + ac->last_surface_window = d->drawable; + + ac->drawable = NULL; + + /* + * This will destroy the surface drawable if there are + * no references to it. + * It also subtracts 1 from the reference_count. + * If there are references to it, then it's probably made + * current in another context. + */ + d->destroy(d); + } + } +} + +bool +apple_glx_context_uses_stereo(void *ptr) +{ + struct apple_glx_context *ac = ptr; + + return ac->uses_stereo; +} |