glxext.c revision 6747b715
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 "g_disptab.h" 43#include "unpack.h" 44#include "glxutil.h" 45#include "glxext.h" 46#include "indirect_table.h" 47#include "indirect_util.h" 48 49/* 50** The last context used by the server. It is the context that is current 51** from the server's perspective. 52*/ 53__GLXcontext *__glXLastContext; 54__GLXcontext *__glXContextList; 55 56/* 57** X resources. 58*/ 59RESTYPE __glXContextRes; 60RESTYPE __glXDrawableRes; 61RESTYPE __glXSwapBarrierRes; 62 63/* 64** Reply for most singles. 65*/ 66xGLXSingleReply __glXReply; 67 68static DevPrivateKeyRec glxClientPrivateKeyRec; 69#define glxClientPrivateKey (&glxClientPrivateKeyRec) 70 71/* 72** Client that called into GLX dispatch. 73*/ 74ClientPtr __pGlxClient; 75 76/* 77** Forward declarations. 78*/ 79static int __glXDispatch(ClientPtr); 80 81/* 82** Called when the extension is reset. 83*/ 84static void ResetExtension(ExtensionEntry* extEntry) 85{ 86 __glXFlushContextCache(); 87} 88 89/* 90** Reset state used to keep track of large (multi-request) commands. 91*/ 92void __glXResetLargeCommandStatus(__GLXclientState *cl) 93{ 94 cl->largeCmdBytesSoFar = 0; 95 cl->largeCmdBytesTotal = 0; 96 cl->largeCmdRequestsSoFar = 0; 97 cl->largeCmdRequestsTotal = 0; 98} 99 100/* 101** This procedure is called when the client who created the context goes 102** away OR when glXDestroyContext is called. In either case, all we do is 103** flag that the ID is no longer valid, and (maybe) free the context. 104** use. 105*/ 106static int ContextGone(__GLXcontext* cx, XID id) 107{ 108 cx->idExists = GL_FALSE; 109 if (!cx->isCurrent) { 110 __glXFreeContext(cx); 111 } 112 113 return True; 114} 115 116static __GLXcontext *glxPendingDestroyContexts; 117static __GLXcontext *glxAllContexts; 118static int glxServerLeaveCount; 119static int glxBlockClients; 120 121/* 122** Destroy routine that gets called when a drawable is freed. A drawable 123** contains the ancillary buffers needed for rendering. 124*/ 125static Bool DrawableGone(__GLXdrawable *glxPriv, XID xid) 126{ 127 __GLXcontext *c, *next; 128 129 /* If this drawable was created using glx 1.3 drawable 130 * constructors, we added it as a glx drawable resource under both 131 * its glx drawable ID and it X drawable ID. Remove the other 132 * resource now so we don't a callback for freed memory. */ 133 if (glxPriv->drawId != glxPriv->pDraw->id) { 134 if (xid == glxPriv->drawId) 135 FreeResourceByType(glxPriv->pDraw->id, __glXDrawableRes, TRUE); 136 else 137 FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE); 138 } 139 140 for (c = glxAllContexts; c; c = next) { 141 next = c->next; 142 if (c->isCurrent && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) { 143 int i; 144 145 (*c->loseCurrent)(c); 146 c->isCurrent = GL_FALSE; 147 if (c == __glXLastContext) 148 __glXFlushContextCache(); 149 150 for (i = 1; i < currentMaxClients; i++) { 151 if (clients[i]) { 152 __GLXclientState *cl = glxGetClient(clients[i]); 153 154 if (cl->inUse) { 155 int j; 156 157 for (j = 0; j < cl->numCurrentContexts; j++) { 158 if (cl->currentContexts[j] == c) 159 cl->currentContexts[j] = NULL; 160 } 161 } 162 } 163 } 164 } 165 if (c->drawPriv == glxPriv) 166 c->drawPriv = NULL; 167 if (c->readPriv == glxPriv) 168 c->readPriv = NULL; 169 if (!c->idExists && !c->isCurrent) 170 __glXFreeContext(c); 171 } 172 173 glxPriv->destroy(glxPriv); 174 175 return True; 176} 177 178void __glXAddToContextList(__GLXcontext *cx) 179{ 180 cx->next = glxAllContexts; 181 glxAllContexts = cx; 182} 183 184static void __glXRemoveFromContextList(__GLXcontext *cx) 185{ 186 __GLXcontext *c, *prev; 187 188 if (cx == glxAllContexts) 189 glxAllContexts = cx->next; 190 else { 191 prev = glxAllContexts; 192 for (c = glxAllContexts; c; c = c->next) { 193 if (c == cx) 194 prev->next = c->next; 195 prev = c; 196 } 197 } 198} 199 200/* 201** Free a context. 202*/ 203GLboolean __glXFreeContext(__GLXcontext *cx) 204{ 205 if (cx->idExists || cx->isCurrent) return GL_FALSE; 206 207 free(cx->feedbackBuf); 208 free(cx->selectBuf); 209 if (cx == __glXLastContext) { 210 __glXFlushContextCache(); 211 } 212 213 __glXRemoveFromContextList(cx); 214 215 /* We can get here through both regular dispatching from 216 * __glXDispatch() or as a callback from the resource manager. In 217 * the latter case we need to lift the DRI lock manually. */ 218 219 if (!glxBlockClients) { 220 __glXleaveServer(GL_FALSE); 221 cx->destroy(cx); 222 __glXenterServer(GL_FALSE); 223 } else { 224 cx->next = glxPendingDestroyContexts; 225 glxPendingDestroyContexts = cx; 226 } 227 228 return GL_TRUE; 229} 230 231extern RESTYPE __glXSwapBarrierRes; 232 233static int SwapBarrierGone(int screen, XID drawable) 234{ 235 __GLXscreen *pGlxScreen = glxGetScreen(screenInfo.screens[screen]); 236 237 if (pGlxScreen->swapBarrierFuncs) { 238 pGlxScreen->swapBarrierFuncs->bindSwapBarrierFunc(screen, drawable, 0); 239 } 240 FreeResourceByType(drawable, __glXSwapBarrierRes, FALSE); 241 return True; 242} 243 244/************************************************************************/ 245 246/* 247** These routines can be used to check whether a particular GL command 248** has caused an error. Specifically, we use them to check whether a 249** given query has caused an error, in which case a zero-length data 250** reply is sent to the client. 251*/ 252 253static GLboolean errorOccured = GL_FALSE; 254 255/* 256** The GL was will call this routine if an error occurs. 257*/ 258void __glXErrorCallBack(GLenum code) 259{ 260 errorOccured = GL_TRUE; 261} 262 263/* 264** Clear the error flag before calling the GL command. 265*/ 266void __glXClearErrorOccured(void) 267{ 268 errorOccured = GL_FALSE; 269} 270 271/* 272** Check if the GL command caused an error. 273*/ 274GLboolean __glXErrorOccured(void) 275{ 276 return errorOccured; 277} 278 279static int __glXErrorBase; 280int __glXEventBase; 281 282int __glXError(int error) 283{ 284 return __glXErrorBase + error; 285} 286 287__GLXclientState * 288glxGetClient(ClientPtr pClient) 289{ 290 return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey); 291} 292 293static void 294glxClientCallback (CallbackListPtr *list, 295 pointer closure, 296 pointer data) 297{ 298 NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; 299 ClientPtr pClient = clientinfo->client; 300 __GLXclientState *cl = glxGetClient(pClient); 301 __GLXcontext *cx; 302 int i; 303 304 switch (pClient->clientState) { 305 case ClientStateRunning: 306 /* 307 ** By default, assume that the client supports 308 ** GLX major version 1 minor version 0 protocol. 309 */ 310 cl->GLClientmajorVersion = 1; 311 cl->GLClientminorVersion = 0; 312 cl->client = pClient; 313 break; 314 315 case ClientStateGone: 316 for (i = 0; i < cl->numCurrentContexts; i++) { 317 cx = cl->currentContexts[i]; 318 if (cx) { 319 cx->isCurrent = GL_FALSE; 320 if (!cx->idExists) 321 __glXFreeContext(cx); 322 } 323 } 324 325 free(cl->returnBuf); 326 free(cl->largeCmdBuf); 327 free(cl->currentContexts); 328 free(cl->GLClientextensions); 329 break; 330 331 default: 332 break; 333 } 334} 335 336/************************************************************************/ 337 338static __GLXprovider *__glXProviderStack; 339 340void GlxPushProvider(__GLXprovider *provider) 341{ 342 provider->next = __glXProviderStack; 343 __glXProviderStack = provider; 344} 345 346/* 347** Initialize the GLX extension. 348*/ 349void GlxExtensionInit(void) 350{ 351 ExtensionEntry *extEntry; 352 ScreenPtr pScreen; 353 int i; 354 __GLXprovider *p; 355 Bool glx_provided = False; 356 357 __glXContextRes = CreateNewResourceType((DeleteType)ContextGone, 358 "GLXContext"); 359 __glXDrawableRes = CreateNewResourceType((DeleteType)DrawableGone, 360 "GLXDrawable"); 361 __glXSwapBarrierRes = CreateNewResourceType((DeleteType)SwapBarrierGone, 362 "GLXSwapBarrier"); 363 if (!__glXContextRes || !__glXDrawableRes || !__glXSwapBarrierRes) 364 return; 365 366 if (!dixRegisterPrivateKey(&glxClientPrivateKeyRec, PRIVATE_CLIENT, sizeof (__GLXclientState))) 367 return; 368 if (!AddCallback (&ClientStateCallback, glxClientCallback, 0)) 369 return; 370 371 for (i = 0; i < screenInfo.numScreens; i++) { 372 pScreen = screenInfo.screens[i]; 373 374 for (p = __glXProviderStack; p != NULL; p = p->next) { 375 __GLXscreen *glxScreen; 376 377 glxScreen = p->screenProbe(pScreen); 378 if (glxScreen != NULL) { 379 if (glxScreen->GLXminor < glxMinorVersion) 380 glxMinorVersion = glxScreen->GLXminor; 381 LogMessage(X_INFO, 382 "GLX: Initialized %s GL provider for screen %d\n", 383 p->name, i); 384 break; 385 } 386 387 } 388 389 if (!p) 390 LogMessage(X_INFO, 391 "GLX: no usable GL providers found for screen %d\n", i); 392 else 393 glx_provided = True; 394 } 395 396 /* don't register extension if GL is not provided on any screen */ 397 if (!glx_provided) 398 return; 399 400 /* 401 ** Add extension to server extensions. 402 */ 403 extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS, 404 __GLX_NUMBER_ERRORS, __glXDispatch, 405 __glXDispatch, ResetExtension, 406 StandardMinorOpcode); 407 if (!extEntry) { 408 FatalError("__glXExtensionInit: AddExtensions failed\n"); 409 return; 410 } 411 if (!AddExtensionAlias(GLX_EXTENSION_ALIAS, extEntry)) { 412 ErrorF("__glXExtensionInit: AddExtensionAlias failed\n"); 413 return; 414 } 415 416 __glXErrorBase = extEntry->errorBase; 417 __glXEventBase = extEntry->eventBase; 418} 419 420/************************************************************************/ 421 422void __glXFlushContextCache(void) 423{ 424 __glXLastContext = 0; 425} 426 427/* 428** Make a context the current one for the GL (in this implementation, there 429** is only one instance of the GL, and we use it to serve all GL clients by 430** switching it between different contexts). While we are at it, look up 431** a context by its tag and return its (__GLXcontext *). 432*/ 433__GLXcontext *__glXForceCurrent(__GLXclientState *cl, GLXContextTag tag, 434 int *error) 435{ 436 __GLXcontext *cx; 437 438 /* 439 ** See if the context tag is legal; it is managed by the extension, 440 ** so if it's invalid, we have an implementation error. 441 */ 442 cx = (__GLXcontext *) __glXLookupContextByTag(cl, tag); 443 if (!cx) { 444 cl->client->errorValue = tag; 445 *error = __glXError(GLXBadContextTag); 446 return 0; 447 } 448 449 if (!cx->isDirect) { 450 if (cx->drawPriv == NULL) { 451 /* 452 ** The drawable has vanished. It must be a window, because only 453 ** windows can be destroyed from under us; GLX pixmaps are 454 ** refcounted and don't go away until no one is using them. 455 */ 456 *error = __glXError(GLXBadCurrentWindow); 457 return 0; 458 } 459 } 460 461 if (cx->wait && (*cx->wait)(cx, cl, error)) 462 return NULL; 463 464 if (cx == __glXLastContext) { 465 /* No need to re-bind */ 466 return cx; 467 } 468 469 /* Make this context the current one for the GL. */ 470 if (!cx->isDirect) { 471 if (!(*cx->forceCurrent)(cx)) { 472 /* Bind failed, and set the error code. Bummer */ 473 cl->client->errorValue = cx->id; 474 *error = __glXError(GLXBadContextState); 475 return 0; 476 } 477 } 478 __glXLastContext = cx; 479 return cx; 480} 481 482/************************************************************************/ 483 484void glxSuspendClients(void) 485{ 486 int i; 487 488 for (i = 1; i < currentMaxClients; i++) { 489 if (clients[i] && glxGetClient(clients[i])->inUse) 490 IgnoreClient(clients[i]); 491 } 492 493 glxBlockClients = TRUE; 494} 495 496void glxResumeClients(void) 497{ 498 __GLXcontext *cx, *next; 499 int i; 500 501 glxBlockClients = FALSE; 502 503 for (i = 1; i < currentMaxClients; i++) { 504 if (clients[i] && glxGetClient(clients[i])->inUse) 505 AttendClient(clients[i]); 506 } 507 508 __glXleaveServer(GL_FALSE); 509 for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) { 510 next = cx->next; 511 512 cx->destroy(cx); 513 } 514 glxPendingDestroyContexts = NULL; 515 __glXenterServer(GL_FALSE); 516} 517 518static void 519__glXnopEnterServer(GLboolean rendering) 520{ 521} 522 523static void 524__glXnopLeaveServer(GLboolean rendering) 525{ 526} 527 528static void (*__glXenterServerFunc)(GLboolean) = __glXnopEnterServer; 529static void (*__glXleaveServerFunc)(GLboolean) = __glXnopLeaveServer; 530 531void __glXsetEnterLeaveServerFuncs(void (*enter)(GLboolean), 532 void (*leave)(GLboolean)) 533{ 534 __glXenterServerFunc = enter; 535 __glXleaveServerFunc = leave; 536} 537 538 539void __glXenterServer(GLboolean rendering) 540{ 541 glxServerLeaveCount--; 542 543 if (glxServerLeaveCount == 0) 544 (*__glXenterServerFunc)(rendering); 545} 546 547void __glXleaveServer(GLboolean rendering) 548{ 549 if (glxServerLeaveCount == 0) 550 (*__glXleaveServerFunc)(rendering); 551 552 glxServerLeaveCount++; 553} 554 555/* 556** Top level dispatcher; all commands are executed from here down. 557*/ 558static int __glXDispatch(ClientPtr client) 559{ 560 REQUEST(xGLXSingleReq); 561 CARD8 opcode; 562 __GLXdispatchSingleProcPtr proc; 563 __GLXclientState *cl; 564 int retval; 565 566 opcode = stuff->glxCode; 567 cl = glxGetClient(client); 568 /* Mark it in use so we suspend it on VT switch. */ 569 cl->inUse = TRUE; 570 571 /* 572 ** If we're expecting a glXRenderLarge request, this better be one. 573 */ 574 if ((cl->largeCmdRequestsSoFar != 0) && (opcode != X_GLXRenderLarge)) { 575 client->errorValue = stuff->glxCode; 576 return __glXError(GLXBadLargeRequest); 577 } 578 579 /* If we're currently blocking GLX clients, just put this guy to 580 * sleep, reset the request and return. */ 581 if (glxBlockClients) { 582 ResetCurrentRequest(client); 583 client->sequence--; 584 IgnoreClient(client); 585 return Success; 586 } 587 588 /* 589 ** Use the opcode to index into the procedure table. 590 */ 591 proc = (__GLXdispatchSingleProcPtr) __glXGetProtocolDecodeFunction(& Single_dispatch_info, 592 opcode, 593 client->swapped); 594 if (proc != NULL) { 595 GLboolean rendering = opcode <= X_GLXRenderLarge; 596 __glXleaveServer(rendering); 597 598 __pGlxClient = client; 599 600 retval = (*proc)(cl, (GLbyte *) stuff); 601 602 __glXenterServer(rendering); 603 } 604 else { 605 retval = BadRequest; 606 } 607 608 return retval; 609} 610