Home | History | Annotate | Download | only in gtk
      1 /*
      2  * Copyright (C) 2011 Igalia S.L.
      3  *
      4  *  This library is free software; you can redistribute it and/or
      5  *  modify it under the terms of the GNU Lesser General Public
      6  *  License as published by the Free Software Foundation; either
      7  *  version 2 of the License, or (at your option) any later version.
      8  *
      9  *  This library is distributed in the hope that it will be useful,
     10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  *  Lesser General Public License for more details.
     13  *
     14  *  You should have received a copy of the GNU Lesser General Public
     15  *  License along with this library; if not, write to the Free
     16  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  *  Boston, MA 02110-1301 USA
     18  */
     19 
     20 #include "config.h"
     21 #include "GraphicsContext3DInternal.h"
     22 
     23 #if ENABLE(WEBGL)
     24 
     25 #include "GraphicsContext3D.h"
     26 #include "OpenGLShims.h"
     27 #include <GL/glx.h>
     28 #include <dlfcn.h>
     29 
     30 // We do not want to call glXMakeContextCurrent using different Display pointers,
     31 // because it might lead to crashes in some drivers (fglrx). We use a shared display
     32 // pointer here.
     33 static Display* gSharedDisplay = 0;
     34 static Display* sharedDisplay()
     35 {
     36     if (!gSharedDisplay)
     37         gSharedDisplay = XOpenDisplay(0);
     38     return gSharedDisplay;
     39 }
     40 
     41 namespace WebCore {
     42 
     43 // Because of driver bugs, exiting the program when there are active pbuffers
     44 // can crash the X server (this has been observed with the official Nvidia drivers).
     45 // We need to ensure that we clean everything up on exit. There are several reasons
     46 // that GraphicsContext3Ds will still be alive at exit, including user error (memory
     47 // leaks) and the page cache. In any case, we don't want the X server to crash.
     48 static bool cleaningUpAtExit = false;
     49 static Vector<GraphicsContext3D*>& activeGraphicsContexts()
     50 {
     51     DEFINE_STATIC_LOCAL(Vector<GraphicsContext3D*>, contexts, ());
     52     return contexts;
     53 }
     54 
     55 void GraphicsContext3DInternal::addActiveGraphicsContext(GraphicsContext3D* context)
     56 {
     57     static bool addedAtExitHandler = false;
     58     if (!addedAtExitHandler) {
     59         atexit(&GraphicsContext3DInternal::cleanupActiveContextsAtExit);
     60         addedAtExitHandler = true;
     61     }
     62     activeGraphicsContexts().append(context);
     63 }
     64 
     65 void GraphicsContext3DInternal::removeActiveGraphicsContext(GraphicsContext3D* context)
     66 {
     67     if (cleaningUpAtExit)
     68         return;
     69 
     70     Vector<GraphicsContext3D*>& contexts = activeGraphicsContexts();
     71     size_t location = contexts.find(context);
     72     if (location != WTF::notFound)
     73         contexts.remove(location);
     74 }
     75 
     76 void GraphicsContext3DInternal::cleanupActiveContextsAtExit()
     77 {
     78     cleaningUpAtExit = true;
     79 
     80     Vector<GraphicsContext3D*>& contexts = activeGraphicsContexts();
     81     for (size_t i = 0; i < contexts.size(); i++)
     82         contexts[i]->~GraphicsContext3D();
     83 
     84     if (!gSharedDisplay)
     85         return;
     86     XCloseDisplay(gSharedDisplay);
     87     gSharedDisplay = 0;
     88 }
     89 
     90 GraphicsContext3DInternal* GraphicsContext3DInternal::create()
     91 {
     92     if (!sharedDisplay())
     93         return 0;
     94 
     95     static bool initialized = false;
     96     static bool success = true;
     97     if (!initialized) {
     98         success = initializeOpenGLShims();
     99         initialized = true;
    100     }
    101     if (!success)
    102         return 0;
    103 
    104     GraphicsContext3DInternal* internal = createPbufferContext();
    105     if (!internal)
    106         internal = createPixmapContext();
    107     if (!internal)
    108         return 0;
    109 
    110     // The GraphicsContext3D constructor requires that this context is the current OpenGL context.
    111     internal->makeContextCurrent();
    112     return internal;
    113 }
    114 
    115 GraphicsContext3DInternal* GraphicsContext3DInternal::createPbufferContext()
    116 {
    117     int fbConfigAttributes[] = {
    118         GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
    119         GLX_RENDER_TYPE, GLX_RGBA_BIT,
    120         GLX_RED_SIZE, 1,
    121         GLX_GREEN_SIZE, 1,
    122         GLX_BLUE_SIZE, 1,
    123         GLX_ALPHA_SIZE, 1,
    124         GLX_DEPTH_SIZE, 1,
    125         GLX_STENCIL_SIZE, 1,
    126         GLX_SAMPLE_BUFFERS, 1,
    127         GLX_DOUBLEBUFFER, GL_FALSE,
    128         GLX_SAMPLES, 4,
    129         0
    130     };
    131     int returnedElements;
    132     GLXFBConfig* configs = glXChooseFBConfig(sharedDisplay(), 0, fbConfigAttributes, &returnedElements);
    133     if (!configs) {
    134         fbConfigAttributes[20] = 0; // Attempt without anti-aliasing.
    135         configs = glXChooseFBConfig(sharedDisplay(), 0, fbConfigAttributes, &returnedElements);
    136     }
    137     if (!returnedElements) {
    138         XFree(configs);
    139         return 0;
    140     }
    141 
    142     // We will be rendering to a texture, so our pbuffer does not need to be large.
    143     static const int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 };
    144     GLXPbuffer pbuffer = glXCreatePbuffer(sharedDisplay(), configs[0], pbufferAttributes);
    145     if (!pbuffer) {
    146         XFree(configs);
    147         return 0;
    148     }
    149 
    150     GLXContext context = glXCreateNewContext(sharedDisplay(), configs[0], GLX_RGBA_TYPE, 0, GL_TRUE);
    151     XFree(configs);
    152     if (!context)
    153         return 0;
    154     return new GraphicsContext3DInternal(context, pbuffer);
    155 }
    156 
    157 GraphicsContext3DInternal* GraphicsContext3DInternal::createPixmapContext()
    158 {
    159     static int visualAttributes[] = {
    160         GLX_RGBA,
    161         GLX_RED_SIZE, 1,
    162         GLX_GREEN_SIZE, 1,
    163         GLX_BLUE_SIZE, 1,
    164         GLX_ALPHA_SIZE, 1,
    165         GLX_DOUBLEBUFFER,
    166         0
    167     };
    168 
    169     XVisualInfo* visualInfo = glXChooseVisual(sharedDisplay(), DefaultScreen(sharedDisplay()), visualAttributes);
    170     if (!visualInfo)
    171         return 0;
    172 
    173     GLXContext context = glXCreateContext(sharedDisplay(), visualInfo, 0, GL_TRUE);
    174     if (!context) {
    175         XFree(visualInfo);
    176         return 0;
    177     }
    178 
    179     Pixmap pixmap = XCreatePixmap(sharedDisplay(), DefaultRootWindow(sharedDisplay()), 1, 1, visualInfo->depth);
    180     if (!pixmap) {
    181         XFree(visualInfo);
    182         return 0;
    183     }
    184 
    185     GLXPixmap glxPixmap = glXCreateGLXPixmap(sharedDisplay(), visualInfo, pixmap);
    186     if (!glxPixmap) {
    187         XFreePixmap(sharedDisplay(), pixmap);
    188         XFree(visualInfo);
    189         return 0;
    190     }
    191 
    192     return new GraphicsContext3DInternal(context, pixmap, glxPixmap);
    193 }
    194 
    195 GraphicsContext3DInternal::GraphicsContext3DInternal(GLXContext context, GLXPbuffer pbuffer)
    196     : m_context(context)
    197     , m_pbuffer(pbuffer)
    198     , m_pixmap(0)
    199     , m_glxPixmap(0)
    200 {
    201 }
    202 
    203 GraphicsContext3DInternal::GraphicsContext3DInternal(GLXContext context, Pixmap pixmap, GLXPixmap glxPixmap)
    204     : m_context(context)
    205     , m_pbuffer(0)
    206     , m_pixmap(pixmap)
    207     , m_glxPixmap(glxPixmap)
    208 {
    209 }
    210 
    211 GraphicsContext3DInternal::~GraphicsContext3DInternal()
    212 {
    213     if (m_context) {
    214         // This may be necessary to prevent crashes with NVidia's closed source drivers. Originally
    215         // from Mozilla's 3D canvas implementation at: http://bitbucket.org/ilmari/canvas3d/
    216         ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    217 
    218         ::glXMakeContextCurrent(sharedDisplay(), 0, 0, 0);
    219         ::glXDestroyContext(sharedDisplay(), m_context);
    220         m_context = 0;
    221     }
    222 
    223     if (m_pbuffer) {
    224         ::glXDestroyPbuffer(sharedDisplay(), m_pbuffer);
    225         m_pbuffer = 0;
    226     }
    227     if (m_glxPixmap) {
    228         glXDestroyGLXPixmap(sharedDisplay(), m_glxPixmap);
    229         m_glxPixmap = 0;
    230     }
    231     if (m_pixmap) {
    232         XFreePixmap(sharedDisplay(), m_pixmap);
    233         m_pixmap = 0;
    234     }
    235 }
    236 
    237 void GraphicsContext3DInternal::makeContextCurrent()
    238 {
    239     if (::glXGetCurrentContext() == m_context)
    240         return;
    241     if (!m_context)
    242         return;
    243     if (m_pbuffer) {
    244         ::glXMakeCurrent(sharedDisplay(), m_pbuffer, m_context);
    245         return;
    246     }
    247 
    248     ASSERT(m_glxPixmap);
    249     ::glXMakeCurrent(sharedDisplay(), m_glxPixmap, m_context);
    250 }
    251 
    252 } // namespace WebCore
    253 
    254 #endif // ENABLE_WEBGL
    255