glxcurrent.c revision 23215ef4d60a86d9f3b3fdc08e3fdadc59e98890
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/** 32 * \file glxcurrent.c 33 * Client-side GLX interface for current context management. 34 */ 35 36#ifdef PTHREADS 37#include <pthread.h> 38#endif 39 40#include "glxclient.h" 41#ifdef GLX_USE_APPLEGL 42#include <stdlib.h> 43 44#include "apple_glx.h" 45#include "apple_glx_context.h" 46#else 47#include "glapi.h" 48#include "indirect_init.h" 49#endif 50 51/* 52** We setup some dummy structures here so that the API can be used 53** even if no context is current. 54*/ 55 56static GLubyte dummyBuffer[__GLX_BUFFER_LIMIT_SIZE]; 57 58/* 59** Dummy context used by small commands when there is no current context. 60** All the 61** gl and glx entry points are designed to operate as nop's when using 62** the dummy context structure. 63*/ 64static __GLXcontext dummyContext = { 65 &dummyBuffer[0], 66 &dummyBuffer[0], 67 &dummyBuffer[0], 68 &dummyBuffer[__GLX_BUFFER_LIMIT_SIZE], 69 sizeof(dummyBuffer), 70}; 71 72 73#ifndef GLX_USE_APPLEGL 74/* 75** All indirect rendering contexts will share the same indirect dispatch table. 76*/ 77static __GLapi *IndirectAPI = NULL; 78#endif 79 80/* 81 * Current context management and locking 82 */ 83 84#if defined( PTHREADS ) 85 86_X_HIDDEN pthread_mutex_t __glXmutex = PTHREAD_MUTEX_INITIALIZER; 87 88# if defined( GLX_USE_TLS ) 89 90/** 91 * Per-thread GLX context pointer. 92 * 93 * \c __glXSetCurrentContext is written is such a way that this pointer can 94 * \b never be \c NULL. This is important! Because of this 95 * \c __glXGetCurrentContext can be implemented as trivial macro. 96 */ 97__thread void *__glX_tls_Context __attribute__ ((tls_model("initial-exec"))) 98 = &dummyContext; 99 100_X_HIDDEN void 101__glXSetCurrentContext(__GLXcontext * c) 102{ 103 __glX_tls_Context = (c != NULL) ? c : &dummyContext; 104} 105 106# else 107 108static pthread_once_t once_control = PTHREAD_ONCE_INIT; 109 110/** 111 * Per-thread data key. 112 * 113 * Once \c init_thread_data has been called, the per-thread data key will 114 * take a value of \c NULL. As each new thread is created the default 115 * value, in that thread, will be \c NULL. 116 */ 117static pthread_key_t ContextTSD; 118 119/** 120 * Initialize the per-thread data key. 121 * 122 * This function is called \b exactly once per-process (not per-thread!) to 123 * initialize the per-thread data key. This is ideally done using the 124 * \c pthread_once mechanism. 125 */ 126static void 127init_thread_data(void) 128{ 129 if (pthread_key_create(&ContextTSD, NULL) != 0) { 130 perror("pthread_key_create"); 131 exit(-1); 132 } 133} 134 135_X_HIDDEN void 136__glXSetCurrentContext(__GLXcontext * c) 137{ 138 pthread_once(&once_control, init_thread_data); 139 pthread_setspecific(ContextTSD, c); 140} 141 142_X_HIDDEN __GLXcontext * 143__glXGetCurrentContext(void) 144{ 145 void *v; 146 147 pthread_once(&once_control, init_thread_data); 148 149 v = pthread_getspecific(ContextTSD); 150 return (v == NULL) ? &dummyContext : (__GLXcontext *) v; 151} 152 153# endif /* defined( GLX_USE_TLS ) */ 154 155#elif defined( THREADS ) 156 157#error Unknown threading method specified. 158 159#else 160 161/* not thread safe */ 162_X_HIDDEN __GLXcontext *__glXcurrentContext = &dummyContext; 163 164#endif 165 166 167_X_HIDDEN void 168__glXSetCurrentContextNull(void) 169{ 170 __glXSetCurrentContext(&dummyContext); 171#ifndef GLX_USE_APPLEGL 172#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 173 _glapi_set_dispatch(NULL); /* no-op functions */ 174 _glapi_set_context(NULL); 175#endif 176#endif 177} 178 179 180/************************************************************************/ 181 182PUBLIC GLXContext 183glXGetCurrentContext(void) 184{ 185 GLXContext cx = __glXGetCurrentContext(); 186 187 if (cx == &dummyContext) { 188 return NULL; 189 } 190 else { 191 return cx; 192 } 193} 194 195PUBLIC GLXDrawable 196glXGetCurrentDrawable(void) 197{ 198 GLXContext gc = __glXGetCurrentContext(); 199 return gc->currentDrawable; 200} 201 202 203#ifndef GLX_USE_APPLEGL 204/************************************************************************/ 205 206/** 207 * Sends a GLX protocol message to the specified display to make the context 208 * and the drawables current. 209 * 210 * \param dpy Display to send the message to. 211 * \param opcode Major opcode value for the display. 212 * \param gc_id Context tag for the context to be made current. 213 * \param draw Drawable ID for the "draw" drawable. 214 * \param read Drawable ID for the "read" drawable. 215 * \param reply Space to store the X-server's reply. 216 * 217 * \warning 218 * This function assumes that \c dpy is locked with \c LockDisplay on entry. 219 */ 220static Bool 221SendMakeCurrentRequest(Display * dpy, CARD8 opcode, 222 GLXContextID gc_id, GLXContextTag gc_tag, 223 GLXDrawable draw, GLXDrawable read, 224 xGLXMakeCurrentReply * reply) 225{ 226 Bool ret; 227 228 229 LockDisplay(dpy); 230 231 if (draw == read) { 232 xGLXMakeCurrentReq *req; 233 234 GetReq(GLXMakeCurrent, req); 235 req->reqType = opcode; 236 req->glxCode = X_GLXMakeCurrent; 237 req->drawable = draw; 238 req->context = gc_id; 239 req->oldContextTag = gc_tag; 240 } 241 else { 242 __GLXdisplayPrivate *priv = __glXInitialize(dpy); 243 244 /* If the server can support the GLX 1.3 version, we should 245 * perfer that. Not only that, some servers support GLX 1.3 but 246 * not the SGI extension. 247 */ 248 249 if ((priv->majorVersion > 1) || (priv->minorVersion >= 3)) { 250 xGLXMakeContextCurrentReq *req; 251 252 GetReq(GLXMakeContextCurrent, req); 253 req->reqType = opcode; 254 req->glxCode = X_GLXMakeContextCurrent; 255 req->drawable = draw; 256 req->readdrawable = read; 257 req->context = gc_id; 258 req->oldContextTag = gc_tag; 259 } 260 else { 261 xGLXVendorPrivateWithReplyReq *vpreq; 262 xGLXMakeCurrentReadSGIReq *req; 263 264 GetReqExtra(GLXVendorPrivateWithReply, 265 sz_xGLXMakeCurrentReadSGIReq - 266 sz_xGLXVendorPrivateWithReplyReq, vpreq); 267 req = (xGLXMakeCurrentReadSGIReq *) vpreq; 268 req->reqType = opcode; 269 req->glxCode = X_GLXVendorPrivateWithReply; 270 req->vendorCode = X_GLXvop_MakeCurrentReadSGI; 271 req->drawable = draw; 272 req->readable = read; 273 req->context = gc_id; 274 req->oldContextTag = gc_tag; 275 } 276 } 277 278 ret = _XReply(dpy, (xReply *) reply, 0, False); 279 280 UnlockDisplay(dpy); 281 SyncHandle(); 282 283 return ret; 284} 285 286 287#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 288static __GLXDRIdrawable * 289FetchDRIDrawable(Display * dpy, GLXDrawable glxDrawable, GLXContext gc) 290{ 291 __GLXdisplayPrivate *const priv = __glXInitialize(dpy); 292 __GLXDRIdrawable *pdraw; 293 __GLXscreenConfigs *psc; 294 295 if (priv == NULL) 296 return NULL; 297 298 psc = &priv->screenConfigs[gc->screen]; 299 if (psc->drawHash == NULL) 300 return NULL; 301 302 if (__glxHashLookup(psc->drawHash, glxDrawable, (void *) &pdraw) == 0) 303 return pdraw; 304 305 pdraw = psc->driScreen->createDrawable(psc, glxDrawable, 306 glxDrawable, gc->mode); 307 if (__glxHashInsert(psc->drawHash, glxDrawable, pdraw)) { 308 (*pdraw->destroyDrawable) (pdraw); 309 return NULL; 310 } 311 312 return pdraw; 313} 314#endif /* GLX_DIRECT_RENDERING */ 315 316static void 317__glXGenerateError(Display * dpy, GLXContext gc, XID resource, 318 BYTE errorCode, CARD16 minorCode) 319{ 320 xError error; 321 322 error.errorCode = errorCode; 323 error.resourceID = resource; 324 error.sequenceNumber = dpy->request; 325 error.type = X_Error; 326 error.majorCode = gc->majorOpcode; 327 error.minorCode = minorCode; 328 _XError(dpy, &error); 329} 330 331#endif /* GLX_USE_APPLEGL */ 332 333/** 334 * Make a particular context current. 335 * 336 * \note This is in this file so that it can access dummyContext. 337 */ 338static Bool 339MakeContextCurrent(Display * dpy, GLXDrawable draw, 340 GLXDrawable read, GLXContext gc) 341{ 342 const GLXContext oldGC = __glXGetCurrentContext(); 343#ifdef GLX_USE_APPLEGL 344 bool error = apple_glx_make_current_context(dpy, 345 (oldGC && oldGC != &dummyContext) ? oldGC->driContext : NULL, 346 gc ? gc->driContext : NULL, draw); 347 348 apple_glx_diagnostic("%s: error %s\n", __func__, error ? "YES" : "NO"); 349 if(error) 350 return GL_FALSE; 351#else 352 xGLXMakeCurrentReply reply; 353 const CARD8 opcode = __glXSetupForCommand(dpy); 354 const CARD8 oldOpcode = ((gc == oldGC) || (oldGC == &dummyContext)) 355 ? opcode : __glXSetupForCommand(oldGC->currentDpy); 356 Bool bindReturnValue; 357 __GLXattribute *state; 358 359 if (!opcode || !oldOpcode) { 360 return GL_FALSE; 361 } 362 363 /* Make sure that the new context has a nonzero ID. In the request, 364 * a zero context ID is used only to mean that we bind to no current 365 * context. 366 */ 367 if ((gc != NULL) && (gc->xid == None)) { 368 return GL_FALSE; 369 } 370 371 if (gc == NULL && (draw != None || read != None)) { 372 __glXGenerateError(dpy, gc, (draw != None) ? draw : read, 373 BadMatch, X_GLXMakeContextCurrent); 374 return False; 375 } 376 if (gc != NULL && (draw == None || read == None)) { 377 __glXGenerateError(dpy, gc, None, BadMatch, X_GLXMakeContextCurrent); 378 return False; 379 } 380 381 _glapi_check_multithread(); 382 383 if (gc != NULL && gc->thread_id != 0 && gc->thread_id != _glthread_GetID()) { 384 __glXGenerateError(dpy, gc, gc->xid, 385 BadAccess, X_GLXMakeContextCurrent); 386 return False; 387 } 388 389#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 390 /* Bind the direct rendering context to the drawable */ 391 if (gc && gc->driContext) { 392 __GLXDRIdrawable *pdraw = FetchDRIDrawable(dpy, draw, gc); 393 __GLXDRIdrawable *pread = FetchDRIDrawable(dpy, read, gc); 394 395 if ((pdraw == NULL) || (pread == NULL)) { 396 __glXGenerateError(dpy, gc, (pdraw == NULL) ? draw : read, 397 GLXBadDrawable, X_GLXMakeContextCurrent); 398 return False; 399 } 400 401 bindReturnValue = 402 (gc->driContext->bindContext) (gc->driContext, pdraw, pread); 403 } 404 else if (!gc && oldGC && oldGC->driContext) { 405 bindReturnValue = True; 406 } 407 else 408#endif 409 { 410 /* Send a glXMakeCurrent request to bind the new context. */ 411 bindReturnValue = 412 SendMakeCurrentRequest(dpy, opcode, gc ? gc->xid : None, 413 ((dpy != oldGC->currentDpy) 414 || oldGC->isDirect) 415 ? None : oldGC->currentContextTag, draw, read, 416 &reply); 417 } 418 419 420 if (!bindReturnValue) { 421 return False; 422 } 423 424#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 425 if ((dpy != oldGC->currentDpy || (gc && gc->driContext)) && 426 !oldGC->isDirect && oldGC != &dummyContext) { 427#else 428 if ((dpy != oldGC->currentDpy) && oldGC != &dummyContext) { 429#endif 430 xGLXMakeCurrentReply dummy_reply; 431 432 /* We are either switching from one dpy to another and have to 433 * send a request to the previous dpy to unbind the previous 434 * context, or we are switching away from a indirect context to 435 * a direct context and have to send a request to the dpy to 436 * unbind the previous context. 437 */ 438 (void) SendMakeCurrentRequest(oldGC->currentDpy, oldOpcode, None, 439 oldGC->currentContextTag, None, None, 440 &dummy_reply); 441 } 442#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 443 else if (oldGC->driContext && oldGC != gc) { 444 oldGC->driContext->unbindContext(oldGC->driContext); 445 } 446#endif 447 448#endif /* GLX_USE_APPLEGL */ 449 450 /* Update our notion of what is current */ 451 __glXLock(); 452 if (gc == oldGC) { 453 /* Even though the contexts are the same the drawable might have 454 * changed. Note that gc cannot be the dummy, and that oldGC 455 * cannot be NULL, therefore if they are the same, gc is not 456 * NULL and not the dummy. 457 */ 458 if(gc) { 459 gc->currentDrawable = draw; 460 gc->currentReadable = read; 461 } 462 } 463 else { 464 if (oldGC != &dummyContext) { 465 /* Old current context is no longer current to anybody */ 466 oldGC->currentDpy = 0; 467 oldGC->currentDrawable = None; 468 oldGC->currentReadable = None; 469 oldGC->currentContextTag = 0; 470 oldGC->thread_id = 0; 471#ifdef GLX_USE_APPLEGL 472 473 /* 474 * At this point we should check if the context has been 475 * through glXDestroyContext, and redestroy it if so. 476 */ 477 if(oldGC->do_destroy) { 478 __glXUnlock(); 479 /* glXDestroyContext uses the same global lock. */ 480 glXDestroyContext(dpy, oldGC); 481 __glXLock(); 482#else 483 if (oldGC->xid == None) { 484 /* We are switching away from a context that was 485 * previously destroyed, so we need to free the memory 486 * for the old handle. 487 */ 488#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 489 /* Destroy the old direct rendering context */ 490 if (oldGC->driContext) { 491 oldGC->driContext->destroyContext(oldGC->driContext, 492 oldGC->psc, 493 oldGC->createDpy); 494 oldGC->driContext = NULL; 495 } 496#endif 497 __glXFreeContext(oldGC); 498#endif /* GLX_USE_APPLEGL */ 499 } 500 } 501 if (gc) { 502 __glXSetCurrentContext(gc); 503 504 gc->currentDpy = dpy; 505 gc->currentDrawable = draw; 506 gc->currentReadable = read; 507#ifndef GLX_USE_APPLEGL 508 gc->thread_id = _glthread_GetID(); 509 510#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 511 if (!gc->driContext) { 512#endif 513 if (!IndirectAPI) 514 IndirectAPI = __glXNewIndirectAPI(); 515 _glapi_set_dispatch(IndirectAPI); 516 517 state = (__GLXattribute *) (gc->client_state_private); 518 519 gc->currentContextTag = reply.contextTag; 520 if (state->array_state == NULL) { 521 (void) glGetString(GL_EXTENSIONS); 522 (void) glGetString(GL_VERSION); 523 __glXInitVertexArrayState(gc); 524 } 525#if defined(GLX_DIRECT_RENDERING) && !defined(GLX_USE_APPLEGL) 526 } 527 else { 528 gc->currentContextTag = -1; 529 } 530#endif 531#endif /* GLX_USE_APPLEGL */ 532 } 533 else { 534 __glXSetCurrentContextNull(); 535 } 536 } 537 __glXUnlock(); 538 return GL_TRUE; 539} 540 541 542PUBLIC Bool 543glXMakeCurrent(Display * dpy, GLXDrawable draw, GLXContext gc) 544{ 545 return MakeContextCurrent(dpy, draw, draw, gc); 546} 547 548PUBLIC 549GLX_ALIAS(Bool, glXMakeCurrentReadSGI, 550 (Display * dpy, GLXDrawable d, GLXDrawable r, GLXContext ctx), 551 (dpy, d, r, ctx), MakeContextCurrent) 552 553PUBLIC 554GLX_ALIAS(Bool, glXMakeContextCurrent, 555 (Display * dpy, GLXDrawable d, GLXDrawable r, 556 GLXContext ctx), (dpy, d, r, ctx), MakeContextCurrent) 557