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

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