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