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