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
63QT_BEGIN_NAMESPACE
64
65typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
66typedef 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
112static 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
132static Window createDummyWindow(Display *dpy, GLXFBConfig config, int screenNumber, Window rootWin)
133{
134 XVisualInfo *visualInfo = glXGetVisualFromFBConfig(dpy, config);
135 if (Q_UNLIKELY(!visualInfo))
136 qFatal(msg: "Could not initialize GLX");
137 Window window = createDummyWindow(dpy, visualInfo, screenNumber, rootWin);
138 XFree(visualInfo);
139 return window;
140}
141
142static inline QByteArray getGlString(GLenum param)
143{
144 if (const GLubyte *s = glGetString(name: param))
145 return QByteArray(reinterpret_cast<const char*>(s));
146 return QByteArray();
147}
148
149static 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(haystack: exts, needle: ext);
154 } else {
155 auto glGetStringi = reinterpret_cast<glGetStringiProc>(
156 glXGetProcAddress(procname: reinterpret_cast<const GLubyte*>("glGetStringi")));
157 if (glGetStringi) {
158 GLint n = 0;
159 glGetIntegerv(GL_NUM_EXTENSIONS, params: &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(s1: p, s2: ext))
163 return true;
164 }
165 }
166 return false;
167 }
168}
169
170static 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(option: QSurfaceFormat::StereoBuffers);
182 format.setOptions(QSurfaceFormat::FormatOptions());
183 // Restore flags that come from the VisualInfo/FBConfig.
184 if (isStereo)
185 format.setOption(option: QSurfaceFormat::StereoBuffers);
186
187 if (format.renderableType() == QSurfaceFormat::OpenGL) {
188 if (hasGlExtension(format, ext: "GL_ARB_robustness")) {
189 GLint value = 0;
190 glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, params: &value);
191 if (value == GL_LOSE_CONTEXT_ON_RESET_ARB)
192 format.setOption(option: QSurfaceFormat::ResetNotification);
193 }
194
195 if (format.version() < qMakePair(x: 3, y: 0)) {
196 format.setOption(option: 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, params: &value);
204 if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT))
205 format.setOption(option: QSurfaceFormat::DeprecatedFunctions);
206 if (value & GL_CONTEXT_FLAG_DEBUG_BIT)
207 format.setOption(option: QSurfaceFormat::DebugContext);
208 if (format.version() < qMakePair(x: 3, y: 2))
209 return;
210
211 // Version 3.2 and newer have a profile
212 value = 0;
213 glGetIntegerv(GL_CONTEXT_PROFILE_MASK, params: &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
222QGLXContext::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
241void 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(display: m_display, screen: screen->screenNumber(), format: 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(dpy: m_display, screen: screen->screenNumber())).split(sep: ' ');
262
263 // Resolve entry point for glXCreateContextAttribsARB
264 glXCreateContextAttribsARBProc glXCreateContextAttribsARB = nullptr;
265 if (glxExt.contains(t: "GLX_ARB_create_context"))
266 glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress(procname: (const GLubyte*)"glXCreateContextAttribsARB");
267
268 const bool supportsProfiles = glxExt.contains(t: "GLX_ARB_create_context_profile");
269 const bool supportsRobustness = glxExt.contains(t: "GLX_ARB_create_context_robustness");
270 const bool supportsVideoMemoryPurge = glxExt.contains(t: "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(t: "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(a: m_format.minorVersion(), b: 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(option: QSurfaceFormat::ResetNotification))
298 m_format.setOption(option: 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(option: 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(option: 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(option: 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(procName: "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(dpy: m_display, config, GLX_RGBA_TYPE, shareList: m_shareContext, direct: true);
376 if (!m_context && m_shareContext) {
377 // re-try without a shared glx context
378 m_context = glXCreateNewContext(dpy: m_display, config, GLX_RGBA_TYPE, shareList: nullptr, direct: 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(format: &m_format, display: m_display, config);
387
388 // Create a temporary window so that we can make the new context current
389 window = createDummyWindow(dpy: m_display, config, screenNumber: screen->screenNumber(), rootWin: 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(display: m_display, screen: screen->screenNumber(), format: &m_format);
397 if (Q_UNLIKELY(!visualInfo))
398 qFatal(msg: "Could not initialize GLX");
399 m_context = glXCreateContext(dpy: m_display, vis: visualInfo, shareList: m_shareContext, direct: true);
400 if (!m_context && m_shareContext) {
401 // re-try without a shared glx context
402 m_shareContext = nullptr;
403 m_context = glXCreateContext(dpy: m_display, vis: visualInfo, shareList: nullptr, direct: true);
404 }
405
406 // Create a temporary window so that we can make the new context current
407 window = createDummyWindow(dpy: m_display, visualInfo, screenNumber: screen->screenNumber(), rootWin: 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(dpy: m_display, drawable: window, ctx: m_context);
416 updateFormatFromContext(format&: m_format);
417
418 // Make our context non-current
419 glXMakeCurrent(dpy: m_display, drawable: prevDrawable, ctx: prevContext);
420 }
421
422 // Destroy our temporary window
423 XDestroyWindow(m_display, window);
424}
425
426void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share, const QVariant &nativeHandle)
427{
428 if (!nativeHandle.canConvert<QGLXNativeContext>()) {
429 qWarning(msg: "QGLXContext: Requires a QGLXNativeContext");
430 return;
431 }
432 QGLXNativeContext handle = qvariant_cast<QGLXNativeContext>(v: nativeHandle);
433 GLXContext context = handle.context();
434 if (!context) {
435 qWarning(msg: "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, ctx: context, GLX_FBCONFIG_ID, value: &configId) != Success) {
477 qWarning(msg: "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: screen->screenNumber(), attribList: attribs, nitems: &numConfigs);
485 if (!configs || numConfigs < 1) {
486 qWarning(msg: "QGLXContext: Failed to find config");
487 return;
488 }
489 if (configs && numConfigs > 1) // this is suspicious so warn but let it continue
490 qWarning(msg: "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, visualInfo: vinfo, screenNumber, RootWindow(dpy, screenNumber));
503 else
504 window = createDummyWindow(dpy, config, screenNumber, RootWindow(dpy, screenNumber));
505 if (!window) {
506 qWarning(msg: "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, drawable: window, ctx: context)) {
514 qWarning(msg: "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(format&: m_format);
521 if (vinfo)
522 qglx_surfaceFormatFromVisualInfo(format: &m_format, display: dpy, visualInfo: vinfo);
523 else
524 qglx_surfaceFormatFromGLXFBConfig(format: &m_format, display: dpy, config);
525 glXMakeCurrent(dpy, drawable: prevDrawable, ctx: 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
538QGLXContext::~QGLXContext()
539{
540 if (m_ownsContext)
541 glXDestroyContext(dpy: m_display, ctx: m_context);
542}
543
544static 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
555QVariant QGLXContext::nativeHandle() const
556{
557 return QVariant::fromValue<QGLXNativeContext>(value: QGLXNativeContext(m_context));
558}
559
560bool 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(dpy: m_display, drawable: glxDrawable, ctx: 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(dpy: m_display, draw: glxDrawable, read: glxDrawable, ctx: 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(dpy: m_display,
604 screen: screen->screenNumber())).split(sep: ' ');
605 if (glxExt.contains(t: "GLX_EXT_swap_control"))
606 glXSwapIntervalEXT = (qt_glXSwapIntervalEXT) getProcAddress(procName: "glXSwapIntervalEXT");
607 if (glxExt.contains(t: "GLX_MESA_swap_control"))
608 glXSwapIntervalMESA = (qt_glXSwapIntervalMESA) getProcAddress(procName: "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
621void QGLXContext::doneCurrent()
622{
623 if (m_isPBufferCurrent)
624 glXMakeContextCurrent(dpy: m_display, draw: 0, read: 0, ctx: nullptr);
625 else
626 glXMakeCurrent(dpy: m_display, drawable: 0, ctx: nullptr);
627 m_isPBufferCurrent = false;
628}
629
630void 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(dpy: m_display, drawable: 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
649QFunctionPointer QGLXContext::getProcAddress(const char *procName)
650{
651 return glXGetProcAddress(procname: reinterpret_cast<const GLubyte *>(procName));
652}
653
654QSurfaceFormat QGLXContext::format() const
655{
656 return m_format;
657}
658
659bool QGLXContext::isSharing() const
660{
661 return m_shareContext != nullptr;
662}
663
664bool QGLXContext::isValid() const
665{
666 return m_context != nullptr && !m_lost;
667}
668
669bool QGLXContext::m_queriedDummyContext = false;
670bool 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.
676static const char *qglx_threadedgl_blacklist_renderer[] = {
677 "Chromium", // QTBUG-32225 (initialization fails)
678 nullptr
679};
680
681static 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
687void QGLXContext::queryDummyContext()
688{
689 if (m_queriedDummyContext)
690 return;
691 m_queriedDummyContext = true;
692
693 static bool skip = qEnvironmentVariableIsSet(varName: "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(dpy: display, GLX_VENDOR);
712 if (glxvendor && !strcmp(s1: glxvendor, s2: "ATI")) {
713 QWindow *window = new QWindow;
714 window->resize(w: 64, h: 64);
715 window->setSurfaceType(QSurface::OpenGLSurface);
716 window->create();
717 surface.reset(other: window);
718 } else {
719 QOffscreenSurface *offSurface = new QOffscreenSurface;
720 offSurface->create();
721 surface.reset(other: offSurface);
722 }
723
724 QOpenGLContext context;
725 if (!context.create() || !context.makeCurrent(surface: surface.data())) {
726 qWarning(msg: "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(haystack: renderer, needle: 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(haystack: vendor, needle: 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(haystack: glxvendor, needle: "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(subject: QString::fromLatin1(str: mesaVersionStr));
774 int versionNr = 0;
775 if (result.hasMatch())
776 versionNr = result.captured(nth: 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(varName: "QT_XCB_NO_THREADED_OPENGL");
790 if (nomultithread)
791 m_supportsThreading = false;
792
793 context.doneCurrent();
794 if (oldContext && oldSurface)
795 oldContext->makeCurrent(surface: 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
803bool QGLXContext::supportsThreading()
804{
805 queryDummyContext();
806 return m_supportsThreading;
807}
808
809QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface)
810 : QPlatformOffscreenSurface(offscreenSurface)
811 , m_screen(static_cast<QXcbScreen *>(offscreenSurface->screen()->handle()))
812 , m_format(m_screen->surfaceFormatFor(format: offscreenSurface->requestedFormat()))
813 , m_display(static_cast<Display *>(m_screen->connection()->xlib_display()))
814 , m_pbuffer(0)
815{
816 GLXFBConfig config = qglx_findConfig(display: m_display, screen: m_screen->screenNumber(), format: 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(dpy: m_display, config, attribList: attributes);
828
829 if (m_pbuffer)
830 qglx_surfaceFormatFromGLXFBConfig(format: &m_format, display: m_display, config);
831 }
832}
833
834QGLXPbuffer::~QGLXPbuffer()
835{
836 if (m_pbuffer)
837 glXDestroyPbuffer(dpy: m_display, pbuf: m_pbuffer);
838}
839
840
841QT_END_NAMESPACE
842

source code of qtbase/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp