Home | History | Annotate | Download | only in glx
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "gl/GLTestContext.h"
      9 #include "SkOnce.h"
     10 
     11 #include <X11/Xlib.h>
     12 #include <GL/glx.h>
     13 #include <GL/glu.h>
     14 
     15 #include <vector>
     16 #include <utility>
     17 
     18 namespace {
     19 
     20 /* Note: Skia requires glx 1.3 or newer */
     21 
     22 /* This struct is taken from a mesa demo.  Please update as required */
     23 static const std::vector<std::pair<int, int>> gl_versions = {
     24    {1, 0},
     25    {1, 1},
     26    {1, 2},
     27    {1, 3},
     28    {1, 4},
     29    {1, 5},
     30    {2, 0},
     31    {2, 1},
     32    {3, 0},
     33    {3, 1},
     34    {3, 2},
     35    {3, 3},
     36    {4, 0},
     37    {4, 1},
     38    {4, 2},
     39    {4, 3},
     40    {4, 4},
     41 };
     42 
     43 static const std::vector<std::pair<int, int>> gles_versions = {
     44     {2, 0},
     45     {3, 0},
     46 };
     47 
     48 static bool ctxErrorOccurred = false;
     49 static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
     50     ctxErrorOccurred = true;
     51     return 0;
     52 }
     53 
     54 class GLXGLTestContext : public sk_gpu_test::GLTestContext {
     55 public:
     56     GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* shareList);
     57     ~GLXGLTestContext() override;
     58 
     59 private:
     60     void destroyGLContext();
     61     static GLXContext CreateBestContext(bool isES, Display* display, GLXFBConfig bestFbc,
     62                                         GLXContext glxSharedContext);
     63 
     64     void onPlatformMakeCurrent() const override;
     65     std::function<void()> onPlatformGetAutoContextRestore() const override;
     66     void onPlatformSwapBuffers() const override;
     67     GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
     68 
     69     GLXContext fContext;
     70     Display* fDisplay;
     71     Pixmap fPixmap;
     72     GLXPixmap fGlxPixmap;
     73 };
     74 
     75 static Display* get_display() {
     76     class AutoDisplay {
     77     public:
     78         AutoDisplay() { fDisplay = XOpenDisplay(nullptr); }
     79         ~AutoDisplay() {
     80             if (fDisplay) {
     81                 XCloseDisplay(fDisplay);
     82             }
     83         }
     84         Display* display() const { return fDisplay; }
     85     private:
     86         Display* fDisplay;
     87     };
     88     static std::unique_ptr<AutoDisplay> ad;
     89     static SkOnce once;
     90     once([] { ad.reset(new AutoDisplay{}); });
     91     return ad->display();
     92 }
     93 
     94 std::function<void()> context_restorer() {
     95     auto display = glXGetCurrentDisplay();
     96     auto drawable = glXGetCurrentDrawable();
     97     auto context = glXGetCurrentContext();
     98     // On some systems calling glXMakeCurrent with a null display crashes.
     99     if (!display) {
    100         display = get_display();
    101     }
    102     return [display, drawable, context] { glXMakeCurrent(display, drawable, context); };
    103 }
    104 
    105 GLXGLTestContext::GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext* shareContext)
    106     : fContext(nullptr)
    107     , fDisplay(nullptr)
    108     , fPixmap(0)
    109     , fGlxPixmap(0) {
    110     // We cross our fingers that this is the first X call in the program and that if the application
    111     // is actually threaded that this succeeds.
    112     static SkOnce gOnce;
    113     gOnce([] { XInitThreads(); });
    114 
    115     fDisplay = get_display();
    116 
    117     GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr;
    118 
    119     if (!fDisplay) {
    120         SkDebugf("Failed to open X display.\n");
    121         this->destroyGLContext();
    122         return;
    123     }
    124 
    125     // Get a matching FB config
    126     static int visual_attribs[] = {
    127         GLX_X_RENDERABLE    , True,
    128         GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
    129         None
    130     };
    131 
    132     int glx_major, glx_minor;
    133 
    134     // FBConfigs were added in GLX version 1.3.
    135     if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) ||
    136             ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) {
    137         SkDebugf("GLX version 1.3 or higher required.\n");
    138         this->destroyGLContext();
    139         return;
    140     }
    141 
    142     //SkDebugf("Getting matching framebuffer configs.\n");
    143     int fbcount;
    144     GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
    145                                           visual_attribs, &fbcount);
    146     if (!fbc) {
    147         SkDebugf("Failed to retrieve a framebuffer config.\n");
    148         this->destroyGLContext();
    149         return;
    150     }
    151     //SkDebugf("Found %d matching FB configs.\n", fbcount);
    152 
    153     // Pick the FB config/visual with the most samples per pixel
    154     //SkDebugf("Getting XVisualInfos.\n");
    155     int best_fbc = -1, best_num_samp = -1;
    156 
    157     int i;
    158     for (i = 0; i < fbcount; ++i) {
    159         XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
    160         if (vi) {
    161             int samp_buf, samples;
    162             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
    163             glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
    164 
    165             //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
    166             //       " SAMPLES = %d\n",
    167             //        i, (unsigned int)vi->visualid, samp_buf, samples);
    168 
    169             if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) {
    170                 best_fbc = i;
    171                 best_num_samp = samples;
    172             }
    173         }
    174         XFree(vi);
    175     }
    176 
    177     GLXFBConfig bestFbc = fbc[best_fbc];
    178 
    179     // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
    180     XFree(fbc);
    181 
    182     // Get a visual
    183     XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
    184     //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
    185 
    186     fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
    187 
    188     if (!fPixmap) {
    189         SkDebugf("Failed to create pixmap.\n");
    190         this->destroyGLContext();
    191         return;
    192     }
    193 
    194     fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
    195 
    196     // Done with the visual info data
    197     XFree(vi);
    198 
    199     // Get the default screen's GLX extension list
    200     const char *glxExts = glXQueryExtensionsString(
    201         fDisplay, DefaultScreen(fDisplay)
    202     );
    203     // Check for the GLX_ARB_create_context extension string and the function.
    204     // If either is not present, use GLX 1.3 context creation method.
    205     if (!gluCheckExtension(reinterpret_cast<const GLubyte*>("GLX_ARB_create_context"),
    206                            reinterpret_cast<const GLubyte*>(glxExts))) {
    207         if (kGLES_GrGLStandard != forcedGpuAPI) {
    208             fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True);
    209         }
    210     } else {
    211         if (kGLES_GrGLStandard == forcedGpuAPI) {
    212             if (gluCheckExtension(
    213                     reinterpret_cast<const GLubyte*>("GLX_EXT_create_context_es2_profile"),
    214                     reinterpret_cast<const GLubyte*>(glxExts))) {
    215                 fContext = CreateBestContext(true, fDisplay, bestFbc, glxShareContext);
    216             }
    217         } else {
    218             fContext = CreateBestContext(false, fDisplay, bestFbc, glxShareContext);
    219         }
    220     }
    221     if (!fContext) {
    222         SkDebugf("Failed to create an OpenGL context.\n");
    223         this->destroyGLContext();
    224         return;
    225     }
    226 
    227     // Verify that context is a direct context
    228     if (!glXIsDirect(fDisplay, fContext)) {
    229         //SkDebugf("Indirect GLX rendering context obtained.\n");
    230     } else {
    231         //SkDebugf("Direct GLX rendering context obtained.\n");
    232     }
    233 
    234     SkScopeExit restorer(context_restorer());
    235     //SkDebugf("Making context current.\n");
    236     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
    237       SkDebugf("Could not set the context.\n");
    238         this->destroyGLContext();
    239         return;
    240     }
    241 
    242     auto gl = GrGLMakeNativeInterface();
    243     if (!gl) {
    244         SkDebugf("Failed to create gl interface");
    245         this->destroyGLContext();
    246         return;
    247     }
    248 
    249     if (!gl->validate()) {
    250         SkDebugf("Failed to validate gl interface");
    251         this->destroyGLContext();
    252         return;
    253     }
    254 
    255     this->init(std::move(gl));
    256 }
    257 
    258 
    259 GLXGLTestContext::~GLXGLTestContext() {
    260     this->teardown();
    261     this->destroyGLContext();
    262 }
    263 
    264 void GLXGLTestContext::destroyGLContext() {
    265     if (fDisplay) {
    266         if (fContext) {
    267             if (glXGetCurrentContext() == fContext) {
    268                 // This will ensure that the context is immediately deleted.
    269                 glXMakeContextCurrent(fDisplay, None, None, nullptr);
    270             }
    271             glXDestroyContext(fDisplay, fContext);
    272             fContext = nullptr;
    273         }
    274 
    275         if (fGlxPixmap) {
    276             glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
    277             fGlxPixmap = 0;
    278         }
    279 
    280         if (fPixmap) {
    281             XFreePixmap(fDisplay, fPixmap);
    282             fPixmap = 0;
    283         }
    284 
    285         fDisplay = nullptr;
    286     }
    287 }
    288 
    289 /* Create a context with the highest possible version.
    290  *
    291  * Disable Xlib errors for the duration of this function (by default they abort
    292  * the program) and try to get a context starting from the highest version
    293  * number - there is no way to just directly ask what the highest supported
    294  * version is.
    295  *
    296  * Returns the correct context or NULL on failure.
    297  */
    298 GLXContext GLXGLTestContext::CreateBestContext(bool isES, Display* display, GLXFBConfig bestFbc,
    299                                                GLXContext glxShareContext) {
    300     auto glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
    301         glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
    302     if (!glXCreateContextAttribsARB) {
    303         SkDebugf("Failed to get address of glXCreateContextAttribsARB");
    304         return nullptr;
    305     }
    306     GLXContext context = nullptr;
    307     // Install Xlib error handler that will set ctxErrorOccurred.
    308     // WARNING: It is global for all threads.
    309     ctxErrorOccurred = false;
    310     int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler);
    311 
    312     auto versions = isES ? gles_versions : gl_versions;
    313     // Well, unfortunately GLX will not just give us the highest context so
    314     // instead we have to do this nastiness
    315     for (int i = versions.size() - 1; i >= 0 ; i--) {
    316         // WARNING: Don't try to optimize this and make this array static. The
    317         // glXCreateContextAttribsARB call writes to it upon failure and the
    318         // next call would fail too.
    319         std::vector<int> flags = {
    320             GLX_CONTEXT_MAJOR_VERSION_ARB, versions[i].first,
    321             GLX_CONTEXT_MINOR_VERSION_ARB, versions[i].second,
    322         };
    323         if (isES) {
    324             flags.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
    325             // the ES2 flag should work even for higher versions
    326             flags.push_back(GLX_CONTEXT_ES2_PROFILE_BIT_EXT);
    327         } else if (versions[i].first > 2) {
    328             flags.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
    329             // TODO When Nvidia implements NVPR on Core profiles, we should start
    330             // requesting core here - currently Nv Path rendering on Nvidia
    331             // requires a compatibility profile.
    332             flags.push_back(GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
    333         }
    334         flags.push_back(0);
    335         context = glXCreateContextAttribsARB(display, bestFbc, glxShareContext, true,
    336                                              &flags[0]);
    337         // Sync to ensure any errors generated are processed.
    338         XSync(display, False);
    339 
    340         if (!ctxErrorOccurred && context) {
    341             break;
    342         }
    343         // try again
    344         ctxErrorOccurred = false;
    345     }
    346     // Restore the original error handler.
    347     XSetErrorHandler(oldHandler);
    348     return context;
    349 }
    350 
    351 void GLXGLTestContext::onPlatformMakeCurrent() const {
    352     if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
    353         SkDebugf("Could not set the context.\n");
    354     }
    355 }
    356 
    357 std::function<void()> GLXGLTestContext::onPlatformGetAutoContextRestore() const {
    358     if (glXGetCurrentContext() == fContext) {
    359         return nullptr;
    360     }
    361     return context_restorer();
    362 }
    363 
    364 void GLXGLTestContext::onPlatformSwapBuffers() const {
    365     glXSwapBuffers(fDisplay, fGlxPixmap);
    366 }
    367 
    368 GrGLFuncPtr GLXGLTestContext::onPlatformGetProcAddress(const char* procName) const {
    369     return glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName));
    370 }
    371 
    372 }  // anonymous namespace
    373 
    374 namespace sk_gpu_test {
    375 GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
    376                                            GLTestContext *shareContext) {
    377     GLXGLTestContext *glxShareContext = reinterpret_cast<GLXGLTestContext *>(shareContext);
    378     GLXGLTestContext *ctx = new GLXGLTestContext(forcedGpuAPI, glxShareContext);
    379     if (!ctx->isValid()) {
    380         delete ctx;
    381         return nullptr;
    382     }
    383     return ctx;
    384 }
    385 }  // namespace sk_gpu_test
    386