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