xserver-multidpi/GL/glx/glxext.c
George Sapountzis f17e3c34df Fix GL context destruction with AIGLX.
The logic for freeing GL contexts introduced by "Fix AIGLX VT switching." is
inverted. As it is now, GL context destruction is deferred for glxDRIEnterVT().
2006-11-16 22:14:53 +02:00

561 lines
13 KiB
C

/*
** The contents of this file are subject to the GLX Public License Version 1.0
** (the "License"). You may not use this file except in compliance with the
** License. You may obtain a copy of the License at Silicon Graphics, Inc.,
** attn: Legal Services, 2011 N. Shoreline Blvd., Mountain View, CA 94043
** or at http://www.sgi.com/software/opensource/glx/license.html.
**
** Software distributed under the License is distributed on an "AS IS"
** basis. ALL WARRANTIES ARE DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY
** IMPLIED WARRANTIES OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR
** PURPOSE OR OF NON- INFRINGEMENT. See the License for the specific
** language governing rights and limitations under the License.
**
** The Original Software is GLX version 1.2 source code, released February,
** 1999. The developer of the Original Software is Silicon Graphics, Inc.
** Those portions of the Subject Software created by Silicon Graphics, Inc.
** are Copyright (c) 1991-9 Silicon Graphics, Inc. All Rights Reserved.
**
*/
#define NEED_REPLIES
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <string.h>
#include "glxserver.h"
#include <windowstr.h>
#include <propertyst.h>
#include <os.h>
#include "g_disptab.h"
#include "unpack.h"
#include "glxutil.h"
#include "glxext.h"
#include "indirect_table.h"
#include "indirect_util.h"
/*
** The last context used by the server. It is the context that is current
** from the server's perspective.
*/
__GLXcontext *__glXLastContext;
/*
** X resources.
*/
RESTYPE __glXContextRes;
RESTYPE __glXClientRes;
RESTYPE __glXPixmapRes;
RESTYPE __glXDrawableRes;
RESTYPE __glXSwapBarrierRes;
/*
** Reply for most singles.
*/
xGLXSingleReply __glXReply;
/*
** A set of state for each client. The 0th one is unused because client
** indices start at 1, not 0.
*/
static __GLXclientState *__glXClients[MAXCLIENTS + 1];
/*
** Forward declarations.
*/
static int __glXDispatch(ClientPtr);
/*
** Called when the extension is reset.
*/
static void ResetExtension(ExtensionEntry* extEntry)
{
__glXFlushContextCache();
__glXResetScreens();
}
/*
** Initialize the per-client context storage.
*/
static void ResetClientState(int clientIndex)
{
__GLXclientState *cl = __glXClients[clientIndex];
if (cl->returnBuf) xfree(cl->returnBuf);
if (cl->largeCmdBuf) xfree(cl->largeCmdBuf);
if (cl->currentContexts) xfree(cl->currentContexts);
memset(cl, 0, sizeof(__GLXclientState));
/*
** By default, assume that the client supports
** GLX major version 1 minor version 0 protocol.
*/
cl->GLClientmajorVersion = 1;
cl->GLClientminorVersion = 0;
if (cl->GLClientextensions)
xfree(cl->GLClientextensions);
}
/*
** Reset state used to keep track of large (multi-request) commands.
*/
void __glXResetLargeCommandStatus(__GLXclientState *cl)
{
cl->largeCmdBytesSoFar = 0;
cl->largeCmdBytesTotal = 0;
cl->largeCmdRequestsSoFar = 0;
cl->largeCmdRequestsTotal = 0;
}
/*
** This procedure is called when the client who created the context goes
** away OR when glXDestroyContext is called. In either case, all we do is
** flag that the ID is no longer valid, and (maybe) free the context.
** use.
*/
static int ContextGone(__GLXcontext* cx, XID id)
{
cx->idExists = GL_FALSE;
if (!cx->isCurrent) {
__glXFreeContext(cx);
}
return True;
}
/*
** Free a client's state.
*/
static int ClientGone(int clientIndex, XID id)
{
__GLXcontext *cx;
__GLXclientState *cl = __glXClients[clientIndex];
int i;
if (cl) {
/*
** Free all the contexts that are current for this client.
*/
for (i=0; i < cl->numCurrentContexts; i++) {
cx = cl->currentContexts[i];
if (cx) {
__glXDeassociateContext(cx);
cx->isCurrent = GL_FALSE;
if (!cx->idExists) {
__glXFreeContext(cx);
}
}
}
/*
** Re-initialize the client state structure. Don't free it because
** we'll probably get another client with this index and use the struct
** again. There is a maximum of MAXCLIENTS of these structures.
*/
ResetClientState(clientIndex);
}
return True;
}
/*
** Free a GLX Pixmap.
*/
static int PixmapGone(__GLXpixmap *pGlxPixmap, XID id)
{
PixmapPtr pPixmap = (PixmapPtr) pGlxPixmap->pDraw;
pGlxPixmap->idExists = False;
if (!pGlxPixmap->refcnt) {
if (pGlxPixmap->pDamage) {
DamageUnregister (pGlxPixmap->pDraw, pGlxPixmap->pDamage);
DamageDestroy(pGlxPixmap->pDamage);
}
/*
** The DestroyPixmap routine should decrement the refcount and free
** only if it's zero.
*/
(*pGlxPixmap->pScreen->DestroyPixmap)(pPixmap);
xfree(pGlxPixmap);
}
return True;
}
/*
** Destroy routine that gets called when a drawable is freed. A drawable
** contains the ancillary buffers needed for rendering.
*/
static Bool DrawableGone(__GLXdrawable *glxPriv, XID xid)
{
__GLXcontext *cx, *cx1;
/*
** Use glxPriv->type to figure out what kind of drawable this is. Don't
** use glxPriv->pDraw->type because by the time this routine is called,
** the pDraw might already have been freed.
*/
if (glxPriv->type == DRAWABLE_WINDOW) {
/*
** When a window is destroyed, notify all context bound to
** it, that there are no longer bound to anything.
*/
for (cx = glxPriv->drawGlxc; cx; cx = cx1) {
cx1 = cx->nextDrawPriv;
cx->pendingState |= __GLX_PENDING_DESTROY;
}
for (cx = glxPriv->readGlxc; cx; cx = cx1) {
cx1 = cx->nextReadPriv;
cx->pendingState |= __GLX_PENDING_DESTROY;
}
}
__glXUnrefDrawable(glxPriv);
return True;
}
static __GLXcontext *glxPendingDestroyContexts;
static int glxServerLeaveCount;
static int glxBlockClients;
/*
** Free a context.
*/
GLboolean __glXFreeContext(__GLXcontext *cx)
{
if (cx->idExists || cx->isCurrent) return GL_FALSE;
if (cx->feedbackBuf) xfree(cx->feedbackBuf);
if (cx->selectBuf) xfree(cx->selectBuf);
if (cx == __glXLastContext) {
__glXFlushContextCache();
}
/* We can get here through both regular dispatching from
* __glXDispatch() or as a callback from the resource manager. In
* the latter case we need to lift the DRI lock manually. */
if (!glxBlockClients) {
__glXleaveServer();
cx->destroy(cx);
__glXenterServer();
} else {
cx->next = glxPendingDestroyContexts;
glxPendingDestroyContexts = cx;
}
return GL_TRUE;
}
extern RESTYPE __glXSwapBarrierRes;
static int SwapBarrierGone(int screen, XID drawable)
{
if (__glXSwapBarrierFuncs &&
__glXSwapBarrierFuncs[screen].bindSwapBarrierFunc != NULL) {
__glXSwapBarrierFuncs[screen].bindSwapBarrierFunc(screen, drawable, 0);
}
FreeResourceByType(drawable, __glXSwapBarrierRes, FALSE);
return True;
}
/************************************************************************/
/*
** These routines can be used to check whether a particular GL command
** has caused an error. Specifically, we use them to check whether a
** given query has caused an error, in which case a zero-length data
** reply is sent to the client.
*/
static GLboolean errorOccured = GL_FALSE;
/*
** The GL was will call this routine if an error occurs.
*/
void __glXErrorCallBack(__GLinterface *gc, GLenum code)
{
errorOccured = GL_TRUE;
}
/*
** Clear the error flag before calling the GL command.
*/
void __glXClearErrorOccured(void)
{
errorOccured = GL_FALSE;
}
/*
** Check if the GL command caused an error.
*/
GLboolean __glXErrorOccured(void)
{
return errorOccured;
}
static int __glXErrorBase;
int __glXError(int error)
{
return __glXErrorBase + error;
}
/************************************************************************/
/*
** Initialize the GLX extension.
*/
void GlxExtensionInit(void)
{
ExtensionEntry *extEntry;
int i;
__glXContextRes = CreateNewResourceType((DeleteType)ContextGone);
__glXClientRes = CreateNewResourceType((DeleteType)ClientGone);
__glXPixmapRes = CreateNewResourceType((DeleteType)PixmapGone);
__glXDrawableRes = CreateNewResourceType((DeleteType)DrawableGone);
__glXSwapBarrierRes = CreateNewResourceType((DeleteType)SwapBarrierGone);
/*
** Add extension to server extensions.
*/
extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS,
__GLX_NUMBER_ERRORS, __glXDispatch,
__glXDispatch, ResetExtension,
StandardMinorOpcode);
if (!extEntry) {
FatalError("__glXExtensionInit: AddExtensions failed\n");
return;
}
if (!AddExtensionAlias(GLX_EXTENSION_ALIAS, extEntry)) {
ErrorF("__glXExtensionInit: AddExtensionAlias failed\n");
return;
}
__glXErrorBase = extEntry->errorBase;
/*
** Initialize table of client state. There is never a client 0.
*/
for (i = 1; i <= MAXCLIENTS; i++) {
__glXClients[i] = 0;
}
/*
** Initialize screen specific data.
*/
__glXInitScreens();
}
/************************************************************************/
void __glXFlushContextCache(void)
{
__glXLastContext = 0;
}
/*
** Make a context the current one for the GL (in this implementation, there
** is only one instance of the GL, and we use it to serve all GL clients by
** switching it between different contexts). While we are at it, look up
** a context by its tag and return its (__GLXcontext *).
*/
__GLXcontext *__glXForceCurrent(__GLXclientState *cl, GLXContextTag tag,
int *error)
{
__GLXcontext *cx;
/*
** See if the context tag is legal; it is managed by the extension,
** so if it's invalid, we have an implementation error.
*/
cx = (__GLXcontext *) __glXLookupContextByTag(cl, tag);
if (!cx) {
cl->client->errorValue = tag;
*error = __glXError(GLXBadContextTag);
return 0;
}
if (!cx->isDirect) {
if (cx->drawPriv == NULL) {
/*
** The drawable has vanished. It must be a window, because only
** windows can be destroyed from under us; GLX pixmaps are
** refcounted and don't go away until no one is using them.
*/
*error = __glXError(GLXBadCurrentWindow);
return 0;
}
}
if (cx == __glXLastContext) {
/* No need to re-bind */
return cx;
}
/* Make this context the current one for the GL. */
if (!cx->isDirect) {
if (!(*cx->forceCurrent)(cx)) {
/* Bind failed, and set the error code. Bummer */
cl->client->errorValue = cx->id;
*error = __glXError(GLXBadContextState);
return 0;
}
}
__glXLastContext = cx;
return cx;
}
/************************************************************************/
void glxSuspendClients(void)
{
int i;
for (i = 1; i <= MAXCLIENTS; i++) {
if (__glXClients[i] == NULL || !__glXClients[i]->inUse)
continue;
IgnoreClient(__glXClients[i]->client);
}
glxBlockClients = TRUE;
}
void glxResumeClients(void)
{
__GLXcontext *cx, *next;
int i;
glxBlockClients = FALSE;
for (i = 1; i <= MAXCLIENTS; i++) {
if (__glXClients[i] == NULL || !__glXClients[i]->inUse)
continue;
AttendClient(__glXClients[i]->client);
}
__glXleaveServer();
for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) {
next = cx->next;
cx->destroy(cx);
}
glxPendingDestroyContexts = NULL;
__glXenterServer();
}
static void
__glXnopEnterServer(void)
{
}
static void
__glXnopLeaveServer(void)
{
}
static void (*__glXenterServerFunc)(void) = __glXnopEnterServer;
static void (*__glXleaveServerFunc)(void) = __glXnopLeaveServer;
void __glXsetEnterLeaveServerFuncs(void (*enter)(void),
void (*leave)(void))
{
__glXenterServerFunc = enter;
__glXleaveServerFunc = leave;
}
void __glXenterServer(void)
{
glxServerLeaveCount--;
if (glxServerLeaveCount == 0)
(*__glXenterServerFunc)();
}
void __glXleaveServer(void)
{
if (glxServerLeaveCount == 0)
(*__glXleaveServerFunc)();
glxServerLeaveCount++;
}
/*
** Top level dispatcher; all commands are executed from here down.
*/
static int __glXDispatch(ClientPtr client)
{
REQUEST(xGLXSingleReq);
CARD8 opcode;
__GLXdispatchSingleProcPtr proc;
__GLXclientState *cl;
int retval;
opcode = stuff->glxCode;
cl = __glXClients[client->index];
if (!cl) {
cl = (__GLXclientState *) xalloc(sizeof(__GLXclientState));
__glXClients[client->index] = cl;
if (!cl) {
return BadAlloc;
}
memset(cl, 0, sizeof(__GLXclientState));
}
if (!cl->inUse) {
/*
** This is first request from this client. Associate a resource
** with the client so we will be notified when the client dies.
*/
XID xid = FakeClientID(client->index);
if (!AddResource( xid, __glXClientRes, (pointer)(long)client->index)) {
return BadAlloc;
}
ResetClientState(client->index);
cl->inUse = GL_TRUE;
cl->client = client;
}
/*
** If we're expecting a glXRenderLarge request, this better be one.
*/
if ((cl->largeCmdRequestsSoFar != 0) && (opcode != X_GLXRenderLarge)) {
client->errorValue = stuff->glxCode;
return __glXError(GLXBadLargeRequest);
}
/* If we're currently blocking GLX clients, just put this guy to
* sleep, reset the request and return. */
if (glxBlockClients) {
ResetCurrentRequest(client);
client->sequence--;
IgnoreClient(client);
return(client->noClientException);
}
/*
** Use the opcode to index into the procedure table.
*/
proc = (__GLXdispatchSingleProcPtr) __glXGetProtocolDecodeFunction(& Single_dispatch_info,
opcode,
client->swapped);
if (proc != NULL) {
__glXleaveServer();
retval = (*proc)(cl, (GLbyte *) stuff);
__glXenterServer();
}
else {
retval = BadRequest;
}
return retval;
}