glxext.c revision 5afdac23
1/* 2 * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) 3 * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice including the dates of first publication and 13 * either this permission notice or a reference to 14 * http://oss.sgi.com/projects/FreeB/ 15 * shall be included in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 22 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 * 25 * Except as contained in this notice, the name of Silicon Graphics, Inc. 26 * shall not be used in advertising or otherwise to promote the sale, use or 27 * other dealings in this Software without prior written authorization from 28 * Silicon Graphics, Inc. 29 */ 30 31#ifdef HAVE_DIX_CONFIG_H 32#include <dix-config.h> 33#endif 34 35#include <string.h> 36#include "glxserver.h" 37#include <windowstr.h> 38#include <propertyst.h> 39#include <registry.h> 40#include "privates.h" 41#include <os.h> 42#include "unpack.h" 43#include "glxutil.h" 44#include "glxext.h" 45#include "indirect_table.h" 46#include "indirect_util.h" 47 48/* 49** The last context used by the server. It is the context that is current 50** from the server's perspective. 51*/ 52__GLXcontext *__glXLastContext; 53__GLXcontext *__glXContextList; 54 55/* 56** X resources. 57*/ 58RESTYPE __glXContextRes; 59RESTYPE __glXDrawableRes; 60 61/* 62** Reply for most singles. 63*/ 64xGLXSingleReply __glXReply; 65 66static DevPrivateKeyRec glxClientPrivateKeyRec; 67#define glxClientPrivateKey (&glxClientPrivateKeyRec) 68 69/* 70** Client that called into GLX dispatch. 71*/ 72ClientPtr __pGlxClient; 73 74/* 75** Forward declarations. 76*/ 77static int __glXDispatch(ClientPtr); 78 79/* 80** Called when the extension is reset. 81*/ 82static void ResetExtension(ExtensionEntry* extEntry) 83{ 84 __glXFlushContextCache(); 85} 86 87/* 88** Reset state used to keep track of large (multi-request) commands. 89*/ 90void __glXResetLargeCommandStatus(__GLXclientState *cl) 91{ 92 cl->largeCmdBytesSoFar = 0; 93 cl->largeCmdBytesTotal = 0; 94 cl->largeCmdRequestsSoFar = 0; 95 cl->largeCmdRequestsTotal = 0; 96} 97 98/* 99** This procedure is called when the client who created the context goes 100** away OR when glXDestroyContext is called. In either case, all we do is 101** flag that the ID is no longer valid, and (maybe) free the context. 102** use. 103*/ 104static int ContextGone(__GLXcontext* cx, XID id) 105{ 106 cx->idExists = GL_FALSE; 107 if (!cx->isCurrent) { 108 __glXFreeContext(cx); 109 } 110 111 return True; 112} 113 114static __GLXcontext *glxPendingDestroyContexts; 115static __GLXcontext *glxAllContexts; 116static int glxServerLeaveCount; 117static int glxBlockClients; 118 119/* 120** Destroy routine that gets called when a drawable is freed. A drawable 121** contains the ancillary buffers needed for rendering. 122*/ 123static Bool DrawableGone(__GLXdrawable *glxPriv, XID xid) 124{ 125 __GLXcontext *c, *next; 126 127 if (glxPriv->type == GLX_DRAWABLE_WINDOW || glxPriv->type == GLX_DRAWABLE_PIXMAP) { 128 /* If this was created by glXCreateWindow, free the matching resource */ 129 if (glxPriv->otherId) { 130 XID other = glxPriv->otherId; 131 glxPriv->otherId = 0; 132 if (xid == other) 133 FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE); 134 else 135 FreeResourceByType(other, __glXDrawableRes, TRUE); 136 } 137 /* otherwise this window was implicitly created by MakeCurrent */ 138 } 139 140 for (c = glxAllContexts; c; c = next) { 141 next = c->next; 142 if (c->isCurrent && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) { 143 (*c->loseCurrent)(c); 144 c->isCurrent = GL_FALSE; 145 if (c == __glXLastContext) 146 __glXFlushContextCache(); 147 } 148 if (c->drawPriv == glxPriv) 149 c->drawPriv = NULL; 150 if (c->readPriv == glxPriv) 151 c->readPriv = NULL; 152 } 153 154 /* drop our reference to any backing pixmap */ 155 if (glxPriv->type == GLX_DRAWABLE_PIXMAP) 156 glxPriv->pDraw->pScreen->DestroyPixmap((PixmapPtr)glxPriv->pDraw); 157 158 glxPriv->destroy(glxPriv); 159 160 return True; 161} 162 163void __glXAddToContextList(__GLXcontext *cx) 164{ 165 cx->next = glxAllContexts; 166 glxAllContexts = cx; 167} 168 169static void __glXRemoveFromContextList(__GLXcontext *cx) 170{ 171 __GLXcontext *c, *prev; 172 173 if (cx == glxAllContexts) 174 glxAllContexts = cx->next; 175 else { 176 prev = glxAllContexts; 177 for (c = glxAllContexts; c; c = c->next) { 178 if (c == cx) 179 prev->next = c->next; 180 prev = c; 181 } 182 } 183} 184 185/* 186** Free a context. 187*/ 188GLboolean __glXFreeContext(__GLXcontext *cx) 189{ 190 if (cx->idExists || cx->isCurrent) return GL_FALSE; 191 192 free(cx->feedbackBuf); 193 free(cx->selectBuf); 194 if (cx == __glXLastContext) { 195 __glXFlushContextCache(); 196 } 197 198 __glXRemoveFromContextList(cx); 199 200 /* We can get here through both regular dispatching from 201 * __glXDispatch() or as a callback from the resource manager. In 202 * the latter case we need to lift the DRI lock manually. */ 203 204 if (!glxBlockClients) { 205 __glXleaveServer(GL_FALSE); 206 cx->destroy(cx); 207 __glXenterServer(GL_FALSE); 208 } else { 209 cx->next = glxPendingDestroyContexts; 210 glxPendingDestroyContexts = cx; 211 } 212 213 return GL_TRUE; 214} 215 216/************************************************************************/ 217 218/* 219** These routines can be used to check whether a particular GL command 220** has caused an error. Specifically, we use them to check whether a 221** given query has caused an error, in which case a zero-length data 222** reply is sent to the client. 223*/ 224 225static GLboolean errorOccured = GL_FALSE; 226 227/* 228** The GL was will call this routine if an error occurs. 229*/ 230void __glXErrorCallBack(GLenum code) 231{ 232 errorOccured = GL_TRUE; 233} 234 235/* 236** Clear the error flag before calling the GL command. 237*/ 238void __glXClearErrorOccured(void) 239{ 240 errorOccured = GL_FALSE; 241} 242 243/* 244** Check if the GL command caused an error. 245*/ 246GLboolean __glXErrorOccured(void) 247{ 248 return errorOccured; 249} 250 251static int __glXErrorBase; 252int __glXEventBase; 253 254int __glXError(int error) 255{ 256 return __glXErrorBase + error; 257} 258 259__GLXclientState * 260glxGetClient(ClientPtr pClient) 261{ 262 return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey); 263} 264 265static void 266glxClientCallback (CallbackListPtr *list, 267 pointer closure, 268 pointer data) 269{ 270 NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; 271 ClientPtr pClient = clientinfo->client; 272 __GLXclientState *cl = glxGetClient(pClient); 273 274 switch (pClient->clientState) { 275 case ClientStateRunning: 276 /* 277 ** By default, assume that the client supports 278 ** GLX major version 1 minor version 0 protocol. 279 */ 280 cl->GLClientmajorVersion = 1; 281 cl->GLClientminorVersion = 0; 282 cl->client = pClient; 283 break; 284 285 case ClientStateGone: 286 free(cl->returnBuf); 287 free(cl->largeCmdBuf); 288 free(cl->GLClientextensions); 289 break; 290 291 default: 292 break; 293 } 294} 295 296/************************************************************************/ 297 298static __GLXprovider *__glXProviderStack; 299 300void GlxPushProvider(__GLXprovider *provider) 301{ 302 provider->next = __glXProviderStack; 303 __glXProviderStack = provider; 304} 305 306/* 307** Initialize the GLX extension. 308*/ 309void GlxExtensionInit(void) 310{ 311 ExtensionEntry *extEntry; 312 ScreenPtr pScreen; 313 int i; 314 __GLXprovider *p; 315 Bool glx_provided = False; 316 317 __glXContextRes = CreateNewResourceType((DeleteType)ContextGone, 318 "GLXContext"); 319 __glXDrawableRes = CreateNewResourceType((DeleteType)DrawableGone, 320 "GLXDrawable"); 321 if (!__glXContextRes || !__glXDrawableRes) 322 return; 323 324 if (!dixRegisterPrivateKey(&glxClientPrivateKeyRec, PRIVATE_CLIENT, sizeof (__GLXclientState))) 325 return; 326 if (!AddCallback (&ClientStateCallback, glxClientCallback, 0)) 327 return; 328 329 for (i = 0; i < screenInfo.numScreens; i++) { 330 pScreen = screenInfo.screens[i]; 331 332 for (p = __glXProviderStack; p != NULL; p = p->next) { 333 __GLXscreen *glxScreen; 334 335 glxScreen = p->screenProbe(pScreen); 336 if (glxScreen != NULL) { 337 if (glxScreen->GLXminor < glxMinorVersion) 338 glxMinorVersion = glxScreen->GLXminor; 339 LogMessage(X_INFO, 340 "GLX: Initialized %s GL provider for screen %d\n", 341 p->name, i); 342 break; 343 } 344 345 } 346 347 if (!p) 348 LogMessage(X_INFO, 349 "GLX: no usable GL providers found for screen %d\n", i); 350 else 351 glx_provided = True; 352 } 353 354 /* don't register extension if GL is not provided on any screen */ 355 if (!glx_provided) 356 return; 357 358 /* 359 ** Add extension to server extensions. 360 */ 361 extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS, 362 __GLX_NUMBER_ERRORS, __glXDispatch, 363 __glXDispatch, ResetExtension, 364 StandardMinorOpcode); 365 if (!extEntry) { 366 FatalError("__glXExtensionInit: AddExtensions failed\n"); 367 return; 368 } 369 if (!AddExtensionAlias(GLX_EXTENSION_ALIAS, extEntry)) { 370 ErrorF("__glXExtensionInit: AddExtensionAlias failed\n"); 371 return; 372 } 373 374 __glXErrorBase = extEntry->errorBase; 375 __glXEventBase = extEntry->eventBase; 376} 377 378/************************************************************************/ 379 380void __glXFlushContextCache(void) 381{ 382 __glXLastContext = 0; 383} 384 385/* 386** Make a context the current one for the GL (in this implementation, there 387** is only one instance of the GL, and we use it to serve all GL clients by 388** switching it between different contexts). While we are at it, look up 389** a context by its tag and return its (__GLXcontext *). 390*/ 391__GLXcontext *__glXForceCurrent(__GLXclientState *cl, GLXContextTag tag, 392 int *error) 393{ 394 __GLXcontext *cx; 395 396 /* 397 ** See if the context tag is legal; it is managed by the extension, 398 ** so if it's invalid, we have an implementation error. 399 */ 400 cx = (__GLXcontext *) __glXLookupContextByTag(cl, tag); 401 if (!cx) { 402 cl->client->errorValue = tag; 403 *error = __glXError(GLXBadContextTag); 404 return 0; 405 } 406 407 if (!cx->isDirect) { 408 if (cx->drawPriv == NULL) { 409 /* 410 ** The drawable has vanished. It must be a window, because only 411 ** windows can be destroyed from under us; GLX pixmaps are 412 ** refcounted and don't go away until no one is using them. 413 */ 414 *error = __glXError(GLXBadCurrentWindow); 415 return 0; 416 } 417 } 418 419 if (cx->wait && (*cx->wait)(cx, cl, error)) 420 return NULL; 421 422 if (cx == __glXLastContext) { 423 /* No need to re-bind */ 424 return cx; 425 } 426 427 /* Make this context the current one for the GL. */ 428 if (!cx->isDirect) { 429 if (!(*cx->forceCurrent)(cx)) { 430 /* Bind failed, and set the error code. Bummer */ 431 cl->client->errorValue = cx->id; 432 *error = __glXError(GLXBadContextState); 433 return 0; 434 } 435 } 436 __glXLastContext = cx; 437 return cx; 438} 439 440/************************************************************************/ 441 442void glxSuspendClients(void) 443{ 444 int i; 445 446 for (i = 1; i < currentMaxClients; i++) { 447 if (clients[i] && glxGetClient(clients[i])->inUse) 448 IgnoreClient(clients[i]); 449 } 450 451 glxBlockClients = TRUE; 452} 453 454void glxResumeClients(void) 455{ 456 __GLXcontext *cx, *next; 457 int i; 458 459 glxBlockClients = FALSE; 460 461 for (i = 1; i < currentMaxClients; i++) { 462 if (clients[i] && glxGetClient(clients[i])->inUse) 463 AttendClient(clients[i]); 464 } 465 466 __glXleaveServer(GL_FALSE); 467 for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) { 468 next = cx->next; 469 470 cx->destroy(cx); 471 } 472 glxPendingDestroyContexts = NULL; 473 __glXenterServer(GL_FALSE); 474} 475 476static void 477__glXnopEnterServer(GLboolean rendering) 478{ 479} 480 481static void 482__glXnopLeaveServer(GLboolean rendering) 483{ 484} 485 486static void (*__glXenterServerFunc)(GLboolean) = __glXnopEnterServer; 487static void (*__glXleaveServerFunc)(GLboolean) = __glXnopLeaveServer; 488 489void __glXsetEnterLeaveServerFuncs(void (*enter)(GLboolean), 490 void (*leave)(GLboolean)) 491{ 492 __glXenterServerFunc = enter; 493 __glXleaveServerFunc = leave; 494} 495 496 497void __glXenterServer(GLboolean rendering) 498{ 499 glxServerLeaveCount--; 500 501 if (glxServerLeaveCount == 0) 502 (*__glXenterServerFunc)(rendering); 503} 504 505void __glXleaveServer(GLboolean rendering) 506{ 507 if (glxServerLeaveCount == 0) 508 (*__glXleaveServerFunc)(rendering); 509 510 glxServerLeaveCount++; 511} 512 513/* 514** Top level dispatcher; all commands are executed from here down. 515*/ 516static int __glXDispatch(ClientPtr client) 517{ 518 REQUEST(xGLXSingleReq); 519 CARD8 opcode; 520 __GLXdispatchSingleProcPtr proc; 521 __GLXclientState *cl; 522 int retval; 523 524 opcode = stuff->glxCode; 525 cl = glxGetClient(client); 526 /* Mark it in use so we suspend it on VT switch. */ 527 cl->inUse = TRUE; 528 529 /* 530 ** If we're expecting a glXRenderLarge request, this better be one. 531 */ 532 if ((cl->largeCmdRequestsSoFar != 0) && (opcode != X_GLXRenderLarge)) { 533 client->errorValue = stuff->glxCode; 534 return __glXError(GLXBadLargeRequest); 535 } 536 537 /* If we're currently blocking GLX clients, just put this guy to 538 * sleep, reset the request and return. */ 539 if (glxBlockClients) { 540 ResetCurrentRequest(client); 541 client->sequence--; 542 IgnoreClient(client); 543 return Success; 544 } 545 546 /* 547 ** Use the opcode to index into the procedure table. 548 */ 549 proc = (__GLXdispatchSingleProcPtr) __glXGetProtocolDecodeFunction(& Single_dispatch_info, 550 opcode, 551 client->swapped); 552 if (proc != NULL) { 553 GLboolean rendering = opcode <= X_GLXRenderLarge; 554 __glXleaveServer(rendering); 555 556 __pGlxClient = client; 557 558 retval = (*proc)(cl, (GLbyte *) stuff); 559 560 __glXenterServer(rendering); 561 } 562 else { 563 retval = BadRequest; 564 } 565 566 return retval; 567} 568