summaryrefslogtreecommitdiff
path: root/src/glx/apple/apple_glx_context.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/glx/apple/apple_glx_context.c')
-rw-r--r--src/glx/apple/apple_glx_context.c616
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;
+}