Source code
Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include <QDebug>
#include "qxcbwindow.h"
#include "qxcbscreen.h"
#define register /* C++17 deprecated register */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#undef register
#include <GL/glx.h>
#if QT_CONFIG(regularexpression)
# include <QtCore/QRegularExpression>
#endif
#include <QtGui/qguiapplication.h>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOffscreenSurface>
#include "qglxintegration.h"
#include <QtGui/private/qglxconvenience_p.h>
#include "qxcbglintegration.h"
QT_BEGIN_NAMESPACE
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
typedef const GLubyte *(*glGetStringiProc)(GLenum, GLuint);
#ifndef GLX_CONTEXT_CORE_PROFILE_BIT_ARB
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#endif
#ifndef GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#endif
#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT
#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
#endif
#ifndef GLX_CONTEXT_PROFILE_MASK_ARB
#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
#endif
#ifndef GL_CONTEXT_FLAG_DEBUG_BIT
#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
#endif
#ifndef GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB
#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
#endif
#ifndef GL_RESET_NOTIFICATION_STRATEGY_ARB
#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
#endif
#ifndef GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB
#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
#endif
#ifndef GL_LOSE_CONTEXT_ON_RESET_ARB
#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252
#endif
#ifndef GLX_LOSE_CONTEXT_ON_RESET_ARB
#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252
#endif
#ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
#endif
static Window createDummyWindow(Display *dpy, XVisualInfo *visualInfo, int screenNumber, Window rootWin)
{
Colormap cmap = XCreateColormap(dpy, rootWin, visualInfo->visual, AllocNone);
XSetWindowAttributes a;
a.background_pixel = WhitePixel(dpy, screenNumber);
a.border_pixel = BlackPixel(dpy, screenNumber);
a.colormap = cmap;
a.override_redirect = true;
Window window = XCreateWindow(dpy, rootWin,
0, 0, 100, 100,
0, visualInfo->depth, InputOutput, visualInfo->visual,
CWBackPixel|CWBorderPixel|CWColormap|CWOverrideRedirect, &a);
#ifndef QT_NO_DEBUG
XStoreName(dpy, window, "Qt GLX dummy window");
#endif
XFreeColormap(dpy, cmap);
return window;
}
static Window createDummyWindow(Display *dpy, GLXFBConfig config, int screenNumber, Window rootWin)
{
XVisualInfo *visualInfo = glXGetVisualFromFBConfig(dpy, config);
if (Q_UNLIKELY(!visualInfo))
qFatal("Could not initialize GLX");
Window window = createDummyWindow(dpy, visualInfo, screenNumber, rootWin);
XFree(visualInfo);
return window;
}
static inline QByteArray getGlString(GLenum param)
{
if (const GLubyte *s = glGetString(param))
return QByteArray(reinterpret_cast<const char*>(s));
return QByteArray();
}
static bool hasGlExtension(const QSurfaceFormat &format, const char *ext)
{
if (format.majorVersion() < 3) {
auto exts = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
return exts && strstr(exts, ext);
} else {
auto glGetStringi = reinterpret_cast<glGetStringiProc>(
glXGetProcAddress(reinterpret_cast<const GLubyte*>("glGetStringi")));
if (glGetStringi) {
GLint n = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
for (GLint i = 0; i < n; ++i) {
const char *p = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
if (p && !strcmp(p, ext))
return true;
}
}
return false;
}
}
static void updateFormatFromContext(QSurfaceFormat &format)
{
// Update the version, profile, and context bit of the format
int major = 0, minor = 0;
QByteArray versionString(getGlString(GL_VERSION));
if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) {
format.setMajorVersion(major);
format.setMinorVersion(minor);
}
format.setProfile(QSurfaceFormat::NoProfile);
const bool isStereo = format.testOption(QSurfaceFormat::StereoBuffers);
format.setOptions(QSurfaceFormat::FormatOptions());
// Restore flags that come from the VisualInfo/FBConfig.
if (isStereo)
format.setOption(QSurfaceFormat::StereoBuffers);
if (format.renderableType() == QSurfaceFormat::OpenGL) {
if (hasGlExtension(format, "GL_ARB_robustness")) {
GLint value = 0;
glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &value);
if (value == GL_LOSE_CONTEXT_ON_RESET_ARB)
format.setOption(QSurfaceFormat::ResetNotification);
}
if (format.version() < std::pair(3, 0)) {
format.setOption(QSurfaceFormat::DeprecatedFunctions);
return;
}
// Version 3.0 onwards - check if it includes deprecated functionality or is
// a debug context
GLint value = 0;
glGetIntegerv(GL_CONTEXT_FLAGS, &value);
if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT))
format.setOption(QSurfaceFormat::DeprecatedFunctions);
if (value & GL_CONTEXT_FLAG_DEBUG_BIT)
format.setOption(QSurfaceFormat::DebugContext);
if (format.version() < std::pair(3, 2))
return;
// Version 3.2 and newer have a profile
value = 0;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
if (value & GL_CONTEXT_CORE_PROFILE_BIT)
format.setProfile(QSurfaceFormat::CoreProfile);
else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
format.setProfile(QSurfaceFormat::CompatibilityProfile);
}
}
QGLXContext::QGLXContext(Display *display, QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share)
: QPlatformOpenGLContext()
, m_display(display)
, m_format(format)
, m_ownsContext(true)
{
if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType)
m_format.setRenderableType(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
? QSurfaceFormat::OpenGL : QSurfaceFormat::OpenGLES);
if (m_format.renderableType() != QSurfaceFormat::OpenGL && m_format.renderableType() != QSurfaceFormat::OpenGLES)
return;
if (share)
m_shareContext = static_cast<const QGLXContext*>(share)->glxContext();
GLXFBConfig config = qglx_findConfig(m_display, screen->screenNumber(), m_format);
m_config = config;
XVisualInfo *visualInfo = nullptr;
Window window = 0; // Temporary window used to query OpenGL context
if (config) {
const QByteArrayList glxExt = QByteArray(glXQueryExtensionsString(m_display, screen->screenNumber())).split(' ');
// Resolve entry point for glXCreateContextAttribsARB
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = nullptr;
if (glxExt.contains("GLX_ARB_create_context"))
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
const bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile");
const bool supportsRobustness = glxExt.contains("GLX_ARB_create_context_robustness");
const bool supportsVideoMemoryPurge = glxExt.contains("GLX_NV_robustness_video_memory_purge");
// Use glXCreateContextAttribsARB if available
// Also, GL ES context creation requires GLX_EXT_create_context_es2_profile
if (glXCreateContextAttribsARB != nullptr
&& (m_format.renderableType() != QSurfaceFormat::OpenGLES || (supportsProfiles && glxExt.contains("GLX_EXT_create_context_es2_profile")))) {
// Try to create an OpenGL context for each known OpenGL version in descending
// order from the requested version.
const int requestedVersion = m_format.majorVersion() * 10 + qMin(m_format.minorVersion(), 9);
QList<int> glVersions;
if (m_format.renderableType() == QSurfaceFormat::OpenGL) {
if (requestedVersion > 46)
glVersions << requestedVersion;
// Don't bother with versions below 2.0
glVersions << 46 << 45 << 44 << 43 << 42 << 41 << 40 << 33 << 32 << 31 << 30 << 21 << 20;
} else if (m_format.renderableType() == QSurfaceFormat::OpenGLES) {
if (requestedVersion > 32)
glVersions << requestedVersion;
// Don't bother with versions below ES 2.0
glVersions << 32 << 31 << 30 << 20;
// ES does not support any format option
m_format.setOptions(QSurfaceFormat::FormatOptions());
}
// Robustness must match that of the shared context.
if (share && share->format().testOption(QSurfaceFormat::ResetNotification))
m_format.setOption(QSurfaceFormat::ResetNotification);
Q_ASSERT(glVersions.size() > 0);
for (int i = 0; !m_context && i < glVersions.size(); i++) {
const int version = glVersions[i];
if (version > requestedVersion)
continue;
const int majorVersion = version / 10;
const int minorVersion = version % 10;
QList<int> contextAttributes;
contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << majorVersion
<< GLX_CONTEXT_MINOR_VERSION_ARB << minorVersion;
if (m_format.renderableType() == QSurfaceFormat::OpenGL) {
// If asking for OpenGL 3.2 or newer we should also specify a profile
if (version >= 32 && supportsProfiles) {
if (m_format.profile() == QSurfaceFormat::CoreProfile)
contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
else
contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
}
int flags = 0;
if (supportsRobustness)
flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
if (m_format.testOption(QSurfaceFormat::DebugContext))
flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
// A forward-compatible context may be requested for 3.0 and later
if (version >= 30 && !m_format.testOption(QSurfaceFormat::DeprecatedFunctions))
flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
if (flags != 0)
contextAttributes << GLX_CONTEXT_FLAGS_ARB << flags;
} else if (m_format.renderableType() == QSurfaceFormat::OpenGLES) {
contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
}
if (supportsRobustness && m_format.testOption(QSurfaceFormat::ResetNotification)) {
QList<int> contextAttributesWithRobustness = contextAttributes;
contextAttributesWithRobustness << GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB << GLX_LOSE_CONTEXT_ON_RESET_ARB;
if (supportsVideoMemoryPurge)
contextAttributesWithRobustness << GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV << GL_TRUE;
contextAttributesWithRobustness << None;
m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true,
contextAttributesWithRobustness.data());
// Context creation against a shared context may fail specifically due to this request, so try
// without before dropping sharing.
}
if (m_context) {
m_getGraphicsResetStatus = reinterpret_cast<GLenum (QOPENGLF_APIENTRYP)()>(getProcAddress("glGetGraphicsResetStatusARB"));
} else {
contextAttributes << None;
m_context = glXCreateContextAttribsARB(m_display, config, m_shareContext, true, contextAttributes.data());
if (!m_context && m_shareContext) {
// re-try without a shared glx context
m_context = glXCreateContextAttribsARB(m_display, config, nullptr, true, contextAttributes.data());
if (m_context)
m_shareContext = nullptr;
}
}
}
}
// Could not create a context using glXCreateContextAttribsARB, falling back to glXCreateNewContext.
if (!m_context) {
// requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out
if (m_format.renderableType() == QSurfaceFormat::OpenGLES)
return;
m_context = glXCreateNewContext(m_display, config, GLX_RGBA_TYPE, m_shareContext, true);
if (!m_context && m_shareContext) {
// re-try without a shared glx context
m_context = glXCreateNewContext(m_display, config, GLX_RGBA_TYPE, nullptr, true);
if (m_context)
m_shareContext = nullptr;
}
}
// Get the basic surface format details
if (m_context)
qglx_surfaceFormatFromGLXFBConfig(&m_format, m_display, config);
// Create a temporary window so that we can make the new context current
window = createDummyWindow(m_display, config, screen->screenNumber(), screen->root());
} else {
// requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out
if (m_format.renderableType() == QSurfaceFormat::OpenGLES)
return;
// Note that m_format gets updated with the used surface format
visualInfo = qglx_findVisualInfo(m_display, screen->screenNumber(), &m_format);
if (Q_UNLIKELY(!visualInfo))
qFatal("Could not initialize GLX");
m_context = glXCreateContext(m_display, visualInfo, m_shareContext, true);
if (!m_context && m_shareContext) {
// re-try without a shared glx context
m_shareContext = nullptr;
m_context = glXCreateContext(m_display, visualInfo, nullptr, true);
}
// Create a temporary window so that we can make the new context current
window = createDummyWindow(m_display, visualInfo, screen->screenNumber(), screen->root());
XFree(visualInfo);
}
// Query the OpenGL version and profile
if (m_context && window) {
GLXContext prevContext = glXGetCurrentContext();
GLXDrawable prevDrawable = glXGetCurrentDrawable();
glXMakeCurrent(m_display, window, m_context);
updateFormatFromContext(m_format);
// Make our context non-current
glXMakeCurrent(m_display, prevDrawable, prevContext);
}
// Destroy our temporary window
XDestroyWindow(m_display, window);
}
QGLXContext::QGLXContext(Display *display, GLXContext context, void *visualInfo, QPlatformOpenGLContext *share)
: QPlatformOpenGLContext()
, m_display(display)
{
// Legacy contexts created using glXCreateContext are created using a
// XVisualInfo. If the user passed one we should use that.
XVisualInfo *vinfo = static_cast<XVisualInfo*>(visualInfo);
// Otherwise assume the context was created with an FBConfig using the modern functions
if (!vinfo) {
int configId = 0;
if (glXQueryContext(m_display, context, GLX_FBCONFIG_ID, &configId) != Success) {
qWarning("QGLXContext: Failed to query config from the provided context");
return;
}
int screenNumber = 0;
if (glXQueryContext(m_display, context, GLX_SCREEN, &screenNumber) != Success) {
qWarning("QGLXContext: Failed to query screen from the provided context");
screenNumber = DefaultScreen(m_display);
}
GLXFBConfig *configs;
int numConfigs = 0;
static const int attribs[] = { GLX_FBCONFIG_ID, configId, None };
configs = glXChooseFBConfig(m_display, screenNumber, attribs, &numConfigs);
if (!configs) {
qWarning("QGLXContext: Failed to find config(invalid arguments for glXChooseFBConfig)");
return;
} else if (numConfigs < 1) {
qWarning("QGLXContext: Failed to find config");
XFree(configs);
return;
}
if (configs && numConfigs > 1) // this is suspicious so warn but let it continue
qWarning("QGLXContext: Multiple configs for FBConfig ID %d", configId);
m_config = configs[0];
XFree(configs);
}
Q_ASSERT(vinfo || m_config);
int screenNumber = DefaultScreen(m_display);
Window window;
if (vinfo)
window = createDummyWindow(m_display, vinfo, screenNumber, RootWindow(m_display, screenNumber));
else
window = createDummyWindow(m_display, m_config, screenNumber, RootWindow(m_display, screenNumber));
if (!window) {
qWarning("QGLXContext: Failed to create dummy window");
return;
}
// Update OpenGL version and buffer sizes in our format.
GLXContext prevContext = glXGetCurrentContext();
GLXDrawable prevDrawable = glXGetCurrentDrawable();
if (!glXMakeCurrent(m_display, window, context)) {
qWarning("QGLXContext: Failed to make provided context current");
return;
}
m_format = QSurfaceFormat();
m_format.setRenderableType(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
? QSurfaceFormat::OpenGL : QSurfaceFormat::OpenGLES);
updateFormatFromContext(m_format);
if (vinfo)
qglx_surfaceFormatFromVisualInfo(&m_format, m_display, vinfo);
else
qglx_surfaceFormatFromGLXFBConfig(&m_format, m_display, m_config);
glXMakeCurrent(m_display, prevDrawable, prevContext);
XDestroyWindow(m_display, window);
if (vinfo)
XFree(vinfo);
// Success. Store the context. From this point on isValid() is true.
m_context = context;
if (share)
m_shareContext = static_cast<const QGLXContext*>(share)->glxContext();
}
QGLXContext::~QGLXContext()
{
if (m_ownsContext)
glXDestroyContext(m_display, m_context);
}
static QXcbScreen *screenForPlatformSurface(QPlatformSurface *surface)
{
QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass();
if (surfaceClass == QSurface::Window) {
return static_cast<QXcbScreen *>(static_cast<QXcbWindow *>(surface)->screen());
} else if (surfaceClass == QSurface::Offscreen) {
return static_cast<QXcbScreen *>(static_cast<QGLXPbuffer *>(surface)->screen());
}
return nullptr;
}
bool QGLXContext::makeCurrent(QPlatformSurface *surface)
{
bool success = false;
Q_ASSERT(surface->surface()->supportsOpenGL());
GLXDrawable glxDrawable = 0;
QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass();
if (surfaceClass == QSurface::Window) {
m_isPBufferCurrent = false;
QXcbWindow *window = static_cast<QXcbWindow *>(surface);
glxDrawable = window->xcb_window();
success = glXMakeCurrent(m_display, glxDrawable, m_context);
m_lost = false;
if (m_getGraphicsResetStatus && m_getGraphicsResetStatus() != GL_NO_ERROR) {
m_lost = true;
success = false;
// Drop the surface. Will recreate on the next makeCurrent.
window->invalidateSurface();
}
} else if (surfaceClass == QSurface::Offscreen) {
m_isPBufferCurrent = true;
QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface);
glxDrawable = pbuffer->pbuffer();
success = glXMakeContextCurrent(m_display, glxDrawable, glxDrawable, m_context);
m_lost = false;
if (m_getGraphicsResetStatus && m_getGraphicsResetStatus() != GL_NO_ERROR) {
m_lost = true;
success = false;
}
}
if (success && surfaceClass == QSurface::Window) {
int interval = surface->format().swapInterval();
QXcbWindow *window = static_cast<QXcbWindow *>(surface);
QXcbScreen *screen = screenForPlatformSurface(surface);
// Always re-apply the swap interval to have it set correctly when the
// same drawable is used with multiple contexts. See QTBUG-147854.
if (interval >= 0 && screen) {
typedef void (*qt_glXSwapIntervalEXT)(Display *, GLXDrawable, int);
typedef void (*qt_glXSwapIntervalMESA)(unsigned int);
static qt_glXSwapIntervalEXT glXSwapIntervalEXT = nullptr;
static qt_glXSwapIntervalMESA glXSwapIntervalMESA = nullptr;
static bool resolved = false;
if (!resolved) {
resolved = true;
QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(m_display,
screen->screenNumber())).split(' ');
if (glxExt.contains("GLX_EXT_swap_control"))
glXSwapIntervalEXT = (qt_glXSwapIntervalEXT) getProcAddress("glXSwapIntervalEXT");
if (glxExt.contains("GLX_MESA_swap_control"))
glXSwapIntervalMESA = (qt_glXSwapIntervalMESA) getProcAddress("glXSwapIntervalMESA");
}
if (glXSwapIntervalEXT)
glXSwapIntervalEXT(m_display, glxDrawable, interval);
else if (glXSwapIntervalMESA)
glXSwapIntervalMESA(interval);
window->setSwapInterval(interval);
}
}
return success;
}
void QGLXContext::doneCurrent()
{
if (m_isPBufferCurrent)
glXMakeContextCurrent(m_display, 0, 0, nullptr);
else
glXMakeCurrent(m_display, 0, nullptr);
m_isPBufferCurrent = false;
}
void QGLXContext::swapBuffers(QPlatformSurface *surface)
{
GLXDrawable glxDrawable = 0;
if (surface->surface()->surfaceClass() == QSurface::Offscreen)
glxDrawable = static_cast<QGLXPbuffer *>(surface)->pbuffer();
else
glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window();
glXSwapBuffers(m_display, glxDrawable);
if (surface->surface()->surfaceClass() == QSurface::Window) {
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(surface);
// OpenGL context might be bound to a non-gui thread use QueuedConnection to sync
// the window from the platformWindow's thread as QXcbWindow is no QObject, an
// event is sent to QXcbConnection. (this is faster than a metacall)
if (platformWindow->needsSync())
platformWindow->postSyncWindowRequest();
}
}
QFunctionPointer QGLXContext::getProcAddress(const char *procName)
{
return glXGetProcAddress(reinterpret_cast<const GLubyte *>(procName));
}
QSurfaceFormat QGLXContext::format() const
{
return m_format;
}
bool QGLXContext::isSharing() const
{
return m_shareContext != nullptr;
}
bool QGLXContext::isValid() const
{
return m_context != nullptr && !m_lost;
}
bool QGLXContext::m_queriedDummyContext = false;
bool QGLXContext::m_supportsThreading = true;
// If this list grows to any significant size, change it a
// proper string table and make the implementation below use
// binary search.
static const char *qglx_threadedgl_blacklist_renderer[] = {
"Chromium", // QTBUG-32225 (initialization fails)
nullptr
};
static const char *qglx_threadedgl_blacklist_vendor[] = {
"llvmpipe", // QTCREATORBUG-10666
nullptr
};
void QGLXContext::queryDummyContext()
{
if (m_queriedDummyContext)
return;
m_queriedDummyContext = true;
static bool skip = qEnvironmentVariableIsSet("QT_OPENGL_NO_SANITY_CHECK");
if (skip)
return;
QOpenGLContext *oldContext = QOpenGLContext::currentContext();
QSurface *oldSurface = nullptr;
if (oldContext)
oldSurface = oldContext->surface();
QScopedPointer<QSurface> surface;
Display *display = glXGetCurrentDisplay();
if (!display) {
// FIXME: Since Qt 5.6 we don't need to check whether primary screen is NULL
if (QScreen *screen = QGuiApplication::primaryScreen()) {
QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(screen->handle());
display = static_cast<Display *>(xcbScreen->connection()->xlib_display());
}
}
const char *glxvendor = glXGetClientString(display, GLX_VENDOR);
if (glxvendor && !strcmp(glxvendor, "ATI")) {
QWindow *window = new QWindow;
window->resize(64, 64);
window->setSurfaceType(QSurface::OpenGLSurface);
window->create();
surface.reset(window);
} else {
QOffscreenSurface *offSurface = new QOffscreenSurface;
offSurface->create();
surface.reset(offSurface);
}
QOpenGLContext context;
if (!context.create() || !context.makeCurrent(surface.data())) {
qWarning("QGLXContext: Failed to create dummy context");
m_supportsThreading = false;
return;
}
m_supportsThreading = true;
if (const char *renderer = (const char *) glGetString(GL_RENDERER)) {
for (int i = 0; qglx_threadedgl_blacklist_renderer[i]; ++i) {
if (strstr(renderer, qglx_threadedgl_blacklist_renderer[i]) != nullptr) {
qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "
"blacklisted renderer \""
<< qglx_threadedgl_blacklist_renderer[i]
<< "\"";
m_supportsThreading = false;
break;
}
}
}
if (const char *vendor = (const char *) glGetString(GL_VENDOR)) {
for (int i = 0; qglx_threadedgl_blacklist_vendor[i]; ++i) {
if (strstr(vendor, qglx_threadedgl_blacklist_vendor[i]) != nullptr) {
qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "
"blacklisted vendor \""
<< qglx_threadedgl_blacklist_vendor[i]
<< "\"";
m_supportsThreading = false;
break;
}
}
}
if (glxvendor && m_supportsThreading) {
// Blacklist Mesa drivers due to QTCREATORBUG-10875 (crash in creator),
// QTBUG-34492 (flickering in fullscreen) and QTBUG-38221
const char *mesaVersionStr = nullptr;
if (strstr(glxvendor, "Mesa Project") != nullptr) {
mesaVersionStr = (const char *) glGetString(GL_VERSION);
m_supportsThreading = false;
}
if (mesaVersionStr) {
// The issue was fixed in Xcb 1.11, but we can't check for that
// at runtime, so instead assume it fixed with recent Mesa versions
// released several years after the Xcb fix.
#if QT_CONFIG(regularexpression)
QRegularExpression versionTest(QStringLiteral("Mesa (\\d+)"));
QRegularExpressionMatch result = versionTest.match(QString::fromLatin1(mesaVersionStr));
int versionNr = 0;
if (result.hasMatch())
versionNr = result.captured(1).toInt();
if (versionNr >= 17) {
// White-listed
m_supportsThreading = true;
}
#endif
}
if (!m_supportsThreading) {
qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "
"blacklisted vendor \"Mesa Project\"";
}
}
static bool nomultithread = qEnvironmentVariableIsSet("QT_XCB_NO_THREADED_OPENGL");
if (nomultithread)
m_supportsThreading = false;
context.doneCurrent();
if (oldContext && oldSurface)
oldContext->makeCurrent(oldSurface);
if (!m_supportsThreading) {
qCDebug(lcQpaGl) << "Force-enable multithreaded OpenGL by setting "
"environment variable QT_OPENGL_NO_SANITY_CHECK";
}
}
bool QGLXContext::supportsThreading()
{
queryDummyContext();
return m_supportsThreading;
}
QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface)
: QPlatformOffscreenSurface(offscreenSurface)
, m_screen(static_cast<QXcbScreen *>(offscreenSurface->screen()->handle()))
, m_format(m_screen->surfaceFormatFor(offscreenSurface->requestedFormat()))
, m_display(static_cast<Display *>(m_screen->connection()->xlib_display()))
, m_pbuffer(0)
{
GLXFBConfig config = qglx_findConfig(m_display, m_screen->screenNumber(), m_format);
if (config) {
const int attributes[] = {
GLX_PBUFFER_WIDTH, offscreenSurface->size().width(),
GLX_PBUFFER_HEIGHT, offscreenSurface->size().height(),
GLX_LARGEST_PBUFFER, False,
GLX_PRESERVED_CONTENTS, False,
None
};
m_pbuffer = glXCreatePbuffer(m_display, config, attributes);
if (m_pbuffer)
qglx_surfaceFormatFromGLXFBConfig(&m_format, m_display, config);
}
}
QGLXPbuffer::~QGLXPbuffer()
{
if (m_pbuffer)
glXDestroyPbuffer(m_display, m_pbuffer);
}
QT_END_NAMESPACE