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