| 1 | /* |
| 2 | * Copyright (C) 2011, 2012 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 Software |
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | */ |
| 18 | |
| 19 | #include "config.h" |
| 20 | #include "GLContextGLX.h" |
| 21 | |
| 22 | #if USE(GLX) |
| 23 | #include "GraphicsContext3D.h" |
| 24 | #include "OpenGLShims.h" |
| 25 | #include "PlatformDisplayX11.h" |
| 26 | #include "XErrorTrapper.h" |
| 27 | #include <GL/glx.h> |
| 28 | #include <cairo.h> |
| 29 | |
| 30 | #if ENABLE(ACCELERATED_2D_CANVAS) |
| 31 | #include <cairo-gl.h> |
| 32 | #endif |
| 33 | |
| 34 | namespace WebCore { |
| 35 | |
| 36 | #if !defined(PFNGLXSWAPINTERVALSGIPROC) |
| 37 | typedef int (*PFNGLXSWAPINTERVALSGIPROC) (int); |
| 38 | #endif |
| 39 | #if !defined(PFNGLXCREATECONTEXTATTRIBSARBPROC) |
| 40 | typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); |
| 41 | #endif |
| 42 | |
| 43 | static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; |
| 44 | static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB; |
| 45 | |
| 46 | static bool hasSGISwapControlExtension(Display* display) |
| 47 | { |
| 48 | static bool initialized = false; |
| 49 | if (initialized) |
| 50 | return !!glXSwapIntervalSGI; |
| 51 | |
| 52 | initialized = true; |
| 53 | if (!GLContext::isExtensionSupported(glXQueryExtensionsString(display, 0), "GLX_SGI_swap_control" )) |
| 54 | return false; |
| 55 | |
| 56 | glXSwapIntervalSGI = reinterpret_cast<PFNGLXSWAPINTERVALSGIPROC>(glXGetProcAddress(reinterpret_cast<const unsigned char*>("glXSwapIntervalSGI" ))); |
| 57 | return !!glXSwapIntervalSGI; |
| 58 | } |
| 59 | |
| 60 | static bool hasGLXARBCreateContextExtension(Display* display) |
| 61 | { |
| 62 | static bool initialized = false; |
| 63 | if (initialized) |
| 64 | return !!glXCreateContextAttribsARB; |
| 65 | |
| 66 | initialized = true; |
| 67 | if (!GLContext::isExtensionSupported(glXQueryExtensionsString(display, 0), "GLX_ARB_create_context" )) |
| 68 | return false; |
| 69 | |
| 70 | glXCreateContextAttribsARB = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddress(reinterpret_cast<const unsigned char*>("glXCreateContextAttribsARB" ))); |
| 71 | return !!glXCreateContextAttribsARB; |
| 72 | } |
| 73 | |
| 74 | static GLXContext createGLXARBContext(Display* display, GLXFBConfig config, GLXContext sharingContext) |
| 75 | { |
| 76 | // We want to create a context with version >= 3.2 core profile, cause that ensures that the i965 driver won't |
| 77 | // use the software renderer. If that doesn't work, we will use whatever version available. Unfortunately, |
| 78 | // there's no way to know whether glXCreateContextAttribsARB can provide an OpenGL version >= 3.2 until |
| 79 | // we actually call it and check the return value. To make things more fun, if a version >= 3.2 cannot be |
| 80 | // provided, glXCreateContextAttribsARB will throw a GLXBadFBConfig X error, causing the app to crash. |
| 81 | // So, the first time a context is requested, we set a X error trap to disable crashes with GLXBadFBConfig |
| 82 | // and then check whether the return value is a context or not. |
| 83 | |
| 84 | static bool canCreate320Context = false; |
| 85 | static bool canCreate320ContextInitialized = false; |
| 86 | |
| 87 | static const int contextAttributes[] = { |
| 88 | GLX_CONTEXT_MAJOR_VERSION_ARB, 3, |
| 89 | GLX_CONTEXT_MINOR_VERSION_ARB, 2, |
| 90 | 0 |
| 91 | }; |
| 92 | |
| 93 | if (!canCreate320ContextInitialized) { |
| 94 | canCreate320ContextInitialized = true; |
| 95 | |
| 96 | { |
| 97 | // Set an X error trapper that ignores errors to avoid crashing on GLXBadFBConfig. Use a scope |
| 98 | // here to limit the error trap to just this context creation call. |
| 99 | XErrorTrapper trapper(display, XErrorTrapper::Policy::Ignore); |
| 100 | GLXContext context = glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, contextAttributes); |
| 101 | if (context) { |
| 102 | canCreate320Context = true; |
| 103 | return context; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | // Creating the 3.2 context failed, so use whatever is available. |
| 108 | return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, nullptr); |
| 109 | } |
| 110 | |
| 111 | if (canCreate320Context) |
| 112 | return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, contextAttributes); |
| 113 | |
| 114 | return glXCreateContextAttribsARB(display, config, sharingContext, GL_TRUE, nullptr); |
| 115 | } |
| 116 | |
| 117 | static bool compatibleVisuals(XVisualInfo* a, XVisualInfo* b) |
| 118 | { |
| 119 | return a->c_class == b->c_class |
| 120 | && a->depth == b->depth |
| 121 | && a->red_mask == b->red_mask |
| 122 | && a->green_mask == b->green_mask |
| 123 | && a->blue_mask == b->blue_mask |
| 124 | && a->colormap_size == b->colormap_size |
| 125 | && a->bits_per_rgb == b->bits_per_rgb; |
| 126 | } |
| 127 | |
| 128 | std::unique_ptr<GLContextGLX> GLContextGLX::createWindowContext(GLNativeWindowType window, PlatformDisplay& platformDisplay, GLXContext sharingContext) |
| 129 | { |
| 130 | // In order to create the GLContext, we need to select a GLXFBConfig that has depth and stencil |
| 131 | // buffers that is compatible with the Visual used to create the window. To do this, we request |
| 132 | // all the GLXFBConfigs that have the features we need and compare their XVisualInfo to check whether |
| 133 | // they are compatible with the window one. Then we try to create the GLContext with each of those |
| 134 | // configs until we succeed, and finally fallback to the window config if nothing else works. |
| 135 | Display* display = downcast<PlatformDisplayX11>(platformDisplay).native(); |
| 136 | XWindowAttributes attributes; |
| 137 | if (!XGetWindowAttributes(display, static_cast<Window>(window), &attributes)) |
| 138 | return nullptr; |
| 139 | |
| 140 | XVisualInfo visualInfo; |
| 141 | visualInfo.visualid = XVisualIDFromVisual(attributes.visual); |
| 142 | |
| 143 | int numConfigs = 0; |
| 144 | GLXFBConfig windowConfig = nullptr; |
| 145 | XUniquePtr<GLXFBConfig> configs(glXGetFBConfigs(display, DefaultScreen(display), &numConfigs)); |
| 146 | for (int i = 0; i < numConfigs; i++) { |
| 147 | XUniquePtr<XVisualInfo> glxVisualInfo(glXGetVisualFromFBConfig(display, configs.get()[i])); |
| 148 | if (!glxVisualInfo) |
| 149 | continue; |
| 150 | if (glxVisualInfo.get()->visualid == visualInfo.visualid) { |
| 151 | windowConfig = configs.get()[i]; |
| 152 | break; |
| 153 | } |
| 154 | } |
| 155 | ASSERT(windowConfig); |
| 156 | XUniquePtr<XVisualInfo> windowVisualInfo(glXGetVisualFromFBConfig(display, windowConfig)); |
| 157 | |
| 158 | static const int fbConfigAttributes[] = { |
| 159 | GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, |
| 160 | GLX_RENDER_TYPE, GLX_RGBA_BIT, |
| 161 | GLX_X_RENDERABLE, GL_TRUE, |
| 162 | GLX_RED_SIZE, 1, |
| 163 | GLX_GREEN_SIZE, 1, |
| 164 | GLX_BLUE_SIZE, 1, |
| 165 | GLX_ALPHA_SIZE, 1, |
| 166 | GLX_DEPTH_SIZE, 1, |
| 167 | GLX_STENCIL_SIZE, 1, |
| 168 | GLX_DOUBLEBUFFER, GL_TRUE, |
| 169 | GLX_CONFIG_CAVEAT, GLX_NONE, |
| 170 | #ifdef GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT |
| 171 | // Discard sRGB configs if any sRGB extension is installed. |
| 172 | GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, GL_FALSE, |
| 173 | #endif |
| 174 | 0 |
| 175 | }; |
| 176 | configs.reset(glXChooseFBConfig(display, DefaultScreen(display), fbConfigAttributes, &numConfigs)); |
| 177 | XUniqueGLXContext context; |
| 178 | for (int i = 0; i < numConfigs; i++) { |
| 179 | XUniquePtr<XVisualInfo> configVisualInfo(glXGetVisualFromFBConfig(display, configs.get()[i])); |
| 180 | if (!configVisualInfo) |
| 181 | continue; |
| 182 | if (compatibleVisuals(windowVisualInfo.get(), configVisualInfo.get())) { |
| 183 | // Try to create a context with this config. Use the trapper in case we get an XError. |
| 184 | XErrorTrapper trapper(display, XErrorTrapper::Policy::Ignore); |
| 185 | if (hasGLXARBCreateContextExtension(display)) |
| 186 | context.reset(createGLXARBContext(display, configs.get()[i], sharingContext)); |
| 187 | else { |
| 188 | // Legacy OpenGL version. |
| 189 | context.reset(glXCreateContext(display, configVisualInfo.get(), sharingContext, True)); |
| 190 | } |
| 191 | |
| 192 | if (context) |
| 193 | return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), window)); |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | // Fallback to the config used by the window. We don't probably have the buffers we need in |
| 198 | // this config and that will cause artifacts, but it's better than not rendering anything. |
| 199 | if (hasGLXARBCreateContextExtension(display)) |
| 200 | context.reset(createGLXARBContext(display, windowConfig, sharingContext)); |
| 201 | else { |
| 202 | // Legacy OpenGL version. |
| 203 | context.reset(glXCreateContext(display, windowVisualInfo.get(), sharingContext, True)); |
| 204 | } |
| 205 | |
| 206 | if (!context) |
| 207 | return nullptr; |
| 208 | |
| 209 | return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), window)); |
| 210 | } |
| 211 | |
| 212 | std::unique_ptr<GLContextGLX> GLContextGLX::createPbufferContext(PlatformDisplay& platformDisplay, GLXContext sharingContext) |
| 213 | { |
| 214 | static const int fbConfigAttributes[] = { |
| 215 | GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, |
| 216 | GLX_RENDER_TYPE, GLX_RGBA_BIT, |
| 217 | GLX_RED_SIZE, 1, |
| 218 | GLX_GREEN_SIZE, 1, |
| 219 | GLX_BLUE_SIZE, 1, |
| 220 | GLX_ALPHA_SIZE, 1, |
| 221 | GLX_DOUBLEBUFFER, GL_FALSE, |
| 222 | 0 |
| 223 | }; |
| 224 | |
| 225 | int returnedElements; |
| 226 | Display* display = downcast<PlatformDisplayX11>(platformDisplay).native(); |
| 227 | XUniquePtr<GLXFBConfig> configs(glXChooseFBConfig(display, 0, fbConfigAttributes, &returnedElements)); |
| 228 | if (!returnedElements) |
| 229 | return nullptr; |
| 230 | |
| 231 | // We will be rendering to a texture, so our pbuffer does not need to be large. |
| 232 | static const int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 }; |
| 233 | XUniqueGLXPbuffer pbuffer(glXCreatePbuffer(display, configs.get()[0], pbufferAttributes)); |
| 234 | if (!pbuffer) |
| 235 | return nullptr; |
| 236 | |
| 237 | XUniqueGLXContext context; |
| 238 | if (hasGLXARBCreateContextExtension(display)) |
| 239 | context.reset(createGLXARBContext(display, configs.get()[0], sharingContext)); |
| 240 | else { |
| 241 | // Legacy OpenGL version. |
| 242 | context.reset(glXCreateNewContext(display, configs.get()[0], GLX_RGBA_TYPE, sharingContext, GL_TRUE)); |
| 243 | } |
| 244 | |
| 245 | if (!context) |
| 246 | return nullptr; |
| 247 | |
| 248 | return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), WTFMove(pbuffer))); |
| 249 | } |
| 250 | |
| 251 | std::unique_ptr<GLContextGLX> GLContextGLX::createPixmapContext(PlatformDisplay& platformDisplay, GLXContext sharingContext) |
| 252 | { |
| 253 | static int visualAttributes[] = { |
| 254 | GLX_RGBA, |
| 255 | GLX_RED_SIZE, 1, |
| 256 | GLX_GREEN_SIZE, 1, |
| 257 | GLX_BLUE_SIZE, 1, |
| 258 | GLX_ALPHA_SIZE, 1, |
| 259 | 0 |
| 260 | }; |
| 261 | |
| 262 | Display* display = downcast<PlatformDisplayX11>(platformDisplay).native(); |
| 263 | XUniquePtr<XVisualInfo> visualInfo(glXChooseVisual(display, DefaultScreen(display), visualAttributes)); |
| 264 | if (!visualInfo) |
| 265 | return nullptr; |
| 266 | |
| 267 | XUniqueGLXContext context(glXCreateContext(display, visualInfo.get(), sharingContext, GL_TRUE)); |
| 268 | if (!context) |
| 269 | return nullptr; |
| 270 | |
| 271 | XUniquePixmap pixmap(XCreatePixmap(display, DefaultRootWindow(display), 1, 1, visualInfo->depth)); |
| 272 | if (!pixmap) |
| 273 | return nullptr; |
| 274 | |
| 275 | XUniqueGLXPixmap glxPixmap(glXCreateGLXPixmap(display, visualInfo.get(), pixmap.get())); |
| 276 | if (!glxPixmap) |
| 277 | return nullptr; |
| 278 | |
| 279 | return std::unique_ptr<GLContextGLX>(new GLContextGLX(platformDisplay, WTFMove(context), WTFMove(pixmap), WTFMove(glxPixmap))); |
| 280 | } |
| 281 | |
| 282 | std::unique_ptr<GLContextGLX> GLContextGLX::createContext(GLNativeWindowType window, PlatformDisplay& platformDisplay) |
| 283 | { |
| 284 | GLXContext glxSharingContext = platformDisplay.sharingGLContext() ? static_cast<GLContextGLX*>(platformDisplay.sharingGLContext())->m_context.get() : nullptr; |
| 285 | auto context = window ? createWindowContext(window, platformDisplay, glxSharingContext) : nullptr; |
| 286 | if (!context) |
| 287 | context = createPbufferContext(platformDisplay, glxSharingContext); |
| 288 | if (!context) |
| 289 | context = createPixmapContext(platformDisplay, glxSharingContext); |
| 290 | |
| 291 | return context; |
| 292 | } |
| 293 | |
| 294 | std::unique_ptr<GLContextGLX> GLContextGLX::createSharingContext(PlatformDisplay& platformDisplay) |
| 295 | { |
| 296 | auto context = createPbufferContext(platformDisplay); |
| 297 | if (!context) |
| 298 | context = createPixmapContext(platformDisplay); |
| 299 | return context; |
| 300 | } |
| 301 | |
| 302 | GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, GLNativeWindowType window) |
| 303 | : GLContext(display) |
| 304 | , m_x11Display(downcast<PlatformDisplayX11>(m_display).native()) |
| 305 | , m_context(WTFMove(context)) |
| 306 | , m_window(static_cast<Window>(window)) |
| 307 | { |
| 308 | } |
| 309 | |
| 310 | GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, XUniqueGLXPbuffer&& pbuffer) |
| 311 | : GLContext(display) |
| 312 | , m_x11Display(downcast<PlatformDisplayX11>(m_display).native()) |
| 313 | , m_context(WTFMove(context)) |
| 314 | , m_pbuffer(WTFMove(pbuffer)) |
| 315 | { |
| 316 | } |
| 317 | |
| 318 | GLContextGLX::GLContextGLX(PlatformDisplay& display, XUniqueGLXContext&& context, XUniquePixmap&& pixmap, XUniqueGLXPixmap&& glxPixmap) |
| 319 | : GLContext(display) |
| 320 | , m_x11Display(downcast<PlatformDisplayX11>(m_display).native()) |
| 321 | , m_context(WTFMove(context)) |
| 322 | , m_pixmap(WTFMove(pixmap)) |
| 323 | , m_glxPixmap(WTFMove(glxPixmap)) |
| 324 | { |
| 325 | } |
| 326 | |
| 327 | GLContextGLX::~GLContextGLX() |
| 328 | { |
| 329 | if (m_cairoDevice) |
| 330 | cairo_device_destroy(m_cairoDevice); |
| 331 | |
| 332 | if (m_context) { |
| 333 | // Due to a bug in some nvidia drivers, we need bind the default framebuffer in a context before |
| 334 | // destroying it to avoid a crash. In order to do that, we need to make the context current and, |
| 335 | // after the bind change, we need to set the previous context again. |
| 336 | GLContext* previousActiveContext = GLContext::current(); |
| 337 | makeContextCurrent(); |
| 338 | glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); |
| 339 | if (previousActiveContext && previousActiveContext != this) { |
| 340 | // If there was a previous context different from this one, just make it current again. |
| 341 | previousActiveContext->makeContextCurrent(); |
| 342 | } else { |
| 343 | // If there was no previous context or this was the previous, set a void context as current. |
| 344 | // We use the GLX function here, and the destructor of GLContext will clean the pointer |
| 345 | // returned by GLContext::current(). |
| 346 | glXMakeCurrent(m_x11Display, None, None); |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | bool GLContextGLX::canRenderToDefaultFramebuffer() |
| 352 | { |
| 353 | return m_window; |
| 354 | } |
| 355 | |
| 356 | IntSize GLContextGLX::defaultFrameBufferSize() |
| 357 | { |
| 358 | if (!canRenderToDefaultFramebuffer() || !m_window) |
| 359 | return IntSize(); |
| 360 | |
| 361 | int x, y; |
| 362 | Window rootWindow; |
| 363 | unsigned int width, height, borderWidth, depth; |
| 364 | if (!XGetGeometry(m_x11Display, m_window, &rootWindow, &x, &y, &width, &height, &borderWidth, &depth)) |
| 365 | return IntSize(); |
| 366 | |
| 367 | return IntSize(width, height); |
| 368 | } |
| 369 | |
| 370 | bool GLContextGLX::makeContextCurrent() |
| 371 | { |
| 372 | ASSERT(m_context && (m_window || m_pbuffer || m_glxPixmap)); |
| 373 | |
| 374 | GLContext::makeContextCurrent(); |
| 375 | if (glXGetCurrentContext() == m_context.get()) |
| 376 | return true; |
| 377 | |
| 378 | if (m_window) |
| 379 | return glXMakeCurrent(m_x11Display, m_window, m_context.get()); |
| 380 | |
| 381 | if (m_pbuffer) |
| 382 | return glXMakeCurrent(m_x11Display, m_pbuffer.get(), m_context.get()); |
| 383 | |
| 384 | return ::glXMakeCurrent(m_x11Display, m_glxPixmap.get(), m_context.get()); |
| 385 | } |
| 386 | |
| 387 | void GLContextGLX::swapBuffers() |
| 388 | { |
| 389 | if (m_window) |
| 390 | glXSwapBuffers(m_x11Display, m_window); |
| 391 | } |
| 392 | |
| 393 | void GLContextGLX::waitNative() |
| 394 | { |
| 395 | glXWaitX(); |
| 396 | } |
| 397 | |
| 398 | void GLContextGLX::swapInterval(int interval) |
| 399 | { |
| 400 | if (!hasSGISwapControlExtension(m_x11Display)) |
| 401 | return; |
| 402 | glXSwapIntervalSGI(interval); |
| 403 | } |
| 404 | |
| 405 | cairo_device_t* GLContextGLX::cairoDevice() |
| 406 | { |
| 407 | if (m_cairoDevice) |
| 408 | return m_cairoDevice; |
| 409 | |
| 410 | #if ENABLE(ACCELERATED_2D_CANVAS) && CAIRO_HAS_GLX_FUNCTIONS |
| 411 | m_cairoDevice = cairo_glx_device_create(m_x11Display, m_context.get()); |
| 412 | #endif |
| 413 | |
| 414 | return m_cairoDevice; |
| 415 | } |
| 416 | |
| 417 | #if ENABLE(GRAPHICS_CONTEXT_3D) |
| 418 | PlatformGraphicsContext3D GLContextGLX::platformContext() |
| 419 | { |
| 420 | return m_context.get(); |
| 421 | } |
| 422 | #endif |
| 423 | |
| 424 | } // namespace WebCore |
| 425 | |
| 426 | #endif // USE(GLX) |
| 427 | |