Patchwork [1/6] gl: Add an OpenGL offscreen rendering API and backends.

login
register
mail settings
Submitter andrzej zaborowski
Date Jan. 9, 2012, 7:57 a.m.
Message ID <1326095876-31319-2-git-send-email-balrogg@gmail.com>
Download mbox | patch
Permalink /patch/135091/
State New
Headers show

Comments

andrzej zaborowski - Jan. 9, 2012, 7:57 a.m.
This helps the OpenGL passthrough code access OpenGL rendering
without cluttering the screen and removes the differences between
host platforms.  It's not tied to qemu's SDL window and can in
theory connect to an entirely different X server.  Most GL calls are
still made directly from the code outside of gloffscreen, so no
overhead is added, while making the initialisation and such platform
independent.  It lets qemu create an offscreen rendering surface,
render something on it and then request the contents.

Two backends are provided: GLX for X windows (including MacOS) and WGL
for Ms-Windows.

SDL as a backend may also be possible to support the more rare
hosts.  We've not attempted it.

Signed-off-by: Andrzej Zaborowski <andrew.zaborowski@intel.com>
---
 gl/gloffscreen-common.c     |  579 ++++++++++++++++++++++++++++++
 gl/gloffscreen-wgl.c        |  832 +++++++++++++++++++++++++++++++++++++++++++
 gl/gloffscreen-xcomposite.c |  518 +++++++++++++++++++++++++++
 gl/gloffscreen.h            |  138 +++++++
 4 files changed, 2067 insertions(+), 0 deletions(-)
 create mode 100644 gl/gloffscreen-common.c
 create mode 100644 gl/gloffscreen-wgl.c
 create mode 100644 gl/gloffscreen-xcomposite.c
 create mode 100644 gl/gloffscreen.h

Patch

diff --git a/gl/gloffscreen-common.c b/gl/gloffscreen-common.c
new file mode 100644
index 0000000..2142715
--- /dev/null
+++ b/gl/gloffscreen-common.c
@@ -0,0 +1,579 @@ 
+/*
+ * Offscreen OpenGL abstraction layer - Common utilities
+ *
+ * Copyright (c) 2010-2012 Intel Corporation
+ * Authors:
+ *   Gordon Williams <gordon.williams@collabora.co.uk>
+ *   Ian Molton <ian.molton@collabora.co.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#include <GL/gl.h>
+#include <GL/glext.h>
+#else
+#include <GL/gl.h>
+#endif
+
+#include "gloffscreen.h"
+#include "sysemu.h"
+
+/*
+ * (Copied from glx.h because we need these on windows too)
+ * Tokens for glXChooseVisual and glXGetConfig:
+ */
+#define GLX_USE_GL		1
+#define GLX_BUFFER_SIZE		2
+#define GLX_LEVEL		3
+#define GLX_RGBA		4
+#define GLX_DOUBLEBUFFER	5
+#define GLX_STEREO		6
+#define GLX_AUX_BUFFERS		7
+#define GLX_RED_SIZE		8
+#define GLX_GREEN_SIZE		9
+#define GLX_BLUE_SIZE		10
+#define GLX_ALPHA_SIZE		11
+#define GLX_DEPTH_SIZE		12
+#define GLX_STENCIL_SIZE	13
+#define GLX_ACCUM_RED_SIZE	14
+#define GLX_ACCUM_GREEN_SIZE	15
+#define GLX_ACCUM_BLUE_SIZE	16
+#define GLX_ACCUM_ALPHA_SIZE	17
+/*
+ * GLX 1.3 and later:
+ */
+#define GLX_CONFIG_CAVEAT		0x20
+#define GLX_DONT_CARE			0xFFFFFFFF
+#define GLX_X_VISUAL_TYPE		0x22
+#define GLX_TRANSPARENT_TYPE		0x23
+#define GLX_TRANSPARENT_INDEX_VALUE	0x24
+#define GLX_TRANSPARENT_RED_VALUE	0x25
+#define GLX_TRANSPARENT_GREEN_VALUE	0x26
+#define GLX_TRANSPARENT_BLUE_VALUE	0x27
+#define GLX_TRANSPARENT_ALPHA_VALUE	0x28
+#define GLX_WINDOW_BIT			0x00000001
+#define GLX_NONE			0x8000
+#define GLX_TRUE_COLOR			0x8002
+#define GLX_VISUAL_ID			0x800B
+#define GLX_DRAWABLE_TYPE		0x8010
+#define GLX_RENDER_TYPE			0x8011
+#define GLX_X_RENDERABLE		0x8012
+#define GLX_FBCONFIG_ID			0x8013
+#define GLX_RGBA_TYPE			0x8014
+#define GLX_MAX_PBUFFER_WIDTH		0x8016
+#define GLX_MAX_PBUFFER_HEIGHT		0x8017
+#define GLX_MAX_PBUFFER_PIXELS		0x8018
+#define GLX_LARGEST_PBUFFER		0x801C
+#define GLX_RGBA_BIT			0x00000001
+
+int glo_flags_get_depth_bits(int format_flags)
+{
+    switch (format_flags & GLO_FF_DEPTH_MASK) {
+    case GLO_FF_DEPTH_16:
+        return 16;
+    case GLO_FF_DEPTH_24:
+        return 24;
+    case GLO_FF_DEPTH_32:
+        return 32;
+    default:
+        return 0;
+    }
+}
+
+int glo_flags_get_stencil_bits(int format_flags)
+{
+    switch (format_flags & GLO_FF_STENCIL_MASK) {
+    case GLO_FF_STENCIL_8:
+        return 8;
+    default:
+        return 0;
+    }
+}
+
+void glo_flags_get_rgba_bits(int format_flags, int *rgba)
+{
+    int alpha = (format_flags & GLO_FF_ALPHA) != 0;
+
+    switch (format_flags & GLO_FF_BITS_MASK) {
+    case GLO_FF_BITS_16:
+        rgba[0] = alpha ? 4 : 5;
+        rgba[1] = alpha ? 4 : 6;
+        rgba[2] = alpha ? 4 : 5;
+        rgba[3] = alpha ? 4 : 0;
+        break;
+    case GLO_FF_BITS_24:
+        /* ignore alpha */
+        rgba[0] = 8;
+        rgba[1] = 8;
+        rgba[2] = 8;
+        rgba[3] = 0;
+        break;
+    case GLO_FF_BITS_32:
+        rgba[0] = 8;
+        rgba[1] = 8;
+        rgba[2] = 8;
+        rgba[3] = 8;
+        break;
+    default:
+        rgba[0] = 8;
+        rgba[1] = 8;
+        rgba[2] = 8;
+        rgba[3] = 0;
+        break;
+    }
+}
+
+int glo_flags_get_bytes_per_pixel(int format_flags)
+{
+    switch (format_flags & GLO_FF_BITS_MASK) {
+    case GLO_FF_BITS_16:
+        return 2;
+    case GLO_FF_BITS_24:
+        return 3;
+    case GLO_FF_BITS_32:
+        return 4;
+    default:
+        return 3;
+    }
+}
+
+void glo_flags_get_readpixel_type(int format_flags,
+                int *gl_format, int *gl_type)
+{
+    GLenum gformat, gtype;
+
+    if (format_flags & GLO_FF_ALPHA) {
+        switch (format_flags & GLO_FF_BITS_MASK) {
+        case GLO_FF_BITS_16:
+            gformat = GL_RGBA;
+            gtype = GL_UNSIGNED_SHORT_4_4_4_4;
+            break;
+        case GLO_FF_BITS_24:
+        case GLO_FF_BITS_32:
+        default:
+            gformat = GL_BGRA;
+            gtype = GL_UNSIGNED_BYTE;
+            break;
+        }
+    } else {
+        switch (format_flags & GLO_FF_BITS_MASK) {
+        case GLO_FF_BITS_16:
+            gformat = GL_RGB;
+            gtype = GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case GLO_FF_BITS_24:
+        case GLO_FF_BITS_32:
+        default:
+            gformat = GL_BGR;
+            gtype = GL_UNSIGNED_BYTE;
+            break;
+        }
+    }
+
+    if (gl_format) {
+        *gl_format = gformat;
+    }
+    if (gl_type) {
+        *gl_type = gtype;
+    }
+}
+
+int glo_flags_score(int format_flags_expected, int format_flags_real)
+{
+    int score = 1;
+
+    if (format_flags_expected == format_flags_real) {
+        return 0;
+    }
+
+    /* we wanted alpha, but we didn't get it */
+    if ((format_flags_expected & GLO_FF_ALPHA_MASK) <
+            (format_flags_real & GLO_FF_ALPHA_MASK)) {
+        score++;
+    }
+
+    /* fewer bits than we expected */
+    if ((format_flags_expected & GLO_FF_BITS_MASK) <
+            (format_flags_real & GLO_FF_BITS_MASK)) {
+        score++;
+    }
+
+    /* fewer depth bits than we expected */
+    if ((format_flags_expected & GLO_FF_DEPTH_MASK) <
+            (format_flags_real & GLO_FF_DEPTH_MASK)) {
+        score++;
+    }
+
+    /* fewer stencil bits than we expected */
+    if ((format_flags_expected & GLO_FF_STENCIL_MASK) <
+            (format_flags_real & GLO_FF_STENCIL_MASK)) {
+        score++;
+    }
+
+    return score;
+}
+
+int glo_flags_get_from_glx(const uint32_t *fb_config, int assume_booleans)
+{
+    int buffer_size = 0;
+    int depth_size = 0;
+    int stencil_size = 0;
+    int rgba_size[] = { 0, 0, 0, 0 };
+    int flags = 0;
+
+    while (*fb_config) {
+        int is_single = 0;
+        switch (*fb_config) {
+        case GLX_USE_GL:
+            is_single = 1;
+            break;
+        case GLX_BUFFER_SIZE:
+            buffer_size = fb_config[1];
+            break;
+        case GLX_LEVEL:
+            break;
+        case GLX_RGBA:
+            flags |= GLO_FF_ALPHA;
+            break;
+        case GLX_DOUBLEBUFFER:
+            is_single = 1;
+            break;
+        case GLX_STEREO:
+            is_single = 1;
+            break;
+        case GLX_AUX_BUFFERS:
+            break;
+        case GLX_RED_SIZE:
+            rgba_size[0] = fb_config[1];
+            break;
+        case GLX_GREEN_SIZE:
+            rgba_size[1] = fb_config[1];
+            break;
+        case GLX_BLUE_SIZE:
+            rgba_size[2] = fb_config[1];
+            break;
+        case GLX_ALPHA_SIZE:
+            rgba_size[3] = fb_config[1];
+            break;
+        case GLX_DEPTH_SIZE:
+            depth_size = fb_config[1];
+            break;
+        case GLX_STENCIL_SIZE:
+            stencil_size = fb_config[1];
+            break;
+        case GLX_ACCUM_RED_SIZE:
+        case GLX_ACCUM_GREEN_SIZE:
+        case GLX_ACCUM_BLUE_SIZE:
+        case GLX_ACCUM_ALPHA_SIZE:
+            break;
+        }
+
+        /* Next */
+        if (is_single && assume_booleans) {
+            fb_config++;
+        } else {
+            fb_config += 2;
+        }
+    }
+
+    if (rgba_size[3]) {
+        flags |= GLO_FF_ALPHA;
+    }
+
+    /* Ensure we have room for *some* alpha */
+    if ((flags & GLO_FF_ALPHA) && rgba_size[3] == 0) {
+        rgba_size[3] = 1;
+    }
+
+    /* Buffer size flag */
+    if (buffer_size == 0) {
+        buffer_size = rgba_size[0] + rgba_size[1] + rgba_size[2] + rgba_size[3];
+        buffer_size = (flags & GLO_FF_ALPHA) ? 32 : 24;
+    }
+    if (buffer_size <= 16) {
+        flags |= GLO_FF_BITS_16;
+    } else if (buffer_size <= 24) {
+        flags |= GLO_FF_BITS_24;
+    } else {
+        flags |= GLO_FF_BITS_32;
+    }
+
+    /* Depth */
+    if (depth_size <= 16) {
+        flags |= GLO_FF_DEPTH_16;
+    } else if (depth_size <= 24) {
+        flags |= GLO_FF_DEPTH_24;
+    } else {
+        flags |= GLO_FF_DEPTH_32;
+    }
+
+    /* Stencil */
+    if (stencil_size > 0) {
+        flags |= GLO_FF_STENCIL_8;
+    }
+
+    return flags;
+}
+
+void glo_surface_getcontents_readpixels(int format_flags, int stride, int bpp,
+                int width, int height, void *data)
+{
+    int gl_format, gl_type, rl, pa;
+    static int once;
+
+    glo_flags_get_readpixel_type(format_flags, &gl_format, &gl_type);
+    switch(bpp) {
+    case 24:
+        if (gl_format != GL_BGR) {
+            if (!once) {
+                fprintf(stderr, "vmgl: Warning: compressing alpha\n");
+                once = 1;
+            }
+            gl_format = GL_BGR;
+        }
+        break;
+    case 32:
+        if (gl_format != GL_BGRA) {
+            fprintf(stderr, "vmgl: Warning: expanding alpha!\n");
+            gl_format = GL_BGRA;
+        }
+        break;
+    default:
+        fprintf(stderr, "vmgl: Warning: unsupported colourdepth\n");
+        break;
+    }
+
+    /* Save guest processes GL state before we ReadPixels() */
+    glGetIntegerv(GL_PACK_ROW_LENGTH, &rl);
+    glGetIntegerv(GL_PACK_ALIGNMENT, &pa);
+    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+    glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+#ifdef GETCONTENTS_INDIVIDUAL
+    GLubyte *b = (GLubyte *) data;
+    int irow;
+
+    for (irow = height - 1; irow >= 0; irow--) {
+        glReadPixels(0, irow, width, 1, gl_format, gl_type, b);
+        b += stride;
+    }
+#else
+    /* Faster buffer flip */
+    GLubyte *b = (GLubyte *) data;
+    GLubyte *c = &((GLubyte *) data)[stride * (height - 1)];
+    GLubyte *tmp = (GLubyte*) g_malloc(stride);
+    int irow;
+
+    glReadPixels(0, 0, width, height, gl_format, gl_type, data);
+
+    for (irow = 0; irow < height / 2; irow++) {
+        memcpy(tmp, b, stride);
+        memcpy(b, c, stride);
+        memcpy(c, tmp, stride);
+        b += stride;
+        c -= stride;
+    }
+    g_free(tmp);
+#endif
+
+    /* Restore GL state */
+    glPixelStorei(GL_PACK_ROW_LENGTH, rl);
+    glPixelStorei(GL_PACK_ALIGNMENT, pa);
+}
+
+uint32_t glo_get_glx_from_flags(int format_flags, int glx_enum)
+{
+    int rgba[4];
+
+    glo_flags_get_rgba_bits(format_flags, rgba);
+    switch (glx_enum) {
+    case GLX_USE_GL:
+        return 1;
+    case GLX_BUFFER_SIZE:
+        return glo_flags_get_bytes_per_pixel(format_flags) * 8;
+    case GLX_LEVEL:
+        return 0;
+    case GLX_RGBA:
+        return format_flags & GLO_FF_ALPHA;
+    case GLX_DOUBLEBUFFER:
+        return 1;
+    case GLX_STEREO:
+        return 0;
+    case GLX_AUX_BUFFERS:
+        return 0;
+    case GLX_RED_SIZE:
+        return rgba[0];
+    case GLX_GREEN_SIZE:
+        return rgba[1];
+    case GLX_BLUE_SIZE:
+        return rgba[2];
+    case GLX_ALPHA_SIZE:
+        return rgba[3];
+    case GLX_DEPTH_SIZE:
+        return glo_flags_get_depth_bits(format_flags);
+    case GLX_STENCIL_SIZE:
+        return glo_flags_get_stencil_bits(format_flags);
+    case GLX_ACCUM_RED_SIZE:
+    case GLX_ACCUM_GREEN_SIZE:
+    case GLX_ACCUM_BLUE_SIZE:
+    case GLX_ACCUM_ALPHA_SIZE:
+        return 0;
+
+    /* The attributes for glXGetFBConfigAttrib */
+    case GLX_FBCONFIG_ID:
+        return 0;
+    case GLX_RENDER_TYPE:
+        return GLX_RGBA_BIT;
+    case GLX_DRAWABLE_TYPE:
+        return GLX_WINDOW_BIT;
+    case GLX_X_RENDERABLE:
+        return 1;
+    case GLX_VISUAL_ID:
+    case GLX_X_VISUAL_TYPE:
+        /* The real value is obtained client-side.  */
+        return 0;
+    case GLX_CONFIG_CAVEAT:
+        return GLX_NONE;
+    case GLX_TRANSPARENT_TYPE:
+        return GLX_NONE;
+    case GLX_TRANSPARENT_INDEX_VALUE:
+    case GLX_TRANSPARENT_RED_VALUE:
+    case GLX_TRANSPARENT_GREEN_VALUE:
+    case GLX_TRANSPARENT_BLUE_VALUE:
+    case GLX_TRANSPARENT_ALPHA_VALUE:
+        return 0;
+    case GLX_MAX_PBUFFER_WIDTH:
+    case GLX_MAX_PBUFFER_HEIGHT:
+    case GLX_MAX_PBUFFER_PIXELS:
+        return 0;
+    }
+
+    return 0;
+}
+
+int glo_acceleration_capability_check(void)
+{
+    int test_failure = 0;
+    GloContext *context;
+    GloSurface *surface;
+
+#define TX 32
+#define TY 32
+    uint8_t *datain = (uint8_t *) g_malloc(4 * TX * TY);
+    uint8_t *datain_flip = (uint8_t *) g_malloc(4 * TX * TY);
+    uint8_t *dataout = (uint8_t *) g_malloc(4 * TX * TY);
+    uint8_t *p;
+    int x, y;
+    uint32_t buffer_attributes[] = {
+        GLX_RED_SIZE,      8,
+        GLX_GREEN_SIZE,    8,
+        GLX_BLUE_SIZE,     8,
+        GLX_ALPHA_SIZE,    8,
+        GLX_DEPTH_SIZE,    0,
+        GLX_STENCIL_SIZE,  0,
+        0,
+    };
+    int buffer_flags = glo_flags_get_from_glx(buffer_attributes, 0);
+    int bpp = glo_flags_get_bytes_per_pixel(buffer_flags);
+    int gl_format, gl_type;
+
+    if (glo_sanity_test() != 0) {
+        return 1;
+    }
+
+    memset(datain_flip, 0, TX * TY * 4);
+    memset(datain, 0, TX * TY * 4);
+
+    p = datain;
+    for (y = 0; y < TY; y++) {
+        for (x = 0; x < TX; x++) {
+            p[0] = x;
+            p[1] = y;
+            if (bpp > 2) {
+                p[2] = 0;
+            }
+            if (bpp > 3) {
+                p[3] = 0xff;
+            }
+            p += bpp;
+        }
+        memcpy(&datain_flip[((TY - 1) - y) * bpp * TX],
+                        &datain[y * bpp * TX], bpp * TX);
+    }
+
+    glo_init();
+    /* new surface */
+    context = glo_context_create(buffer_flags, 0);
+    if (!context) {
+        test_failure = 1;
+        goto test_end;
+    }
+    surface = glo_surface_create(TX, TY, context);
+    if (!surface) {
+        glo_context_destroy(context);
+        test_failure = 1;
+        goto test_end;
+    }
+    glo_surface_makecurrent(surface);
+
+    if (strstr((const char *) glGetString(GL_RENDERER), "Software")) {
+        /* Host does not have OpenGL hardware acceleration..
+         * Note that most of the time (i.e. without cpu virtualisation)
+         * software rendering on the host is still going to be way
+         * faster than software rendering on the guest.  A much better
+         * solution would be for the guest to send a rough benchmark
+         * result inside the _init command so that it can be compared
+         * against host performance.
+         */
+        test_failure = 1;
+        goto test_end;
+    }
+
+    /* fill with stuff (in correctly ordered way) */
+    glClear(GL_COLOR_BUFFER_BIT);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, TX, 0, TY, 0, 1);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glRasterPos2f(0, 0);
+    glo_flags_get_readpixel_type(buffer_flags, &gl_format, &gl_type);
+    glDrawPixels(TX, TY, gl_format, gl_type, datain_flip);
+    glFlush();
+
+    memset(dataout, 0, bpp * TX * TY);
+
+    glo_surface_getcontents(surface, TX * bpp, bpp * 8, dataout);
+
+    /* destroy surface */
+    glo_surface_destroy(surface);
+    glo_context_destroy(context);
+    /* compare */
+    if (memcmp(datain, dataout, bpp * TX * TY) != 0) {
+        test_failure = 1;
+    }
+
+test_end:
+    g_free(datain);
+    g_free(datain_flip);
+    g_free(dataout);
+    return test_failure;
+}
diff --git a/gl/gloffscreen-wgl.c b/gl/gloffscreen-wgl.c
new file mode 100644
index 0000000..1073085
--- /dev/null
+++ b/gl/gloffscreen-wgl.c
@@ -0,0 +1,832 @@ 
+/*
+ * Offscreen OpenGL abstraction layer - WGL (windows) specific
+ *
+ * Copyright (c) 2010 Intel Corporation
+ * Authors:
+ *   Gordon Williams <gordon.williams@collabora.co.uk>
+ *   Ian Molton <ian.molton@collabora.co.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <windows.h>
+#include <wingdi.h>
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <GL/wglext.h>
+
+#include "gloffscreen.h"
+#include "sysemu.h"
+
+/* In Windows, you must create a window *before* you can create a pbuffer or
+ * get a context.  So we create a hidden Window on startup
+ * (see glo_init/GloMain).
+ *
+ * Also, you can't share contexts that have different pixel formats, so we
+ * can't just create a new context from the window. We must create a whole
+ * new PBuffer just for a context :(
+ */
+
+static struct GloMain {
+    HINSTANCE hInstance;
+    HDC       hDC;
+    HWND      hWnd; /* Our hidden window */
+    HGLRC     hContext;
+} glo;
+static int glo_inited = 0;
+
+struct GloContext {
+    int         format_flags;
+
+    /* Pixel format returned by wglChoosePixelFormat */
+    int         wglPixelFormat;
+    /* We need a pbuffer to make a context of the right pixel_format :( */
+    HPBUFFERARB hPBuffer;
+    HDC         hDC;
+    HGLRC       hContext;
+};
+
+struct GloSurface {
+    GLuint      width;
+    GLuint      height;
+
+    GloContext  *context;
+    HPBUFFERARB hPBuffer;
+    HDC         hDC;
+};
+
+#define GLO_WINDOW_CLASS "QEmuGLClass"
+#define DEFAULT_DEPTH_BUFFER 16
+
+static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
+static PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB;
+static PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB;
+static PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB;
+static PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB;
+
+int glo_initialised(void)
+{
+    return glo_inited;
+}
+
+/* Sanity test of the host GL capabilities to see whether gloffscreen
+ * requirements are well supported */
+int glo_sanity_test(void)
+{
+    PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
+    PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB;
+    PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB;
+    PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB;
+    PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB;
+
+    wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)
+            wglGetProcAddress("wglChoosePixelFormatARB");
+    wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC)
+            wglGetProcAddress("wglGetPbufferDCARB");
+    wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC)
+            wglGetProcAddress("wglReleasePbufferDCARB");
+    wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC)
+            wglGetProcAddress("wglCreatePbufferARB");
+    wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC)
+            wglGetProcAddress("wglDestroyPbufferARB");
+    if (!wglChoosePixelFormatARB || !wglGetPbufferDCARB ||
+            !wglReleasePbufferDCARB || !wglCreatePbufferARB ||
+            !wglDestroyPbufferARB) {
+        fprintf(stderr, "vmgl: Unable to load the required WGL extensions\n");
+        return 1;
+    }
+
+    /* Check for shader support.  It is for mcompositor to run.  */
+
+    if (!wglGetProcAddress("glShaderSource")) {
+        fprintf(stderr, "vmgl: Unable to find shader support\n");
+        return 1;
+    }
+    return 0;
+}
+
+/* Initialise gloffscreen */
+void glo_init(void)
+{
+    WNDCLASSEX wcx;
+    PIXELFORMATDESCRIPTOR pfd;
+    unsigned int pixel_format;
+
+    if (glo_inited) {
+        return 0;
+    }
+
+    glo.hInstance = GetModuleHandle(NULL); /* Grab an instance for our window */
+
+    wcx.cbSize = sizeof(wcx);
+    wcx.style = 0;
+    wcx.lpfnWndProc = DefWindowProc;
+    wcx.cbClsExtra = 0;
+    wcx.cbWndExtra = 0;
+    wcx.hInstance = glo.hInstance;
+    wcx.hIcon = NULL;
+    wcx.hCursor = NULL;
+    wcx.hbrBackground = NULL;
+    wcx.lpszMenuName =  NULL;
+    wcx.lpszClassName = GLO_WINDOW_CLASS;
+    wcx.hIconSm = NULL;
+    RegisterClassEx(&wcx);
+
+    glo.hWnd = CreateWindow(GLO_WINDOW_CLASS, "QEmuGL", 0, 0, 0, 0, 0,
+                    NULL, NULL, glo.hInstance, NULL);
+    if (!glo.hWnd) {
+        fprintf(stderr, "vmgl: Unable to create window\n");
+        return -1;
+    }
+    glo.hDC = GetDC(glo.hWnd);
+
+    memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
+    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+    pfd.nVersion = 1;
+    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
+    pfd.iPixelType = PFD_TYPE_RGBA;
+    pfd.cColorBits = 24;
+    pfd.iLayerType = PFD_MAIN_PLANE;
+
+    pixel_format = ChoosePixelFormat(glo.hDC, &pfd);
+    DescribePixelFormat(glo.hDC, pixel_format,
+                    sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+    if (!SetPixelFormat(glo.hDC, pixel_format, &pfd)) {
+        /* FIXME: free resources, if any */
+        return;
+    }
+
+    glo.hContext = wglCreateContext(glo.hDC);
+    if (glo.hContext == NULL) {
+        fprintf(stderr, "vmgl: Unable to create GL context\n");
+        /* FIXME: free resources */
+        return -1;
+    }
+    wglMakeCurrent(glo.hDC, glo.hContext);
+
+    /* FIXME: GW, Need to share lists AND copy state */
+
+    /* Load in the extensions we need */
+    wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)
+            wglGetProcAddress("wglChoosePixelFormatARB");
+    wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC)
+            wglGetProcAddress("wglGetPbufferDCARB");
+    wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC)
+            wglGetProcAddress("wglReleasePbufferDCARB");
+    wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC)
+            wglGetProcAddress("wglCreatePbufferARB");
+    wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC)
+            wglGetProcAddress("wglDestroyPbufferARB");
+    if (!wglChoosePixelFormatARB || !wglGetPbufferDCARB ||
+            !wglReleasePbufferDCARB || !wglCreatePbufferARB ||
+            !wglDestroyPbufferARB) {
+        fprintf(stderr, "vmgl: Unable to load the required WGL extensions\n");
+        /* FIXME: free resources */
+        return -1;
+    }
+
+    glo_inited = 1;
+
+    return 0;
+}
+
+/* Uninitialise gloffscreen */
+void glo_kill(void)
+{
+    if (glo.hContext) {
+        wglMakeCurrent(NULL, NULL);
+        wglDeleteContext(glo.hContext);
+        glo.hContext = NULL;
+    }
+    if (glo.hDC) {
+        ReleaseDC(glo.hWnd, glo.hDC);
+        glo.hDC = NULL;
+    }
+    if (glo.hWnd) {
+        DestroyWindow(glo.hWnd);
+        glo.hWnd = NULL;
+    }
+    UnregisterClass(GLO_WINDOW_CLASS, glo.hInstance);
+}
+
+const char *glo_glxqueryextensionsstring(void)
+{
+    return "";
+}
+
+static const char *standard_gl_functions ={
+    /* Miscellaneous */
+    "glClearIndex\0"
+    "glClearColor\0"
+    "glClear\0"
+    "glIndexMask\0"
+    "glColorMask\0"
+    "glAlphaFunc\0"
+    "glBlendFunc\0"
+    "glLogicOp\0"
+    "glCullFace\0"
+    "glFrontFace\0"
+    "glPointSize\0"
+    "glLineWidth\0"
+    "glLineStipple\0"
+    "glPolygonMode\0"
+    "glPolygonOffset\0"
+    "glPolygonStipple\0"
+    "glGetPolygonStipple\0"
+    "glEdgeFlag\0"
+    "glEdgeFlagv\0"
+    "glScissor\0"
+    "glClipPlane\0"
+    "glGetClipPlane\0"
+    "glDrawBuffer\0"
+    "glReadBuffer\0"
+    "glEnable\0"
+    "glDisable\0"
+    "glIsEnabled\0"
+    "glEnableClientState\0"
+    "glDisableClientState\0"
+    "glGetBooleanv\0"
+    "glGetDoublev\0"
+    "glGetFloatv\0"
+    "glGetIntegerv\0"
+    "glPushAttrib\0"
+    "glPopAttrib\0"
+    "glPushClientAttrib\0"
+    "glPopClientAttrib\0"
+    "glRenderMode\0"
+    "glGetError\0"
+    "glGetString\0"
+    "glFinish\0"
+    "glFlush\0"
+    "glHint\0"
+    /* Depth Buffer */
+    "glClearDepth\0"
+    "glDepthFunc\0"
+    "glDepthMask\0"
+    "glDepthRange\0"
+    /* Accumulation Buffer */
+    "glClearAccum\0"
+    "glAccum\0"
+    /* Transformation */
+    "glMatrixMode\0"
+    "glOrtho\0"
+    "glFrustum\0"
+    "glViewport\0"
+    "glPushMatrix\0"
+    "glPopMatrix\0"
+    "glLoadIdentity\0"
+    "glLoadMatrixd\0"
+    "glLoadMatrixf\0"
+    "glMultMatrixd\0"
+    "glMultMatrixf\0"
+    "glRotated\0"
+    "glRotatef\0"
+    "glScaled\0"
+    "glScalef\0"
+    "glTranslated\0"
+    "glTranslatef\0"
+    /* Display Lists */
+    "glIsList\0"
+    "glDeleteLists\0"
+    "glGenLists\0"
+    "glNewList\0"
+    "glEndList\0"
+    "glCallList\0"
+    "glCallLists\0"
+    "glListBase\0"
+    /* Drawing Functions */
+    "glBegin\0"
+    "glEnd\0"
+    "glVertex2d\0"
+    "glVertex2f\0"
+    "glVertex2i\0"
+    "glVertex2s\0"
+    "glVertex3d\0"
+    "glVertex3f\0"
+    "glVertex3i\0"
+    "glVertex3s\0"
+    "glVertex4d\0"
+    "glVertex4f\0"
+    "glVertex4i\0"
+    "glVertex4s\0"
+    "glVertex2dv\0"
+    "glVertex2fv\0"
+    "glVertex2iv\0"
+    "glVertex2sv\0"
+    "glVertex3dv\0"
+    "glVertex3fv\0"
+    "glVertex3iv\0"
+    "glVertex3sv\0"
+    "glVertex4dv\0"
+    "glVertex4fv\0"
+    "glVertex4iv\0"
+    "glVertex4sv\0"
+    "glNormal3b\0"
+    "glNormal3d\0"
+    "glNormal3f\0"
+    "glNormal3i\0"
+    "glNormal3s\0"
+    "glNormal3bv\0"
+    "glNormal3dv\0"
+    "glNormal3fv\0"
+    "glNormal3iv\0"
+    "glNormal3sv\0"
+    "glIndexd\0"
+    "glIndexf\0"
+    "glIndexi\0"
+    "glIndexs\0"
+    "glIndexub\0"
+    "glIndexdv\0"
+    "glIndexfv\0"
+    "glIndexiv\0"
+    "glIndexsv\0"
+    "glIndexubv\0"
+    "glColor3b\0"
+    "glColor3d\0"
+    "glColor3f\0"
+    "glColor3i\0"
+    "glColor3s\0"
+    "glColor3ub\0"
+    "glColor3ui\0"
+    "glColor3us\0"
+    "glColor4b\0"
+    "glColor4d\0"
+    "glColor4f\0"
+    "glColor4i\0"
+    "glColor4s\0"
+    "glColor4ub\0"
+    "glColor4ui\0"
+    "glColor4us\0"
+    "glColor3bv\0"
+    "glColor3dv\0"
+    "glColor3fv\0"
+    "glColor3iv\0"
+    "glColor3sv\0"
+    "glColor3ubv\0"
+    "glColor3uiv\0"
+    "glColor3usv\0"
+    "glColor4bv\0"
+    "glColor4dv\0"
+    "glColor4fv\0"
+    "glColor4iv\0"
+    "glColor4sv\0"
+    "glColor4ubv\0"
+    "glColor4uiv\0"
+    "glColor4usv\0"
+    "glTexCoord1d\0"
+    "glTexCoord1f\0"
+    "glTexCoord1i\0"
+    "glTexCoord1s\0"
+    "glTexCoord2d\0"
+    "glTexCoord2f\0"
+    "glTexCoord2i\0"
+    "glTexCoord2s\0"
+    "glTexCoord3d\0"
+    "glTexCoord3f\0"
+    "glTexCoord3i\0"
+    "glTexCoord3s\0"
+    "glTexCoord4d\0"
+    "glTexCoord4f\0"
+    "glTexCoord4i\0"
+    "glTexCoord4s\0"
+    "glTexCoord1dv\0"
+    "glTexCoord1fv\0"
+    "glTexCoord1iv\0"
+    "glTexCoord1sv\0"
+    "glTexCoord2dv\0"
+    "glTexCoord2fv\0"
+    "glTexCoord2iv\0"
+    "glTexCoord2sv\0"
+    "glTexCoord3dv\0"
+    "glTexCoord3fv\0"
+    "glTexCoord3iv\0"
+    "glTexCoord3sv\0"
+    "glTexCoord4dv\0"
+    "glTexCoord4fv\0"
+    "glTexCoord4iv\0"
+    "glTexCoord4sv\0"
+    "glRasterPos2d\0"
+    "glRasterPos2f\0"
+    "glRasterPos2i\0"
+    "glRasterPos2s\0"
+    "glRasterPos3d\0"
+    "glRasterPos3f\0"
+    "glRasterPos3i\0"
+    "glRasterPos3s\0"
+    "glRasterPos4d\0"
+    "glRasterPos4f\0"
+    "glRasterPos4i\0"
+    "glRasterPos4s\0"
+    "glRasterPos2dv\0"
+    "glRasterPos2fv\0"
+    "glRasterPos2iv\0"
+    "glRasterPos2sv\0"
+    "glRasterPos3dv\0"
+    "glRasterPos3fv\0"
+    "glRasterPos3iv\0"
+    "glRasterPos3sv\0"
+    "glRasterPos4dv\0"
+    "glRasterPos4fv\0"
+    "glRasterPos4iv\0"
+    "glRasterPos4sv\0"
+    "glRectd\0"
+    "glRectf\0"
+    "glRecti\0"
+    "glRects\0"
+    "glRectdv\0"
+    "glRectfv\0"
+    "glRectiv\0"
+    "glRectsv\0"
+    /* Lighting */
+    "glShadeModel\0"
+    "glLightf\0"
+    "glLighti\0"
+    "glLightfv\0"
+    "glLightiv\0"
+    "glGetLightfv\0"
+    "glGetLightiv\0"
+    "glLightModelf\0"
+    "glLightModeli\0"
+    "glLightModelfv\0"
+    "glLightModeliv\0"
+    "glMaterialf\0"
+    "glMateriali\0"
+    "glMaterialfv\0"
+    "glMaterialiv\0"
+    "glGetMaterialfv\0"
+    "glGetMaterialiv\0"
+    "glColorMaterial\0"
+    /* Raster functions */
+    "glPixelZoom\0"
+    "glPixelStoref\0"
+    "glPixelStorei\0"
+    "glPixelTransferf\0"
+    "glPixelTransferi\0"
+    "glPixelMapfv\0"
+    "glPixelMapuiv\0"
+    "glPixelMapusv\0"
+    "glGetPixelMapfv\0"
+    "glGetPixelMapuiv\0"
+    "glGetPixelMapusv\0"
+    "glBitmap\0"
+    "glReadPixels\0"
+    "glDrawPixels\0"
+    "glCopyPixels\0"
+    /* Stenciling */
+    "glStencilFunc\0"
+    "glStencilMask\0"
+    "glStencilOp\0"
+    "glClearStencil\0"
+    /* Texture mapping */
+    "glTexGend\0"
+    "glTexGenf\0"
+    "glTexGeni\0"
+    "glTexGendv\0"
+    "glTexGenfv\0"
+    "glTexGeniv\0"
+    "glGetTexGendv\0"
+    "glGetTexGenfv\0"
+    "glGetTexGeniv\0"
+    "glTexEnvf\0"
+    "glTexEnvi\0"
+    "glTexEnvfv\0"
+    "glTexEnviv\0"
+    "glGetTexEnvfv\0"
+    "glGetTexEnviv\0"
+    "glTexParameterf\0"
+    "glTexParameteri\0"
+    "glTexParameterfv\0"
+    "glTexParameteriv\0"
+    "glGetTexParameterfv\0"
+    "glGetTexParameteriv\0"
+    "glGetTexLevelParameterfv\0"
+    "glGetTexLevelParameteriv\0"
+    "glTexImage1D\0"
+    "glTexImage2D\0"
+    "glGetTexImage\0"
+    /* Evaluators */
+    "glMap1d\0"
+    "glMap1f\0"
+    "glMap2d\0"
+    "glMap2f\0"
+    "glGetMapdv\0"
+    "glGetMapfv\0"
+    "glGetMapiv\0"
+    "glEvalCoord1d\0"
+    "glEvalCoord1f\0"
+    "glEvalCoord1dv\0"
+    "glEvalCoord1fv\0"
+    "glEvalCoord2d\0"
+    "glEvalCoord2f\0"
+    "glEvalCoord2dv\0"
+    "glEvalCoord2fv\0"
+    "glMapGrid1d\0"
+    "glMapGrid1f\0"
+    "glMapGrid2d\0"
+    "glMapGrid2f\0"
+    "glEvalPoint1\0"
+    "glEvalPoint2\0"
+    "glEvalMesh1\0"
+    "glEvalMesh2\0"
+    /* Fog */
+    "glFogf\0"
+    "glFogi\0"
+    "glFogfv\0"
+    "glFogiv\0"
+    /* Selection and Feedback */
+    "glFeedbackBuffer\0"
+    "glPassThrough\0"
+    "glSelectBuffer\0"
+    "glInitNames\0"
+    "glLoadName\0"
+    "glPushName\0"
+    "glPopName\0"
+    /* 1.1 functions */
+    /* texture objects */
+    "glGenTextures\0"
+    "glDeleteTextures\0"
+    "glBindTexture\0"
+    "glPrioritizeTextures\0"
+    "glAreTexturesResident\0"
+    "glIsTexture\0"
+    /* texture mapping */
+    "glTexSubImage1D\0"
+    "glTexSubImage2D\0"
+    "glCopyTexImage1D\0"
+    "glCopyTexImage2D\0"
+    "glCopyTexSubImage1D\0"
+    "glCopyTexSubImage2D\0"
+    /* vertex arrays */
+    "glVertexPointer\0"
+    "glNormalPointer\0"
+    "glColorPointer\0"
+    "glIndexPointer\0"
+    "glTexCoordPointer\0"
+    "glEdgeFlagPointer\0"
+    "glGetPointerv\0"
+    "glArrayElement\0"
+    "glDrawArrays\0"
+    "glDrawElements\0"
+    "glInterleavedArrays\0"
+    /* GLX */
+    "glXChooseVisual\0"
+    "glXQueryExtensionsString\0"
+    "glXQueryServerString\0"
+    "glXGetClientString\0"
+    "glXCreateContext\0"
+    "glXCreateNewContext\0"
+    "glXCopyContext\0"
+    "glXDestroyContext\0"
+    "glXQueryVersion\0"
+    "glXMakeCurrent\0"
+    "glXSwapBuffers\0"
+    "glXGetConfig\0"
+    "glXQueryExtension\0"
+    "glXChooseFBConfig\0"
+    "glXGetFBConfigs\0"
+    "glXGetFBConfigAttrib\0"
+    "glXQueryContext\0"
+    "glXQueryDrawable\0"
+    "glXGetVisualFromFBConfig\0"
+    "glXIsDirect\0"
+    "\0"
+};
+
+/* Like wglGetProcAddress/glxGetProcAddress */
+void *glo_getprocaddress(const char *proc_name)
+{
+    HGLRC old_ctx;
+    HDC old_dc;
+    void *proc_addr;
+
+    old_ctx = wglGetCurrentContext();
+    old_dc = wglGetCurrentDC();
+    if (old_dc != glo.hDC || old_ctx != glo.hContext) {
+        wglMakeCurrent(glo.hDC, glo.hContext);
+    }
+
+    proc_addr = wglGetProcAddress(proc_name);
+
+    if (old_dc != glo.hDC || old_ctx != glo.hContext) {
+        wglMakeCurrent(old_dc, old_ctx);
+    }
+
+    /* wGL doesn't know about the glx functions - but
+     * we never call these anyway (they're implemented in
+     * opengl_exec), so all we need to do is return a non-zero value...
+     *
+     * But we also have to check for 'standard' GL function names
+     * too as wGL doesn't return those either!  */
+    if (!proc_addr) {
+        const char *p = standard_gl_functions;
+        while (*p) {
+            if (!strcmp(proc_name, p)) {
+                proc_addr = (void *) 1;
+                break;
+            }
+            /* skip to the next '0' and then just over it */
+            while (*p) {
+                p++;
+            }
+            p++;
+        }
+    }
+
+    return proc_addr;
+}
+
+/* Create an OpenGL context for a certain pixel format. format_flags
+ * are made up of the GLO_ constants */
+GloContext *glo_context_create(int format_flags, GloContext *share_lists)
+{
+    GloContext *context;
+    int pf_attri[] = {
+        WGL_SUPPORT_OPENGL_ARB, TRUE,
+        WGL_DRAW_TO_PBUFFER_ARB, TRUE,
+        WGL_RED_BITS_ARB, 8,
+        WGL_GREEN_BITS_ARB, 8,
+        WGL_BLUE_BITS_ARB, 8,
+        WGL_ALPHA_BITS_ARB, 8,
+        WGL_DEPTH_BITS_ARB, 0,
+        WGL_STENCIL_BITS_ARB, 0,
+        WGL_DOUBLE_BUFFER_ARB, FALSE,
+        0
+    };
+    float pf_attrf[] = { 0, 0 };
+    unsigned int rgba_all = 0;
+    int pb_attr[] = { 0 };
+    int rgba_bits[4];
+
+    context = g_malloc0(sizeof(GloContext));
+    context->format_flags = format_flags;
+
+    /* Set up the surface format from the flags we were given */
+    glo_flags_get_rgba_bits(context->format_flags, rgba_bits);
+    pf_attri[5]  = rgba_bits[0];
+    pf_attri[7]  = rgba_bits[1];
+    pf_attri[9]  = rgba_bits[2];
+    pf_attri[11] = rgba_bits[3];
+    pf_attri[13] = glo_flags_get_depth_bits(context->format_flags);
+    pf_attri[15] = glo_flags_get_stencil_bits(context->format_flags);
+
+    /* Find out what pixel format to use */
+    wglChoosePixelFormatARB(glo.hDC, pf_attri, pf_attrf, 1,
+                    &context->wglPixelFormat, &rgba_all);
+    if (rgba_all == 0) {
+        g_free(context);
+        fprintf(stderr, "vmgl: No matching configs found.\n");
+        return NULL;
+    }
+
+    /* We create a tiny pbuffer - just so we can make a context
+     * of the right pixel format */
+    context->hPBuffer = wglCreatePbufferARB(glo.hDC,
+                    context->wglPixelFormat, 16, 16, pb_attr);
+    if (!context->hPBuffer) {
+        g_free(context);
+        fprintf(stderr, "vmgl: Couldn't create the PBuffer\n");
+        return NULL;
+    }
+    context->hDC = wglGetPbufferDCARB(context->hPBuffer);
+    if (!context->hDC) {
+        g_free(context);
+        /* TODO: free other resources, if any */
+        fprintf(stderr, "vmgl: Couldn't create the DC\n");
+        return NULL;
+    }
+
+    context->hContext = wglCreateContext(context->hDC);
+    if (context->hContext == NULL) {
+        g_free(context);
+        /* TODO: free other resources, if any */
+        fprintf(stderr, "vmgl: Unable to create GL context\n");
+        return NULL;
+    }
+
+    if (share_lists) {
+        /* Need to share lists... */
+        wglShareLists(share_lists->hContext, context->hContext);
+    }
+
+    return context;
+}
+
+/* Destroy a previouslu created OpenGL context */
+void glo_context_destroy(GloContext *context)
+{
+    if (!context) {
+        return;
+    }
+
+    wglMakeCurrent(NULL, NULL);
+    if (context->hPBuffer != NULL) {
+        wglReleasePbufferDCARB(context->hPBuffer, context->hDC);
+        wglDestroyPbufferARB(context->hPBuffer);
+    }
+    if (context->hDC != NULL) {
+        ReleaseDC(glo.hWnd, context->hDC);
+    }
+    if (context->hContext) {
+        wglDeleteContext(context->hContext);
+    }
+    free(context);
+}
+
+/* Create a surface with given width and height, format_flags are made up of
+ * the GLO_ constants */
+GloSurface *glo_surface_create(int width, int height, GloContext *context)
+{
+    GloSurface *surface;
+    int pb_attr[] = { 0 };
+
+    /* Create the p-buffer... */
+    surface = g_malloc0(sizeof(GloSurface));
+    surface->width = width;
+    surface->height = height;
+    surface->context = context;
+
+    surface->hPBuffer = wglCreatePbufferARB(glo.hDC,
+                    context->wglPixelFormat, surface->width,
+                    surface->height, pb_attr);
+    if (!surface->hPBuffer) {
+        g_free(surface);
+        fprintf(stderr, "vmgl: Couldn't create the PBuffer\n");
+        return NULL;
+    }
+    surface->hDC = wglGetPbufferDCARB(surface->hPBuffer);
+    if (!surface->hDC) {
+        g_free(surface);
+        /* TODO: Free other resources, if any */
+        fprintf(stderr, "vmgl: Couldn't create the DC\n");
+        return NULL;
+    }
+
+    return surface;
+}
+
+/* Destroy the given surface */
+void glo_surface_destroy(GloSurface *surface)
+{
+    if (!surface) {
+        return;
+    }
+
+    wglMakeCurrent(NULL, NULL);
+    if (surface->hPBuffer != NULL) {
+        wglReleasePbufferDCARB(surface->hPBuffer, surface->hDC);
+        wglDestroyPbufferARB(surface->hPBuffer);
+    }
+    if (surface->hDC != NULL) {
+        ReleaseDC(glo.hWnd, surface->hDC);
+    }
+    g_free(surface);
+}
+
+/* Make the given surface current */
+int glo_surface_makecurrent(GloSurface *surface)
+{
+    if (surface) {
+        return wglMakeCurrent(surface->hDC, surface->context->hContext);
+    } else {
+        return wglMakeCurrent(NULL, NULL);
+    }
+}
+
+/* Get the contents of the given surface */
+void glo_surface_getcontents(GloSurface *surface,
+                int stride, int bpp, void *data)
+{
+    if (!surface) {
+        return;
+    }
+
+    /* Compatible / fallback method.  */
+    glo_surface_getcontents_readpixels(surface->context->format_flags,
+                    stride, bpp, surface->width, surface->height, data);
+}
+
+/* Return the width and height of the given surface */
+void glo_surface_get_size(GloSurface *surface, int *width, int *height)
+{
+    if (width) {
+        *width = surface->width;
+    }
+    if (height)
+        *height = surface->height;
+    }
+}
diff --git a/gl/gloffscreen-xcomposite.c b/gl/gloffscreen-xcomposite.c
new file mode 100644
index 0000000..5c63ed4
--- /dev/null
+++ b/gl/gloffscreen-xcomposite.c
@@ -0,0 +1,518 @@ 
+/*
+ * Offscreen OpenGL abstraction layer - GLX specific
+ *
+ * Copyright (c) 2010 Intel Corporation
+ * Authors:
+ *   Gordon Williams <gordon.williams@collabora.co.uk>
+ *   Ian Molton <ian.molton@collabora.co.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#include <X11/extensions/Xcomposite.h>
+
+#include "gloffscreen.h"
+#include "sysemu.h"
+
+static struct GloMain {
+    Display *dpy;
+    int use_ximage;
+} glo;
+static int glo_inited = 0;
+
+struct GloContext {
+    GLuint format_flags;
+    GLXFBConfig fb_config;
+    GLXContext context;
+};
+
+struct GloSurface {
+    GLuint width;
+    GLuint height;
+
+    GloContext *context;
+    Window window;
+    Colormap colormap;
+
+    /* For use by the 'fast' copy code.  */
+    Pixmap pixmap;
+    XImage *image;
+    XShmSegmentInfo shminfo;
+};
+
+int glo_initialised(void)
+{
+    return glo_inited;
+}
+
+/* The X error was disabled, otherwise QEMU might abort.  Printing more error
+ * messages inside below function could help debug potential bugs.  On the
+ * other hand, since the X errors could be caused by the GL calls forwarded
+ * from client OS side, which does not make sense to abort the whole QEMU,
+ * so it is reasonable to redirect error handler here.
+ */
+static int x_errhandler(Display *dpy, XErrorEvent *e)
+{
+    return 0;
+}
+
+/* Sanity test of the host GL capabilities to see whether the gl offscreen
+ * requirements are well supported */
+int glo_sanity_test(void)
+{
+    return 0;
+}
+
+static int glo_can_readback(void)
+{
+    GloContext *context;
+    GloSurface *surface;
+
+#define TX 17
+#define TY 16
+    uint8_t *datain = (uint8_t *) g_malloc(4 * TX * TY);
+    uint8_t *datain_flip = (uint8_t *) g_malloc(4 * TX * TY);
+    uint8_t *dataout = (uint8_t *) g_malloc(4 * TX * TY);
+    uint8_t *p;
+    int x, y, ret = 0;
+
+    const uint32_t buffer_attributes[] = {
+        GLX_RED_SIZE,     8,
+        GLX_GREEN_SIZE,   8,
+        GLX_BLUE_SIZE,    8,
+        GLX_ALPHA_SIZE,   8,
+        GLX_DEPTH_SIZE,   0,
+        GLX_STENCIL_SIZE, 0,
+        0,
+    };
+
+    int buffer_flags = glo_flags_get_from_glx(buffer_attributes, 0);
+    int bpp = glo_flags_get_bytes_per_pixel(buffer_flags);
+    int gl_format, gl_type;
+
+    memset(datain_flip, 0, TX * TY * 4);
+    memset(datain, 0, TX * TY * 4);
+
+    p = datain;
+    for (y = 0; y < TY; y++) {
+        for (x = 0; x < TX; x++) {
+            p[0] = x;
+            p[1] = y;
+            if (bpp > 2) {
+                p[2] = 0;
+            }
+            if (bpp > 3) {
+                p[3] = 0xff;
+            }
+            p += bpp;
+        }
+        memcpy(&datain_flip[((TY - 1) - y) * bpp * TX],
+                        &datain[y * bpp * TX], bpp * TX);
+    }
+
+    context = glo_context_create(buffer_flags, 0);
+    if (!context) {
+        goto done;
+    }
+    surface = glo_surface_create(TX, TY, context);
+    if (!surface) {
+        glo_context_destroy(context);
+        goto done;
+    }
+
+    glo_surface_makecurrent(surface);
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, TX, 0, TY, 0, 1);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glRasterPos2f(0, 0);
+    glo_flags_get_readpixel_type(buffer_flags, &gl_format, &gl_type);
+    glDrawPixels(TX, TY, gl_format, gl_type, datain_flip);
+    glFlush();
+
+    memset(dataout, 0, bpp * TX * TY);
+
+    glo_surface_getcontents(surface, TX * 4, bpp * 8, dataout);
+
+    glo_surface_destroy(surface);
+    glo_context_destroy(context);
+
+    ret = !memcmp(datain, dataout, bpp * TX * TY);
+done:
+    g_free(datain);
+    g_free(datain_flip);
+    g_free(dataout);
+    return ret;
+}
+
+static void glo_test_readback_methods(void)
+{
+    int min, maj;
+
+    glo.use_ximage = 0;
+
+    if (!XCompositeQueryVersion(glo.dpy, &min, &maj)) {
+        return;
+    }
+
+    glo.use_ximage = glo_can_readback();
+}
+
+/* Initialise gloffscreen */
+int glo_init(void)
+{
+    XErrorHandler old_handler;
+
+    if (glo_inited) {
+        return 0;
+    }
+
+    /* Open a connection to the X server */
+    /* TODO: Should we accept a command line switch or an envvar so that
+     * users can give a value different than DISPLAY? */
+    glo.dpy = XOpenDisplay(NULL);
+    if (glo.dpy == NULL) {
+        fprintf(stderr, "vmgl: Unable to open a connection to the X server\n");
+        return -1;
+    }
+
+    /* Safe because we are single threaded. Otherwise we cause recursion
+     * on the next call.  Set the X error handler.  */
+    glo_inited = 1;
+    old_handler = XSetErrorHandler(x_errhandler);
+    glo_test_readback_methods();
+
+    return 0;
+}
+
+/* Uninitialise gloffscreen */
+void glo_kill(void)
+{
+    XCloseDisplay(glo.dpy);
+    glo.dpy = NULL;
+}
+
+/* Like wglGetProcAddress/glxGetProcAddress */
+void *glo_getprocaddress(const char *proc_name)
+{
+    return glXGetProcAddressARB((const GLubyte *) proc_name);
+}
+
+/* Create an OpenGL context for a certain pixel format.
+ * format_flags are made up of the GLO_ constants */
+GloContext *glo_context_create(int format_flags, GloContext *share_lists)
+{
+    GLXFBConfig *fb_configs;
+    int rgba_bits_all;
+    GloContext *context;
+    int rgba_bits[4];
+    int buffer_attributes[] = {
+        GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+        GLX_RENDER_TYPE,   GLX_RGBA_BIT,
+        GLX_RED_SIZE,      8,
+        GLX_GREEN_SIZE,    8,
+        GLX_BLUE_SIZE,     8,
+        GLX_ALPHA_SIZE,    8,
+        GLX_DEPTH_SIZE,    0,
+        GLX_STENCIL_SIZE,  0,
+        None
+    };
+
+    /* Set up the surface format from the flags we were given */
+    glo_flags_get_rgba_bits(format_flags, rgba_bits);
+    buffer_attributes[5]  = rgba_bits[0];
+    buffer_attributes[7]  = rgba_bits[1];
+    buffer_attributes[9]  = rgba_bits[2];
+    buffer_attributes[11] = rgba_bits[3];
+    buffer_attributes[13] = glo_flags_get_depth_bits(format_flags);
+    buffer_attributes[15] = glo_flags_get_stencil_bits(format_flags);
+
+    fb_configs = glXChooseFBConfig(glo.dpy, DefaultScreen(glo.dpy),
+                    buffer_attributes, &rgba_bits_all);
+    if (rgba_bits_all == 0) {
+        fprintf(stderr, "vmgl: No matching GLX FB configs found.\n");
+        return NULL;
+    }
+    context = g_malloc0(sizeof(GloContext));
+    context->format_flags = format_flags;
+    context->fb_config = fb_configs[0];
+
+    /* Create a GLX context for OpenGL rendering */
+    context->context = glXCreateNewContext(glo.dpy, context->fb_config,
+                    GLX_RGBA_TYPE, share_lists ? share_lists->context : NULL,
+                    True);
+    if (!context->context) {
+        g_free(context);
+        fprintf(stderr, "vmgl: glXCreateNewContext failed\n");
+        return NULL;
+    }
+
+    return context;
+}
+
+/* Destroy a previously created OpenGL context */
+void glo_context_destroy(GloContext *context)
+{
+    if (!context) {
+        return;
+    }
+
+    /* TODO: check for GloSurfaces using this? */
+    glXDestroyContext(glo.dpy, context->context);
+    g_free(context);
+}
+
+static void glo_surface_free_xshm_image(GloSurface *surface)
+{
+    XShmDetach(glo.dpy, &surface->shminfo);
+    surface->image->data = NULL;
+    XDestroyImage(surface->image);
+    shmdt(surface->shminfo.shmaddr);
+    shmctl(surface->shminfo.shmid, IPC_RMID, NULL);
+}
+
+/* TODO: handle errors */
+static void glo_surface_try_alloc_xshm_image(GloSurface *surface)
+{
+    if (surface->image) {
+        glo_surface_free_xshm_image(surface);
+    }
+
+    surface->image = XShmCreateImage(glo.dpy, DefaultVisual(glo.dpy, 0),
+                    24, ZPixmap, NULL, &surface->shminfo,
+                    surface->width, surface->height);
+
+    surface->shminfo.shmid = shmget(IPC_PRIVATE,
+                    surface->image->bytes_per_line * surface->height,
+                    IPC_CREAT | 0777);
+    surface->shminfo.shmaddr = shmat(surface->shminfo.shmid, NULL, 0);
+    surface->image->data = surface->shminfo.shmaddr;
+    surface->shminfo.readOnly = False;
+    XShmAttach(glo.dpy, &surface->shminfo);
+}
+
+/* Create a surface with given width and height, format_flags are made up
+ * of the GLO_ constants */
+GloSurface *glo_surface_create(int width, int height, GloContext *context)
+{
+    GloSurface *surface;
+    XSetWindowAttributes attr = { 0 };
+    uint32_t mask;
+    XVisualInfo *vis;
+
+    if (!context) {
+        return 0;
+    }
+
+    surface = g_malloc0(sizeof(GloSurface));
+    surface->width = width;
+    surface->height = height;
+    surface->context = context;
+
+    vis = glXGetVisualFromFBConfig(glo.dpy,
+                    ((struct GloContext *) context)->fb_config);
+    surface->colormap = XCreateColormap(glo.dpy, DefaultRootWindow(glo.dpy),
+                    vis->visual, AllocNone);
+
+    attr.background_pixel = 0xff000000;
+    attr.border_pixel = 0;
+    attr.colormap = surface->colormap;
+    attr.event_mask = 0;
+    attr.save_under = True;
+    attr.override_redirect = True;
+    attr.cursor = None;
+    mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask |
+            CWOverrideRedirect | CWSaveUnder;
+
+    surface->window = XCreateWindow(glo.dpy, DefaultRootWindow(glo.dpy),
+                    -width - 1000, 0, width, height, 0,
+                    vis->depth, InputOutput, vis->visual, mask, &attr);
+
+    if (!surface->window) {
+        XFreeColormap(glo.dpy, attr.colormap);
+        g_free(surface);
+        fprintf(stderr, "vmgl: XCreateWindow failed\n");
+        return NULL;
+    }
+
+    XMapWindow(glo.dpy, surface->window);
+
+    if (glo.use_ximage) {
+        XCompositeRedirectWindow(glo.dpy, surface->window,
+                        CompositeRedirectAutomatic);
+        surface->pixmap = XCompositeNameWindowPixmap(glo.dpy, surface->window);
+    } else {
+        surface->pixmap = XCreatePixmap(glo.dpy, DefaultRootWindow(glo.dpy),
+                        width, height,
+                        glo_flags_get_bytes_per_pixel(context->format_flags) *
+                        8);
+    }
+
+    if (!surface->pixmap) {
+        XDestroyWindow(glo.dpy, surface->window);
+        XFreeColormap(glo.dpy, attr.colormap);
+        g_free(surface);
+        fprintf(stderr, "vmgl: Failed to allocate pixmap!\n");
+        return NULL;
+    }
+
+    XSync(glo.dpy, 0);
+
+    /* set hints and properties */
+    {
+        XSizeHints sizehints;
+
+        sizehints.x = 0;
+        sizehints.y = 0;
+        sizehints.width = width;
+        sizehints.height = height;
+        sizehints.flags = USSize | USPosition;
+        XSetWMNormalHints(glo.dpy, surface->window, &sizehints);
+        XSetStandardProperties(glo.dpy, surface->window, "", "", None,
+                        NULL, 0, &sizehints);
+
+        XSync(glo.dpy, 0);
+    }
+
+    /* If we're using XImages to pull the data from the graphics card... */
+    glo_surface_try_alloc_xshm_image(surface);
+
+    return surface;
+}
+
+/* Destroy the given surface */
+void glo_surface_destroy(GloSurface *surface)
+{
+    if (surface->pixmap) {
+        XFreePixmap(glo.dpy, surface->pixmap);
+    }
+    XDestroyWindow(glo.dpy, surface->window);
+    XFreeColormap(glo.dpy, surface->colormap);
+    if (surface->image) {
+        glo_surface_free_xshm_image(surface);
+    }
+    g_free(surface);
+}
+
+/* Make the given surface current */
+int glo_surface_makecurrent(GloSurface *surface)
+{
+    int ret;
+
+    if (surface) {
+        ret = glXMakeCurrent(glo.dpy, surface->window,
+                        surface->context->context);
+    } else {
+        ret = glXMakeCurrent(glo.dpy, 0, NULL);
+    }
+
+    return ret;
+}
+
+/* Get the contents of the given surface */
+void glo_surface_getcontents(GloSurface *surface,
+                int stride, int bpp, void *data)
+{
+    static int once;
+    XImage *img;
+
+    if (!surface) {
+        return;
+    }
+
+    if (glo.use_ximage) {
+        glXWaitGL();
+
+        if (surface->image) {
+            XShmGetImage(glo.dpy, surface->pixmap, surface->image,
+                            0, 0, AllPlanes);
+            img = surface->image;
+        } else {
+            img = XGetImage(glo.dpy, surface->pixmap, 0, 0, surface->width,
+                            surface->height, AllPlanes, ZPixmap);
+        }
+
+        if (img) {
+            if (bpp != 32 && bpp != 24 && !once) {
+                fprintf(stderr, "vmgl: Warning: unsupported colourdepth\n");
+                once = 1;
+            }
+
+            if (bpp == img->bits_per_pixel && stride == img->bytes_per_line) {
+                 memcpy(data, img->data, stride * surface->height);
+            } else {
+                int x, y;
+                for (y = 0; y < surface->height; y++) {
+                    for (x = 0; x < surface->width; x++) {
+                        uint8_t *src = ((uint8_t *) img->data) +
+                                x * (img->bits_per_pixel / 8) +
+                                y * img->bytes_per_line;
+                        uint8_t *dst = ((uint8_t *) data) +
+                                x * (bpp / 8) + y * stride;
+                        dst[0] = src[0];
+                        dst[1] = src[1];
+                        dst[2] = src[2];
+                        if (bpp == 32) {
+                            /* When guest is 32 bit and host is 24 */
+                            dst[3] = 0xff;
+                        }
+                    }
+                }
+            }
+
+            /* If we're not using Shm */
+            if (!surface->image) {
+                XDestroyImage(img);
+            }
+
+            return;  /* We're done.  */
+        }
+        /* Uh oh... better fall back.  Perhaps reset glo.use_ximage to 0?  */
+    }
+
+    /* Compatible / fallback method.  */
+    glo_surface_getcontents_readpixels(surface->context->format_flags,
+                    stride, bpp, surface->width, surface->height, data);
+}
+
+/* Return the width and height of the given surface */
+void glo_surface_get_size(GloSurface *surface, int *width, int *height)
+{
+    if (width) {
+        *width = surface->width;
+    }
+    if (height) {
+        *height = surface->height;
+    }
+}
+
+/* Abstract glXQueryExtensionString() */
+const char *glo_glxqueryextensionsstring(void)
+{
+    return glXQueryExtensionsString(glo.dpy, 0);
+}
diff --git a/gl/gloffscreen.h b/gl/gloffscreen.h
new file mode 100644
index 0000000..3788203
--- /dev/null
+++ b/gl/gloffscreen.h
@@ -0,0 +1,138 @@ 
+/*
+ * Offscreen OpenGL abstraction layer
+ *
+ * Copyright (c) 2010-2012 Intel Corporation
+ * Authors:
+ *   Gordon Williams <gordon.williams@collabora.co.uk>
+ *   Ian Molton <ian.molton@collabora.co.uk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GLOFFSCREEN_H_
+#define GLOFFSCREEN_H_
+
+/* Used to hold data for the OpenGL context */
+typedef struct GloContext GloContext;
+/* Used to hold data for an offscreen surface. */
+typedef struct GloSurface GloSurface;
+
+/* Format flags for glo_surface_create */
+#define GLO_FF_ALPHA_MASK  (0x0001)
+#define GLO_FF_NOALPHA     (0x0000)
+#define GLO_FF_ALPHA       (0x0001)
+
+#define GLO_FF_BITS_MASK   (0x00F0)
+#define GLO_FF_BITS_16     (0x0020)
+#define GLO_FF_BITS_24     (0x0030)
+#define GLO_FF_BITS_32     (0x0040)
+
+#define GLO_FF_DEPTH_MASK   (0x0F00)
+#define GLO_FF_DEPTH_16     (0x0100)
+#define GLO_FF_DEPTH_24     (0x0200)
+#define GLO_FF_DEPTH_32     (0x0300)
+
+#define GLO_FF_STENCIL_MASK   (0xF000)
+#define GLO_FF_STENCIL_8      (0x1000)
+
+/* The only currently supported format */
+#define GLO_FF_DEFAULT     (GLO_FF_BITS_24|GLO_FF_DEPTH_24)
+
+/* Has gloffscreen been previously initialised? */
+int glo_initialised(void);
+
+/* Initialise gloffscreen */
+int glo_init(void);
+
+/* Uninitialise gloffscreen */
+void glo_kill(void);
+
+/* Sanity test of the host GL capabilities to see whether the gl offscreen
+ * requirements are well supported */
+int glo_sanity_test(void);
+
+/* Like glxGetProcAddress/wglGetProcAddress */
+void *glo_getprocaddress(const char *procName);
+
+/* OS-independent glXQueryExtensionsString */
+const char *glo_glxqueryextensionsstring(void);
+
+/* Create an OpenGL context for a certain pixel format. format_flags
+ * are a set of GLO_ constants */
+GloContext *glo_context_create(int format_flags, GloContext *share_lists);
+
+/* Destroy a previouslu created OpenGL context */
+void glo_context_destroy(GloContext *context);
+
+/* Create a surface with given width and height, */
+GloSurface *glo_surface_create(int width, int height, GloContext *context);
+
+/* Destroy the given surface */
+void glo_surface_destroy(GloSurface *surface);
+
+/* Make the given surface current (like glXMakeCurrent) */
+int glo_surface_makecurrent(GloSurface *surface);
+
+/* Get the contents of the given surface. Note that this is top-down, not
+ * bottom-up as glReadPixels would do. */
+void glo_surface_getcontents(GloSurface *surface,
+                int stride, int type, void *data);
+void glo_surface_getcontents_readpixels(int format_flags, int stride, int bpp,
+                int width, int height, void *data);
+
+/* In terms of speed, glReadPixels actually seems the best we can do.
+ * * On Windows PFB_DRAW_TO_BITMAP is software-only.
+ * * http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt would be
+ * useful if we didn't want the data right away (as we could avoid flushing the
+ * pipeline).
+ * * The internal data format seems to be GL_BGRA - and this is indeed faster.
+ * * Apple suggests using GL_UNSIGNED_INT_8_8_8_8_REV instead of
+ * GL_UNSIGNED_BYTE, but there don't appear to be any speed increase from
+ * doing this on Windows at least.
+ */
+
+/* Return the width and height of the given surface */
+void glo_surface_get_size(GloSurface *surface, int *width, int *height);
+
+/* Functions to decode the format flags */
+int glo_flags_get_depth_bits(int format_flags);
+int glo_flags_get_stencil_bits(int format_flags);
+void glo_flags_get_rgba_bits(int format_flags, int *rgba);
+int glo_flags_get_bytes_per_pixel(int format_flags);
+void glo_flags_get_readpixel_type(int format_flags,
+                int *gl_format, int *gl_type);
+
+/* Score how close the given format flags match. 0=great, >0 not so great */
+int glo_flags_score(int format_flags_expected, int format_flags_real);
+
+/* Create a set of format flags from a null-terminated list
+ * of GLX fb_config flags. If assume_booleans is set, items such
+ * as GLX_RGBA/GLX_DOUBLEBUFFER are treated as booleans, not key-value
+ * pairs (glXChooseVisual treats them as booleans, glXChooseFBConfig
+ * as key-value pairs) */
+extern int glo_flags_get_from_glx(const uint32_t *fb_config,
+                int assume_booleans);
+
+/* Use in place of glxGetConfig - returns information from flags based
+ * on a GLX enum */
+uint32_t glo_get_glx_from_flags(int format_flags, int glx_enum);
+
+int glo_acceleration_capability_check(void);
+
+#endif /* GLOFFSCREEN_H_ */