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