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