Add GL context creation for X so that gm can run GPU on Linux.
http://codereview.appspot.com/4548047/


git-svn-id: http://skia.googlecode.com/svn/trunk@1343 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/utils/unix/SkEGLContext_Unix.cpp b/src/utils/unix/SkEGLContext_Unix.cpp
new file mode 100644
index 0000000..7921b8a
--- /dev/null
+++ b/src/utils/unix/SkEGLContext_Unix.cpp
@@ -0,0 +1,264 @@
+#include "SkEGLContext.h"
+#include "SkTypes.h"
+
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <GL/glu.h>
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#define SK_GL_GET_PROC(T, F) T F = NULL; \
+        F = (T) glXGetProcAddressARB(reinterpret_cast<const GLubyte*>(#F));
+
+static bool ctxErrorOccurred = false;
+static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
+    ctxErrorOccurred = true;
+    return 0;
+}
+
+SkEGLContext::SkEGLContext() : context(NULL), display(NULL), pixmap(0), glxPixmap(0) {
+}
+
+SkEGLContext::~SkEGLContext() {
+    if (this->display) {
+        glXMakeCurrent(this->display, 0, 0);
+
+        if (this->context)
+            glXDestroyContext(this->display, this->context);
+
+        if (this->glxPixmap)
+            glXDestroyGLXPixmap(this->display, this->glxPixmap);
+
+        if (this->pixmap)
+            XFreePixmap(this->display, this->pixmap);
+
+        XCloseDisplay(this->display);
+    }
+}
+
+bool SkEGLContext::init(const int width, const int height) {
+    Display *display = XOpenDisplay(0);
+    this->display = display;
+
+    if (!display) {
+        SkDebugf("Failed to open X display.\n");
+        return false;
+    }
+
+    // Get a matching FB config
+    static int visual_attribs[] = {
+        GLX_X_RENDERABLE    , True,
+        GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
+        GLX_RENDER_TYPE     , GLX_RGBA_BIT,
+        GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
+        GLX_RED_SIZE        , 8,
+        GLX_GREEN_SIZE      , 8,
+        GLX_BLUE_SIZE       , 8,
+        GLX_ALPHA_SIZE      , 8,
+        GLX_DEPTH_SIZE      , 24,
+        GLX_STENCIL_SIZE    , 8,
+        GLX_DOUBLEBUFFER    , True,
+        //GLX_SAMPLE_BUFFERS  , 1,
+        //GLX_SAMPLES         , 4,
+        None
+    };
+
+    int glx_major, glx_minor;
+
+    // FBConfigs were added in GLX version 1.3.
+    if (!glXQueryVersion( display, &glx_major, &glx_minor) ||
+            ( (glx_major == 1) && (glx_minor < 3) ) || (glx_major < 1))
+    {
+        SkDebugf("Invalid GLX version.");
+        return false;
+    }
+
+    //SkDebugf("Getting matching framebuffer configs.\n");
+    int fbcount;
+    GLXFBConfig *fbc = glXChooseFBConfig(display, DefaultScreen(display),
+                                          visual_attribs, &fbcount);
+    if (!fbc) {
+        SkDebugf("Failed to retrieve a framebuffer config.\n");
+        return false;
+    }
+    //SkDebugf("Found %d matching FB configs.\n", fbcount);
+
+    // Pick the FB config/visual with the most samples per pixel
+    //SkDebugf("Getting XVisualInfos.\n");
+    int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
+
+    int i;
+    for (i = 0; i < fbcount; ++i) {
+        XVisualInfo *vi = glXGetVisualFromFBConfig(display, fbc[i]);
+        if (vi) {
+            int samp_buf, samples;
+            glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+            glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLES, &samples);
+
+            //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
+            //       " SAMPLES = %d\n",
+            //        i, (unsigned int)vi->visualid, samp_buf, samples);
+
+            if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
+                best_fbc = i, best_num_samp = samples;
+            if (worst_fbc < 0 || !samp_buf || samples < worst_num_samp)
+                worst_fbc = i, worst_num_samp = samples;
+        }
+        XFree(vi);
+    }
+
+    GLXFBConfig bestFbc = fbc[best_fbc];
+
+    // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
+    XFree(fbc);
+
+    // Get a visual
+    XVisualInfo *vi = glXGetVisualFromFBConfig(display, bestFbc);
+    //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
+
+    Pixmap pixmap = XCreatePixmap(
+        display, RootWindow(display, vi->screen), width, height, vi->depth
+    );
+
+    this->pixmap = pixmap;
+    if (!pixmap) {
+        SkDebugf("Failed to create pixmap.\n");
+        return false;
+    }
+
+    GLXPixmap glxPixmap = glXCreateGLXPixmap(display, vi, pixmap);
+    this->glxPixmap = glxPixmap;
+
+    // Done with the visual info data
+    XFree(vi);
+
+    // Create the context
+    GLXContext ctx = 0;
+
+    // Install an X error handler so the application won't exit if GL 3.0
+    // context allocation fails.
+    //
+    // Note this error handler is global.
+    // All display connections in all threads of a process use the same
+    // error handler, so be sure to guard against other threads issuing
+    // X commands while this code is running.
+    ctxErrorOccurred = false;
+    int (*oldHandler)(Display*, XErrorEvent*) =
+        XSetErrorHandler(&ctxErrorHandler);
+
+    // Get the default screen's GLX extension list
+    const char *glxExts = glXQueryExtensionsString(
+        display, DefaultScreen(display)
+    );
+    // Check for the GLX_ARB_create_context extension string and the function.
+    // If either is not present, use GLX 1.3 context creation method.
+    if (!gluCheckExtension(
+          reinterpret_cast<const GLubyte*>("GLX_ARB_create_context")
+          , reinterpret_cast<const GLubyte*>(glxExts)))
+    {
+        //SkDebugf("GLX_ARB_create_context not found."
+        //       " Using old-style GLX context.\n");
+        ctx = glXCreateNewContext(display, bestFbc, GLX_RGBA_TYPE, 0, True);
+
+    } else {
+        //SkDebugf("Creating context.\n");
+
+        SK_GL_GET_PROC(PFNGLXCREATECONTEXTATTRIBSARBPROC, glXCreateContextAttribsARB)
+        int context_attribs[] = {
+            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
+            //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+            None
+        };
+        ctx = glXCreateContextAttribsARB(
+            display, bestFbc, 0, True, context_attribs
+        );
+
+        // Sync to ensure any errors generated are processed.
+        XSync(display, False);
+        if (!ctxErrorOccurred && ctx) {
+           //SkDebugf( "Created GL 3.0 context.\n" );
+        } else {
+            // Couldn't create GL 3.0 context.
+            // Fall back to old-style 2.x context.
+            // When a context version below 3.0 is requested,
+            // implementations will return the newest context version compatible
+            // with OpenGL versions less than version 3.0.
+
+            // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
+            context_attribs[1] = 1;
+            // GLX_CONTEXT_MINOR_VERSION_ARB = 0
+            context_attribs[3] = 0;
+
+            ctxErrorOccurred = false;
+
+            //SkDebugf("Failed to create GL 3.0 context."
+            //       " Using old-style GLX context.\n");
+            ctx = glXCreateContextAttribsARB(
+                display, bestFbc, 0, True, context_attribs
+            );
+        }
+    }
+
+    // Sync to ensure any errors generated are processed.
+    XSync(display, False);
+
+    // Restore the original error handler
+    XSetErrorHandler(oldHandler);
+
+    if (ctxErrorOccurred || !ctx) {
+        SkDebugf("Failed to create an OpenGL context.\n");
+        return false;
+    }
+    this->context = ctx;
+
+    // Verify that context is a direct context
+    if (!glXIsDirect(display, ctx)) {
+        //SkDebugf("Indirect GLX rendering context obtained.\n");
+    } else {
+        //SkDebugf("Direct GLX rendering context obtained.\n");
+    }
+
+    //SkDebugf("Making context current.\n");
+    if (!glXMakeCurrent(display, glxPixmap, ctx)) {
+      SkDebugf("Could not set the context.\n");
+      return false;
+    }
+
+    //Setup the framebuffers
+    const GLubyte* glExts = glGetString(GL_EXTENSIONS);
+    if (!gluCheckExtension(
+          reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object")
+          , glExts))
+    {
+      SkDebugf("GL_EXT_framebuffer_object not found.\n");
+      return false;
+    }
+    SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffersEXT)
+    SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebufferEXT)
+    SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffersEXT)
+    SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbufferEXT)
+    SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorageEXT)
+    SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbufferEXT)
+    SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatusEXT)
+
+    GLuint fboID;
+    GLuint cbID;
+    GLuint dsID;
+    glGenFramebuffersEXT(1, &fboID);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
+    glGenRenderbuffersEXT(1, &cbID);
+    glBindRenderbufferEXT(GL_RENDERBUFFER, cbID);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, width, height);
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
+    glGenRenderbuffersEXT(1, &dsID);
+    glBindRenderbufferEXT(GL_RENDERBUFFER, dsID);
+    glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
+    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
+    glViewport(0, 0, width, height);
+    glClearStencil(0);
+    glClear(GL_STENCIL_BUFFER_BIT);
+
+    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
+    return GL_FRAMEBUFFER_COMPLETE == status;
+}