Home | History | Annotate | Download | only in unix
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "gl/SkNativeGLContext.h"
      9 
     10 #include <GL/glu.h>
     11 
     12 /* Note: Skia requires glx 1.3 or newer */
     13 
     14 SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
     15     fOldGLXContext = glXGetCurrentContext();
     16     fOldDisplay = glXGetCurrentDisplay();
     17     fOldDrawable = glXGetCurrentDrawable();
     18 }
     19 
     20 SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
     21     if (fOldDisplay) {
     22         glXMakeCurrent(fOldDisplay, fOldDrawable, fOldGLXContext);
     23     }
     24 }
     25 
     26 ///////////////////////////////////////////////////////////////////////////////
     27 
     28 static bool ctxErrorOccurred = false;
     29 static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
     30     ctxErrorOccurred = true;
     31     return 0;
     32 }
     33 
     34 SkNativeGLContext::SkNativeGLContext()
     35     : fContext(NULL)
     36     , fDisplay(NULL)
     37     , fPixmap(0)
     38     , fGlxPixmap(0) {
     39 }
     40 
     41 SkNativeGLContext::~SkNativeGLContext() {
     42     this->destroyGLContext();
     43 }
     44 
     45 void SkNativeGLContext::destroyGLContext() {
     46     if (fDisplay) {
     47         glXMakeCurrent(fDisplay, 0, 0);
     48 
     49         if (fContext) {
     50             glXDestroyContext(fDisplay, fContext);
     51             fContext = NULL;
     52         }
     53 
     54         if (fGlxPixmap) {
     55             glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
     56             fGlxPixmap = 0;
     57         }
     58 
     59         if (fPixmap) {
     60             XFreePixmap(fDisplay, fPixmap);
     61             fPixmap = 0;
     62         }
     63 
     64         XCloseDisplay(fDisplay);
     65         fDisplay = NULL;
     66     }
     67 }
     68 
     69 const GrGLInterface* SkNativeGLContext::createGLContext(GrGLStandard forcedGpuAPI) {
     70     fDisplay = XOpenDisplay(0);
     71 
     72     if (!fDisplay) {
     73         SkDebugf("Failed to open X display.\n");
     74         this->destroyGLContext();
     75         return NULL;
     76     }
     77 
     78     // Get a matching FB config
     79     static int visual_attribs[] = {
     80         GLX_X_RENDERABLE    , True,
     81         GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
     82         None
     83     };
     84 
     85     int glx_major, glx_minor;
     86 
     87     // FBConfigs were added in GLX version 1.3.
     88     if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) ||
     89             ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) {
     90         SkDebugf("GLX version 1.3 or higher required.\n");
     91         this->destroyGLContext();
     92         return NULL;
     93     }
     94 
     95     //SkDebugf("Getting matching framebuffer configs.\n");
     96     int fbcount;
     97     GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
     98                                           visual_attribs, &fbcount);
     99     if (!fbc) {
    100         SkDebugf("Failed to retrieve a framebuffer config.\n");
    101         this->destroyGLContext();
    102         return NULL;
    103     }
    104     //SkDebugf("Found %d matching FB configs.\n", fbcount);
    105 
    106     // Pick the FB config/visual with the most samples per pixel
    107     //SkDebugf("Getting XVisualInfos.\n");
    108     int best_fbc = -1, best_num_samp = -1;
    109 
    110     int i;
    111     for (i = 0; i < fbcount; ++i) {
    112         XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
    113         if (vi) {
    114             int samp_buf, samples;
    115             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
    116             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
    117 
    118             //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
    119             //       " SAMPLES = %d\n",
    120             //        i, (unsigned int)vi->visualid, samp_buf, samples);
    121 
    122             if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
    123                 best_fbc = i, best_num_samp = samples;
    124         }
    125         XFree(vi);
    126     }
    127 
    128     GLXFBConfig bestFbc = fbc[best_fbc];
    129 
    130     // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
    131     XFree(fbc);
    132 
    133     // Get a visual
    134     XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
    135     //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
    136 
    137     fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
    138 
    139     if (!fPixmap) {
    140         SkDebugf("Failed to create pixmap.\n");
    141         this->destroyGLContext();
    142         return NULL;
    143     }
    144 
    145     fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
    146 
    147     // Done with the visual info data
    148     XFree(vi);
    149 
    150     // Create the context
    151 
    152     // Install an X error handler so the application won't exit if GL 3.0
    153     // context allocation fails.
    154     //
    155     // Note this error handler is global.
    156     // All display connections in all threads of a process use the same
    157     // error handler, so be sure to guard against other threads issuing
    158     // X commands while this code is running.
    159     ctxErrorOccurred = false;
    160     int (*oldHandler)(Display*, XErrorEvent*) =
    161         XSetErrorHandler(&ctxErrorHandler);
    162 
    163     // Get the default screen's GLX extension list
    164     const char *glxExts = glXQueryExtensionsString(
    165         fDisplay, DefaultScreen(fDisplay)
    166     );
    167 
    168 
    169     // Check for the GLX_ARB_create_context extension string and the function.
    170     // If either is not present, use GLX 1.3 context creation method.
    171     if (!gluCheckExtension(reinterpret_cast<const GLubyte*>("GLX_ARB_create_context"),
    172                            reinterpret_cast<const GLubyte*>(glxExts))) {
    173         if (kGLES_GrGLStandard != forcedGpuAPI) {
    174             fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True);
    175         }
    176     } else {
    177         //SkDebugf("Creating context.\n");
    178         PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
    179             (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
    180 
    181         if (kGLES_GrGLStandard == forcedGpuAPI) {
    182             if (gluCheckExtension(
    183                     reinterpret_cast<const GLubyte*>("GLX_EXT_create_context_es2_profile"),
    184                     reinterpret_cast<const GLubyte*>(glxExts))) {
    185                 static const int context_attribs_gles[] = {
    186                     GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
    187                     GLX_CONTEXT_MINOR_VERSION_ARB, 0,
    188                     GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT,
    189                     None
    190                 };
    191                 fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, 0, True,
    192                                                       context_attribs_gles);
    193             }
    194         } else {
    195             // Well, unfortunately GLX will not just give us the highest context so instead we have
    196             // to do this nastiness
    197             for (i = NUM_GL_VERSIONS - 2; i > 0 ; i--) {
    198                 /* don't bother below GL 3.0 */
    199                 if (gl_versions[i].major == 3 && gl_versions[i].minor == 0) {
    200                     break;
    201                 }
    202                 // On Nvidia GPUs, to use Nv Path rendering we need a compatibility profile for the
    203                 // time being.
    204                 // TODO when Nvidia implements NVPR on Core profiles, we should start requesting
    205                 // core here
    206                 static const int context_attribs_gl[] = {
    207                       GLX_CONTEXT_MAJOR_VERSION_ARB, gl_versions[i].major,
    208                       GLX_CONTEXT_MINOR_VERSION_ARB, gl_versions[i].minor,
    209                       GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
    210                       None
    211                 };
    212                 fContext =
    213                         glXCreateContextAttribsARB(fDisplay, bestFbc, 0, True, context_attribs_gl);
    214 
    215                 // Sync to ensure any errors generated are processed.
    216                 XSync(fDisplay, False);
    217 
    218                 if (!ctxErrorOccurred && fContext) {
    219                     break;
    220                 }
    221                 // try again
    222                 ctxErrorOccurred = false;
    223             }
    224 
    225             // Couldn't create GL 3.0 context.
    226             // Fall back to old-style 2.x context.
    227             // When a context version below 3.0 is requested,
    228             // implementations will return the newest context version
    229             // compatible with OpenGL versions less than version 3.0.
    230             if (ctxErrorOccurred || !fContext) {
    231                 static const int context_attribs_gl_fallback[] = {
    232                     GLX_CONTEXT_MAJOR_VERSION_ARB, 1,
    233                     GLX_CONTEXT_MINOR_VERSION_ARB, 0,
    234                     None
    235                 };
    236 
    237                 ctxErrorOccurred = false;
    238 
    239                 fContext = glXCreateContextAttribsARB(fDisplay, bestFbc, 0, True,
    240                                                       context_attribs_gl_fallback);
    241             }
    242         }
    243     }
    244 
    245     // Sync to ensure any errors generated are processed.
    246     XSync(fDisplay, False);
    247 
    248     // Restore the original error handler
    249     XSetErrorHandler(oldHandler);
    250 
    251     if (ctxErrorOccurred || !fContext) {
    252         SkDebugf("Failed to create an OpenGL context.\n");
    253         this->destroyGLContext();
    254         return NULL;
    255     }
    256 
    257     // Verify that context is a direct context
    258     if (!glXIsDirect(fDisplay, fContext)) {
    259         //SkDebugf("Indirect GLX rendering context obtained.\n");
    260     } else {
    261         //SkDebugf("Direct GLX rendering context obtained.\n");
    262     }
    263 
    264     //SkDebugf("Making context current.\n");
    265     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
    266       SkDebugf("Could not set the context.\n");
    267         this->destroyGLContext();
    268         return NULL;
    269     }
    270 
    271     const GrGLInterface* interface = GrGLCreateNativeInterface();
    272     if (!interface) {
    273         SkDebugf("Failed to create gl interface");
    274         this->destroyGLContext();
    275         return NULL;
    276     }
    277     return interface;
    278 }
    279 
    280 void SkNativeGLContext::makeCurrent() const {
    281     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
    282         SkDebugf("Could not set the context.\n");
    283     }
    284 }
    285 
    286 void SkNativeGLContext::swapBuffers() const {
    287     glXSwapBuffers(fDisplay, fGlxPixmap);
    288 }
    289