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