glxext.c (20195B)
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 "extinit.h" 43 #include "glx_extinit.h" 44 #include "unpack.h" 45 #include "glxutil.h" 46 #include "glxext.h" 47 #include "indirect_table.h" 48 #include "indirect_util.h" 49 #include "glxvndabi.h" 50 51 /* 52 ** X resources. 53 */ 54 static int glxGeneration; 55 RESTYPE __glXContextRes; 56 RESTYPE __glXDrawableRes; 57 58 static DevPrivateKeyRec glxClientPrivateKeyRec; 59 static GlxServerVendor *glvnd_vendor = NULL; 60 61 #define glxClientPrivateKey (&glxClientPrivateKeyRec) 62 63 /* 64 ** Forward declarations. 65 */ 66 static int __glXDispatch(ClientPtr); 67 static GLboolean __glXFreeContext(__GLXcontext * cx); 68 69 /* 70 * This procedure is called when the client who created the context goes away 71 * OR when glXDestroyContext is called. If the context is current for a client 72 * the dispatch layer will have moved the context struct to a fake resource ID 73 * and cx here will be NULL. Otherwise we really free the context. 74 */ 75 static int 76 ContextGone(__GLXcontext * cx, XID id) 77 { 78 if (!cx) 79 return TRUE; 80 81 if (!cx->currentClient) 82 __glXFreeContext(cx); 83 84 return TRUE; 85 } 86 87 static __GLXcontext *glxPendingDestroyContexts; 88 static __GLXcontext *glxAllContexts; 89 static int glxBlockClients; 90 91 /* 92 ** Destroy routine that gets called when a drawable is freed. A drawable 93 ** contains the ancillary buffers needed for rendering. 94 */ 95 static Bool 96 DrawableGone(__GLXdrawable * glxPriv, XID xid) 97 { 98 __GLXcontext *c, *next; 99 100 if (glxPriv->type == GLX_DRAWABLE_WINDOW) { 101 /* If this was created by glXCreateWindow, free the matching resource */ 102 if (glxPriv->drawId != glxPriv->pDraw->id) { 103 if (xid == glxPriv->drawId) 104 FreeResourceByType(glxPriv->pDraw->id, __glXDrawableRes, TRUE); 105 else 106 FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE); 107 } 108 /* otherwise this window was implicitly created by MakeCurrent */ 109 } 110 111 for (c = glxAllContexts; c; c = next) { 112 next = c->next; 113 if (c->currentClient && 114 (c->drawPriv == glxPriv || c->readPriv == glxPriv)) { 115 /* flush the context */ 116 glFlush(); 117 /* just force a re-bind the next time through */ 118 (*c->loseCurrent) (c); 119 lastGLContext = NULL; 120 } 121 if (c->drawPriv == glxPriv) 122 c->drawPriv = NULL; 123 if (c->readPriv == glxPriv) 124 c->readPriv = NULL; 125 } 126 127 /* drop our reference to any backing pixmap */ 128 if (glxPriv->type == GLX_DRAWABLE_PIXMAP) 129 glxPriv->pDraw->pScreen->DestroyPixmap((PixmapPtr) glxPriv->pDraw); 130 131 glxPriv->destroy(glxPriv); 132 133 return TRUE; 134 } 135 136 Bool 137 __glXAddContext(__GLXcontext * cx) 138 { 139 /* Register this context as a resource. 140 */ 141 if (!AddResource(cx->id, __glXContextRes, (void *)cx)) { 142 return FALSE; 143 } 144 145 cx->next = glxAllContexts; 146 glxAllContexts = cx; 147 return TRUE; 148 } 149 150 static void 151 __glXRemoveFromContextList(__GLXcontext * cx) 152 { 153 __GLXcontext *c, *prev; 154 155 if (cx == glxAllContexts) 156 glxAllContexts = cx->next; 157 else { 158 prev = glxAllContexts; 159 for (c = glxAllContexts; c; c = c->next) { 160 if (c == cx) 161 prev->next = c->next; 162 prev = c; 163 } 164 } 165 } 166 167 /* 168 ** Free a context. 169 */ 170 static GLboolean 171 __glXFreeContext(__GLXcontext * cx) 172 { 173 if (cx->idExists || cx->currentClient) 174 return GL_FALSE; 175 176 __glXRemoveFromContextList(cx); 177 178 free(cx->feedbackBuf); 179 free(cx->selectBuf); 180 free(cx->largeCmdBuf); 181 if (cx == lastGLContext) { 182 lastGLContext = NULL; 183 } 184 185 /* We can get here through both regular dispatching from 186 * __glXDispatch() or as a callback from the resource manager. In 187 * the latter case we need to lift the DRI lock manually. */ 188 189 if (!glxBlockClients) { 190 cx->destroy(cx); 191 } 192 else { 193 cx->next = glxPendingDestroyContexts; 194 glxPendingDestroyContexts = cx; 195 } 196 197 return GL_TRUE; 198 } 199 200 /************************************************************************/ 201 202 /* 203 ** These routines can be used to check whether a particular GL command 204 ** has caused an error. Specifically, we use them to check whether a 205 ** given query has caused an error, in which case a zero-length data 206 ** reply is sent to the client. 207 */ 208 209 static GLboolean errorOccured = GL_FALSE; 210 211 /* 212 ** The GL was will call this routine if an error occurs. 213 */ 214 void 215 __glXErrorCallBack(GLenum code) 216 { 217 errorOccured = GL_TRUE; 218 } 219 220 /* 221 ** Clear the error flag before calling the GL command. 222 */ 223 void 224 __glXClearErrorOccured(void) 225 { 226 errorOccured = GL_FALSE; 227 } 228 229 /* 230 ** Check if the GL command caused an error. 231 */ 232 GLboolean 233 __glXErrorOccured(void) 234 { 235 return errorOccured; 236 } 237 238 static int __glXErrorBase; 239 int __glXEventBase; 240 241 int 242 __glXError(int error) 243 { 244 return __glXErrorBase + error; 245 } 246 247 __GLXclientState * 248 glxGetClient(ClientPtr pClient) 249 { 250 return dixLookupPrivate(&pClient->devPrivates, glxClientPrivateKey); 251 } 252 253 static void 254 glxClientCallback(CallbackListPtr *list, void *closure, void *data) 255 { 256 NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; 257 ClientPtr pClient = clientinfo->client; 258 __GLXclientState *cl = glxGetClient(pClient); 259 260 switch (pClient->clientState) { 261 case ClientStateGone: 262 free(cl->returnBuf); 263 free(cl->GLClientextensions); 264 cl->returnBuf = NULL; 265 cl->GLClientextensions = NULL; 266 break; 267 268 default: 269 break; 270 } 271 } 272 273 /************************************************************************/ 274 275 static __GLXprovider *__glXProviderStack = &__glXDRISWRastProvider; 276 277 void 278 GlxPushProvider(__GLXprovider * provider) 279 { 280 provider->next = __glXProviderStack; 281 __glXProviderStack = provider; 282 } 283 284 static Bool 285 checkScreenVisuals(void) 286 { 287 int i, j; 288 289 for (i = 0; i < screenInfo.numScreens; i++) { 290 ScreenPtr screen = screenInfo.screens[i]; 291 for (j = 0; j < screen->numVisuals; j++) { 292 if ((screen->visuals[j].class == TrueColor || 293 screen->visuals[j].class == DirectColor) && 294 screen->visuals[j].nplanes > 12) 295 return TRUE; 296 } 297 } 298 299 return FALSE; 300 } 301 302 static void 303 GetGLXDrawableBytes(void *value, XID id, ResourceSizePtr size) 304 { 305 __GLXdrawable *draw = value; 306 307 size->resourceSize = 0; 308 size->pixmapRefSize = 0; 309 size->refCnt = 1; 310 311 if (draw->type == GLX_DRAWABLE_PIXMAP) { 312 SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP); 313 ResourceSizeRec pixmapSize = { 0, }; 314 pixmapSizeFunc((PixmapPtr)draw->pDraw, draw->pDraw->id, &pixmapSize); 315 size->pixmapRefSize += pixmapSize.pixmapRefSize; 316 } 317 } 318 319 static void 320 xorgGlxCloseExtension(const ExtensionEntry *extEntry) 321 { 322 if (glvnd_vendor != NULL) { 323 glxServer.destroyVendor(glvnd_vendor); 324 glvnd_vendor = NULL; 325 } 326 lastGLContext = NULL; 327 } 328 329 static int 330 xorgGlxHandleRequest(ClientPtr client) 331 { 332 return __glXDispatch(client); 333 } 334 335 static ScreenPtr 336 screenNumToScreen(int screen) 337 { 338 if (screen < 0 || screen >= screenInfo.numScreens) 339 return NULL; 340 341 return screenInfo.screens[screen]; 342 } 343 344 static int 345 maybe_swap32(ClientPtr client, int x) 346 { 347 return client->swapped ? bswap_32(x) : x; 348 } 349 350 static GlxServerVendor * 351 vendorForScreen(ClientPtr client, int screen) 352 { 353 screen = maybe_swap32(client, screen); 354 355 return glxServer.getVendorForScreen(client, screenNumToScreen(screen)); 356 } 357 358 /* this ought to be generated */ 359 static int 360 xorgGlxThunkRequest(ClientPtr client) 361 { 362 REQUEST(xGLXVendorPrivateReq); 363 CARD32 vendorCode = maybe_swap32(client, stuff->vendorCode); 364 GlxServerVendor *vendor = NULL; 365 XID resource = 0; 366 int ret; 367 368 switch (vendorCode) { 369 case X_GLXvop_QueryContextInfoEXT: { 370 xGLXQueryContextInfoEXTReq *req = (void *)stuff; 371 REQUEST_AT_LEAST_SIZE(*req); 372 if (!(vendor = glxServer.getXIDMap(maybe_swap32(client, req->context)))) 373 return __glXError(GLXBadContext); 374 break; 375 } 376 377 case X_GLXvop_GetFBConfigsSGIX: { 378 xGLXGetFBConfigsSGIXReq *req = (void *)stuff; 379 REQUEST_AT_LEAST_SIZE(*req); 380 if (!(vendor = vendorForScreen(client, req->screen))) 381 return BadValue; 382 break; 383 } 384 385 case X_GLXvop_CreateContextWithConfigSGIX: { 386 xGLXCreateContextWithConfigSGIXReq *req = (void *)stuff; 387 REQUEST_AT_LEAST_SIZE(*req); 388 resource = maybe_swap32(client, req->context); 389 if (!(vendor = vendorForScreen(client, req->screen))) 390 return BadValue; 391 break; 392 } 393 394 case X_GLXvop_CreateGLXPixmapWithConfigSGIX: { 395 xGLXCreateGLXPixmapWithConfigSGIXReq *req = (void *)stuff; 396 REQUEST_AT_LEAST_SIZE(*req); 397 resource = maybe_swap32(client, req->glxpixmap); 398 if (!(vendor = vendorForScreen(client, req->screen))) 399 return BadValue; 400 break; 401 } 402 403 case X_GLXvop_CreateGLXPbufferSGIX: { 404 xGLXCreateGLXPbufferSGIXReq *req = (void *)stuff; 405 REQUEST_AT_LEAST_SIZE(*req); 406 resource = maybe_swap32(client, req->pbuffer); 407 if (!(vendor = vendorForScreen(client, req->screen))) 408 return BadValue; 409 break; 410 } 411 412 /* same offset for the drawable for these three */ 413 case X_GLXvop_DestroyGLXPbufferSGIX: 414 case X_GLXvop_ChangeDrawableAttributesSGIX: 415 case X_GLXvop_GetDrawableAttributesSGIX: { 416 xGLXGetDrawableAttributesSGIXReq *req = (void *)stuff; 417 REQUEST_AT_LEAST_SIZE(*req); 418 if (!(vendor = glxServer.getXIDMap(maybe_swap32(client, 419 req->drawable)))) 420 return __glXError(GLXBadDrawable); 421 break; 422 } 423 424 /* most things just use the standard context tag */ 425 default: { 426 /* size checked by vnd layer already */ 427 GLXContextTag tag = maybe_swap32(client, stuff->contextTag); 428 vendor = glxServer.getContextTag(client, tag); 429 if (!vendor) 430 return __glXError(GLXBadContextTag); 431 break; 432 } 433 } 434 435 /* If we're creating a resource, add the map now */ 436 if (resource) { 437 LEGAL_NEW_RESOURCE(resource, client); 438 if (!glxServer.addXIDMap(resource, vendor)) 439 return BadAlloc; 440 } 441 442 ret = glxServer.forwardRequest(vendor, client); 443 444 if (ret == Success && vendorCode == X_GLXvop_DestroyGLXPbufferSGIX) { 445 xGLXDestroyGLXPbufferSGIXReq *req = (void *)stuff; 446 glxServer.removeXIDMap(maybe_swap32(client, req->pbuffer)); 447 } 448 449 if (ret != Success) 450 glxServer.removeXIDMap(resource); 451 452 return ret; 453 } 454 455 static GlxServerDispatchProc 456 xorgGlxGetDispatchAddress(CARD8 minorOpcode, CARD32 vendorCode) 457 { 458 /* we don't support any other GLX opcodes */ 459 if (minorOpcode != X_GLXVendorPrivate && 460 minorOpcode != X_GLXVendorPrivateWithReply) 461 return NULL; 462 463 /* we only support some vendor private requests */ 464 if (!__glXGetProtocolDecodeFunction(&VendorPriv_dispatch_info, vendorCode, 465 FALSE)) 466 return NULL; 467 468 return xorgGlxThunkRequest; 469 } 470 471 static Bool 472 xorgGlxServerPreInit(const ExtensionEntry *extEntry) 473 { 474 if (glxGeneration != serverGeneration) { 475 /* Mesa requires at least one True/DirectColor visual */ 476 if (!checkScreenVisuals()) 477 return FALSE; 478 479 __glXContextRes = CreateNewResourceType((DeleteType) ContextGone, 480 "GLXContext"); 481 __glXDrawableRes = CreateNewResourceType((DeleteType) DrawableGone, 482 "GLXDrawable"); 483 if (!__glXContextRes || !__glXDrawableRes) 484 return FALSE; 485 486 if (!dixRegisterPrivateKey 487 (&glxClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(__GLXclientState))) 488 return FALSE; 489 if (!AddCallback(&ClientStateCallback, glxClientCallback, 0)) 490 return FALSE; 491 492 __glXErrorBase = extEntry->errorBase; 493 __glXEventBase = extEntry->eventBase; 494 495 SetResourceTypeSizeFunc(__glXDrawableRes, GetGLXDrawableBytes); 496 #if PRESENT 497 __glXregisterPresentCompleteNotify(); 498 #endif 499 500 glxGeneration = serverGeneration; 501 } 502 503 return glxGeneration == serverGeneration; 504 } 505 506 static void 507 xorgGlxInitGLVNDVendor(void) 508 { 509 if (glvnd_vendor == NULL) { 510 GlxServerImports *imports = NULL; 511 imports = glxServer.allocateServerImports(); 512 513 if (imports != NULL) { 514 imports->extensionCloseDown = xorgGlxCloseExtension; 515 imports->handleRequest = xorgGlxHandleRequest; 516 imports->getDispatchAddress = xorgGlxGetDispatchAddress; 517 imports->makeCurrent = xorgGlxMakeCurrent; 518 glvnd_vendor = glxServer.createVendor(imports); 519 glxServer.freeServerImports(imports); 520 } 521 } 522 } 523 524 static void 525 xorgGlxServerInit(CallbackListPtr *pcbl, void *param, void *ext) 526 { 527 const ExtensionEntry *extEntry = ext; 528 int i; 529 530 if (!xorgGlxServerPreInit(extEntry)) { 531 return; 532 } 533 534 xorgGlxInitGLVNDVendor(); 535 if (!glvnd_vendor) { 536 return; 537 } 538 539 for (i = 0; i < screenInfo.numScreens; i++) { 540 ScreenPtr pScreen = screenInfo.screens[i]; 541 __GLXprovider *p; 542 543 if (glxServer.getVendorForScreen(NULL, pScreen) != NULL) { 544 // There's already a vendor registered. 545 LogMessage(X_INFO, "GLX: Another vendor is already registered for screen %d\n", i); 546 continue; 547 } 548 549 for (p = __glXProviderStack; p != NULL; p = p->next) { 550 __GLXscreen *glxScreen = p->screenProbe(pScreen); 551 if (glxScreen != NULL) { 552 LogMessage(X_INFO, 553 "GLX: Initialized %s GL provider for screen %d\n", 554 p->name, i); 555 break; 556 } 557 558 } 559 560 if (p) { 561 glxServer.setScreenVendor(pScreen, glvnd_vendor); 562 } else { 563 LogMessage(X_INFO, 564 "GLX: no usable GL providers found for screen %d\n", i); 565 } 566 } 567 } 568 569 Bool 570 xorgGlxCreateVendor(void) 571 { 572 return AddCallback(glxServer.extensionInitCallback, xorgGlxServerInit, NULL); 573 } 574 575 /************************************************************************/ 576 577 /* 578 ** Make a context the current one for the GL (in this implementation, there 579 ** is only one instance of the GL, and we use it to serve all GL clients by 580 ** switching it between different contexts). While we are at it, look up 581 ** a context by its tag and return its (__GLXcontext *). 582 */ 583 __GLXcontext * 584 __glXForceCurrent(__GLXclientState * cl, GLXContextTag tag, int *error) 585 { 586 ClientPtr client = cl->client; 587 REQUEST(xGLXSingleReq); 588 589 __GLXcontext *cx; 590 591 /* 592 ** See if the context tag is legal; it is managed by the extension, 593 ** so if it's invalid, we have an implementation error. 594 */ 595 cx = __glXLookupContextByTag(cl, tag); 596 if (!cx) { 597 cl->client->errorValue = tag; 598 *error = __glXError(GLXBadContextTag); 599 return 0; 600 } 601 602 /* If we're expecting a glXRenderLarge request, this better be one. */ 603 if (cx->largeCmdRequestsSoFar != 0 && stuff->glxCode != X_GLXRenderLarge) { 604 client->errorValue = stuff->glxCode; 605 *error = __glXError(GLXBadLargeRequest); 606 return 0; 607 } 608 609 if (!cx->isDirect) { 610 if (cx->drawPriv == NULL) { 611 /* 612 ** The drawable has vanished. It must be a window, because only 613 ** windows can be destroyed from under us; GLX pixmaps are 614 ** refcounted and don't go away until no one is using them. 615 */ 616 *error = __glXError(GLXBadCurrentWindow); 617 return 0; 618 } 619 } 620 621 if (cx->wait && (*cx->wait) (cx, cl, error)) 622 return NULL; 623 624 if (cx == lastGLContext) { 625 /* No need to re-bind */ 626 return cx; 627 } 628 629 /* Make this context the current one for the GL. */ 630 if (!cx->isDirect) { 631 /* 632 * If it is being forced, it means that this context was already made 633 * current. So it cannot just be made current again without decrementing 634 * refcount's 635 */ 636 (*cx->loseCurrent) (cx); 637 lastGLContext = cx; 638 if (!(*cx->makeCurrent) (cx)) { 639 /* Bind failed, and set the error code. Bummer */ 640 lastGLContext = NULL; 641 cl->client->errorValue = cx->id; 642 *error = __glXError(GLXBadContextState); 643 return 0; 644 } 645 } 646 return cx; 647 } 648 649 /************************************************************************/ 650 651 void 652 glxSuspendClients(void) 653 { 654 int i; 655 656 for (i = 1; i < currentMaxClients; i++) { 657 if (clients[i] && glxGetClient(clients[i])->client) 658 IgnoreClient(clients[i]); 659 } 660 661 glxBlockClients = TRUE; 662 } 663 664 void 665 glxResumeClients(void) 666 { 667 __GLXcontext *cx, *next; 668 int i; 669 670 glxBlockClients = FALSE; 671 672 for (i = 1; i < currentMaxClients; i++) { 673 if (clients[i] && glxGetClient(clients[i])->client) 674 AttendClient(clients[i]); 675 } 676 677 for (cx = glxPendingDestroyContexts; cx != NULL; cx = next) { 678 next = cx->next; 679 680 cx->destroy(cx); 681 } 682 glxPendingDestroyContexts = NULL; 683 } 684 685 static glx_gpa_proc _get_proc_address; 686 687 void 688 __glXsetGetProcAddress(glx_gpa_proc get_proc_address) 689 { 690 _get_proc_address = get_proc_address; 691 } 692 693 void *__glGetProcAddress(const char *proc) 694 { 695 void *ret = (void *) _get_proc_address(proc); 696 697 return ret ? ret : (void *) NoopDDA; 698 } 699 700 /* 701 ** Top level dispatcher; all commands are executed from here down. 702 */ 703 static int 704 __glXDispatch(ClientPtr client) 705 { 706 REQUEST(xGLXSingleReq); 707 CARD8 opcode; 708 __GLXdispatchSingleProcPtr proc; 709 __GLXclientState *cl; 710 int retval = BadRequest; 711 712 opcode = stuff->glxCode; 713 cl = glxGetClient(client); 714 715 716 if (!cl->client) 717 cl->client = client; 718 719 /* If we're currently blocking GLX clients, just put this guy to 720 * sleep, reset the request and return. */ 721 if (glxBlockClients) { 722 ResetCurrentRequest(client); 723 client->sequence--; 724 IgnoreClient(client); 725 return Success; 726 } 727 728 /* 729 ** Use the opcode to index into the procedure table. 730 */ 731 proc = __glXGetProtocolDecodeFunction(&Single_dispatch_info, opcode, 732 client->swapped); 733 if (proc != NULL) 734 retval = (*proc) (cl, (GLbyte *) stuff); 735 736 return retval; 737 }