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