Path: blob/21.2-virgl/src/glx/apple/apple_glx_context.c
4560 views
/*1Copyright (c) 2008, 2009 Apple Inc.23Permission is hereby granted, free of charge, to any person4obtaining a copy of this software and associated documentation files5(the "Software"), to deal in the Software without restriction,6including without limitation the rights to use, copy, modify, merge,7publish, distribute, sublicense, and/or sell copies of the Software,8and to permit persons to whom the Software is furnished to do so,9subject to the following conditions:1011The above copyright notice and this permission notice shall be12included in all copies or substantial portions of the Software.1314THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT18HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,19WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER21DEALINGS IN THE SOFTWARE.2223Except as contained in this notice, the name(s) of the above24copyright holders shall not be used in advertising or otherwise to25promote the sale, use or other dealings in this Software without26prior written authorization.27*/2829#include <stdbool.h>30#include <stdio.h>31#include <stdlib.h>32#include <limits.h>33#include <assert.h>34#include <pthread.h>3536#include <fcntl.h>37#include <sys/mman.h>38#include <unistd.h>3940// Get the newer glext.h first41#include <GL/gl.h>42#include <GL/glext.h>4344#include <OpenGL/CGLTypes.h>45#include <OpenGL/CGLCurrent.h>46#include <OpenGL/OpenGL.h>4748#include "glxclient.h"4950#include "apple_glx.h"51#include "apple_glx_context.h"52#include "appledri.h"53#include "apple_visual.h"54#include "apple_cgl.h"55#include "apple_glx_drawable.h"5657#include "util/debug.h"5859static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER;6061/*62* This should be locked on creation and destruction of the63* apple_glx_contexts.64*65* It's also locked when the surface_notify_handler is searching66* for a uid associated with a surface.67*/68static struct apple_glx_context *context_list = NULL;6970/* This guards the context_list above. */71static void72lock_context_list(void)73{74int err;7576err = pthread_mutex_lock(&context_lock);7778if (err) {79fprintf(stderr, "pthread_mutex_lock failure in %s: %d\n",80__func__, err);81abort();82}83}8485static void86unlock_context_list(void)87{88int err;8990err = pthread_mutex_unlock(&context_lock);9192if (err) {93fprintf(stderr, "pthread_mutex_unlock failure in %s: %d\n",94__func__, err);95abort();96}97}9899static bool100is_context_valid(struct apple_glx_context *ac)101{102struct apple_glx_context *i;103104lock_context_list();105106for (i = context_list; i; i = i->next) {107if (ac == i) {108unlock_context_list();109return true;110}111}112113unlock_context_list();114115return false;116}117118/* This creates an apple_private_context struct.119*120* It's typically called to save the struct in a GLXContext.121*122* This is also where the CGLContextObj is created, and the CGLPixelFormatObj.123*/124bool125apple_glx_create_context(void **ptr, Display * dpy, int screen,126const void *mode, void *sharedContext,127int *errorptr, bool * x11errorptr)128{129struct apple_glx_context *ac;130struct apple_glx_context *sharedac = sharedContext;131CGLError error;132133*ptr = NULL;134135ac = malloc(sizeof *ac);136137if (NULL == ac) {138*errorptr = BadAlloc;139*x11errorptr = true;140return true;141}142143if (sharedac && !is_context_valid(sharedac)) {144*errorptr = GLXBadContext;145*x11errorptr = false;146free(ac);147return true;148}149150ac->context_obj = NULL;151ac->pixel_format_obj = NULL;152ac->drawable = NULL;153ac->thread_id = pthread_self();154ac->screen = screen;155ac->double_buffered = false;156ac->uses_stereo = false;157ac->need_update = false;158ac->is_current = false;159ac->made_current = false;160ac->last_surface_window = None;161162apple_visual_create_pfobj(&ac->pixel_format_obj, mode,163&ac->double_buffered, &ac->uses_stereo,164/*offscreen */ false);165166error = apple_cgl.create_context(ac->pixel_format_obj,167sharedac ? sharedac->context_obj : NULL,168&ac->context_obj);169170171if (error) {172(void) apple_cgl.destroy_pixel_format(ac->pixel_format_obj);173174free(ac);175176if (kCGLBadMatch == error) {177*errorptr = BadMatch;178*x11errorptr = true;179}180else {181*errorptr = GLXBadContext;182*x11errorptr = false;183}184185DebugMessageF("error: %s\n", apple_cgl.error_string(error));186187return true;188}189190/* The context creation succeeded, so we can link in the new context. */191lock_context_list();192193if (context_list)194context_list->previous = ac;195196ac->previous = NULL;197ac->next = context_list;198context_list = ac;199200*ptr = ac;201202apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",203__func__, (void *) ac, (void *) ac->context_obj);204205unlock_context_list();206207return false;208}209210void211apple_glx_destroy_context(void **ptr, Display * dpy)212{213struct apple_glx_context *ac = *ptr;214215if (NULL == ac)216return;217218apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",219__func__, (void *) ac, (void *) ac->context_obj);220221if (apple_cgl.get_current_context() == ac->context_obj) {222apple_glx_diagnostic("%s: context ac->context_obj %p "223"is still current!\n", __func__,224(void *) ac->context_obj);225if (apple_cgl.set_current_context(NULL)) {226abort();227}228}229230/* Remove ac from the context_list as soon as possible. */231lock_context_list();232233if (ac->previous) {234ac->previous->next = ac->next;235}236else {237context_list = ac->next;238}239240if (ac->next) {241ac->next->previous = ac->previous;242}243244unlock_context_list();245246247if (apple_cgl.clear_drawable(ac->context_obj)) {248fprintf(stderr, "error: while clearing drawable!\n");249abort();250}251252/*253* This potentially causes surface_notify_handler to be called in254* apple_glx.c...255* We can NOT have a lock held at this point. It would result in256* an abort due to an attempted deadlock. This is why we earlier257* removed the ac pointer from the double-linked list.258*/259if (ac->drawable) {260ac->drawable->destroy(ac->drawable);261}262263if (apple_cgl.destroy_pixel_format(ac->pixel_format_obj)) {264fprintf(stderr, "error: destroying pixel format in %s\n", __func__);265abort();266}267268if (apple_cgl.destroy_context(ac->context_obj)) {269fprintf(stderr, "error: destroying context_obj in %s\n", __func__);270abort();271}272273free(ac);274275*ptr = NULL;276277apple_glx_garbage_collect_drawables(dpy);278}279280281/* Return true if an error occurred. */282bool283apple_glx_make_current_context(Display * dpy, void *oldptr, void *ptr,284GLXDrawable drawable)285{286struct apple_glx_context *oldac = oldptr;287struct apple_glx_context *ac = ptr;288struct apple_glx_drawable *newagd = NULL;289CGLError cglerr;290bool same_drawable = false;291292#if 0293apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n",294__func__, (void *) oldac, (void *) ac, drawable);295296apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n",297__func__,298(void *) (oldac ? oldac->context_obj : NULL),299(void *) (ac ? ac->context_obj : NULL));300#endif301302/* This a common path for GLUT and other apps, so special case it. */303if (ac && ac->drawable && ac->drawable->drawable == drawable) {304same_drawable = true;305306if (ac->is_current)307return false;308}309310/* Reset the is_current state of the old context, if non-NULL. */311if (oldac && (ac != oldac))312oldac->is_current = false;313314if (NULL == ac) {315/*Clear the current context for this thread. */316apple_cgl.set_current_context(NULL);317318if (oldac) {319oldac->is_current = false;320321if (oldac->drawable) {322oldac->drawable->destroy(oldac->drawable);323oldac->drawable = NULL;324}325326/* Invalidate this to prevent surface recreation. */327oldac->last_surface_window = None;328}329330return false;331}332333if (None == drawable) {334bool error = false;335336/* Clear the current drawable for this context_obj. */337338if (apple_cgl.set_current_context(ac->context_obj))339error = true;340341if (apple_cgl.clear_drawable(ac->context_obj))342error = true;343344if (ac->drawable) {345ac->drawable->destroy(ac->drawable);346ac->drawable = NULL;347}348349/* Invalidate this to prevent surface recreation. */350ac->last_surface_window = None;351352apple_glx_diagnostic("%s: drawable is None, error is: %d\n",353__func__, error);354355return error;356}357358/* This is an optimisation to avoid searching for the current drawable. */359if (ac->drawable && ac->drawable->drawable == drawable) {360newagd = ac->drawable;361}362else {363/* Find the drawable if possible, and retain a reference to it. */364newagd =365apple_glx_drawable_find(drawable, APPLE_GLX_DRAWABLE_REFERENCE);366}367368/*369* Try to destroy the old drawable, so long as the new one370* isn't the old.371*/372if (ac->drawable && !same_drawable) {373ac->drawable->destroy(ac->drawable);374ac->drawable = NULL;375}376377if (NULL == newagd) {378if (apple_glx_surface_create(dpy, ac->screen, drawable, &newagd))379return true;380381/* The drawable is referenced once by apple_glx_surface_create. */382383/*384* FIXME: We actually need 2 references to prevent premature surface385* destruction. The problem is that the surface gets destroyed in386* the case of the context being reused for another window, and387* we then lose the surface contents. Wait for destruction of a388* window to destroy a surface.389*390* Note: this may leave around surfaces we don't want around, if391* say we are using X for raster drawing after OpenGL rendering,392* but it will be compatible with the old libGL's behavior.393*394* Someday the X11 and OpenGL rendering must be unified at some395* layer. I suspect we can do that via shared memory and396* multiple threads in the X server (1 for each context created397* by a client). This would also allow users to render from398* multiple clients to the same OpenGL surface. In fact it could399* all be OpenGL.400*401*/402newagd->reference(newagd);403404/* Save the new drawable with the context structure. */405ac->drawable = newagd;406}407else {408/* We are reusing an existing drawable structure. */409410if (same_drawable) {411assert(ac->drawable == newagd);412/* The drawable_find above retained a reference for us. */413}414else {415ac->drawable = newagd;416}417}418419/*420* Avoid this costly path if this is the same drawable and the421* context is already current.422*/423424if (same_drawable && ac->is_current) {425apple_glx_diagnostic("same_drawable and ac->is_current\n");426return false;427}428429cglerr = apple_cgl.set_current_context(ac->context_obj);430431if (kCGLNoError != cglerr) {432fprintf(stderr, "set current error: %s\n",433apple_cgl.error_string(cglerr));434return true;435}436437ac->is_current = true;438439assert(NULL != ac->context_obj);440assert(NULL != ac->drawable);441442ac->thread_id = pthread_self();443444/* This will be set if the pending_destroy code indicates it should be: */445ac->last_surface_window = None;446447switch (ac->drawable->type) {448case APPLE_GLX_DRAWABLE_PBUFFER:449case APPLE_GLX_DRAWABLE_SURFACE:450case APPLE_GLX_DRAWABLE_PIXMAP:451if (ac->drawable->callbacks.make_current) {452if (ac->drawable->callbacks.make_current(ac, ac->drawable))453return true;454}455break;456457default:458fprintf(stderr, "internal error: invalid drawable type: %d\n",459ac->drawable->type);460abort();461}462463return false;464}465466bool467apple_glx_is_current_drawable(Display * dpy, void *ptr, GLXDrawable drawable)468{469struct apple_glx_context *ac = ptr;470471if (ac->drawable && ac->drawable->drawable == drawable) {472return true;473}474else if (NULL == ac->drawable && None != ac->last_surface_window) {475apple_glx_context_update(dpy, ac);476477return (ac->drawable && ac->drawable->drawable == drawable);478}479480return false;481}482483bool484apple_glx_copy_context(void *currentptr, void *srcptr, void *destptr,485unsigned long mask, int *errorptr, bool * x11errorptr)486{487struct apple_glx_context *src, *dest;488CGLError err;489490src = srcptr;491dest = destptr;492493if (src->screen != dest->screen) {494*errorptr = BadMatch;495*x11errorptr = true;496return true;497}498499if (dest == currentptr || dest->is_current) {500*errorptr = BadAccess;501*x11errorptr = true;502return true;503}504505/*506* If srcptr is the current context then we should do an implicit glFlush.507*/508if (currentptr == srcptr)509glFlush();510511err = apple_cgl.copy_context(src->context_obj, dest->context_obj,512(GLbitfield) mask);513514if (kCGLNoError != err) {515*errorptr = GLXBadContext;516*x11errorptr = false;517return true;518}519520return false;521}522523/*524* The value returned is the total number of contexts set to update.525* It's meant for debugging/introspection.526*/527int528apple_glx_context_surface_changed(unsigned int uid, pthread_t caller)529{530struct apple_glx_context *ac;531int updated = 0;532533lock_context_list();534535for (ac = context_list; ac; ac = ac->next) {536if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type537&& ac->drawable->types.surface.uid == uid) {538539if (caller == ac->thread_id) {540apple_glx_diagnostic("caller is the same thread for uid %u\n",541uid);542543xp_update_gl_context(ac->context_obj);544}545else {546ac->need_update = true;547++updated;548}549}550}551552unlock_context_list();553554return updated;555}556557void558apple_glx_context_update(Display * dpy, void *ptr)559{560struct apple_glx_context *ac = ptr;561562if (NULL == ac->drawable && None != ac->last_surface_window) {563bool failed;564565/* Attempt to recreate the surface for a destroyed drawable. */566failed =567apple_glx_make_current_context(dpy, ac, ac, ac->last_surface_window);568569apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__,570failed ? "YES" : "NO");571}572573if (ac->need_update) {574xp_update_gl_context(ac->context_obj);575ac->need_update = false;576577apple_glx_diagnostic("%s: updating context %p\n", __func__, ptr);578}579580if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type581&& ac->drawable->types.surface.pending_destroy) {582apple_glx_diagnostic("%s: clearing drawable %p\n", __func__, ptr);583apple_cgl.clear_drawable(ac->context_obj);584585if (ac->drawable) {586struct apple_glx_drawable *d;587588apple_glx_diagnostic("%s: attempting to destroy drawable %p\n",589__func__, ptr);590apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n",591__func__, ac->drawable->drawable);592593d = ac->drawable;594595ac->last_surface_window = d->drawable;596597ac->drawable = NULL;598599/*600* This will destroy the surface drawable if there are601* no references to it.602* It also subtracts 1 from the reference_count.603* If there are references to it, then it's probably made604* current in another context.605*/606d->destroy(d);607}608}609}610611bool612apple_glx_context_uses_stereo(void *ptr)613{614struct apple_glx_context *ac = ptr;615616return ac->uses_stereo;617}618619620