[PATCH xserver 3/7] glx: Import glxvnd server module (v2)

Adam Jackson ajax at redhat.com
Wed Jan 10 18:05:44 UTC 2018


From: Kyle Brenneman <kbrenneman at nvidia.com>

This is based on an out-of-tree module written by Kyle:

https://github.com/kbrenneman/libglvnd/tree/server-libglx

I (ajax) did a bunch of cosmetic fixes, ported it off xfree86 API,
added request length checks, and fixed a minor bug or two.

v2: Use separate functions to set/get a context tag's private data, and
call the backend's MakeCurrent when a client disconnects to unbind the
context. (Kyle Brenneman)

Signed-off-by: Adam Jackson <ajax at redhat.com>
---
 glx/Makefile.am          |   8 +-
 glx/meson.build          |  21 ++
 glx/vnd_dispatch_stubs.c | 525 +++++++++++++++++++++++++++++++++++++++++++++++
 glx/vndcmds.c            | 478 ++++++++++++++++++++++++++++++++++++++++++
 glx/vndext.c             | 306 +++++++++++++++++++++++++++
 glx/vndserver.h          | 119 +++++++++++
 glx/vndservermapping.c   | 196 ++++++++++++++++++
 glx/vndservervendor.c    |  91 ++++++++
 glx/vndservervendor.h    |  68 ++++++
 include/Makefile.am      |   1 +
 include/glxvndabi.h      | 307 +++++++++++++++++++++++++++
 include/meson.build      |   1 +
 12 files changed, 2120 insertions(+), 1 deletion(-)
 create mode 100644 glx/vnd_dispatch_stubs.c
 create mode 100644 glx/vndcmds.c
 create mode 100644 glx/vndext.c
 create mode 100644 glx/vndserver.h
 create mode 100644 glx/vndservermapping.c
 create mode 100644 glx/vndservervendor.c
 create mode 100644 glx/vndservervendor.h
 create mode 100644 include/glxvndabi.h

diff --git a/glx/Makefile.am b/glx/Makefile.am
index 699de63b8..cb71a3763 100644
--- a/glx/Makefile.am
+++ b/glx/Makefile.am
@@ -2,7 +2,7 @@ if DRI2
 GLXDRI_LIBRARY = libglxdri.la
 endif
 
-noinst_LTLIBRARIES = libglx.la $(GLXDRI_LIBRARY)
+noinst_LTLIBRARIES = libglx.la $(GLXDRI_LIBRARY) libglxvnd.la
 
 AM_CFLAGS = \
 	@DIX_CFLAGS@ \
@@ -83,3 +83,9 @@ libglx_la_SOURCES = \
         xfont.c
 
 libglx_la_LIBADD = $(DLOPEN_LIBS)
+
+libglxvnd_la_SOURCES = \
+	vndcmds.c \
+	vndext.c \
+	vndservermapping.c \
+	vndservervendor.c
diff --git a/glx/meson.build b/glx/meson.build
index f13f8f24b..5f93a75a5 100644
--- a/glx/meson.build
+++ b/glx/meson.build
@@ -53,3 +53,24 @@ srcs_glxdri2 = []
 if build_dri2 or build_dri3
     srcs_glxdri2 = files('glxdri2.c')
 endif
+
+srcs_vnd = [
+    'vndcmds.c',
+    'vndext.c',
+    'vndservermapping.c',
+    'vndservervendor.c',
+]
+
+libglxvnd = ''
+if build_glx
+    libglxvnd = static_library('libglxvnd',
+	srcs_vnd,
+	include_directories: inc,
+        dependencies: [
+            common_dep,
+            dl_dep,
+            dependency('glproto', version: '>= 1.4.17'),
+            dependency('gl', version: '>= 9.2.0'),
+        ],
+    )
+endif
diff --git a/glx/vnd_dispatch_stubs.c b/glx/vnd_dispatch_stubs.c
new file mode 100644
index 000000000..629160fe7
--- /dev/null
+++ b/glx/vnd_dispatch_stubs.c
@@ -0,0 +1,525 @@
+
+#include <dix-config.h>
+#include <dix.h>
+#include "vndserver.h"
+
+// HACK: The opcode in old glxproto.h has a typo in it.
+#if !defined(X_GLXCreateContextAttribsARB)
+#define X_GLXCreateContextAttribsARB X_GLXCreateContextAtrribsARB
+#endif
+
+static int dispatch_Render(ClientPtr client)
+{
+    REQUEST(xGLXRenderReq);
+    CARD32 contextTag;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+    contextTag = GlxCheckSwap(client, stuff->contextTag);
+    vendor = glxServer.getContextTag(client, contextTag);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = contextTag;
+        return GlxErrorBase + GLXBadContextTag;
+    }
+}
+static int dispatch_RenderLarge(ClientPtr client)
+{
+    REQUEST(xGLXRenderLargeReq);
+    CARD32 contextTag;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+    contextTag = GlxCheckSwap(client, stuff->contextTag);
+    vendor = glxServer.getContextTag(client, contextTag);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = contextTag;
+        return GlxErrorBase + GLXBadContextTag;
+    }
+}
+static int dispatch_CreateContext(ClientPtr client)
+{
+    REQUEST(xGLXCreateContextReq);
+    CARD32 screen, context;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    context = GlxCheckSwap(client, stuff->context);
+    LEGAL_NEW_RESOURCE(context, client);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        if (!glxServer.addXIDMap(context, vendor)) {
+            return BadAlloc;
+        }
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret != Success) {
+            glxServer.removeXIDMap(context);
+        }
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_DestroyContext(ClientPtr client)
+{
+    REQUEST(xGLXDestroyContextReq);
+    CARD32 context;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    context = GlxCheckSwap(client, stuff->context);
+    vendor = glxServer.getXIDMap(context);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret == Success) {
+            glxServer.removeXIDMap(context);
+        }
+        return ret;
+    } else {
+        client->errorValue = context;
+        return GlxErrorBase + GLXBadContext;
+    }
+}
+static int dispatch_WaitGL(ClientPtr client)
+{
+    REQUEST(xGLXWaitGLReq);
+    CARD32 contextTag;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    contextTag = GlxCheckSwap(client, stuff->contextTag);
+    vendor = glxServer.getContextTag(client, contextTag);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = contextTag;
+        return GlxErrorBase + GLXBadContextTag;
+    }
+}
+static int dispatch_WaitX(ClientPtr client)
+{
+    REQUEST(xGLXWaitXReq);
+    CARD32 contextTag;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    contextTag = GlxCheckSwap(client, stuff->contextTag);
+    vendor = glxServer.getContextTag(client, contextTag);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = contextTag;
+        return GlxErrorBase + GLXBadContextTag;
+    }
+}
+static int dispatch_UseXFont(ClientPtr client)
+{
+    REQUEST(xGLXUseXFontReq);
+    CARD32 contextTag;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    contextTag = GlxCheckSwap(client, stuff->contextTag);
+    vendor = glxServer.getContextTag(client, contextTag);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = contextTag;
+        return GlxErrorBase + GLXBadContextTag;
+    }
+}
+static int dispatch_CreateGLXPixmap(ClientPtr client)
+{
+    REQUEST(xGLXCreateGLXPixmapReq);
+    CARD32 screen, glxpixmap;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    glxpixmap = GlxCheckSwap(client, stuff->glxpixmap);
+    LEGAL_NEW_RESOURCE(glxpixmap, client);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        if (!glxServer.addXIDMap(glxpixmap, vendor)) {
+            return BadAlloc;
+        }
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret != Success) {
+            glxServer.removeXIDMap(glxpixmap);
+        }
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_GetVisualConfigs(ClientPtr client)
+{
+    REQUEST(xGLXGetVisualConfigsReq);
+    CARD32 screen;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_DestroyGLXPixmap(ClientPtr client)
+{
+    REQUEST(xGLXDestroyGLXPixmapReq);
+    CARD32 glxpixmap;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    glxpixmap = GlxCheckSwap(client, stuff->glxpixmap);
+    vendor = glxServer.getXIDMap(glxpixmap);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = glxpixmap;
+        return GlxErrorBase + GLXBadPixmap;
+    }
+}
+static int dispatch_QueryExtensionsString(ClientPtr client)
+{
+    REQUEST(xGLXQueryExtensionsStringReq);
+    CARD32 screen;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_QueryServerString(ClientPtr client)
+{
+    REQUEST(xGLXQueryServerStringReq);
+    CARD32 screen;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_ChangeDrawableAttributes(ClientPtr client)
+{
+    REQUEST(xGLXChangeDrawableAttributesReq);
+    CARD32 drawable;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+    drawable = GlxCheckSwap(client, stuff->drawable);
+    vendor = glxServer.getXIDMap(drawable);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = drawable;
+        return BadDrawable;
+    }
+}
+static int dispatch_CreateNewContext(ClientPtr client)
+{
+    REQUEST(xGLXCreateNewContextReq);
+    CARD32 screen, context;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    context = GlxCheckSwap(client, stuff->context);
+    LEGAL_NEW_RESOURCE(context, client);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        if (!glxServer.addXIDMap(context, vendor)) {
+            return BadAlloc;
+        }
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret != Success) {
+            glxServer.removeXIDMap(context);
+        }
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_CreatePbuffer(ClientPtr client)
+{
+    REQUEST(xGLXCreatePbufferReq);
+    CARD32 screen, pbuffer;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    pbuffer = GlxCheckSwap(client, stuff->pbuffer);
+    LEGAL_NEW_RESOURCE(pbuffer, client);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        if (!glxServer.addXIDMap(pbuffer, vendor)) {
+            return BadAlloc;
+        }
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret != Success) {
+            glxServer.removeXIDMap(pbuffer);
+        }
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_CreatePixmap(ClientPtr client)
+{
+    REQUEST(xGLXCreatePixmapReq);
+    CARD32 screen, glxpixmap;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    glxpixmap = GlxCheckSwap(client, stuff->glxpixmap);
+    LEGAL_NEW_RESOURCE(glxpixmap, client);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        if (!glxServer.addXIDMap(glxpixmap, vendor)) {
+            return BadAlloc;
+        }
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret != Success) {
+            glxServer.removeXIDMap(glxpixmap);
+        }
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_CreateWindow(ClientPtr client)
+{
+    REQUEST(xGLXCreateWindowReq);
+    CARD32 screen, glxwindow;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    glxwindow = GlxCheckSwap(client, stuff->glxwindow);
+    LEGAL_NEW_RESOURCE(glxwindow, client);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        if (!glxServer.addXIDMap(glxwindow, vendor)) {
+            return BadAlloc;
+        }
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret != Success) {
+            glxServer.removeXIDMap(glxwindow);
+        }
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_CreateContextAttribsARB(ClientPtr client)
+{
+    REQUEST(xGLXCreateContextAttribsARBReq);
+    CARD32 screen, context;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    context = GlxCheckSwap(client, stuff->context);
+    LEGAL_NEW_RESOURCE(context, client);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        if (!glxServer.addXIDMap(context, vendor)) {
+            return BadAlloc;
+        }
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret != Success) {
+            glxServer.removeXIDMap(context);
+        }
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_DestroyPbuffer(ClientPtr client)
+{
+    REQUEST(xGLXDestroyPbufferReq);
+    CARD32 pbuffer;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    pbuffer = GlxCheckSwap(client, stuff->pbuffer);
+    vendor = glxServer.getXIDMap(pbuffer);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret == Success) {
+            glxServer.removeXIDMap(pbuffer);
+        }
+        return ret;
+    } else {
+        client->errorValue = pbuffer;
+        return GlxErrorBase + GLXBadPbuffer;
+    }
+}
+static int dispatch_DestroyPixmap(ClientPtr client)
+{
+    REQUEST(xGLXDestroyPixmapReq);
+    CARD32 glxpixmap;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    glxpixmap = GlxCheckSwap(client, stuff->glxpixmap);
+    vendor = glxServer.getXIDMap(glxpixmap);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret == Success) {
+            glxServer.removeXIDMap(glxpixmap);
+        }
+        return ret;
+    } else {
+        client->errorValue = glxpixmap;
+        return GlxErrorBase + GLXBadPixmap;
+    }
+}
+static int dispatch_DestroyWindow(ClientPtr client)
+{
+    REQUEST(xGLXDestroyWindowReq);
+    CARD32 glxwindow;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    glxwindow = GlxCheckSwap(client, stuff->glxwindow);
+    vendor = glxServer.getXIDMap(glxwindow);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        if (ret == Success) {
+            glxServer.removeXIDMap(glxwindow);
+        }
+        return ret;
+    } else {
+        client->errorValue = glxwindow;
+        return GlxErrorBase + GLXBadWindow;
+    }
+}
+static int dispatch_GetDrawableAttributes(ClientPtr client)
+{
+    REQUEST(xGLXGetDrawableAttributesReq);
+    CARD32 drawable;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    drawable = GlxCheckSwap(client, stuff->drawable);
+    vendor = glxServer.getXIDMap(drawable);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = drawable;
+        return BadDrawable;
+    }
+}
+static int dispatch_GetFBConfigs(ClientPtr client)
+{
+    REQUEST(xGLXGetFBConfigsReq);
+    CARD32 screen;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    screen = GlxCheckSwap(client, stuff->screen);
+    if (screen < screenInfo.numScreens) {
+        vendor = glxServer.getVendorForScreen(client, screenInfo.screens[screen]);
+    }
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = screen;
+        return BadMatch;
+    }
+}
+static int dispatch_QueryContext(ClientPtr client)
+{
+    REQUEST(xGLXQueryContextReq);
+    CARD32 context;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    context = GlxCheckSwap(client, stuff->context);
+    vendor = glxServer.getXIDMap(context);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = context;
+        return GlxErrorBase + GLXBadContext;
+    }
+}
+static int dispatch_IsDirect(ClientPtr client)
+{
+    REQUEST(xGLXIsDirectReq);
+    CARD32 context;
+    GlxServerVendor *vendor = NULL;
+    REQUEST_SIZE_MATCH(*stuff);
+    context = GlxCheckSwap(client, stuff->context);
+    vendor = glxServer.getXIDMap(context);
+    if (vendor != NULL) {
+        int ret;
+        ret = glxServer.forwardRequest(vendor, client);
+        return ret;
+    } else {
+        client->errorValue = context;
+        return GlxErrorBase + GLXBadContext;
+    }
+}
diff --git a/glx/vndcmds.c b/glx/vndcmds.c
new file mode 100644
index 000000000..395f8d125
--- /dev/null
+++ b/glx/vndcmds.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ */
+
+#include <dix-config.h>
+
+#include "hashtable.h"
+#include "vndserver.h"
+#include "vndservervendor.h"
+
+/**
+ * The length of the dispatchFuncs array. Every opcode above this is a
+ * X_GLsop_* code, which all can use the same handler.
+ */
+#define OPCODE_ARRAY_LEN 100
+
+// This hashtable is used to keep track of the dispatch stubs for
+// GLXVendorPrivate and GLXVendorPrivateWithReply.
+typedef struct GlxVendorPrivDispatchRec {
+    CARD32 vendorCode;
+    GlxServerDispatchProc proc;
+    HashTable hh;
+} GlxVendorPrivDispatch;
+
+static GlxServerDispatchProc dispatchFuncs[OPCODE_ARRAY_LEN] = {};
+static HashTable vendorPrivHash = NULL;
+static HtGenericHashSetupRec vendorPrivSetup = {
+    .keySize = sizeof(void*)
+};
+
+static int DispatchBadRequest(ClientPtr client)
+{
+    return BadRequest;
+}
+
+static GlxVendorPrivDispatch *LookupVendorPrivDispatch(CARD32 vendorCode, Bool create)
+{
+    GlxVendorPrivDispatch *disp = NULL;
+
+    disp = ht_find(vendorPrivHash, &vendorCode);
+    if (disp == NULL && create) {
+        if ((disp = ht_add(vendorPrivHash, &vendorCode))) {
+            disp->vendorCode = vendorCode;
+            disp->proc = NULL;
+        }
+    }
+
+    return disp;
+}
+
+static GlxServerDispatchProc GetVendorDispatchFunc(CARD8 opcode, CARD32 vendorCode)
+{
+    GlxServerVendor *vendor;
+
+    xorg_list_for_each_entry(vendor, &GlxVendorList, entry) {
+        GlxServerDispatchProc proc = vendor->glxvc.getDispatchAddress(opcode, vendorCode);
+        if (proc != NULL) {
+            return proc;
+        }
+    }
+
+    return DispatchBadRequest;
+}
+
+static void SetReplyHeader(ClientPtr client, void *replyPtr)
+{
+    xGenericReply *rep = (xGenericReply *) replyPtr;
+    rep->type = X_Reply;
+    rep->sequenceNumber = client->sequence;
+    rep->length = 0;
+}
+
+/* Include the trivial dispatch handlers */
+#include "vnd_dispatch_stubs.c"
+
+static int dispatch_GLXQueryVersion(ClientPtr client)
+{
+    xGLXQueryVersionReply reply;
+    REQUEST_SIZE_MATCH(xGLXQueryVersionReq);
+
+    SetReplyHeader(client, &reply);
+    reply.majorVersion = GlxCheckSwap(client, 1);
+    reply.minorVersion = GlxCheckSwap(client, 4);
+
+    WriteToClient(client, sz_xGLXQueryVersionReply, &reply);
+    return Success;
+}
+
+/**
+ * This function is used for X_GLXClientInfo, X_GLXSetClientInfoARB, and
+ * X_GLXSetClientInfo2ARB.
+ */
+static int dispatch_GLXClientInfo(ClientPtr client)
+{
+    GlxServerVendor *vendor;
+    void *requestCopy = NULL;
+    size_t requestSize = client->req_len * 4;
+
+    if (client->minorOp == X_GLXClientInfo) {
+        REQUEST_AT_LEAST_SIZE(xGLXClientInfoReq);
+    } else if (client->minorOp == X_GLXSetClientInfoARB) {
+        REQUEST_AT_LEAST_SIZE(xGLXSetClientInfoARBReq);
+    } else if (client->minorOp == X_GLXSetConfigInfo2ARB) {
+        REQUEST_AT_LEAST_SIZE(xGLXSetClientInfo2ARBReq);
+    } else {
+        return BadImplementation;
+    }
+
+    // We'll forward this request to each vendor library. Since a vendor might
+    // modify the request data in place (e.g., for byte swapping), make a copy
+    // of the request first.
+    requestCopy = malloc(requestSize);
+    if (requestCopy == NULL) {
+        return BadAlloc;
+    }
+    memcpy(requestCopy, client->requestBuffer, requestSize);
+
+    xorg_list_for_each_entry(vendor, &GlxVendorList, entry) {
+        vendor->glxvc.handleRequest(client);
+        // Revert the request buffer back to our copy.
+        memcpy(client->requestBuffer, requestCopy, requestSize);
+    }
+    free(requestCopy);
+    return Success;
+}
+
+static int CommonLoseCurrent(ClientPtr client, GlxContextTagInfo *tagInfo)
+{
+    int ret;
+
+    ret = tagInfo->vendor->glxvc.makeCurrent(client,
+            tagInfo->tag, // No old context tag,
+            None, None, None, 0);
+
+    if (ret == Success) {
+        GlxFreeContextTag(tagInfo);
+    }
+    return ret;
+}
+
+static int CommonMakeNewCurrent(ClientPtr client,
+        GlxServerVendor *vendor,
+        GLXDrawable drawable,
+        GLXDrawable readdrawable,
+        GLXContextID context,
+        GLXContextTag *newContextTag)
+{
+    int ret = BadAlloc;
+    GlxContextTagInfo *tagInfo;
+
+    tagInfo = GlxAllocContextTag(client, vendor);
+
+    if (tagInfo) {
+        ret = vendor->glxvc.makeCurrent(client,
+                0, // No old context tag,
+                drawable, readdrawable, context,
+                tagInfo->tag);
+
+        if (ret == Success) {
+            tagInfo->drawable = drawable;
+            tagInfo->readdrawable = readdrawable;
+            tagInfo->context = context;
+            *newContextTag = tagInfo->tag;
+        } else {
+            GlxFreeContextTag(tagInfo);
+        }
+    }
+
+    return ret;
+}
+
+static int CommonMakeCurrent(ClientPtr client,
+        GLXContextTag oldContextTag,
+        GLXDrawable drawable,
+        GLXDrawable readdrawable,
+        GLXContextID context)
+{
+    xGLXMakeCurrentReply reply = {};
+    GlxContextTagInfo *oldTag = NULL;
+    GlxServerVendor *newVendor = NULL;
+
+    oldContextTag = GlxCheckSwap(client, oldContextTag);
+    drawable = GlxCheckSwap(client, drawable);
+    readdrawable = GlxCheckSwap(client, readdrawable);
+    context = GlxCheckSwap(client, context);
+
+    SetReplyHeader(client, &reply);
+
+    if (oldContextTag != 0) {
+        oldTag = GlxLookupContextTag(client, oldContextTag);
+        if (oldTag == NULL) {
+            return GlxErrorBase + GLXBadContextTag;
+        }
+    }
+    if (context != 0) {
+        newVendor = GlxGetXIDMap(context);
+        if (newVendor == NULL) {
+            return GlxErrorBase + GLXBadContext;
+        }
+    }
+
+    if (oldTag == NULL && newVendor == NULL) {
+        // Nothing to do here. Just send a successful reply.
+        reply.contextTag = 0;
+    } else if (oldTag != NULL && newVendor != NULL
+            && oldTag->context == context
+            && oldTag->drawable == drawable
+            && oldTag->readdrawable == readdrawable)
+    {
+        // The old and new values are all the same, so send a successful reply.
+        reply.contextTag = oldTag->tag;
+    } else {
+        // TODO: For switching contexts in a single vendor, just make one
+        // makeCurrent call?
+
+        // TODO: When changing vendors, would it be better to do the
+        // MakeCurrent(new) first, then the LoseCurrent(old)?
+        // If the MakeCurrent(new) fails, then the old context will still be current.
+        // If the LoseCurrent(old) fails, then we can (probably) undo the MakeCurrent(new) with
+        // a LoseCurrent(old).
+        // But, if the recovery LoseCurrent(old) fails, then we're really in a bad state.
+
+        // Clear the old context first.
+        if (oldTag != NULL) {
+            int ret = CommonLoseCurrent(client, oldTag);
+            if (ret != Success) {
+                return ret;
+            }
+            oldTag = NULL;
+        }
+
+        if (newVendor != NULL) {
+            int ret = CommonMakeNewCurrent(client, newVendor, drawable, readdrawable, context, &reply.contextTag);
+            if (ret != Success) {
+                return ret;
+            }
+        } else {
+            reply.contextTag = 0;
+        }
+    }
+
+    reply.contextTag = GlxCheckSwap(client, reply.contextTag);
+    WriteToClient(client, sz_xGLXMakeCurrentReply, &reply);
+    return Success;
+}
+
+static int dispatch_GLXMakeCurrent(ClientPtr client)
+{
+    REQUEST(xGLXMakeCurrentReq);
+    REQUEST_SIZE_MATCH(*stuff);
+
+    return CommonMakeCurrent(client, stuff->oldContextTag,
+            stuff->drawable, stuff->drawable, stuff->context);
+}
+
+static int dispatch_GLXMakeContextCurrent(ClientPtr client)
+{
+    REQUEST(xGLXMakeContextCurrentReq);
+    REQUEST_SIZE_MATCH(*stuff);
+
+    return CommonMakeCurrent(client, stuff->oldContextTag,
+            stuff->drawable, stuff->readdrawable, stuff->context);
+}
+
+static int dispatch_GLXMakeCurrentReadSGI(ClientPtr client)
+{
+    REQUEST(xGLXMakeCurrentReadSGIReq);
+    REQUEST_SIZE_MATCH(*stuff);
+
+    return CommonMakeCurrent(client, stuff->oldContextTag,
+            stuff->drawable, stuff->readable, stuff->context);
+}
+
+static int dispatch_GLXCopyContext(ClientPtr client)
+{
+    REQUEST(xGLXCopyContextReq);
+    GlxServerVendor *vendor;
+    REQUEST_SIZE_MATCH(*stuff);
+
+    // If we've got a context tag, then we'll use it to select a vendor. If we
+    // don't have a tag, then we'll look up one of the contexts. In either
+    // case, it's up to the vendor library to make sure that the context ID's
+    // are valid.
+    if (stuff->contextTag != 0) {
+        GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
+        if (tagInfo == NULL) {
+            return GlxErrorBase + GLXBadContextTag;
+        }
+        vendor = tagInfo->vendor;
+    } else {
+        vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->source));
+        if (vendor == NULL) {
+            return GlxErrorBase + GLXBadContext;
+        }
+    }
+    return vendor->glxvc.handleRequest(client);
+}
+
+static int dispatch_GLXSwapBuffers(ClientPtr client)
+{
+    GlxServerVendor *vendor = NULL;
+    REQUEST(xGLXSwapBuffersReq);
+    REQUEST_SIZE_MATCH(*stuff);
+
+    if (stuff->contextTag != 0) {
+        // If the request has a context tag, then look up a vendor from that.
+        // The vendor library is then responsible for validating the drawable.
+        GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
+        if (tagInfo == NULL) {
+            return GlxErrorBase + GLXBadContextTag;
+        }
+        vendor = tagInfo->vendor;
+    } else {
+        // We don't have a context tag, so look up the vendor from the
+        // drawable.
+        vendor = GlxGetXIDMap(GlxCheckSwap(client, stuff->drawable));
+        if (vendor == NULL) {
+            return GlxErrorBase + GLXBadDrawable;
+        }
+    }
+
+    return vendor->glxvc.handleRequest(client);
+}
+
+/**
+ * This is a generic handler for all of the X_GLXsop* requests.
+ */
+static int dispatch_GLXSingle(ClientPtr client)
+{
+    REQUEST(xGLXSingleReq);
+    GlxContextTagInfo *tagInfo;
+    REQUEST_AT_LEAST_SIZE(*stuff);
+
+    tagInfo = GlxLookupContextTag(client, GlxCheckSwap(client, stuff->contextTag));
+    if (tagInfo != NULL) {
+        return tagInfo->vendor->glxvc.handleRequest(client);
+    } else {
+        return GlxErrorBase + GLXBadContextTag;
+    }
+}
+
+static int dispatch_GLXVendorPriv(ClientPtr client)
+{
+    GlxVendorPrivDispatch *disp;
+    REQUEST(xGLXVendorPrivateReq);
+    REQUEST_AT_LEAST_SIZE(*stuff);
+
+    disp = LookupVendorPrivDispatch(GlxCheckSwap(client, stuff->vendorCode), TRUE);
+    if (disp == NULL) {
+        return BadAlloc;
+    }
+
+    if (disp->proc == NULL) {
+        // We don't have a dispatch function for this request yet. Check with
+        // each vendor library to find one.
+        // Note that even if none of the vendors provides a dispatch stub,
+        // we'll still add an entry to the dispatch table, so that we don't
+        // have to look it up again later.
+        disp = (GlxVendorPrivDispatch *) malloc(sizeof(GlxVendorPrivDispatch));
+        disp->proc = GetVendorDispatchFunc(stuff->glxCode,
+                                           GlxCheckSwap(client,
+                                                        stuff->vendorCode));
+        if (disp->proc == NULL) {
+            disp->proc = DispatchBadRequest;
+        }
+    }
+    return disp->proc(client);
+}
+
+Bool GlxDispatchInit(void)
+{
+    GlxVendorPrivDispatch *disp;
+
+    vendorPrivHash = ht_create(sizeof(CARD32), sizeof(GlxVendorPrivDispatch),
+                               ht_generic_hash, ht_generic_compare,
+                               (void *) &vendorPrivSetup);
+    if (!vendorPrivHash) {
+        return FALSE;
+    }
+
+    // Assign a custom dispatch stub GLXMakeCurrentReadSGI. This is the only
+    // vendor private request that we need to deal with in libglvnd itself.
+    disp = LookupVendorPrivDispatch(X_GLXvop_MakeCurrentReadSGI, TRUE);
+    if (disp == NULL) {
+        return FALSE;
+    }
+    disp->proc = dispatch_GLXMakeCurrentReadSGI;
+
+    // Assign the dispatch stubs for requests that need special handling.
+    dispatchFuncs[X_GLXQueryVersion] = dispatch_GLXQueryVersion;
+    dispatchFuncs[X_GLXMakeCurrent] = dispatch_GLXMakeCurrent;
+    dispatchFuncs[X_GLXMakeContextCurrent] = dispatch_GLXMakeContextCurrent;
+    dispatchFuncs[X_GLXCopyContext] = dispatch_GLXCopyContext;
+    dispatchFuncs[X_GLXSwapBuffers] = dispatch_GLXSwapBuffers;
+
+    dispatchFuncs[X_GLXClientInfo] = dispatch_GLXClientInfo;
+    dispatchFuncs[X_GLXSetClientInfoARB] = dispatch_GLXClientInfo;
+    dispatchFuncs[X_GLXSetConfigInfo2ARB] = dispatch_GLXClientInfo;
+
+    dispatchFuncs[X_GLXVendorPrivate] = dispatch_GLXVendorPriv;
+    dispatchFuncs[X_GLXVendorPrivateWithReply] = dispatch_GLXVendorPriv;
+
+    // Assign the trivial stubs
+    dispatchFuncs[X_GLXRender] = dispatch_Render;
+    dispatchFuncs[X_GLXRenderLarge] = dispatch_RenderLarge;
+    dispatchFuncs[X_GLXCreateContext] = dispatch_CreateContext;
+    dispatchFuncs[X_GLXDestroyContext] = dispatch_DestroyContext;
+    dispatchFuncs[X_GLXWaitGL] = dispatch_WaitGL;
+    dispatchFuncs[X_GLXWaitX] = dispatch_WaitX;
+    dispatchFuncs[X_GLXUseXFont] = dispatch_UseXFont;
+    dispatchFuncs[X_GLXCreateGLXPixmap] = dispatch_CreateGLXPixmap;
+    dispatchFuncs[X_GLXGetVisualConfigs] = dispatch_GetVisualConfigs;
+    dispatchFuncs[X_GLXDestroyGLXPixmap] = dispatch_DestroyGLXPixmap;
+    dispatchFuncs[X_GLXQueryExtensionsString] = dispatch_QueryExtensionsString;
+    dispatchFuncs[X_GLXQueryServerString] = dispatch_QueryServerString;
+    dispatchFuncs[X_GLXChangeDrawableAttributes] = dispatch_ChangeDrawableAttributes;
+    dispatchFuncs[X_GLXCreateNewContext] = dispatch_CreateNewContext;
+    dispatchFuncs[X_GLXCreatePbuffer] = dispatch_CreatePbuffer;
+    dispatchFuncs[X_GLXCreatePixmap] = dispatch_CreatePixmap;
+    dispatchFuncs[X_GLXCreateWindow] = dispatch_CreateWindow;
+    dispatchFuncs[X_GLXCreateContextAttribsARB] = dispatch_CreateContextAttribsARB;
+    dispatchFuncs[X_GLXDestroyPbuffer] = dispatch_DestroyPbuffer;
+    dispatchFuncs[X_GLXDestroyPixmap] = dispatch_DestroyPixmap;
+    dispatchFuncs[X_GLXDestroyWindow] = dispatch_DestroyWindow;
+    dispatchFuncs[X_GLXGetDrawableAttributes] = dispatch_GetDrawableAttributes;
+    dispatchFuncs[X_GLXGetFBConfigs] = dispatch_GetFBConfigs;
+    dispatchFuncs[X_GLXQueryContext] = dispatch_QueryContext;
+    dispatchFuncs[X_GLXIsDirect] = dispatch_IsDirect;
+
+    return TRUE;
+}
+
+void GlxDispatchReset(void)
+{
+    memset(dispatchFuncs, 0, sizeof(dispatchFuncs));
+
+    ht_destroy(vendorPrivHash);
+    vendorPrivHash = NULL;
+}
+
+int GlxDispatchRequest(ClientPtr client)
+{
+    REQUEST(xReq);
+    if (stuff->data < OPCODE_ARRAY_LEN) {
+        if (dispatchFuncs[stuff->data] == NULL) {
+            // Try to find a dispatch stub.
+            dispatchFuncs[stuff->data] = GetVendorDispatchFunc(stuff->data, 0);
+        }
+        return dispatchFuncs[stuff->data](client);
+    } else {
+        return dispatch_GLXSingle(client);
+    }
+}
diff --git a/glx/vndext.c b/glx/vndext.c
new file mode 100644
index 000000000..f593c499a
--- /dev/null
+++ b/glx/vndext.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ */
+
+#include "vndserver.h"
+
+#include <string.h>
+#include <scrnintstr.h>
+#include <windowstr.h>
+#include <dixstruct.h>
+#include <extnsionst.h>
+#include <glx_extinit.h>
+
+#include <GL/glxproto.h>
+#include "vndservervendor.h"
+
+int GlxErrorBase = 0;
+static CallbackListPtr vndInitCallbackList;
+static DevPrivateKeyRec glvXGLVScreenPrivKey;
+static DevPrivateKeyRec glvXGLVClientPrivKey;
+
+// The resource type used to keep track of the vendor library for XID's.
+RESTYPE idResource;
+
+static int
+idResourceDeleteCallback(void *value, XID id)
+{
+    return 0;
+}
+
+static GlxScreenPriv *
+xglvGetScreenPrivate(ScreenPtr pScreen)
+{
+    return dixLookupPrivate(&pScreen->devPrivates, &glvXGLVScreenPrivKey);
+}
+
+static void
+xglvSetScreenPrivate(ScreenPtr pScreen, void *priv)
+{
+    dixSetPrivate(&pScreen->devPrivates, &glvXGLVScreenPrivKey, priv);
+}
+
+GlxScreenPriv *
+GlxGetScreen(ScreenPtr pScreen)
+{
+    if (pScreen != NULL) {
+        GlxScreenPriv *priv = xglvGetScreenPrivate(pScreen);
+        if (priv == NULL) {
+            priv = calloc(1, sizeof(GlxScreenPriv));
+            if (priv == NULL) {
+                return NULL;
+            }
+
+            xglvSetScreenPrivate(pScreen, priv);
+        }
+        return priv;
+    } else {
+        return NULL;
+    }
+}
+
+static void
+GlxMappingReset(void)
+{
+    int i;
+
+    for (i=0; i<screenInfo.numScreens; i++) {
+        GlxScreenPriv *priv = xglvGetScreenPrivate(screenInfo.screens[i]);
+        if (priv != NULL) {
+            xglvSetScreenPrivate(screenInfo.screens[i], NULL);
+            free(priv);
+        }
+    }
+}
+
+static Bool
+GlxMappingInit(void)
+{
+    int i;
+
+    for (i=0; i<screenInfo.numScreens; i++) {
+        if (GlxGetScreen(screenInfo.screens[i]) == NULL) {
+            GlxMappingReset();
+            return FALSE;
+        }
+    }
+
+    idResource = CreateNewResourceType(idResourceDeleteCallback,
+                                       "GLXServerIDRes");
+    if (idResource == RT_NONE)
+    {
+        GlxMappingReset();
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static GlxClientPriv *
+xglvGetClientPrivate(ClientPtr pClient)
+{
+    return dixLookupPrivate(&pClient->devPrivates, &glvXGLVClientPrivKey);
+}
+
+static void
+xglvSetClientPrivate(ClientPtr pClient, void *priv)
+{
+    dixSetPrivate(&pClient->devPrivates, &glvXGLVClientPrivKey, priv);
+}
+
+GlxClientPriv *
+GlxGetClientData(ClientPtr client)
+{
+    GlxClientPriv *cl = xglvGetClientPrivate(client);
+    if (cl == NULL) {
+        cl = calloc(1, sizeof(GlxClientPriv));
+        if (cl != NULL) {
+            xglvSetClientPrivate(client, cl);
+        }
+    }
+    return cl;
+}
+
+void
+GlxFreeClientData(ClientPtr client)
+{
+    GlxClientPriv *cl = xglvGetClientPrivate(client);
+    if (cl != NULL) {
+        unsigned int i;
+        for (i = 0; i < cl->contextTagCount; i++) {
+            GlxContextTagInfo *tag = &cl->contextTags[i];
+            if (tag->vendor != NULL) {
+                tag->vendor->glxvc.makeCurrent(client, tag->tag,
+                                               None, None, None, 0);
+            }
+        }
+        xglvSetClientPrivate(client, NULL);
+        free(cl->contextTags);
+        free(cl);
+    }
+}
+
+static void
+GLXClientCallback(CallbackListPtr *list, void *closure, void *data)
+{
+    NewClientInfoRec *clientinfo = (NewClientInfoRec *) data;
+    ClientPtr client = clientinfo->client;
+
+    switch (client->clientState)
+    {
+        case ClientStateRetained:
+        case ClientStateGone:
+            GlxFreeClientData(client);
+            break;
+    }
+}
+
+static void
+GLXReset(ExtensionEntry *extEntry)
+{
+    // xf86Msg(X_INFO, "GLX: GLXReset\n");
+
+    GlxVendorExtensionReset(extEntry);
+    GlxDispatchReset();
+    GlxMappingReset();
+}
+
+void
+GlxExtensionInit(void)
+{
+    ExtensionEntry *extEntry;
+
+    // Init private keys, per-screen data
+    if (!dixRegisterPrivateKey(&glvXGLVScreenPrivKey, PRIVATE_SCREEN, 0))
+        return;
+    if (!dixRegisterPrivateKey(&glvXGLVClientPrivKey, PRIVATE_CLIENT, 0))
+        return;
+
+    if (!GlxMappingInit()) {
+        return;
+    }
+
+    if (!GlxDispatchInit()) {
+        return;
+    }
+
+    if (!AddCallback(&ClientStateCallback, GLXClientCallback, NULL)) {
+        return;
+    }
+
+    extEntry = AddExtension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS,
+                            __GLX_NUMBER_ERRORS, GlxDispatchRequest,
+                            GlxDispatchRequest, GLXReset, StandardMinorOpcode);
+    if (!extEntry) {
+        return;
+    }
+
+    GlxErrorBase = extEntry->errorBase;
+    CallCallbacks(&vndInitCallbackList, extEntry);
+}
+
+static int
+GlxForwardRequest(GlxServerVendor *vendor, ClientPtr client)
+{
+    return vendor->glxvc.handleRequest(client);
+}
+
+static GlxServerVendor *
+GlxGetContextTag(ClientPtr client, GLXContextTag tag)
+{
+    GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, tag);
+
+    if (tagInfo != NULL) {
+        return tagInfo->vendor;
+    } else {
+        return NULL;
+    }
+}
+
+static Bool
+GlxSetContextTagPrivate(ClientPtr client, GLXContextTag tag, void *data)
+{
+    GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, tag);
+    if (tagInfo != NULL) {
+        tagInfo->data = data;
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+static void *
+GlxGetContextTagPrivate(ClientPtr client, GLXContextTag tag)
+{
+    GlxContextTagInfo *tagInfo = GlxLookupContextTag(client, tag);
+    if (tagInfo != NULL) {
+        return tagInfo->data;
+    } else {
+        return NULL;
+    }
+}
+
+static GlxServerImports *
+GlxAllocateServerImports(void)
+{
+    return calloc(1, sizeof(GlxServerImports));
+}
+
+static void
+GlxFreeServerImports(GlxServerImports *imports)
+{
+    free(imports);
+}
+
+_X_EXPORT const GlxServerExports glxServer = {
+    GLXSERVER_VENDOR_ABI_MAJOR_VERSION, // majorVersion
+    GLXSERVER_VENDOR_ABI_MINOR_VERSION, // minorVersion
+
+    &vndInitCallbackList,
+
+    GlxAllocateServerImports, // allocateServerImports
+    GlxFreeServerImports, // freeServerImports
+
+    GlxCreateVendor, // createVendor
+    GlxDestroyVendor, // destroyVendor
+    GlxSetScreenVendor, // setScreenVendor
+
+    GlxAddXIDMap, // addXIDMap
+    GlxGetXIDMap, // getXIDMap
+    GlxRemoveXIDMap, // removeXIDMap
+    GlxGetContextTag, // getContextTag
+    GlxSetContextTagPrivate, // setContextTagPrivate
+    GlxGetContextTagPrivate, // getContextTagPrivate
+    GlxGetVendorForScreen, // getVendorForScreen
+    GlxForwardRequest, // forwardRequest
+};
+
+const GlxServerExports *
+glvndGetExports(void)
+{
+    return &glxServer;
+}
diff --git a/glx/vndserver.h b/glx/vndserver.h
new file mode 100644
index 000000000..12297349c
--- /dev/null
+++ b/glx/vndserver.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ */
+
+#ifndef VNDSERVER_H
+#define VNDSERVER_H
+
+#include <dix-config.h>
+#include "glxvndabi.h"
+
+#define GLXContextID CARD32
+#define GLXDrawable CARD32
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct GlxScreenPrivRec {
+    GlxServerVendor *vendor;
+} GlxScreenPriv;
+
+typedef struct GlxContextTagInfoRec {
+    GLXContextTag tag;
+    ClientPtr client;
+    GlxServerVendor *vendor;
+    void *data;
+    GLXContextID context;
+    GLXDrawable drawable;
+    GLXDrawable readdrawable;
+} GlxContextTagInfo;
+
+typedef struct GlxClientPrivRec {
+    GlxContextTagInfo *contextTags;
+    unsigned int contextTagCount;
+} GlxClientPriv;
+
+extern int GlxErrorBase;
+extern RESTYPE idResource;
+
+// Defined in glxext.c.
+const ExtensionEntry *GlxGetExtensionEntry(void);
+
+Bool GlxDispatchInit(void);
+void GlxDispatchReset(void);
+
+/**
+ * Handles a request from the client.
+ *
+ * This function will look up the correct handler function and forward the
+ * request to it.
+ */
+int GlxDispatchRequest(ClientPtr client);
+
+/**
+ * Looks up the GlxClientPriv struct for a client. If we don't have a
+ * GlxClientPriv struct yet, then allocate one.
+ */
+GlxClientPriv *GlxGetClientData(ClientPtr client);
+
+/**
+ * Frees any data that's specific to a client. This should be called when a
+ * client disconnects.
+ */
+void GlxFreeClientData(ClientPtr client);
+
+Bool GlxAddXIDMap(XID id, GlxServerVendor *vendor);
+GlxServerVendor * GlxGetXIDMap(XID id);
+void GlxRemoveXIDMap(XID id);
+
+GlxContextTagInfo *GlxAllocContextTag(ClientPtr client, GlxServerVendor *vendor);
+GlxContextTagInfo *GlxLookupContextTag(ClientPtr client, GLXContextTag tag);
+void GlxFreeContextTag(GlxContextTagInfo *tagInfo);
+
+Bool GlxSetScreenVendor(ScreenPtr screen, GlxServerVendor *vendor);
+GlxScreenPriv *GlxGetScreen(ScreenPtr pScreen);
+GlxServerVendor *GlxGetVendorForScreen(ClientPtr client, ScreenPtr screen);
+
+static inline CARD32 GlxCheckSwap(ClientPtr client, CARD32 value)
+{
+    if (client->swapped)
+    {
+        value = ((value & 0XFF000000) >> 24) | ((value & 0X00FF0000) >>  8)
+            | ((value & 0X0000FF00) <<  8) | ((value & 0X000000FF) << 24);
+    }
+    return value;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+_X_EXPORT const GlxServerExports *glvndGetExports(void);
+
+#endif // VNDSERVER_H
diff --git a/glx/vndservermapping.c b/glx/vndservermapping.c
new file mode 100644
index 000000000..fd3be92d9
--- /dev/null
+++ b/glx/vndservermapping.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ */
+
+#include "vndserver.h"
+
+#include <pixmapstr.h>
+
+#include "vndservervendor.h"
+
+static GlxServerVendor *LookupXIDMapResource(XID id)
+{
+    void *ptr = NULL;
+    int rv;
+
+    rv = dixLookupResourceByType(&ptr, id, idResource, NULL, DixReadAccess);
+    if (rv == Success) {
+        return (GlxServerVendor *) ptr;
+    } else {
+        return NULL;
+    }
+}
+
+GlxServerVendor *GlxGetXIDMap(XID id)
+{
+    GlxServerVendor *vendor = LookupXIDMapResource(id);
+
+    if (vendor == NULL) {
+        // If we haven't seen this XID before, then it may be a drawable that
+        // wasn't created through GLX, like a regular X window or pixmap. Try
+        // to look up a matching drawable to find a screen number for it.
+        void *ptr = NULL;
+        int rv = dixLookupResourceByClass(&ptr, id, RC_DRAWABLE, NULL,
+                                         DixGetAttrAccess);
+        if (rv == Success && ptr != NULL) {
+            DrawablePtr draw = (DrawablePtr) ptr;
+            GlxScreenPriv *screenPriv = GlxGetScreen(draw->pScreen);
+            if (screenPriv != NULL) {
+                vendor = screenPriv->vendor;
+            }
+        }
+    }
+    return vendor;
+}
+
+Bool GlxAddXIDMap(XID id, GlxServerVendor *vendor)
+{
+    if (id == 0 || vendor == NULL) {
+        return FALSE;
+    }
+    if (LookupXIDMapResource(id) != NULL) {
+        return FALSE;
+    }
+    return AddResource(id, idResource, vendor);
+}
+
+void GlxRemoveXIDMap(XID id)
+{
+    FreeResourceByType(id, idResource, FALSE);
+}
+
+GlxContextTagInfo *GlxAllocContextTag(ClientPtr client, GlxServerVendor *vendor)
+{
+    GlxClientPriv *cl;
+    unsigned int index;
+
+    if (vendor == NULL) {
+        return NULL;
+    }
+
+    cl = GlxGetClientData(client);
+    if (cl == NULL) {
+        return NULL;
+    }
+
+    // Look for a free tag index.
+    for (index=0; index<cl->contextTagCount; index++) {
+        if (cl->contextTags[index].vendor == NULL) {
+            break;
+        }
+    }
+    if (index >= cl->contextTagCount) {
+        // We didn't find a free entry, so grow the array.
+        GlxContextTagInfo *newTags;
+        unsigned int newSize = cl->contextTagCount * 2;
+        if (newSize == 0) {
+            // TODO: What's a good starting size for this?
+            newSize = 16;
+        }
+
+        newTags = (GlxContextTagInfo *)
+            realloc(cl->contextTags, newSize * sizeof(GlxContextTagInfo));
+        if (newTags == NULL) {
+            return NULL;
+        }
+
+        memset(&newTags[cl->contextTagCount], 0,
+                (newSize - cl->contextTagCount) * sizeof(GlxContextTagInfo));
+
+        index = cl->contextTagCount;
+        cl->contextTags = newTags;
+        cl->contextTagCount = newSize;
+    }
+
+    assert(index >= 0);
+    assert(index < cl->contextTagCount);
+    memset(&cl->contextTags[index], 0, sizeof(GlxContextTagInfo));
+    cl->contextTags[index].tag = (GLXContextTag) (index + 1);
+    cl->contextTags[index].client = client;
+    cl->contextTags[index].vendor = vendor;
+    return &cl->contextTags[index];
+}
+
+GlxContextTagInfo *GlxLookupContextTag(ClientPtr client, GLXContextTag tag)
+{
+    GlxClientPriv *cl = GlxGetClientData(client);
+    if (cl == NULL) {
+        return NULL;
+    }
+
+    if (tag > 0 && (tag - 1) < cl->contextTagCount) {
+        if (cl->contextTags[tag - 1].vendor != NULL) {
+            assert(cl->contextTags[tag - 1].client == client);
+            return &cl->contextTags[tag - 1];
+        }
+    }
+    return NULL;
+}
+
+void GlxFreeContextTag(GlxContextTagInfo *tagInfo)
+{
+    if (tagInfo != NULL) {
+        tagInfo->vendor = NULL;
+        tagInfo->vendor = NULL;
+        tagInfo->data = NULL;
+        tagInfo->context = None;
+        tagInfo->drawable = None;
+        tagInfo->readdrawable = None;
+    }
+}
+
+Bool GlxSetScreenVendor(ScreenPtr screen, GlxServerVendor *vendor)
+{
+    GlxScreenPriv *priv;
+
+    if (vendor == NULL) {
+        return FALSE;
+    }
+
+    priv = GlxGetScreen(screen);
+    if (priv == NULL) {
+        return FALSE;
+    }
+
+    if (priv->vendor != NULL) {
+        return FALSE;
+    }
+
+    priv->vendor = vendor;
+    return TRUE;
+}
+
+GlxServerVendor *GlxGetVendorForScreen(ClientPtr client, ScreenPtr screen)
+{
+    GlxScreenPriv *priv = GlxGetScreen(screen);
+    if (priv != NULL) {
+        return priv->vendor;
+    } else {
+        return NULL;
+    }
+}
diff --git a/glx/vndservervendor.c b/glx/vndservervendor.c
new file mode 100644
index 000000000..bd86c67f7
--- /dev/null
+++ b/glx/vndservervendor.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ */
+
+#include "vndservervendor.h"
+
+struct xorg_list GlxVendorList = { &GlxVendorList, &GlxVendorList };
+
+GlxServerVendor *GlxCreateVendor(const GlxServerImports *imports)
+{
+    GlxServerVendor *vendor = NULL;
+
+    if (imports == NULL) {
+        ErrorF("GLX: Vendor library did not provide an imports table\n");
+        return NULL;
+    }
+
+    if (imports->extensionCloseDown == NULL
+            || imports->handleRequest == NULL
+            || imports->getDispatchAddress == NULL
+            || imports->makeCurrent == NULL) {
+        ErrorF("GLX: Vendor library is missing required callback functions.\n");
+        return NULL;
+    }
+
+    vendor = (GlxServerVendor *) calloc(1, sizeof(GlxServerVendor));
+    if (vendor == NULL) {
+        ErrorF("GLX: Can't allocate vendor library.\n");
+        return NULL;
+    }
+    memcpy(&vendor->glxvc, imports, sizeof(GlxServerImports));
+
+    xorg_list_append(&vendor->entry, &GlxVendorList);
+    return vendor;
+}
+
+void GlxDestroyVendor(GlxServerVendor *vendor)
+{
+    if (vendor != NULL) {
+        xorg_list_del(&vendor->entry);
+        free(vendor);
+    }
+}
+
+void GlxVendorExtensionReset(const ExtensionEntry *extEntry)
+{
+    GlxServerVendor *vendor, *tempVendor;
+
+    // TODO: Do we allow the driver to destroy a vendor library handle from
+    // here?
+    xorg_list_for_each_entry_safe(vendor, tempVendor, &GlxVendorList, entry) {
+        if (vendor->glxvc.extensionCloseDown != NULL) {
+            vendor->glxvc.extensionCloseDown(extEntry);
+        }
+    }
+
+    // If the server is exiting instead of starting a new generation, then
+    // free the remaining GlxServerVendor structs.
+    //
+    // XXX this used to be conditional on xf86ServerIsExiting, but it's
+    // cleaner to just always create the vendor struct on every generation,
+    // if nothing else so all ddxes get the same behavior.
+    xorg_list_for_each_entry_safe(vendor, tempVendor, &GlxVendorList, entry) {
+        GlxDestroyVendor(vendor);
+    }
+}
diff --git a/glx/vndservervendor.h b/glx/vndservervendor.h
new file mode 100644
index 000000000..77cb983a6
--- /dev/null
+++ b/glx/vndservervendor.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ */
+
+#ifndef VND_SERVER_VENDOR_H
+#define VND_SERVER_VENDOR_H
+
+#include <dix-config.h>
+
+#include "glxvndabi.h"
+#include "list.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * Info related to a single vendor library.
+ */
+struct GlxServerVendorRec {
+    GlxServerImports glxvc;
+
+    struct xorg_list entry;
+};
+
+/**
+ * A linked list of vendor libraries.
+ *
+ * Note that this list only includes vendor libraries that were successfully
+ * initialized.
+ */
+extern struct xorg_list GlxVendorList;
+
+GlxServerVendor *GlxCreateVendor(const GlxServerImports *imports);
+void GlxDestroyVendor(GlxServerVendor *vendor);
+
+void GlxVendorExtensionReset(const ExtensionEntry *extEntry);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // VND_SERVER_VENDOR_H
diff --git a/include/Makefile.am b/include/Makefile.am
index cbc4a7c58..afd6db2cf 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -27,6 +27,7 @@ sdk_HEADERS =		\
 	gcstruct.h	\
 	globals.h	\
 	glx_extinit.h	\
+	glxvndabi.h	\
 	input.h		\
 	inputstr.h	\
 	list.h		\
diff --git a/include/glxvndabi.h b/include/glxvndabi.h
new file mode 100644
index 000000000..9fa0e6e23
--- /dev/null
+++ b/include/glxvndabi.h
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * unaltered in all copies or substantial portions of the Materials.
+ * Any additions, deletions, or changes to the original source files
+ * must be clearly indicated in accompanying documentation.
+ *
+ * If only executable code is distributed, then the accompanying
+ * documentation must state that "this software is based in part on the
+ * work of the Khronos Group."
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ */
+
+/**
+ * \file
+ *
+ * Defines the interface between the libglvnd server module and a vendor
+ * library.
+ *
+ * Each screen may have one vendor library assigned to it. The GLVND module
+ * will examine each GLX request to determine which screen it goes to, and then
+ * it will forward that request to whichever vendor is assigned to that screen.
+ *
+ * Each vendor library is represented by an opaque __GLXServerVendor handle.
+ * Display drivers are responsible for creating handles for its GLX
+ * implementations, and assigning those handles to each screen.
+ *
+ * The GLVND module keeps a list of callbacks, which are called from
+ * InitExtensions. Drivers should use that callback to assign a vendor
+ * handle to whichever screens they support.
+ *
+ * Additional notes about dispatching:
+ * - If a request has one or more GLXContextTag values, then the dispatch stub
+ *   must ensure that all of the tags belong to the vendor that it forwards the
+ *   request to. Otherwise, if a vendor library tries to look up the private
+ *   data for the tag, it could get the data from another vendor and crash.
+ * - Following from the last point, if a request takes a GLXContextTag value,
+ *   then the dispatch stub should use the tag to select a vendor. If the
+ *   request takes two or more tags, then the vendor must ensure that they all
+ *   map to the same vendor.
+ */
+
+#ifndef GLXVENDORABI_H
+#define GLXVENDORABI_H
+
+#include <scrnintstr.h>
+#include <extnsionst.h>
+#include <GL/glxproto.h>
+
+/*!
+ * Current version of the ABI.
+ *
+ * This version number contains a major number in the high-order 16 bits, and
+ * a minor version number in the low-order 16 bits.
+ *
+ * The major version number is incremented when an interface change will break
+ * backwards compatibility with existing vendor libraries. The minor version
+ * number is incremented when there's a change but existing vendor libraries
+ * will still work.
+ */
+#define GLXSERVER_VENDOR_ABI_MAJOR_VERSION 0
+#define GLXSERVER_VENDOR_ABI_MINOR_VERSION 0
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * An opaque pointer representing a vendor library.
+ */
+typedef struct GlxServerVendorRec GlxServerVendor;
+
+typedef int (* GlxServerDispatchProc) (ClientPtr client);
+
+typedef struct GlxServerImportsRec GlxServerImports;
+
+/**
+ * Functions exported by libglvnd to the vendor library.
+ */
+typedef struct GlxServerExportsRec {
+    int majorVersion;
+    int minorVersion;
+
+    /**
+     * This callback is called during each server generation when the GLX
+     * extension is initialized.
+     *
+     * Drivers may create a __GLXServerVendor handle at any time, but may only
+     * assign a vendor to a screen from this callback.
+     *
+     * The callback is called with the ExtensionEntry pointer for the GLX
+     * extension.
+     */
+    CallbackListPtr *extensionInitCallback;
+
+    /**
+     * Allocates and zeroes a __GLXserverImports structure.
+     *
+     * Future versions of the GLVND interface may add optional members to the
+     * end of the __GLXserverImports struct. Letting the GLVND layer allocate
+     * the __GLXserverImports struct allows backward compatibility with
+     * existing drivers.
+     */
+    GlxServerImports * (* allocateServerImports) (void);
+
+    /**
+     * Frees a __GLXserverImports structure that was allocated with
+     * \c allocateServerImports.
+     */
+    void (* freeServerImports) (GlxServerImports *imports);
+
+    /**
+     * Creates a new vendor library handle.
+     */
+    GlxServerVendor * (* createVendor) (const GlxServerImports *imports);
+
+    /**
+     * Destroys a vendor library handle.
+     *
+     * This function may not be called while the vendor handle is assigned to a
+     * screen, but it may be called from the __GLXserverImports::extensionCloseDown
+     * callback.
+     */
+    void (* destroyVendor) (GlxServerVendor *vendor);
+
+    /**
+     * Sets the vendor library to use for a screen.
+     *
+     * This function should be called from the screen's CreateScreenResources
+     * callback.
+     */
+    Bool (* setScreenVendor) (ScreenPtr screen, GlxServerVendor *vendor);
+
+
+    /**
+     * Adds an entry to the XID map.
+     *
+     * This mapping is used to dispatch requests based on an XID.
+     *
+     * Client-generated XID's (contexts, drawables, etc) must be added to the
+     * map by the dispatch stub.
+     *
+     * XID's that are generated in the server should be added by the vendor
+     * library.
+     *
+     * Vendor libraries are responsible for keeping track of any additional
+     * data they need for the XID's.
+     *
+     * Note that adding GLXFBConfig ID's appears to be unnecessary -- every GLX
+     * request I can find that takes a GLXFBConfig also takes a screen number.
+     *
+     * \param id The XID to add to the map. The XID must not already be in the
+     *      map.
+     * \param vendor The vendor library to associate with \p id.
+     * \return True on success, or False on failure.
+     */
+    Bool (* addXIDMap) (XID id, GlxServerVendor *vendor);
+
+    /**
+     * Returns the vendor and data for an XID, as added with \c addXIDMap.
+     *
+     * If \p id wasn't added with \c addXIDMap (for example, if it's a regular
+     * X window), then libglvnd will try to look it up as a drawable and return
+     * the vendor for whatever screen it's on.
+     *
+     * \param id The XID to look up.
+     * \return The vendor that owns the XID, or \c NULL if no matching vendor
+     * was found.
+     */
+    GlxServerVendor * (* getXIDMap) (XID id);
+
+    /**
+     * Removes an entry from the XID map.
+     */
+    void (* removeXIDMap) (XID id);
+
+    /**
+     * Looks up a context tag.
+     *
+     * Context tags are created and managed by libglvnd to ensure that they're
+     * unique between vendors.
+     *
+     * \param client The client connection.
+     * \param tag The context tag.
+     * \return The vendor that owns the context tag, or \c NULL if the context
+     * tag is invalid.
+     */
+    GlxServerVendor * (* getContextTag)(ClientPtr client, GLXContextTag tag);
+
+    /**
+     * Assigns a pointer to vendor-private data for a context tag.
+     *
+     * Since the tag values are assigned by GLVND, vendors can use this
+     * function to store any private data they need for a context tag.
+     *
+     * \param client The client connection.
+     * \param tag The context tag.
+     * \param data An arbitrary pointer value.
+     */
+    Bool (* setContextTagPrivate)(ClientPtr client, GLXContextTag tag, void *data);
+
+    /**
+     * Returns the private data pointer that was assigned from
+     * setContextTagPrivate.
+     *
+     * This function is safe to use in __GLXserverImports::makeCurrent to look
+     * up the old context private pointer.
+     *
+     * However, this function is not safe to use from a ClientStateCallback,
+     * because GLVND may have alraedy deleted the tag by that point.
+     */
+    void * (* getContextTagPrivate)(ClientPtr client, GLXContextTag tag);
+
+    GlxServerVendor * (* getVendorForScreen) (ClientPtr client, ScreenPtr screen);
+
+    /**
+     * Forwards a request to a vendor library.
+     *
+     * \param vendor The vendor to send the request to.
+     * \param client The client.
+     */
+    int (* forwardRequest) (GlxServerVendor *vendor, ClientPtr client);
+} GlxServerExports;
+
+extern _X_EXPORT const GlxServerExports glxServer;
+
+/**
+ * Functions exported by the vendor library to libglvnd.
+ */
+struct GlxServerImportsRec {
+    /**
+     * Called on a server reset.
+     *
+     * This is called from the extension's CloseDown callback.
+     *
+     * Note that this is called after freeing all of GLVND's per-screen data,
+     * so the callback may destroy any vendor handles.
+     *
+     * If the server is exiting, then GLVND will free any remaining vendor
+     * handles after calling the extensionCloseDown callbacks.
+     */
+    void (* extensionCloseDown) (const ExtensionEntry *extEntry);
+
+    /**
+     * Handles a GLX request.
+     */
+    int (* handleRequest) (ClientPtr client);
+
+    /**
+     * Returns a dispatch function for a request.
+     *
+     * \param minorOpcode The minor opcode of the request.
+     * \param vendorCode The vendor opcode, if \p minorOpcode
+     *      is \c X_GLXVendorPrivate or \c X_GLXVendorPrivateWithReply.
+     * \return A dispatch function, or NULL if the vendor doesn't support this
+     *      request.
+     */
+    GlxServerDispatchProc (* getDispatchAddress) (CARD8 minorOpcode, CARD32 vendorCode);
+
+    /**
+     * Handles a MakeCurrent request.
+     *
+     * This function is called to handle any MakeCurrent request. The vendor
+     * library should deal with changing the current context. After the vendor
+     * returns GLVND will send the reply.
+     *
+     * In addition, GLVND will call this function with any current contexts
+     * when a client disconnects.
+     *
+     * To ensure that context tags are unique, libglvnd will select a context
+     * tag and pass it to the vendor library.
+     *
+     * iThe vendor can use \c __GLXserverExports::getContextTagPrivate to look
+     * up the private data pointer for \p oldContextTag.
+     *
+     * Likewise, the vendor can use \c __GLXserverExports::setContextTagPrivate
+     * to assign a private data pointer to \p newContextTag.
+     */
+    int (* makeCurrent) (ClientPtr client,
+        GLXContextTag oldContextTag,
+        XID drawable,
+        XID readdrawable,
+        XID context,
+        GLXContextTag newContextTag);
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // GLXVENDORABI_H
diff --git a/include/meson.build b/include/meson.build
index 216744ca2..2a9e21d71 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -324,6 +324,7 @@ if build_xorg
             'gcstruct.h',
             'globals.h',
             'glx_extinit.h',
+            'glxvndabi.h',
             'input.h',
             'inputstr.h',
             'list.h',
-- 
2.14.3



More information about the xorg-devel mailing list