mirror of
https://github.com/Stichting-MINIX-Research-Foundation/xsrc.git
synced 2025-09-16 08:05:30 -04:00
1136 lines
32 KiB
C
1136 lines
32 KiB
C
/* $XFree86: xc/lib/GL/apple/dri_driver.c,v 1.4 2004/12/10 17:47:24 alanh Exp $ */
|
|
/**************************************************************************
|
|
|
|
Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
|
|
Copyright (c) 2002 Apple Computer, Inc.
|
|
Copyright (c) 2004 Torrey T. Lyons
|
|
All Rights Reserved.
|
|
|
|
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, sub license, 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 (including the
|
|
next paragraph) 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 NON-INFRINGEMENT.
|
|
IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS 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.
|
|
|
|
**************************************************************************/
|
|
|
|
/*
|
|
* Original Authors:
|
|
* Kevin E. Martin <kevin@precisioninsight.com>
|
|
* Brian E. Paul <brian@precisioninsight.com>
|
|
*/
|
|
|
|
/*
|
|
* This file follows Mesa's dri_util.c closely. The code in dri_util.c
|
|
* gets compiled into each of the DRI 3D drivers. A typical DRI driver,
|
|
* is loaded dynamically by libGL, so libGL knows nothing about the
|
|
* internal functions here. On Mac OS X the AppleDRI driver code is
|
|
* statically linked into libGL, but otherwise it tries to behave like
|
|
* a standard DRI driver.
|
|
*
|
|
* The functions defined here are called from the GL library via function
|
|
* pointers in the __DRIdisplayRec, __DRIscreenRec, __DRIcontextRec,
|
|
* __DRIdrawableRec structures defined in glxclient.h. Those function
|
|
* pointers are initialized by code in this file. The process starts when
|
|
* libGL calls the __driCreateScreen() function at the end of this file.
|
|
*
|
|
* The above-mentioned DRI structures have no dependencies on Mesa.
|
|
* Each structure instead has a generic (void *) private pointer that
|
|
* points to a private structure. For Mesa drivers, these private
|
|
* structures are the __DRIdrawablePrivateRec, __DRIcontextPrivateRec,
|
|
* __DRIscreenPrivateRec, and __DRIvisualPrivateRec structures defined
|
|
* in dri_mesaint.h. We allocate and attach those structs here in
|
|
* this file.
|
|
*/
|
|
|
|
|
|
#ifdef GLX_DIRECT_RENDERING
|
|
|
|
#include <unistd.h>
|
|
#include <X11/Xlibint.h>
|
|
#include <X11/extensions/Xext.h>
|
|
#include "extutil.h"
|
|
#include "glxclient.h"
|
|
#include "appledri.h"
|
|
#include "dri_driver.h"
|
|
#include "x-list.h"
|
|
#include "x-hash.h"
|
|
|
|
/**
|
|
* This is used in a couple of places that call \c driMesaCreateNewDrawable.
|
|
*/
|
|
static const int empty_attribute_list[1] = { None };
|
|
|
|
/* Context binding */
|
|
static Bool driMesaBindContext(Display *dpy, int scrn,
|
|
GLXDrawable draw, GLXContext gc);
|
|
static Bool driMesaUnbindContext(Display *dpy, int scrn,
|
|
GLXDrawable draw, GLXContext gc,
|
|
int will_rebind);
|
|
|
|
/* Drawable methods */
|
|
static void *driMesaCreateNewDrawable(__DRInativeDisplay *dpy,
|
|
const __GLcontextModes *modes,
|
|
__DRIid draw, __DRIdrawable *pdraw,
|
|
int renderType, const int *attrs);
|
|
static __DRIdrawable *driMesaGetDrawable(__DRInativeDisplay *dpy,
|
|
GLXDrawable draw,
|
|
void *screenPrivate);
|
|
static void driMesaSwapBuffers(__DRInativeDisplay *dpy, void *drawPrivate);
|
|
static void driMesaDestroyDrawable(__DRInativeDisplay *dpy, void *drawPrivate);
|
|
|
|
/* Context methods */
|
|
static void *driMesaCreateContext(Display *dpy, XVisualInfo *vis, void *shared,
|
|
__DRIcontext *pctx);
|
|
static void driMesaDestroyContext(__DRInativeDisplay *dpy, int scrn,
|
|
void *screenPrivate);
|
|
|
|
/* Screen methods */
|
|
static void *driMesaCreateScreen(__DRInativeDisplay *dpy, int scrn,
|
|
__DRIscreen *psc, int numConfigs,
|
|
__GLXvisualConfig *config);
|
|
static void driMesaDestroyScreen(__DRInativeDisplay *dpy, int scrn,
|
|
void *screenPrivate);
|
|
|
|
static void driMesaCreateSurface(Display *dpy, int scrn,
|
|
__DRIdrawablePrivate *pdp);
|
|
|
|
static void unwrap_context(__DRIcontextPrivate *pcp);
|
|
static void wrap_context(__DRIcontextPrivate *pcp);
|
|
|
|
extern const CGLContextObj XAppleDRIGetIndirectContext(void);
|
|
|
|
/*****************************************************************/
|
|
|
|
/* Maintain a list of drawables */
|
|
|
|
static inline Bool
|
|
__driMesaAddDrawable(x_hash_table *drawHash, __DRIdrawable *pdraw)
|
|
{
|
|
__DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)pdraw->private;
|
|
|
|
assert(drawHash != NULL);
|
|
|
|
x_hash_table_insert(drawHash, (void *) pdp->draw, pdraw);
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
static inline __DRIdrawable *
|
|
__driMesaFindDrawable(x_hash_table *drawHash, GLXDrawable draw)
|
|
{
|
|
if (drawHash == NULL)
|
|
return NULL;
|
|
|
|
return x_hash_table_lookup(drawHash, (void *) draw, NULL);
|
|
}
|
|
|
|
struct find_by_uid_closure {
|
|
unsigned int uid;
|
|
__DRIdrawable *ret;
|
|
};
|
|
|
|
static void
|
|
find_by_uid_cb(void *k, void *v, void *data)
|
|
{
|
|
__DRIdrawable *pdraw = v;
|
|
__DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)pdraw->private;
|
|
struct find_by_uid_closure *c = data;
|
|
|
|
if (pdp->uid == c->uid)
|
|
c->ret = pdraw;
|
|
}
|
|
|
|
static __DRIdrawable *
|
|
__driMesaFindDrawableByUID(x_hash_table *drawHash, unsigned int uid)
|
|
{
|
|
struct find_by_uid_closure c;
|
|
|
|
c.uid = uid;
|
|
c.ret = NULL;
|
|
x_hash_table_foreach(drawHash, find_by_uid_cb, &c);
|
|
|
|
return c.ret;
|
|
}
|
|
|
|
static inline void
|
|
__driMesaRemoveDrawable(x_hash_table *drawHash, __DRIdrawable *pdraw)
|
|
{
|
|
__DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)pdraw->private;
|
|
|
|
if (drawHash == NULL)
|
|
return;
|
|
|
|
x_hash_table_remove(drawHash, (void *) pdp->draw);
|
|
}
|
|
|
|
static Bool __driMesaWindowExistsFlag;
|
|
|
|
static int __driMesaWindowExistsErrorHandler(Display *dpy, XErrorEvent *xerr)
|
|
{
|
|
if (xerr->error_code == BadWindow) {
|
|
__driMesaWindowExistsFlag = GL_FALSE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Bool __driMesaWindowExists(Display *dpy, GLXDrawable draw)
|
|
{
|
|
XWindowAttributes xwa;
|
|
int (*oldXErrorHandler)(Display *, XErrorEvent *);
|
|
|
|
__driMesaWindowExistsFlag = GL_TRUE;
|
|
oldXErrorHandler = XSetErrorHandler(__driMesaWindowExistsErrorHandler);
|
|
XGetWindowAttributes(dpy, draw, &xwa); /* dummy request */
|
|
XSetErrorHandler(oldXErrorHandler);
|
|
return __driMesaWindowExistsFlag;
|
|
}
|
|
|
|
static void __driMesaCollectCallback(void *k, void *v, void *data)
|
|
{
|
|
GLXDrawable draw = (GLXDrawable) k;
|
|
__DRIdrawable *pdraw = v;
|
|
x_list **todelete = data;
|
|
|
|
__DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)pdraw->private;
|
|
Display *dpy;
|
|
|
|
dpy = pdp->driScreenPriv->display;
|
|
XSync(dpy, GL_FALSE);
|
|
if (!pdp->destroyed && !__driMesaWindowExists(dpy, draw)) {
|
|
/* Destroy the local drawable data in the hash table, if the
|
|
drawable no longer exists in the Xserver */
|
|
pdp->destroyed = TRUE;
|
|
*todelete = x_list_prepend(*todelete, pdraw);
|
|
}
|
|
}
|
|
|
|
/* pdp->mutex is held. */
|
|
static void __driMesaGarbageCollectDrawables(void *drawHash)
|
|
{
|
|
__DRIdrawable *pdraw;
|
|
__DRIdrawablePrivate *pdp;
|
|
Display *dpy;
|
|
x_list *todelete = NULL, *node;
|
|
|
|
x_hash_table_foreach(drawHash, __driMesaCollectCallback, &todelete);
|
|
|
|
for (node = todelete; node != NULL; node = node->next)
|
|
{
|
|
pdraw = node->data;
|
|
pdp = (__DRIdrawablePrivate *)pdraw->private;
|
|
dpy = pdp->driScreenPriv->display;
|
|
|
|
/* Destroy the local drawable data in the hash table, if the
|
|
drawable no longer exists in the Xserver */
|
|
|
|
__driMesaRemoveDrawable(drawHash, pdraw);
|
|
(*pdraw->destroyDrawable)(dpy, pdraw->private);
|
|
Xfree(pdraw);
|
|
}
|
|
|
|
x_list_free(todelete);
|
|
}
|
|
|
|
/*****************************************************************/
|
|
|
|
/* returns with psp->mutex locked if successful. */
|
|
static Bool
|
|
driMesaFindDrawableByUID(Display *dpy,unsigned int uid,
|
|
__DRIscreenPrivate **psp_ret,
|
|
__DRIdrawablePrivate **pdp_ret)
|
|
{
|
|
__DRIscreen *pDRIScreen;
|
|
__DRIscreenPrivate *psp;
|
|
__DRIdrawable *pdraw;
|
|
int scrn;
|
|
|
|
for (scrn = 0; scrn < ScreenCount(dpy); scrn++)
|
|
{
|
|
if (!(pDRIScreen = __glXFindDRIScreen(dpy, scrn))) {
|
|
/* ERROR!!! */
|
|
return FALSE;
|
|
} else if (!(psp = (__DRIscreenPrivate *)pDRIScreen->private)) {
|
|
/* ERROR!!! */
|
|
return FALSE;
|
|
}
|
|
|
|
xmutex_lock(psp->mutex);
|
|
|
|
pdraw = __driMesaFindDrawableByUID(psp->drawHash, uid);
|
|
if (pdraw != NULL) {
|
|
*psp_ret = psp;
|
|
*pdp_ret = pdraw->private;
|
|
return TRUE;
|
|
};
|
|
|
|
xmutex_unlock(psp->mutex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
unbind_context(__DRIcontextPrivate *pcp)
|
|
{
|
|
/* Unbind the context from its old drawable. */
|
|
|
|
if (pcp->driDrawablePriv != NULL)
|
|
{
|
|
if (pcp->next != NULL)
|
|
pcp->next->prev = pcp->prev;
|
|
if (pcp->prev != NULL)
|
|
pcp->prev->next = pcp->next;
|
|
|
|
if (pcp->driDrawablePriv->driContextPriv == pcp)
|
|
pcp->driDrawablePriv->driContextPriv = pcp->next;
|
|
|
|
pcp->driDrawablePriv = NULL;
|
|
pcp->prev = pcp->next = NULL;
|
|
}
|
|
|
|
if (pcp->surface_id != 0)
|
|
{
|
|
pcp->surface_id = 0;
|
|
pcp->pending_clear = TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
unbind_drawable(__DRIdrawablePrivate *pdp)
|
|
{
|
|
__DRIcontextPrivate *pcp, *next;
|
|
|
|
for (pcp = pdp->driContextPriv; pcp != NULL; pcp = next)
|
|
{
|
|
next = pcp->next;
|
|
unbind_context(pcp);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_context(__DRIcontextPrivate *pcp)
|
|
{
|
|
if (pcp->pending_clear)
|
|
{
|
|
CGLClearDrawable(pcp->ctx);
|
|
pcp->pending_clear = FALSE;
|
|
}
|
|
|
|
if (pcp->pending_update && pcp->surface_id != 0)
|
|
{
|
|
xp_update_gl_context(pcp->ctx);
|
|
pcp->pending_update = FALSE;
|
|
}
|
|
}
|
|
|
|
static Bool driMesaUnbindContext(Display *dpy, int scrn,
|
|
GLXDrawable draw, GLXContext gc,
|
|
int will_rebind)
|
|
{
|
|
__DRIscreen *pDRIScreen;
|
|
// __DRIdrawable *pdraw;
|
|
__DRIcontextPrivate *pcp;
|
|
__DRIscreenPrivate *psp;
|
|
__DRIdrawablePrivate *pdp;
|
|
|
|
/*
|
|
** Assume error checking is done properly in glXMakeCurrent before
|
|
** calling driMesaUnbindContext.
|
|
*/
|
|
|
|
if (gc == NULL || draw == None) {
|
|
/* ERROR!!! */
|
|
return GL_FALSE;
|
|
}
|
|
|
|
if (!(pDRIScreen = __glXFindDRIScreen(dpy, scrn))) {
|
|
/* ERROR!!! */
|
|
return GL_FALSE;
|
|
} else if (!(psp = (__DRIscreenPrivate *)pDRIScreen->private)) {
|
|
/* ERROR!!! */
|
|
return GL_FALSE;
|
|
}
|
|
|
|
xmutex_lock(psp->mutex);
|
|
|
|
pcp = (__DRIcontextPrivate *)gc->driContext.private;
|
|
|
|
pdp = pcp->driDrawablePriv;
|
|
if (pdp == NULL) {
|
|
/* ERROR!!! */
|
|
xmutex_unlock(psp->mutex);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
/* Put this thread back into normal (indirect) dispatch mode. */
|
|
CGLSetCurrentContext(XAppleDRIGetIndirectContext());
|
|
pcp->thread_id = 0;
|
|
|
|
/* Lazily unbind the drawable from the context */
|
|
unbind_context(pcp);
|
|
|
|
if (pdp->refcount == 0) {
|
|
/* ERROR!!! */
|
|
xmutex_unlock(psp->mutex);
|
|
return GL_FALSE;
|
|
} else if (--pdp->refcount == 0) {
|
|
#if 0
|
|
/*
|
|
** NOT_DONE: When a drawable is unbound from one direct
|
|
** rendering context and then bound to another, we do not want
|
|
** to destroy the drawable data structure each time only to
|
|
** recreate it immediatly afterwards when binding to the next
|
|
** context. This also causes conflicts with caching of the
|
|
** drawable stamp.
|
|
**
|
|
** In addition, we don't destroy the drawable here since Mesa
|
|
** keeps private data internally (e.g., software accumulation
|
|
** buffers) that should not be destroyed unless the client
|
|
** explicitly requests that the window be destroyed.
|
|
**
|
|
** When GLX 1.3 is integrated, the create and destroy drawable
|
|
** functions will have user level counterparts and the memory
|
|
** will be able to be recovered.
|
|
**
|
|
** Below is an example of what needs to go into the destroy
|
|
** drawable routine to support GLX 1.3.
|
|
*/
|
|
__driMesaRemoveDrawable(psp->drawHash, pdraw);
|
|
(*pdraw->destroyDrawable)(dpy, pdraw->private);
|
|
Xfree(pdraw);
|
|
#endif
|
|
}
|
|
|
|
xmutex_unlock(psp->mutex);
|
|
return GL_TRUE;
|
|
}
|
|
|
|
static Bool driMesaBindContext(Display *dpy, int scrn,
|
|
GLXDrawable draw, GLXContext gc)
|
|
{
|
|
__DRIscreen *pDRIScreen;
|
|
const __GLcontextModes *modes;
|
|
__DRIdrawable *pdraw;
|
|
__DRIdrawablePrivate *pdp;
|
|
__DRIscreenPrivate *psp;
|
|
__DRIcontextPrivate *pcp;
|
|
|
|
/*
|
|
** Assume error checking is done properly in glXMakeCurrent before
|
|
** calling driMesaBindContext.
|
|
*/
|
|
|
|
if (gc == NULL || draw == None) {
|
|
/* ERROR!!! */
|
|
return GL_FALSE;
|
|
}
|
|
|
|
if (!(pDRIScreen = __glXFindDRIScreen(dpy, scrn))) {
|
|
/* ERROR!!! */
|
|
return GL_FALSE;
|
|
} else if (!(psp = (__DRIscreenPrivate *)pDRIScreen->private)) {
|
|
/* ERROR!!! */
|
|
return GL_FALSE;
|
|
}
|
|
|
|
modes = gc->driContext.mode;
|
|
|
|
if ( modes == NULL ) {
|
|
/* ERROR!!! */
|
|
return GL_FALSE;
|
|
}
|
|
|
|
xmutex_lock(psp->mutex);
|
|
|
|
pdraw = __driMesaFindDrawable(psp->drawHash, draw);
|
|
if (!pdraw) {
|
|
/* Allocate a new drawable */
|
|
pdraw = (__DRIdrawable *)Xmalloc(sizeof(__DRIdrawable));
|
|
if (!pdraw) {
|
|
/* ERROR!!! */
|
|
xmutex_unlock(psp->mutex);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
/* Create a new drawable */
|
|
pdraw->private = driMesaCreateNewDrawable(dpy, modes, draw, pdraw,
|
|
GLX_WINDOW_BIT,
|
|
empty_attribute_list);
|
|
if (!pdraw->private) {
|
|
/* ERROR!!! */
|
|
Xfree(pdraw);
|
|
xmutex_unlock(psp->mutex);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
/* Add pdraw to drawable list */
|
|
if (!__driMesaAddDrawable(psp->drawHash, pdraw)) {
|
|
/* ERROR!!! */
|
|
(*pdraw->destroyDrawable)(dpy, pdraw->private);
|
|
Xfree(pdraw);
|
|
xmutex_unlock(psp->mutex);
|
|
return GL_FALSE;
|
|
}
|
|
}
|
|
|
|
pdp = (__DRIdrawablePrivate *)pdraw->private;
|
|
pcp = (__DRIcontextPrivate *)gc->driContext.private;
|
|
|
|
if (pdp->surface_id == 0)
|
|
{
|
|
/* Surface got destroyed. Try to create a new one. */
|
|
|
|
driMesaCreateSurface(dpy, scrn, pdp);
|
|
}
|
|
|
|
unbind_context(pcp);
|
|
|
|
/* Bind the drawable to the context */
|
|
pcp->driDrawablePriv = pdp;
|
|
pcp->prev = NULL;
|
|
pcp->next = pdp->driContextPriv;
|
|
pdp->driContextPriv = pcp;
|
|
pdp->refcount++;
|
|
|
|
/* And the physical surface to the physical context */
|
|
if (pcp->surface_id != pdp->surface_id)
|
|
{
|
|
pcp->surface_id = 0;
|
|
|
|
/* Attaching the drawable sets the default viewport. But we don't
|
|
want to catch that call to glViewport in our wrappers. */
|
|
unwrap_context(pcp);
|
|
|
|
if (pdp->surface_id == 0)
|
|
CGLClearDrawable(pcp->ctx);
|
|
else if (xp_attach_gl_context(pcp->ctx, pdp->surface_id) == Success)
|
|
pcp->surface_id = pdp->surface_id;
|
|
else
|
|
fprintf(stderr, "failed to bind to surface\n");
|
|
|
|
wrap_context(pcp);
|
|
|
|
pcp->pending_clear = FALSE;
|
|
pcp->pending_update = FALSE;
|
|
}
|
|
else if (pcp->pending_clear)
|
|
{
|
|
CGLClearDrawable(pcp->ctx);
|
|
pcp->pending_clear = FALSE;
|
|
}
|
|
|
|
/* Activate the CGL context and remember which thread it's current for. */
|
|
CGLSetCurrentContext(pcp->ctx);
|
|
pcp->thread_id = xthread_self();
|
|
|
|
xmutex_unlock(psp->mutex);
|
|
return GL_TRUE;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
|
|
static xp_client_id
|
|
get_client_id(void)
|
|
{
|
|
static xp_client_id id;
|
|
|
|
if (id == 0)
|
|
{
|
|
if (xp_init(XP_IN_BACKGROUND) != Success
|
|
|| xp_get_client_id(&id) != Success)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
static void driMesaCreateSurface(Display *dpy, int scrn,
|
|
__DRIdrawablePrivate *pdp)
|
|
{
|
|
xp_client_id client_id;
|
|
unsigned int key[2];
|
|
|
|
pdp->surface_id = 0;
|
|
pdp->uid = 0;
|
|
|
|
client_id = get_client_id();
|
|
if (client_id == 0)
|
|
return;
|
|
|
|
if (XAppleDRICreateSurface(dpy, scrn, pdp->draw,
|
|
client_id, key, &pdp->uid))
|
|
{
|
|
xp_import_surface(key, &pdp->surface_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is called via __DRIscreenRec's createNewDrawable pointer.
|
|
*/
|
|
static void *driMesaCreateNewDrawable(__DRInativeDisplay *dpy,
|
|
const __GLcontextModes *modes,
|
|
__DRIid draw,
|
|
__DRIdrawable *pdraw,
|
|
int renderType,
|
|
const int *attrs)
|
|
{
|
|
__DRIscreen * const pDRIScreen = __glXFindDRIScreen(dpy, modes->screen);
|
|
__DRIscreenPrivate *psp;
|
|
__DRIdrawablePrivate *pdp;
|
|
|
|
|
|
pdraw->private = NULL;
|
|
|
|
/* Since pbuffers are not yet supported, no drawable attributes are
|
|
* supported either.
|
|
*/
|
|
(void) attrs;
|
|
|
|
if ( (pDRIScreen == NULL) || (pDRIScreen->private == NULL) ) {
|
|
return NULL;
|
|
}
|
|
|
|
pdp = (__DRIdrawablePrivate *)Xmalloc(sizeof(__DRIdrawablePrivate));
|
|
if (!pdp) {
|
|
return NULL;
|
|
}
|
|
|
|
pdp->draw = draw;
|
|
pdp->refcount = 0;
|
|
pdp->surface_id = 0;
|
|
pdp->uid = 0;
|
|
pdp->destroyed = FALSE;
|
|
|
|
psp = (__DRIscreenPrivate *)pDRIScreen->private;
|
|
pdp->driScreenPriv = psp;
|
|
pdp->driContextPriv = NULL;
|
|
|
|
driMesaCreateSurface(dpy, modes->screen, pdp);
|
|
if (pdp->surface_id == 0) {
|
|
Xfree(pdp);
|
|
return NULL;
|
|
}
|
|
|
|
pdraw->private = pdp;
|
|
pdraw->destroyDrawable = driMesaDestroyDrawable;
|
|
pdraw->swapBuffers = driMesaSwapBuffers; /* called by glXSwapBuffers() */
|
|
|
|
#if 0
|
|
/* We don't support these yet. */
|
|
if ( driCompareGLXAPIVersion( 20030317 ) >= 0 ) {
|
|
pdraw->getSBC = driGetSBC;
|
|
pdraw->waitForSBC = driWaitForSBC;
|
|
pdraw->waitForMSC = driWaitForMSC;
|
|
pdraw->swapBuffersMSC = driSwapBuffersMSC;
|
|
pdraw->frameTracking = NULL;
|
|
pdraw->queryFrameTracking = driQueryFrameTracking;
|
|
|
|
/* This special default value is replaced with the configured
|
|
* default value when the drawable is first bound to a direct
|
|
* rendering context. */
|
|
pdraw->swap_interval = (unsigned)-1;
|
|
}
|
|
#endif
|
|
|
|
return (void *) pdp;
|
|
}
|
|
|
|
static __DRIdrawable *driMesaGetDrawable(__DRInativeDisplay *dpy,
|
|
GLXDrawable draw,
|
|
void *screenPrivate)
|
|
{
|
|
__DRIscreenPrivate *psp = (__DRIscreenPrivate *) screenPrivate;
|
|
__DRIdrawable *dri_draw;
|
|
|
|
xmutex_lock(psp->mutex);
|
|
|
|
/*
|
|
** Make sure this routine returns NULL if the drawable is not bound
|
|
** to a direct rendering context!
|
|
*/
|
|
dri_draw = __driMesaFindDrawable(psp->drawHash, draw);
|
|
|
|
xmutex_unlock(psp->mutex);
|
|
return dri_draw;
|
|
}
|
|
|
|
static void driMesaSwapBuffers(__DRInativeDisplay *dpy, void *drawPrivate)
|
|
{
|
|
__DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *) drawPrivate;
|
|
__DRIcontextPrivate *pcp;
|
|
xthread_t self = xthread_self();
|
|
static Bool warned;
|
|
|
|
xmutex_lock(pdp->driScreenPriv->mutex);
|
|
|
|
/* FIXME: this is sub-optimal, since we may not always find a context
|
|
bound to the given drawable on this thread. */
|
|
|
|
for (pcp = pdp->driContextPriv; pcp != NULL; pcp = pcp->next)
|
|
{
|
|
if (pcp->thread_id == self || pcp->thread_id == 0)
|
|
break;
|
|
}
|
|
|
|
if (pcp != NULL)
|
|
{
|
|
CGLFlushDrawable(pcp->ctx);
|
|
}
|
|
else
|
|
{
|
|
if (!warned) {
|
|
fprintf(stderr, "glXSwapBuffers: no context for this drawable\n");
|
|
warned = TRUE;
|
|
}
|
|
}
|
|
|
|
xmutex_unlock(pdp->driScreenPriv->mutex);
|
|
}
|
|
|
|
/* pdp->mutex is held. */
|
|
static void driMesaDestroyDrawable(__DRInativeDisplay *dpy, void *drawPrivate)
|
|
{
|
|
__DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)drawPrivate;
|
|
|
|
if (pdp) {
|
|
unbind_drawable(pdp);
|
|
if (pdp->surface_id != 0) {
|
|
xp_destroy_surface(pdp->surface_id);
|
|
pdp->surface_id = 0;
|
|
}
|
|
if (!pdp->destroyed) {
|
|
/* don't try to destroy an already destroyed surface. */
|
|
XAppleDRIDestroySurface(dpy, pdp->driScreenPriv->myNum, pdp->draw);
|
|
}
|
|
Xfree(pdp);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************/
|
|
|
|
static CGLPixelFormatObj
|
|
driCreatePixelFormat(Display *dpy, __DRIscreenPrivate *psp,
|
|
XVisualInfo *visinfo, __GLXvisualConfig *config)
|
|
{
|
|
int i;
|
|
CGLPixelFormatAttribute attr[64]; // currently uses max of 30
|
|
CGLPixelFormatObj result;
|
|
long n_formats;
|
|
|
|
i = 0;
|
|
|
|
if (!config->rgba)
|
|
return NULL;
|
|
|
|
if (config->stereo)
|
|
attr[i++] = kCGLPFAStereo;
|
|
|
|
if (config->doubleBuffer)
|
|
attr[i++] = kCGLPFADoubleBuffer;
|
|
|
|
attr[i++] = kCGLPFAColorSize;
|
|
attr[i++] = config->redSize + config->greenSize + config->blueSize;
|
|
attr[i++] = kCGLPFAAlphaSize;
|
|
attr[i++] = 1; /* FIXME: ignoring config->alphaSize which is always 0 */
|
|
|
|
if (config->accumRedSize + config->accumGreenSize
|
|
+ config->accumBlueSize + config->accumAlphaSize > 0)
|
|
{
|
|
attr[i++] = kCGLPFAAccumSize;
|
|
attr[i++] = (config->accumRedSize + config->accumGreenSize
|
|
+ config->accumBlueSize + config->accumAlphaSize);
|
|
}
|
|
|
|
if (config->depthSize > 0) {
|
|
attr[i++] = kCGLPFADepthSize;
|
|
attr[i++] = config->depthSize;
|
|
}
|
|
|
|
if (config->stencilSize > 0) {
|
|
attr[i++] = kCGLPFAStencilSize;
|
|
attr[i++] = config->stencilSize;
|
|
}
|
|
|
|
if (config->auxBuffers > 0) {
|
|
attr[i++] = kCGLPFAAuxBuffers;
|
|
attr[i++] = config->auxBuffers;
|
|
}
|
|
|
|
/* FIXME: things we don't handle: color/alpha masks, level,
|
|
visualrating, transparentFoo */
|
|
|
|
attr[i++] = 0;
|
|
|
|
result = NULL;
|
|
CGLChoosePixelFormat(attr, &result, &n_formats);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void *driMesaCreateContext(Display *dpy, XVisualInfo *vis, void *shared,
|
|
__DRIcontext *pctx)
|
|
{
|
|
__DRIscreen *pDRIScreen;
|
|
__DRIcontextPrivate *pcp;
|
|
__DRIcontextPrivate *pshare = (__DRIcontextPrivate *)shared;
|
|
__DRIscreenPrivate *psp;
|
|
int i;
|
|
|
|
if (!(pDRIScreen = __glXFindDRIScreen(dpy, vis->screen))) {
|
|
/* ERROR!!! */
|
|
return NULL;
|
|
} else if (!(psp = (__DRIscreenPrivate *)pDRIScreen->private)) {
|
|
/* ERROR!!! */
|
|
return NULL;
|
|
}
|
|
|
|
/* Create the hash table */
|
|
if (!psp->drawHash) {
|
|
xmutex_lock(psp->mutex);
|
|
if (!psp->drawHash)
|
|
psp->drawHash = x_hash_table_new(NULL, NULL, NULL, NULL);
|
|
xmutex_unlock(psp->mutex);
|
|
}
|
|
|
|
pcp = (__DRIcontextPrivate *)Xmalloc(sizeof(__DRIcontextPrivate));
|
|
if (!pcp) {
|
|
return NULL;
|
|
}
|
|
|
|
pcp->display = dpy;
|
|
pcp->driScreenPriv = psp;
|
|
pcp->driDrawablePriv = NULL;
|
|
|
|
pcp->ctx = NULL;
|
|
pcp->surface_id = 0;
|
|
|
|
pcp->pending_clear = FALSE;
|
|
pcp->pending_update = FALSE;
|
|
|
|
pcp->ctx = NULL;
|
|
for (i = 0; pcp->ctx == NULL && i < psp->numVisuals; i++) {
|
|
if (psp->visuals[i].vid == vis->visualid) {
|
|
CGLCreateContext(psp->visuals[i].pixel_format,
|
|
pshare ? pshare->ctx : NULL, &pcp->ctx);
|
|
}
|
|
}
|
|
|
|
if (!pcp->ctx) {
|
|
Xfree(pcp);
|
|
return NULL;
|
|
}
|
|
|
|
pctx->destroyContext = driMesaDestroyContext;
|
|
pctx->bindContext = driMesaBindContext;
|
|
pctx->unbindContext = driMesaUnbindContext;
|
|
|
|
wrap_context(pcp);
|
|
|
|
xmutex_lock(psp->mutex);
|
|
__driMesaGarbageCollectDrawables(pcp->driScreenPriv->drawHash);
|
|
xmutex_unlock(psp->mutex);
|
|
|
|
return pcp;
|
|
}
|
|
|
|
static void driMesaDestroyContext(__DRInativeDisplay *dpy, int scrn,
|
|
void *contextPrivate)
|
|
{
|
|
__DRIcontextPrivate *pcp = (__DRIcontextPrivate *) contextPrivate;
|
|
|
|
if (pcp) {
|
|
xmutex_lock(pcp->driScreenPriv->mutex);
|
|
unbind_context(pcp);
|
|
__driMesaGarbageCollectDrawables(pcp->driScreenPriv->drawHash);
|
|
xmutex_unlock(pcp->driScreenPriv->mutex);
|
|
CGLDestroyContext(pcp->ctx);
|
|
Xfree(pcp);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************/
|
|
|
|
static void *driMesaCreateScreen(__DRInativeDisplay *dpy, int scrn,
|
|
__DRIscreen *psc, int numConfigs,
|
|
__GLXvisualConfig *config)
|
|
{
|
|
int directCapable, i, n;
|
|
__DRIscreenPrivate *psp;
|
|
XVisualInfo visTmpl, *visinfo;
|
|
|
|
if (!XAppleDRIQueryDirectRenderingCapable(dpy, scrn, &directCapable)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!directCapable) {
|
|
return NULL;
|
|
}
|
|
|
|
psp = (__DRIscreenPrivate *)Xmalloc(sizeof(__DRIscreenPrivate));
|
|
if (!psp) {
|
|
return NULL;
|
|
}
|
|
|
|
psp->mutex = xmutex_malloc();
|
|
if (psp->mutex != NULL) {
|
|
xmutex_init (psp->mutex);
|
|
xmutex_set_name (psp->mutex, "AppleDRI");
|
|
}
|
|
psp->display = dpy;
|
|
psp->myNum = scrn;
|
|
|
|
#if 0
|
|
if (!XAppleDRIAuthConnection(dpy, scrn, magic)) {
|
|
Xfree(psp);
|
|
(void)XAppleDRICloseConnection(dpy, scrn);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Allocate space for an array of visual records and initialize them.
|
|
*/
|
|
psp->visuals = (__DRIvisualPrivate *)Xmalloc(numConfigs *
|
|
sizeof(__DRIvisualPrivate));
|
|
if (!psp->visuals) {
|
|
Xfree(psp);
|
|
return NULL;
|
|
}
|
|
|
|
visTmpl.screen = scrn;
|
|
visinfo = XGetVisualInfo(dpy, VisualScreenMask, &visTmpl, &n);
|
|
if (n != numConfigs) {
|
|
Xfree(psp);
|
|
return NULL;
|
|
}
|
|
|
|
psp->numVisuals = 0;
|
|
for (i = 0; i < numConfigs; i++, config++) {
|
|
psp->visuals[psp->numVisuals].vid = visinfo[i].visualid;
|
|
psp->visuals[psp->numVisuals].pixel_format =
|
|
driCreatePixelFormat(dpy, psp, &visinfo[i], config);
|
|
if (psp->visuals[psp->numVisuals].pixel_format != NULL) {
|
|
psp->numVisuals++;
|
|
}
|
|
}
|
|
|
|
XFree(visinfo);
|
|
|
|
if (psp->numVisuals == 0) {
|
|
/* Couldn't create any pixel formats. */
|
|
Xfree(psp->visuals);
|
|
Xfree(psp);
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize the drawHash when the first context is created */
|
|
psp->drawHash = NULL;
|
|
|
|
psc->destroyScreen = driMesaDestroyScreen;
|
|
psc->createContext = driMesaCreateContext;
|
|
psc->createNewDrawable = driMesaCreateNewDrawable;
|
|
psc->getDrawable = driMesaGetDrawable;
|
|
|
|
return (void *)psp;
|
|
}
|
|
|
|
static void driMesaDestroyScreen(__DRInativeDisplay *dpy, int scrn,
|
|
void *screenPrivate)
|
|
{
|
|
__DRIscreenPrivate *psp = (__DRIscreenPrivate *) screenPrivate;
|
|
|
|
if (psp) {
|
|
//FIXME resetDriver ?
|
|
Xfree(psp->visuals);
|
|
Xfree(psp);
|
|
}
|
|
}
|
|
|
|
/* Note: definitely can't make any X protocol requests here. */
|
|
static void driAppleSurfaceNotify(Display *dpy, unsigned int uid, int kind)
|
|
{
|
|
__DRIscreenPrivate *psp;
|
|
__DRIdrawablePrivate *pdp;
|
|
__DRIcontextPrivate *pcp;
|
|
|
|
/* locks psp->mutex if successful. */
|
|
if (driMesaFindDrawableByUID(dpy, uid, &psp, &pdp))
|
|
{
|
|
xthread_t self = xthread_self();
|
|
|
|
switch (kind)
|
|
{
|
|
Bool all_safe;
|
|
|
|
case AppleDRISurfaceNotifyDestroyed:
|
|
xp_destroy_surface(pdp->surface_id);
|
|
pdp->surface_id = 0;
|
|
|
|
for (pcp = pdp->driContextPriv; pcp != NULL; pcp = pcp->next)
|
|
{
|
|
pcp->surface_id = 0;
|
|
|
|
if (pcp->thread_id == self || pcp->thread_id == 0) {
|
|
CGLClearDrawable(pcp->ctx);
|
|
pcp->pending_clear = FALSE;
|
|
} else
|
|
pcp->pending_clear = TRUE;
|
|
}
|
|
break;
|
|
|
|
case AppleDRISurfaceNotifyChanged:
|
|
all_safe = TRUE;
|
|
for (pcp = pdp->driContextPriv; pcp != NULL; pcp = pcp->next)
|
|
{
|
|
if (pcp->thread_id != 0 && pcp->thread_id != self) {
|
|
all_safe = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
for (pcp = pdp->driContextPriv; pcp != NULL; pcp = pcp->next)
|
|
{
|
|
if (all_safe) {
|
|
xp_update_gl_context(pcp->ctx);
|
|
pcp->pending_update = FALSE;
|
|
} else
|
|
pcp->pending_update = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
xmutex_unlock(psp->mutex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Entrypoint function used to create a new driver-private screen structure.
|
|
*
|
|
* \param dpy Display pointer.
|
|
* \param scrn Index of the screen.
|
|
* \param psc DRI screen data (not driver private)
|
|
* \param numConfigs Number of visual configs pointed to by \c configs.
|
|
* \param config Array of GLXvisualConfigs exported by the 2D driver.
|
|
*
|
|
* \deprecated
|
|
* In dynamically linked drivers, this function has been replaced by
|
|
* \c __driCreateNewScreen.
|
|
*/
|
|
void *__driCreateScreen(Display *dpy, int scrn, __DRIscreen *psc,
|
|
int numConfigs, __GLXvisualConfig *config)
|
|
{
|
|
static int here_before;
|
|
|
|
if (!here_before)
|
|
{
|
|
XAppleDRISetSurfaceNotifyHandler(driAppleSurfaceNotify);
|
|
here_before = True;
|
|
}
|
|
|
|
return driMesaCreateScreen(dpy, scrn, psc, numConfigs, config);
|
|
}
|
|
|
|
void __driRegisterExtensions(void)
|
|
{
|
|
}
|
|
|
|
__private_extern__ void XAppleDRIUseIndirectDispatch(void)
|
|
{
|
|
CGLSetCurrentContext(XAppleDRIGetIndirectContext());
|
|
}
|
|
|
|
/*****************************************************************/
|
|
|
|
/*
|
|
* Currently (Mac OS X 10.3) the only way we have of regaining control
|
|
* from threads calling GL and nothing else is by patching the dispatch
|
|
* table of the CGLContext, so that glViewport, glFlush and glFinish
|
|
* call us back.
|
|
*
|
|
* Since glNewList and glEndList overwrite the entire dispatch table we
|
|
* also need to patch those so we can restore the others.
|
|
*
|
|
* WARNING: This is not expected to work on future OS releases.
|
|
*/
|
|
|
|
#define WRAP_CGL(context, vec, fun) \
|
|
do { \
|
|
(context)->disp.vec = (context)->ctx->disp.vec; \
|
|
(context)->ctx->disp.vec = (fun); \
|
|
} while (0)
|
|
|
|
#define UNWRAP_CGL(context, vec) \
|
|
do { \
|
|
(context)->ctx->disp.vec = (context)->disp.vec; \
|
|
} while (0)
|
|
|
|
#define WRAP_BOILERPLATE \
|
|
GLXContext gc; \
|
|
__DRIcontextPrivate *pcp; \
|
|
gc = __glXGetCurrentContext(); \
|
|
if (gc == NULL || !gc->isDirect) return; \
|
|
pcp = (__DRIcontextPrivate *) gc->driContext.private; \
|
|
if (pcp == NULL) return;
|
|
|
|
static void viewport_callback(GLIContext ctx, GLint x, GLint y,
|
|
GLsizei width, GLsizei height)
|
|
{
|
|
WRAP_BOILERPLATE
|
|
|
|
xmutex_lock(pcp->driScreenPriv->mutex);
|
|
update_context(pcp);
|
|
xmutex_unlock(pcp->driScreenPriv->mutex);
|
|
|
|
(*pcp->disp.viewport)(ctx, x, y, width, height);
|
|
}
|
|
|
|
static void new_list_callback(GLIContext ctx, GLuint list, GLenum mode)
|
|
{
|
|
WRAP_BOILERPLATE
|
|
|
|
unwrap_context(pcp);
|
|
(*pcp->ctx->disp.new_list)(ctx, list, mode);
|
|
wrap_context(pcp);
|
|
}
|
|
|
|
static void end_list_callback(GLIContext ctx)
|
|
{
|
|
WRAP_BOILERPLATE
|
|
|
|
unwrap_context(pcp);
|
|
(*pcp->ctx->disp.end_list)(ctx);
|
|
wrap_context(pcp);
|
|
}
|
|
|
|
static void unwrap_context(__DRIcontextPrivate *pcp)
|
|
{
|
|
UNWRAP_CGL(pcp, viewport);
|
|
UNWRAP_CGL(pcp, new_list);
|
|
UNWRAP_CGL(pcp, end_list);
|
|
}
|
|
|
|
static void wrap_context(__DRIcontextPrivate *pcp)
|
|
{
|
|
WRAP_CGL(pcp, new_list, new_list_callback);
|
|
WRAP_CGL(pcp, end_list, end_list_callback);
|
|
WRAP_CGL(pcp, viewport, viewport_callback);
|
|
}
|
|
|
|
#endif /* GLX_DIRECT_RENDERING */
|