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