| 1 | // Copyright (C) 2016 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 3 |  | 
| 4 | #include <QDebug> | 
| 5 |  | 
| 6 | #include "qxcbwindow.h" | 
| 7 | #include "qxcbscreen.h" | 
| 8 |  | 
| 9 | #define register        /* C++17 deprecated register */ | 
| 10 | #include <X11/Xlib.h> | 
| 11 | #include <X11/Xutil.h> | 
| 12 | #undef register | 
| 13 | #include <GL/glx.h> | 
| 14 |  | 
| 15 | #if QT_CONFIG(regularexpression) | 
| 16 | #  include <QtCore/QRegularExpression> | 
| 17 | #endif | 
| 18 | #include <QtGui/qguiapplication.h> | 
| 19 | #include <QtGui/QOpenGLContext> | 
| 20 | #include <QtGui/QOffscreenSurface> | 
| 21 |  | 
| 22 | #include "qglxintegration.h" | 
| 23 | #include <QtGui/private/qglxconvenience_p.h> | 
| 24 |  | 
| 25 | #include "qxcbglintegration.h" | 
| 26 |  | 
| 27 | QT_BEGIN_NAMESPACE | 
| 28 |  | 
| 29 | typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); | 
| 30 | typedef const GLubyte *(*glGetStringiProc)(GLenum, GLuint); | 
| 31 |  | 
| 32 | #ifndef GLX_CONTEXT_CORE_PROFILE_BIT_ARB | 
| 33 | #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 | 
| 34 | #endif | 
| 35 |  | 
| 36 | #ifndef GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB | 
| 37 | #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 | 
| 38 | #endif | 
| 39 |  | 
| 40 | #ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT | 
| 41 | #define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 | 
| 42 | #endif | 
| 43 |  | 
| 44 | #ifndef GLX_CONTEXT_PROFILE_MASK_ARB | 
| 45 | #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 | 
| 46 | #endif | 
| 47 |  | 
| 48 | #ifndef GL_CONTEXT_FLAG_DEBUG_BIT | 
| 49 | #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 | 
| 50 | #endif | 
| 51 |  | 
| 52 | #ifndef GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB | 
| 53 | #define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 | 
| 54 | #endif | 
| 55 |  | 
| 56 | #ifndef GL_RESET_NOTIFICATION_STRATEGY_ARB | 
| 57 | #define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 | 
| 58 | #endif | 
| 59 |  | 
| 60 | #ifndef GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB | 
| 61 | #define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 | 
| 62 | #endif | 
| 63 |  | 
| 64 | #ifndef GL_LOSE_CONTEXT_ON_RESET_ARB | 
| 65 | #define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 | 
| 66 | #endif | 
| 67 |  | 
| 68 | #ifndef GLX_LOSE_CONTEXT_ON_RESET_ARB | 
| 69 | #define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 | 
| 70 | #endif | 
| 71 |  | 
| 72 | #ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV | 
| 73 | #define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7 | 
| 74 | #endif | 
| 75 |  | 
| 76 | static Window createDummyWindow(Display *dpy, XVisualInfo *visualInfo, int screenNumber, Window rootWin) | 
| 77 | { | 
| 78 |     Colormap cmap = XCreateColormap(dpy, rootWin, visualInfo->visual, AllocNone); | 
| 79 |     XSetWindowAttributes a; | 
| 80 |     a.background_pixel = WhitePixel(dpy, screenNumber); | 
| 81 |     a.border_pixel = BlackPixel(dpy, screenNumber); | 
| 82 |     a.colormap = cmap; | 
| 83 |     a.override_redirect = true; | 
| 84 |  | 
| 85 |     Window window = XCreateWindow(dpy, rootWin, | 
| 86 |                                   0, 0, 100, 100, | 
| 87 |                                   0, visualInfo->depth, InputOutput, visualInfo->visual, | 
| 88 |                                   CWBackPixel|CWBorderPixel|CWColormap|CWOverrideRedirect, &a); | 
| 89 | #ifndef QT_NO_DEBUG | 
| 90 |     XStoreName(dpy, window, "Qt GLX dummy window" ); | 
| 91 | #endif | 
| 92 |     XFreeColormap(dpy, cmap); | 
| 93 |     return window; | 
| 94 | } | 
| 95 |  | 
| 96 | static Window createDummyWindow(Display *dpy, GLXFBConfig config, int screenNumber, Window rootWin) | 
| 97 | { | 
| 98 |     XVisualInfo *visualInfo = glXGetVisualFromFBConfig(dpy, config); | 
| 99 |     if (Q_UNLIKELY(!visualInfo)) | 
| 100 |         qFatal(msg: "Could not initialize GLX" ); | 
| 101 |     Window window = createDummyWindow(dpy, visualInfo, screenNumber, rootWin); | 
| 102 |     XFree(visualInfo); | 
| 103 |     return window; | 
| 104 | } | 
| 105 |  | 
| 106 | static inline QByteArray getGlString(GLenum param) | 
| 107 | { | 
| 108 |     if (const GLubyte *s = glGetString(name: param)) | 
| 109 |         return QByteArray(reinterpret_cast<const char*>(s)); | 
| 110 |     return QByteArray(); | 
| 111 | } | 
| 112 |  | 
| 113 | static bool hasGlExtension(const QSurfaceFormat &format, const char *ext) | 
| 114 | { | 
| 115 |     if (format.majorVersion() < 3) { | 
| 116 |         auto exts = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)); | 
| 117 |         return exts && strstr(haystack: exts, needle: ext); | 
| 118 |     } else { | 
| 119 |         auto glGetStringi = reinterpret_cast<glGetStringiProc>( | 
| 120 |                 glXGetProcAddress(procname: reinterpret_cast<const GLubyte*>("glGetStringi" ))); | 
| 121 |         if (glGetStringi) { | 
| 122 |             GLint n = 0; | 
| 123 |             glGetIntegerv(GL_NUM_EXTENSIONS, params: &n); | 
| 124 |             for (GLint i = 0; i < n; ++i) { | 
| 125 |                 const char *p = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)); | 
| 126 |                 if (p && !strcmp(s1: p, s2: ext)) | 
| 127 |                     return true; | 
| 128 |             } | 
| 129 |         } | 
| 130 |         return false; | 
| 131 |     } | 
| 132 | } | 
| 133 |  | 
| 134 | static void updateFormatFromContext(QSurfaceFormat &format) | 
| 135 | { | 
| 136 |     // Update the version, profile, and context bit of the format | 
| 137 |     int major = 0, minor = 0; | 
| 138 |     QByteArray versionString(getGlString(GL_VERSION)); | 
| 139 |     if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) { | 
| 140 |         format.setMajorVersion(major); | 
| 141 |         format.setMinorVersion(minor); | 
| 142 |     } | 
| 143 |  | 
| 144 |     format.setProfile(QSurfaceFormat::NoProfile); | 
| 145 |     const bool isStereo = format.testOption(option: QSurfaceFormat::StereoBuffers); | 
| 146 |     format.setOptions(QSurfaceFormat::FormatOptions()); | 
| 147 |     // Restore flags that come from the VisualInfo/FBConfig. | 
| 148 |     if (isStereo) | 
| 149 |         format.setOption(option: QSurfaceFormat::StereoBuffers); | 
| 150 |  | 
| 151 |     if (format.renderableType() == QSurfaceFormat::OpenGL) { | 
| 152 |         if (hasGlExtension(format, ext: "GL_ARB_robustness" )) { | 
| 153 |             GLint value = 0; | 
| 154 |             glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, params: &value); | 
| 155 |             if (value == GL_LOSE_CONTEXT_ON_RESET_ARB) | 
| 156 |                 format.setOption(option: QSurfaceFormat::ResetNotification); | 
| 157 |         } | 
| 158 |  | 
| 159 |         if (format.version() < std::pair(3, 0)) { | 
| 160 |             format.setOption(option: QSurfaceFormat::DeprecatedFunctions); | 
| 161 |             return; | 
| 162 |         } | 
| 163 |  | 
| 164 |         // Version 3.0 onwards - check if it includes deprecated functionality or is | 
| 165 |         // a debug context | 
| 166 |         GLint value = 0; | 
| 167 |         glGetIntegerv(GL_CONTEXT_FLAGS, params: &value); | 
| 168 |         if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)) | 
| 169 |             format.setOption(option: QSurfaceFormat::DeprecatedFunctions); | 
| 170 |         if (value & GL_CONTEXT_FLAG_DEBUG_BIT) | 
| 171 |             format.setOption(option: QSurfaceFormat::DebugContext); | 
| 172 |         if (format.version() < std::pair(3, 2)) | 
| 173 |             return; | 
| 174 |  | 
| 175 |         // Version 3.2 and newer have a profile | 
| 176 |         value = 0; | 
| 177 |         glGetIntegerv(GL_CONTEXT_PROFILE_MASK, params: &value); | 
| 178 |  | 
| 179 |         if (value & GL_CONTEXT_CORE_PROFILE_BIT) | 
| 180 |             format.setProfile(QSurfaceFormat::CoreProfile); | 
| 181 |         else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) | 
| 182 |             format.setProfile(QSurfaceFormat::CompatibilityProfile); | 
| 183 |     } | 
| 184 | } | 
| 185 |  | 
| 186 | QGLXContext::QGLXContext(Display *display, QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share) | 
| 187 |     : QPlatformOpenGLContext() | 
| 188 |     , m_display(display) | 
| 189 |     , m_format(format) | 
| 190 |     , m_ownsContext(true) | 
| 191 | { | 
| 192 |     if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType) | 
| 193 |         m_format.setRenderableType(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL | 
| 194 |                                    ? QSurfaceFormat::OpenGL : QSurfaceFormat::OpenGLES); | 
| 195 |     if (m_format.renderableType() != QSurfaceFormat::OpenGL && m_format.renderableType() != QSurfaceFormat::OpenGLES) | 
| 196 |         return; | 
| 197 |  | 
| 198 |     if (share) | 
| 199 |         m_shareContext = static_cast<const QGLXContext*>(share)->glxContext(); | 
| 200 |  | 
| 201 |     GLXFBConfig config = qglx_findConfig(display: m_display, screen: screen->screenNumber(), format: m_format); | 
| 202 |     m_config = config; | 
| 203 |     XVisualInfo *visualInfo = nullptr; | 
| 204 |     Window window = 0; // Temporary window used to query OpenGL context | 
| 205 |  | 
| 206 |     if (config) { | 
| 207 |         const QByteArrayList glxExt = QByteArray(glXQueryExtensionsString(dpy: m_display, screen: screen->screenNumber())).split(sep: ' '); | 
| 208 |  | 
| 209 |         // Resolve entry point for glXCreateContextAttribsARB | 
| 210 |         glXCreateContextAttribsARBProc glXCreateContextAttribsARB = nullptr; | 
| 211 |         if (glxExt.contains(t: "GLX_ARB_create_context" )) | 
| 212 |             glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress(procname: (const GLubyte*)"glXCreateContextAttribsARB" ); | 
| 213 |  | 
| 214 |         const bool supportsProfiles = glxExt.contains(t: "GLX_ARB_create_context_profile" ); | 
| 215 |         const bool supportsRobustness = glxExt.contains(t: "GLX_ARB_create_context_robustness" ); | 
| 216 |         const bool supportsVideoMemoryPurge = glxExt.contains(t: "GLX_NV_robustness_video_memory_purge" ); | 
| 217 |  | 
| 218 |         // Use glXCreateContextAttribsARB if available | 
| 219 |         // Also, GL ES context creation requires GLX_EXT_create_context_es2_profile | 
| 220 |         if (glXCreateContextAttribsARB != nullptr | 
| 221 |                 && (m_format.renderableType() != QSurfaceFormat::OpenGLES || (supportsProfiles && glxExt.contains(t: "GLX_EXT_create_context_es2_profile" )))) { | 
| 222 |             // Try to create an OpenGL context for each known OpenGL version in descending | 
| 223 |             // order from the requested version. | 
| 224 |             const int requestedVersion = m_format.majorVersion() * 10 + qMin(a: m_format.minorVersion(), b: 9); | 
| 225 |  | 
| 226 |             QList<int> glVersions; | 
| 227 |             if (m_format.renderableType() == QSurfaceFormat::OpenGL) { | 
| 228 |                 if (requestedVersion > 46) | 
| 229 |                     glVersions << requestedVersion; | 
| 230 |  | 
| 231 |                 // Don't bother with versions below 2.0 | 
| 232 |                 glVersions << 46 << 45 << 44 << 43 << 42 << 41 << 40 << 33 << 32 << 31 << 30 << 21 << 20; | 
| 233 |             } else if (m_format.renderableType() == QSurfaceFormat::OpenGLES) { | 
| 234 |                 if (requestedVersion > 32) | 
| 235 |                     glVersions << requestedVersion; | 
| 236 |  | 
| 237 |                 // Don't bother with versions below ES 2.0 | 
| 238 |                 glVersions << 32 << 31 << 30 << 20; | 
| 239 |                 // ES does not support any format option | 
| 240 |                 m_format.setOptions(QSurfaceFormat::FormatOptions()); | 
| 241 |             } | 
| 242 |             // Robustness must match that of the shared context. | 
| 243 |             if (share && share->format().testOption(option: QSurfaceFormat::ResetNotification)) | 
| 244 |                 m_format.setOption(option: QSurfaceFormat::ResetNotification); | 
| 245 |             Q_ASSERT(glVersions.size() > 0); | 
| 246 |  | 
| 247 |             for (int i = 0; !m_context && i < glVersions.size(); i++) { | 
| 248 |                 const int version = glVersions[i]; | 
| 249 |                 if (version > requestedVersion) | 
| 250 |                     continue; | 
| 251 |  | 
| 252 |                 const int majorVersion = version / 10; | 
| 253 |                 const int minorVersion = version % 10; | 
| 254 |  | 
| 255 |                 QList<int> contextAttributes; | 
| 256 |                 contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << majorVersion | 
| 257 |                                   << GLX_CONTEXT_MINOR_VERSION_ARB << minorVersion; | 
| 258 |  | 
| 259 |  | 
| 260 |                 if (m_format.renderableType() == QSurfaceFormat::OpenGL) { | 
| 261 |                     // If asking for OpenGL 3.2 or newer we should also specify a profile | 
| 262 |                     if (version >= 32 && supportsProfiles) { | 
| 263 |                         if (m_format.profile() == QSurfaceFormat::CoreProfile) | 
| 264 |                             contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_CORE_PROFILE_BIT_ARB; | 
| 265 |                         else | 
| 266 |                             contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; | 
| 267 |                     } | 
| 268 |  | 
| 269 |                     int flags = 0; | 
| 270 |  | 
| 271 |                     if (supportsRobustness) | 
| 272 |                         flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; | 
| 273 |  | 
| 274 |                     if (m_format.testOption(option: QSurfaceFormat::DebugContext)) | 
| 275 |                         flags |= GLX_CONTEXT_DEBUG_BIT_ARB; | 
| 276 |  | 
| 277 |                     // A forward-compatible context may be requested for 3.0 and later | 
| 278 |                     if (version >= 30 && !m_format.testOption(option: QSurfaceFormat::DeprecatedFunctions)) | 
| 279 |                         flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; | 
| 280 |  | 
| 281 |                     if (flags != 0) | 
| 282 |                         contextAttributes << GLX_CONTEXT_FLAGS_ARB << flags; | 
| 283 |                 } else if (m_format.renderableType() == QSurfaceFormat::OpenGLES) { | 
| 284 |                     contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_ES2_PROFILE_BIT_EXT; | 
| 285 |                 } | 
| 286 |  | 
| 287 |                 if (supportsRobustness && m_format.testOption(option: QSurfaceFormat::ResetNotification)) { | 
| 288 |                     QList<int> contextAttributesWithRobustness = contextAttributes; | 
| 289 |                     contextAttributesWithRobustness << GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB << GLX_LOSE_CONTEXT_ON_RESET_ARB; | 
| 290 |                     if (supportsVideoMemoryPurge) | 
| 291 |                         contextAttributesWithRobustness << GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV << GL_TRUE; | 
| 292 |  | 
| 293 |                     contextAttributesWithRobustness << None; | 
| 294 |                     m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, | 
| 295 |                                                            contextAttributesWithRobustness.data()); | 
| 296 |                     // Context creation against a shared context may fail specifically due to this request, so try | 
| 297 |                     // without before dropping sharing. | 
| 298 |                 } | 
| 299 |  | 
| 300 |                 if (m_context) { | 
| 301 |                     m_getGraphicsResetStatus = reinterpret_cast<GLenum (QOPENGLF_APIENTRYP)()>(getProcAddress(procName: "glGetGraphicsResetStatusARB" )); | 
| 302 |                 } else { | 
| 303 |                     contextAttributes << None; | 
| 304 |                     m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributes.data()); | 
| 305 |                     if (!m_context && m_shareContext) { | 
| 306 |                         // re-try without a shared glx context | 
| 307 |                         m_context = glXCreateContextAttribsARB(m_display, config, nullptr, true, contextAttributes.data()); | 
| 308 |                         if (m_context) | 
| 309 |                             m_shareContext = nullptr; | 
| 310 |                     } | 
| 311 |                 } | 
| 312 |             } | 
| 313 |         } | 
| 314 |  | 
| 315 |         // Could not create a context using glXCreateContextAttribsARB, falling back to glXCreateNewContext. | 
| 316 |         if (!m_context) { | 
| 317 |             // requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out | 
| 318 |             if (m_format.renderableType() == QSurfaceFormat::OpenGLES) | 
| 319 |                 return; | 
| 320 |  | 
| 321 |             m_context = glXCreateNewContext(dpy: m_display, config, GLX_RGBA_TYPE, shareList: m_shareContext, direct: true); | 
| 322 |             if (!m_context && m_shareContext) { | 
| 323 |                 // re-try without a shared glx context | 
| 324 |                 m_context = glXCreateNewContext(dpy: m_display, config, GLX_RGBA_TYPE, shareList: nullptr, direct: true); | 
| 325 |                 if (m_context) | 
| 326 |                     m_shareContext = nullptr; | 
| 327 |             } | 
| 328 |         } | 
| 329 |  | 
| 330 |         // Get the basic surface format details | 
| 331 |         if (m_context) | 
| 332 |             qglx_surfaceFormatFromGLXFBConfig(format: &m_format, display: m_display, config); | 
| 333 |  | 
| 334 |         // Create a temporary window so that we can make the new context current | 
| 335 |         window = createDummyWindow(dpy: m_display, config, screenNumber: screen->screenNumber(), rootWin: screen->root()); | 
| 336 |     } else { | 
| 337 |         // requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out | 
| 338 |         if (m_format.renderableType() == QSurfaceFormat::OpenGLES) | 
| 339 |             return; | 
| 340 |  | 
| 341 |         // Note that m_format gets updated with the used surface format | 
| 342 |         visualInfo = qglx_findVisualInfo(display: m_display, screen: screen->screenNumber(), format: &m_format); | 
| 343 |         if (Q_UNLIKELY(!visualInfo)) | 
| 344 |             qFatal(msg: "Could not initialize GLX" ); | 
| 345 |         m_context = glXCreateContext(dpy: m_display, vis: visualInfo, shareList: m_shareContext, direct: true); | 
| 346 |         if (!m_context && m_shareContext) { | 
| 347 |             // re-try without a shared glx context | 
| 348 |             m_shareContext = nullptr; | 
| 349 |             m_context = glXCreateContext(dpy: m_display, vis: visualInfo, shareList: nullptr, direct: true); | 
| 350 |         } | 
| 351 |  | 
| 352 |         // Create a temporary window so that we can make the new context current | 
| 353 |         window = createDummyWindow(dpy: m_display, visualInfo, screenNumber: screen->screenNumber(), rootWin: screen->root()); | 
| 354 |         XFree(visualInfo); | 
| 355 |     } | 
| 356 |  | 
| 357 |     // Query the OpenGL version and profile | 
| 358 |     if (m_context && window) { | 
| 359 |         GLXContext prevContext = glXGetCurrentContext(); | 
| 360 |         GLXDrawable prevDrawable = glXGetCurrentDrawable(); | 
| 361 |         glXMakeCurrent(dpy: m_display, drawable: window, ctx: m_context); | 
| 362 |         updateFormatFromContext(format&: m_format); | 
| 363 |  | 
| 364 |         // Make our context non-current | 
| 365 |         glXMakeCurrent(dpy: m_display, drawable: prevDrawable, ctx: prevContext); | 
| 366 |     } | 
| 367 |  | 
| 368 |     // Destroy our temporary window | 
| 369 |     XDestroyWindow(m_display, window); | 
| 370 | } | 
| 371 |  | 
| 372 | QGLXContext::QGLXContext(Display *display, GLXContext context, void *visualInfo, QPlatformOpenGLContext *share) | 
| 373 |     : QPlatformOpenGLContext() | 
| 374 |     , m_display(display) | 
| 375 | { | 
| 376 |     // Legacy contexts created using glXCreateContext are created using a | 
| 377 |     // XVisualInfo. If the user passed one we should use that. | 
| 378 |     XVisualInfo *vinfo = static_cast<XVisualInfo*>(visualInfo); | 
| 379 |  | 
| 380 |     // Otherwise assume the context was created with an FBConfig using the modern functions | 
| 381 |     if (!vinfo) { | 
| 382 |         int configId = 0; | 
| 383 |         if (glXQueryContext(dpy: m_display, ctx: context, GLX_FBCONFIG_ID, value: &configId) != Success) { | 
| 384 |             qWarning(msg: "QGLXContext: Failed to query config from the provided context" ); | 
| 385 |             return; | 
| 386 |         } | 
| 387 |  | 
| 388 |         int screenNumber = 0; | 
| 389 |         if (glXQueryContext(dpy: m_display, ctx: context, GLX_SCREEN, value: &screenNumber) != Success) { | 
| 390 |             qWarning(msg: "QGLXContext: Failed to query screen from the provided context" ); | 
| 391 |             screenNumber = DefaultScreen(m_display); | 
| 392 |         } | 
| 393 |  | 
| 394 |         GLXFBConfig *configs; | 
| 395 |         int numConfigs = 0; | 
| 396 |         static const int attribs[] = { GLX_FBCONFIG_ID, configId, None }; | 
| 397 |         configs = glXChooseFBConfig(dpy: m_display, screen: screenNumber, attribList: attribs, nitems: &numConfigs); | 
| 398 |         if (!configs) { | 
| 399 |             qWarning(msg: "QGLXContext: Failed to find config(invalid arguments for glXChooseFBConfig)" ); | 
| 400 |             return; | 
| 401 |         } else if (numConfigs < 1) { | 
| 402 |             qWarning(msg: "QGLXContext: Failed to find config" ); | 
| 403 |             XFree(configs); | 
| 404 |             return; | 
| 405 |         } | 
| 406 |         if (configs && numConfigs > 1) // this is suspicious so warn but let it continue | 
| 407 |             qWarning(msg: "QGLXContext: Multiple configs for FBConfig ID %d" , configId); | 
| 408 |  | 
| 409 |         m_config = configs[0]; | 
| 410 |         XFree(configs); | 
| 411 |     } | 
| 412 |  | 
| 413 |     Q_ASSERT(vinfo || m_config); | 
| 414 |  | 
| 415 |     int screenNumber = DefaultScreen(m_display); | 
| 416 |     Window window; | 
| 417 |     if (vinfo) | 
| 418 |         window = createDummyWindow(dpy: m_display, visualInfo: vinfo, screenNumber, RootWindow(m_display, screenNumber)); | 
| 419 |     else | 
| 420 |         window = createDummyWindow(dpy: m_display, config: m_config, screenNumber, RootWindow(m_display, screenNumber)); | 
| 421 |     if (!window) { | 
| 422 |         qWarning(msg: "QGLXContext: Failed to create dummy window" ); | 
| 423 |         return; | 
| 424 |     } | 
| 425 |  | 
| 426 |     // Update OpenGL version and buffer sizes in our format. | 
| 427 |     GLXContext prevContext = glXGetCurrentContext(); | 
| 428 |     GLXDrawable prevDrawable = glXGetCurrentDrawable(); | 
| 429 |     if (!glXMakeCurrent(dpy: m_display, drawable: window, ctx: context)) { | 
| 430 |         qWarning(msg: "QGLXContext: Failed to make provided context current" ); | 
| 431 |         return; | 
| 432 |     } | 
| 433 |     m_format = QSurfaceFormat(); | 
| 434 |     m_format.setRenderableType(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL | 
| 435 |                                ? QSurfaceFormat::OpenGL : QSurfaceFormat::OpenGLES); | 
| 436 |     updateFormatFromContext(format&: m_format); | 
| 437 |     if (vinfo) | 
| 438 |         qglx_surfaceFormatFromVisualInfo(format: &m_format, display: m_display, visualInfo: vinfo); | 
| 439 |     else | 
| 440 |         qglx_surfaceFormatFromGLXFBConfig(format: &m_format, display: m_display, config: m_config); | 
| 441 |     glXMakeCurrent(dpy: m_display, drawable: prevDrawable, ctx: prevContext); | 
| 442 |     XDestroyWindow(m_display, window); | 
| 443 |  | 
| 444 |     if (vinfo) | 
| 445 |         XFree(vinfo); | 
| 446 |  | 
| 447 |     // Success. Store the context. From this point on isValid() is true. | 
| 448 |     m_context = context; | 
| 449 |  | 
| 450 |     if (share) | 
| 451 |         m_shareContext = static_cast<const QGLXContext*>(share)->glxContext(); | 
| 452 | } | 
| 453 |  | 
| 454 | QGLXContext::~QGLXContext() | 
| 455 | { | 
| 456 |     if (m_ownsContext) | 
| 457 |         glXDestroyContext(dpy: m_display, ctx: m_context); | 
| 458 | } | 
| 459 |  | 
| 460 | static QXcbScreen *screenForPlatformSurface(QPlatformSurface *surface) | 
| 461 | { | 
| 462 |     QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass(); | 
| 463 |     if (surfaceClass == QSurface::Window) { | 
| 464 |         return static_cast<QXcbScreen *>(static_cast<QXcbWindow *>(surface)->screen()); | 
| 465 |     } else if (surfaceClass == QSurface::Offscreen) { | 
| 466 |         return static_cast<QXcbScreen *>(static_cast<QGLXPbuffer *>(surface)->screen()); | 
| 467 |     } | 
| 468 |     return nullptr; | 
| 469 | } | 
| 470 |  | 
| 471 | bool QGLXContext::makeCurrent(QPlatformSurface *surface) | 
| 472 | { | 
| 473 |     bool success = false; | 
| 474 |     Q_ASSERT(surface->surface()->supportsOpenGL()); | 
| 475 |  | 
| 476 |     GLXDrawable glxDrawable = 0; | 
| 477 |     QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass(); | 
| 478 |     if (surfaceClass == QSurface::Window) { | 
| 479 |         m_isPBufferCurrent = false; | 
| 480 |         QXcbWindow *window = static_cast<QXcbWindow *>(surface); | 
| 481 |         glxDrawable = window->xcb_window(); | 
| 482 |         success = glXMakeCurrent(dpy: m_display, drawable: glxDrawable, ctx: m_context); | 
| 483 |         m_lost = false; | 
| 484 |         if (m_getGraphicsResetStatus && m_getGraphicsResetStatus() != GL_NO_ERROR) { | 
| 485 |             m_lost = true; | 
| 486 |             success = false; | 
| 487 |             // Drop the surface. Will recreate on the next makeCurrent. | 
| 488 |             window->invalidateSurface(); | 
| 489 |         } | 
| 490 |     } else if (surfaceClass == QSurface::Offscreen) { | 
| 491 |         m_isPBufferCurrent = true; | 
| 492 |         QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface); | 
| 493 |         glxDrawable = pbuffer->pbuffer(); | 
| 494 |         success = glXMakeContextCurrent(dpy: m_display, draw: glxDrawable, read: glxDrawable, ctx: m_context); | 
| 495 |         m_lost = false; | 
| 496 |         if (m_getGraphicsResetStatus && m_getGraphicsResetStatus() != GL_NO_ERROR) { | 
| 497 |             m_lost = true; | 
| 498 |             success = false; | 
| 499 |         } | 
| 500 |     } | 
| 501 |  | 
| 502 |     if (success && surfaceClass == QSurface::Window) { | 
| 503 |         int interval = surface->format().swapInterval(); | 
| 504 |         QXcbWindow *window = static_cast<QXcbWindow *>(surface); | 
| 505 |         QXcbScreen *screen = screenForPlatformSurface(surface); | 
| 506 |         if (interval >= 0 && interval != window->swapInterval() && screen) { | 
| 507 |             typedef void (*qt_glXSwapIntervalEXT)(Display *, GLXDrawable, int); | 
| 508 |             typedef void (*qt_glXSwapIntervalMESA)(unsigned int); | 
| 509 |             static qt_glXSwapIntervalEXT glXSwapIntervalEXT = nullptr; | 
| 510 |             static qt_glXSwapIntervalMESA glXSwapIntervalMESA = nullptr; | 
| 511 |             static bool resolved = false; | 
| 512 |             if (!resolved) { | 
| 513 |                 resolved = true; | 
| 514 |                 QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(dpy: m_display, | 
| 515 |                                                                                screen: screen->screenNumber())).split(sep: ' '); | 
| 516 |                 if (glxExt.contains(t: "GLX_EXT_swap_control" )) | 
| 517 |                     glXSwapIntervalEXT = (qt_glXSwapIntervalEXT) getProcAddress(procName: "glXSwapIntervalEXT" ); | 
| 518 |                 if (glxExt.contains(t: "GLX_MESA_swap_control" )) | 
| 519 |                     glXSwapIntervalMESA = (qt_glXSwapIntervalMESA) getProcAddress(procName: "glXSwapIntervalMESA" ); | 
| 520 |             } | 
| 521 |             if (glXSwapIntervalEXT) | 
| 522 |                 glXSwapIntervalEXT(m_display, glxDrawable, interval); | 
| 523 |             else if (glXSwapIntervalMESA) | 
| 524 |                 glXSwapIntervalMESA(interval); | 
| 525 |             window->setSwapInterval(interval); | 
| 526 |         } | 
| 527 |     } | 
| 528 |  | 
| 529 |     return success; | 
| 530 | } | 
| 531 |  | 
| 532 | void QGLXContext::doneCurrent() | 
| 533 | { | 
| 534 |     if (m_isPBufferCurrent) | 
| 535 |         glXMakeContextCurrent(dpy: m_display, draw: 0, read: 0, ctx: nullptr); | 
| 536 |     else | 
| 537 |         glXMakeCurrent(dpy: m_display, drawable: 0, ctx: nullptr); | 
| 538 |     m_isPBufferCurrent = false; | 
| 539 | } | 
| 540 |  | 
| 541 | void QGLXContext::swapBuffers(QPlatformSurface *surface) | 
| 542 | { | 
| 543 |     GLXDrawable glxDrawable = 0; | 
| 544 |     if (surface->surface()->surfaceClass() == QSurface::Offscreen) | 
| 545 |         glxDrawable = static_cast<QGLXPbuffer *>(surface)->pbuffer(); | 
| 546 |     else | 
| 547 |         glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); | 
| 548 |     glXSwapBuffers(dpy: m_display, drawable: glxDrawable); | 
| 549 |  | 
| 550 |     if (surface->surface()->surfaceClass() == QSurface::Window) { | 
| 551 |         QXcbWindow *platformWindow = static_cast<QXcbWindow *>(surface); | 
| 552 |         // OpenGL context might be bound to a non-gui thread use QueuedConnection to sync | 
| 553 |         // the window from the platformWindow's thread as QXcbWindow is no QObject, an | 
| 554 |         // event is sent to QXcbConnection. (this is faster than a metacall) | 
| 555 |         if (platformWindow->needsSync()) | 
| 556 |             platformWindow->postSyncWindowRequest(); | 
| 557 |     } | 
| 558 | } | 
| 559 |  | 
| 560 | QFunctionPointer QGLXContext::getProcAddress(const char *procName) | 
| 561 | { | 
| 562 |     return glXGetProcAddress(procname: reinterpret_cast<const GLubyte *>(procName)); | 
| 563 | } | 
| 564 |  | 
| 565 | QSurfaceFormat QGLXContext::format() const | 
| 566 | { | 
| 567 |     return m_format; | 
| 568 | } | 
| 569 |  | 
| 570 | bool QGLXContext::isSharing() const | 
| 571 | { | 
| 572 |     return m_shareContext != nullptr; | 
| 573 | } | 
| 574 |  | 
| 575 | bool QGLXContext::isValid() const | 
| 576 | { | 
| 577 |     return m_context != nullptr && !m_lost; | 
| 578 | } | 
| 579 |  | 
| 580 | bool QGLXContext::m_queriedDummyContext = false; | 
| 581 | bool QGLXContext::m_supportsThreading = true; | 
| 582 |  | 
| 583 |  | 
| 584 | // If this list grows to any significant size, change it a | 
| 585 | // proper string table and make the implementation below use | 
| 586 | // binary search. | 
| 587 | static const char *qglx_threadedgl_blacklist_renderer[] = { | 
| 588 |     "Chromium" ,                             // QTBUG-32225 (initialization fails) | 
| 589 |     nullptr | 
| 590 | }; | 
| 591 |  | 
| 592 | static const char *qglx_threadedgl_blacklist_vendor[] = { | 
| 593 |     "llvmpipe" ,                             // QTCREATORBUG-10666 | 
| 594 |     "nouveau" ,                              // https://bugs.freedesktop.org/show_bug.cgi?id=91632 | 
| 595 |     nullptr | 
| 596 | }; | 
| 597 |  | 
| 598 | void QGLXContext::queryDummyContext() | 
| 599 | { | 
| 600 |     if (m_queriedDummyContext) | 
| 601 |         return; | 
| 602 |     m_queriedDummyContext = true; | 
| 603 |  | 
| 604 |     static bool skip = qEnvironmentVariableIsSet(varName: "QT_OPENGL_NO_SANITY_CHECK" ); | 
| 605 |     if (skip) | 
| 606 |         return; | 
| 607 |  | 
| 608 |     QOpenGLContext *oldContext = QOpenGLContext::currentContext(); | 
| 609 |     QSurface *oldSurface = nullptr; | 
| 610 |     if (oldContext) | 
| 611 |         oldSurface = oldContext->surface(); | 
| 612 |  | 
| 613 |     QScopedPointer<QSurface> surface; | 
| 614 |     Display *display = glXGetCurrentDisplay(); | 
| 615 |     if (!display) { | 
| 616 |         // FIXME: Since Qt 5.6 we don't need to check whether primary screen is NULL | 
| 617 |         if (QScreen *screen = QGuiApplication::primaryScreen()) { | 
| 618 |             QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(screen->handle()); | 
| 619 |             display = static_cast<Display *>(xcbScreen->connection()->xlib_display()); | 
| 620 |         } | 
| 621 |     } | 
| 622 |     const char *glxvendor = glXGetClientString(dpy: display, GLX_VENDOR); | 
| 623 |     if (glxvendor && !strcmp(s1: glxvendor, s2: "ATI" )) { | 
| 624 |         QWindow *window = new QWindow; | 
| 625 |         window->resize(w: 64, h: 64); | 
| 626 |         window->setSurfaceType(QSurface::OpenGLSurface); | 
| 627 |         window->create(); | 
| 628 |         surface.reset(other: window); | 
| 629 |     } else { | 
| 630 |         QOffscreenSurface *offSurface = new QOffscreenSurface; | 
| 631 |         offSurface->create(); | 
| 632 |         surface.reset(other: offSurface); | 
| 633 |     } | 
| 634 |  | 
| 635 |     QOpenGLContext context; | 
| 636 |     if (!context.create() || !context.makeCurrent(surface: surface.data())) { | 
| 637 |         qWarning(msg: "QGLXContext: Failed to create dummy context" ); | 
| 638 |         m_supportsThreading = false; | 
| 639 |         return; | 
| 640 |     } | 
| 641 |  | 
| 642 |     m_supportsThreading = true; | 
| 643 |  | 
| 644 |     if (const char *renderer = (const char *) glGetString(GL_RENDERER)) { | 
| 645 |         for (int i = 0; qglx_threadedgl_blacklist_renderer[i]; ++i) { | 
| 646 |             if (strstr(haystack: renderer, needle: qglx_threadedgl_blacklist_renderer[i]) != nullptr) { | 
| 647 |                 qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "  | 
| 648 |                                              "blacklisted renderer \""  | 
| 649 |                                           << qglx_threadedgl_blacklist_renderer[i] | 
| 650 |                                           << "\"" ; | 
| 651 |                 m_supportsThreading = false; | 
| 652 |                 break; | 
| 653 |             } | 
| 654 |         } | 
| 655 |     } | 
| 656 |     if (const char *vendor = (const char *) glGetString(GL_VENDOR)) { | 
| 657 |         for (int i = 0; qglx_threadedgl_blacklist_vendor[i]; ++i) { | 
| 658 |             if (strstr(haystack: vendor, needle: qglx_threadedgl_blacklist_vendor[i]) != nullptr) { | 
| 659 |                 qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "  | 
| 660 |                                               "blacklisted vendor \""  | 
| 661 |                                            << qglx_threadedgl_blacklist_vendor[i] | 
| 662 |                                            << "\"" ; | 
| 663 |                 m_supportsThreading = false; | 
| 664 |                 break; | 
| 665 |             } | 
| 666 |         } | 
| 667 |     } | 
| 668 |  | 
| 669 |     if (glxvendor && m_supportsThreading) { | 
| 670 |         // Blacklist Mesa drivers due to QTCREATORBUG-10875 (crash in creator), | 
| 671 |         // QTBUG-34492 (flickering in fullscreen) and QTBUG-38221 | 
| 672 |         const char *mesaVersionStr = nullptr; | 
| 673 |         if (strstr(haystack: glxvendor, needle: "Mesa Project" ) != nullptr) { | 
| 674 |             mesaVersionStr = (const char *) glGetString(GL_VERSION); | 
| 675 |             m_supportsThreading = false; | 
| 676 |         } | 
| 677 |  | 
| 678 |         if (mesaVersionStr) { | 
| 679 |             // The issue was fixed in Xcb 1.11, but we can't check for that | 
| 680 |             // at runtime, so instead assume it fixed with recent Mesa versions | 
| 681 |             // released several years after the Xcb fix. | 
| 682 | #if QT_CONFIG(regularexpression) | 
| 683 |             QRegularExpression versionTest(QStringLiteral("Mesa (\\d+)" )); | 
| 684 |             QRegularExpressionMatch result = versionTest.match(subject: QString::fromLatin1(ba: mesaVersionStr)); | 
| 685 |             int versionNr = 0; | 
| 686 |             if (result.hasMatch()) | 
| 687 |                 versionNr = result.captured(nth: 1).toInt(); | 
| 688 |             if (versionNr >= 17) { | 
| 689 |                 // White-listed | 
| 690 |                 m_supportsThreading = true; | 
| 691 |             } | 
| 692 | #endif | 
| 693 |         } | 
| 694 |         if (!m_supportsThreading) { | 
| 695 |             qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "  | 
| 696 |                                           "blacklisted vendor \"Mesa Project\"" ; | 
| 697 |         } | 
| 698 |     } | 
| 699 |  | 
| 700 |     static bool nomultithread = qEnvironmentVariableIsSet(varName: "QT_XCB_NO_THREADED_OPENGL" ); | 
| 701 |     if (nomultithread) | 
| 702 |         m_supportsThreading = false; | 
| 703 |  | 
| 704 |     context.doneCurrent(); | 
| 705 |     if (oldContext && oldSurface) | 
| 706 |         oldContext->makeCurrent(surface: oldSurface); | 
| 707 |  | 
| 708 |     if (!m_supportsThreading) { | 
| 709 |         qCDebug(lcQpaGl) << "Force-enable multithreaded OpenGL by setting "  | 
| 710 |                            "environment variable QT_OPENGL_NO_SANITY_CHECK" ; | 
| 711 |     } | 
| 712 | } | 
| 713 |  | 
| 714 | bool QGLXContext::supportsThreading() | 
| 715 | { | 
| 716 |     queryDummyContext(); | 
| 717 |     return m_supportsThreading; | 
| 718 | } | 
| 719 |  | 
| 720 | QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface) | 
| 721 |     : QPlatformOffscreenSurface(offscreenSurface) | 
| 722 |     , m_screen(static_cast<QXcbScreen *>(offscreenSurface->screen()->handle())) | 
| 723 |     , m_format(m_screen->surfaceFormatFor(format: offscreenSurface->requestedFormat())) | 
| 724 |     , m_display(static_cast<Display *>(m_screen->connection()->xlib_display())) | 
| 725 |     , m_pbuffer(0) | 
| 726 | { | 
| 727 |     GLXFBConfig config = qglx_findConfig(display: m_display, screen: m_screen->screenNumber(), format: m_format); | 
| 728 |  | 
| 729 |     if (config) { | 
| 730 |         const int attributes[] = { | 
| 731 |             GLX_PBUFFER_WIDTH, offscreenSurface->size().width(), | 
| 732 |             GLX_PBUFFER_HEIGHT, offscreenSurface->size().height(), | 
| 733 |             GLX_LARGEST_PBUFFER, False, | 
| 734 |             GLX_PRESERVED_CONTENTS, False, | 
| 735 |             None | 
| 736 |         }; | 
| 737 |  | 
| 738 |         m_pbuffer = glXCreatePbuffer(dpy: m_display, config, attribList: attributes); | 
| 739 |  | 
| 740 |         if (m_pbuffer) | 
| 741 |             qglx_surfaceFormatFromGLXFBConfig(format: &m_format, display: m_display, config); | 
| 742 |     } | 
| 743 | } | 
| 744 |  | 
| 745 | QGLXPbuffer::~QGLXPbuffer() | 
| 746 | { | 
| 747 |     if (m_pbuffer) | 
| 748 |         glXDestroyPbuffer(dpy: m_display, pbuf: m_pbuffer); | 
| 749 | } | 
| 750 |  | 
| 751 |  | 
| 752 | QT_END_NAMESPACE | 
| 753 |  |