Fix OpenGL context creation in the XCB plugin
Make it possible to create a core context with OpenGL implementations that don't implement the compatibility profile or the GL_ARB_compatibility extension. Qt was effectively clamping the OpenGL version to 3.0 by assuming that the highest supported backwards compatible version is also the highest supported core version. Since there is no way to check if the implementation supports a context with a given set of attributes without trying to create the context, we have to try every known OpenGL version until we find one that's supported. Note that this commit does not fix similar breakage on other platforms. Change-Id: I9616762b059db9e6182f853ab7f24ff44dc7d529 Reviewed-by: Sean Harmer <sean.harmer@kdab.com> Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
This commit is contained in:
parent
1b08e0307d
commit
4b54c55305
@ -104,57 +104,6 @@ static Window createDummyWindow(QXcbScreen *screen, GLXFBConfig config)
|
|||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-window data for active OpenGL contexts.
|
|
||||||
struct QOpenGLContextData
|
|
||||||
{
|
|
||||||
QOpenGLContextData(Display *display, Window window, GLXContext context)
|
|
||||||
: m_display(display),
|
|
||||||
m_window(window),
|
|
||||||
m_context(context)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QOpenGLContextData()
|
|
||||||
: m_display(0),
|
|
||||||
m_window(0),
|
|
||||||
m_context(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Display *m_display;
|
|
||||||
Window m_window;
|
|
||||||
GLXContext m_context;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline QOpenGLContextData currentOpenGLContextData()
|
|
||||||
{
|
|
||||||
QOpenGLContextData result;
|
|
||||||
result.m_display = glXGetCurrentDisplay();
|
|
||||||
result.m_window = glXGetCurrentDrawable();
|
|
||||||
result.m_context = glXGetCurrentContext();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QOpenGLContextData createDummyWindowOpenGLContextData(QXcbScreen *screen)
|
|
||||||
{
|
|
||||||
QOpenGLContextData result;
|
|
||||||
result.m_display = DISPLAY_FROM_XCB(screen);
|
|
||||||
|
|
||||||
QSurfaceFormat format;
|
|
||||||
GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen), screen->screenNumber(), format);
|
|
||||||
if (config) {
|
|
||||||
result.m_context = glXCreateNewContext(DISPLAY_FROM_XCB(screen), config, GLX_RGBA_TYPE, 0, true);
|
|
||||||
result.m_window = createDummyWindow(screen, config);
|
|
||||||
} else {
|
|
||||||
XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(screen), screen->screenNumber(), &format);
|
|
||||||
if (!visualInfo)
|
|
||||||
qFatal("Could not initialize GLX");
|
|
||||||
result.m_context = glXCreateContext(DISPLAY_FROM_XCB(screen), visualInfo, 0, true);
|
|
||||||
result.m_window = createDummyWindow(screen, visualInfo);
|
|
||||||
XFree(visualInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QByteArray getGlString(GLenum param)
|
static inline QByteArray getGlString(GLenum param)
|
||||||
{
|
{
|
||||||
if (const GLubyte *s = glGetString(param))
|
if (const GLubyte *s = glGetString(param))
|
||||||
@ -202,70 +151,7 @@ static void updateFormatFromContext(QSurfaceFormat &format)
|
|||||||
format.setProfile(QSurfaceFormat::CompatibilityProfile);
|
format.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share)
|
||||||
\class QOpenGLTemporaryContext
|
|
||||||
\brief A temporary context that can be instantiated on the stack.
|
|
||||||
|
|
||||||
Functions like glGetString() only work if there is a current GL context.
|
|
||||||
|
|
||||||
\internal
|
|
||||||
\ingroup qt-lighthouse-xcb
|
|
||||||
*/
|
|
||||||
class QOpenGLTemporaryContext
|
|
||||||
{
|
|
||||||
Q_DISABLE_COPY(QOpenGLTemporaryContext)
|
|
||||||
public:
|
|
||||||
QOpenGLTemporaryContext(QXcbScreen *screen);
|
|
||||||
~QOpenGLTemporaryContext();
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QOpenGLContextData m_previous;
|
|
||||||
const QOpenGLContextData m_current;
|
|
||||||
};
|
|
||||||
|
|
||||||
QOpenGLTemporaryContext::QOpenGLTemporaryContext(QXcbScreen *screen)
|
|
||||||
: m_previous(currentOpenGLContextData()),
|
|
||||||
m_current(createDummyWindowOpenGLContextData(screen))
|
|
||||||
{
|
|
||||||
// Make our temporary context current on our temporary window
|
|
||||||
glXMakeCurrent(m_current.m_display, m_current.m_window, m_current.m_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
QOpenGLTemporaryContext::~QOpenGLTemporaryContext()
|
|
||||||
{
|
|
||||||
// Restore the previous context if possible, otherwise just release our temporary context
|
|
||||||
if (m_previous.m_display)
|
|
||||||
glXMakeCurrent(m_previous.m_display, m_previous.m_window, m_previous.m_context);
|
|
||||||
else
|
|
||||||
glXMakeCurrent(m_current.m_display, 0, 0);
|
|
||||||
|
|
||||||
// Destroy our temporary window
|
|
||||||
XDestroyWindow(m_current.m_display, m_current.m_window);
|
|
||||||
|
|
||||||
// Finally destroy our temporary context itself
|
|
||||||
glXDestroyContext(m_current.m_display, m_current.m_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
QOpenGLDefaultContextInfo::QOpenGLDefaultContextInfo()
|
|
||||||
: vendor(getGlString(GL_VENDOR)),
|
|
||||||
renderer(getGlString(GL_RENDERER))
|
|
||||||
{
|
|
||||||
updateFormatFromContext(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
QOpenGLDefaultContextInfo *QOpenGLDefaultContextInfo::create(QXcbScreen *screen)
|
|
||||||
{
|
|
||||||
// We need a current context for getGLString() to work. To have
|
|
||||||
// the QOpenGLDefaultContextInfo contain the latest supported
|
|
||||||
// context version, we rely upon the QOpenGLTemporaryContext to
|
|
||||||
// correctly obtain a context with the latest version
|
|
||||||
QScopedPointer<QOpenGLTemporaryContext> temporaryContext(new QOpenGLTemporaryContext(screen));
|
|
||||||
QOpenGLDefaultContextInfo *result = new QOpenGLDefaultContextInfo;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share, QOpenGLDefaultContextInfo *defaultContextInfo)
|
|
||||||
: QPlatformOpenGLContext()
|
: QPlatformOpenGLContext()
|
||||||
, m_screen(screen)
|
, m_screen(screen)
|
||||||
, m_context(0)
|
, m_context(0)
|
||||||
@ -287,51 +173,60 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
|
|||||||
QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber())).split(' ');
|
QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber())).split(' ');
|
||||||
bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile");
|
bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile");
|
||||||
|
|
||||||
// Use glXCreateContextAttribsARB if is available
|
// Use glXCreateContextAttribsARB if available
|
||||||
if (glxExt.contains("GLX_ARB_create_context") && glXCreateContextAttribsARB != 0) {
|
if (glxExt.contains("GLX_ARB_create_context") && glXCreateContextAttribsARB != 0) {
|
||||||
// We limit the requested version by the version of the static context as
|
// Try to create an OpenGL context for each known OpenGL version in descending
|
||||||
// glXCreateContextAttribsARB fails and returns NULL if the requested context
|
// order from the requested version.
|
||||||
// version is not supported. This means that we will get the closest supported
|
const int requestedVersion = format.majorVersion() * 10 + qMin(format.minorVersion(), 9);
|
||||||
// context format that that which was requested and is supported by the driver
|
|
||||||
const int maxSupportedVersion = (defaultContextInfo->format.majorVersion() << 8)
|
|
||||||
+ defaultContextInfo->format.minorVersion();
|
|
||||||
const int requestedVersion = qMin((format.majorVersion() << 8) + format.minorVersion(),
|
|
||||||
maxSupportedVersion);
|
|
||||||
const int majorVersion = requestedVersion >> 8;
|
|
||||||
const int minorVersion = requestedVersion & 0xFF;
|
|
||||||
|
|
||||||
QVector<int> contextAttributes;
|
QVector<int> glVersions;
|
||||||
contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << majorVersion
|
if (requestedVersion > 43)
|
||||||
<< GLX_CONTEXT_MINOR_VERSION_ARB << minorVersion;
|
glVersions << requestedVersion;
|
||||||
|
|
||||||
// If asking for OpenGL 3.2 or newer we should also specify a profile
|
// Don't bother with versions below 2.0
|
||||||
if (supportsProfiles && (m_format.majorVersion() > 3 || (m_format.majorVersion() == 3 && m_format.minorVersion() > 1))) {
|
glVersions << 43 << 42 << 41 << 40 << 33 << 32 << 31 << 30 << 21 << 20;
|
||||||
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;
|
for (int i = 0; !m_context && i < glVersions.count(); i++) {
|
||||||
|
const int version = glVersions[i];
|
||||||
|
if (version > requestedVersion)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (m_format.testOption(QSurfaceFormat::DebugContext))
|
const int majorVersion = version / 10;
|
||||||
flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
|
const int minorVersion = version % 10;
|
||||||
|
|
||||||
// A forward-compatible context may be requested for 3.0 and later
|
QVector<int> contextAttributes;
|
||||||
if (m_format.majorVersion() >= 3 && !m_format.testOption(QSurfaceFormat::DeprecatedFunctions))
|
contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << majorVersion
|
||||||
flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
|
<< GLX_CONTEXT_MINOR_VERSION_ARB << minorVersion;
|
||||||
|
|
||||||
if (flags != 0)
|
// If asking for OpenGL 3.2 or newer we should also specify a profile
|
||||||
contextAttributes << GLX_CONTEXT_FLAGS_ARB << flags;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
contextAttributes << None;
|
int flags = 0;
|
||||||
|
|
||||||
m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, m_shareContext, true, contextAttributes.data());
|
if (m_format.testOption(QSurfaceFormat::DebugContext))
|
||||||
if (!m_context && m_shareContext) {
|
flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
|
||||||
// re-try without a shared glx context
|
|
||||||
m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, 0, true, contextAttributes.data());
|
// A forward-compatible context may be requested for 3.0 and later
|
||||||
if (m_context)
|
if (version >= 30 && !m_format.testOption(QSurfaceFormat::DeprecatedFunctions))
|
||||||
m_shareContext = 0;
|
flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
|
||||||
|
|
||||||
|
if (flags != 0)
|
||||||
|
contextAttributes << GLX_CONTEXT_FLAGS_ARB << flags;
|
||||||
|
|
||||||
|
contextAttributes << None;
|
||||||
|
|
||||||
|
m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, m_shareContext, true, contextAttributes.data());
|
||||||
|
if (!m_context && m_shareContext) {
|
||||||
|
// re-try without a shared glx context
|
||||||
|
m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, 0, true, contextAttributes.data());
|
||||||
|
if (m_context)
|
||||||
|
m_shareContext = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,23 +54,10 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QOpenGLDefaultContextInfo
|
|
||||||
{
|
|
||||||
Q_DISABLE_COPY(QOpenGLDefaultContextInfo)
|
|
||||||
QOpenGLDefaultContextInfo();
|
|
||||||
public:
|
|
||||||
static QOpenGLDefaultContextInfo *create(QXcbScreen *screen);
|
|
||||||
|
|
||||||
const QByteArray vendor;
|
|
||||||
const QByteArray renderer;
|
|
||||||
QSurfaceFormat format;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class QGLXContext : public QPlatformOpenGLContext
|
class QGLXContext : public QPlatformOpenGLContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QGLXContext(QXcbScreen *xd, const QSurfaceFormat &format, QPlatformOpenGLContext *share, QOpenGLDefaultContextInfo *defaultContextInfo);
|
QGLXContext(QXcbScreen *xd, const QSurfaceFormat &format, QPlatformOpenGLContext *share);
|
||||||
~QGLXContext();
|
~QGLXContext();
|
||||||
|
|
||||||
bool makeCurrent(QPlatformSurface *surface);
|
bool makeCurrent(QPlatformSurface *surface);
|
||||||
|
@ -222,7 +222,6 @@ void QXcbConnection::updateScreens()
|
|||||||
// Delete any existing screens which are not in activeScreens
|
// Delete any existing screens which are not in activeScreens
|
||||||
for (int i = m_screens.count() - 1; i >= 0; --i) {
|
for (int i = m_screens.count() - 1; i >= 0; --i) {
|
||||||
if (!activeScreens.contains(m_screens[i])) {
|
if (!activeScreens.contains(m_screens[i])) {
|
||||||
((QXcbIntegration*)QGuiApplicationPrivate::platformIntegration())->removeDefaultOpenGLContextInfo(m_screens[i]);
|
|
||||||
delete m_screens[i];
|
delete m_screens[i];
|
||||||
m_screens.removeAt(i);
|
m_screens.removeAt(i);
|
||||||
}
|
}
|
||||||
|
@ -121,9 +121,6 @@ QXcbIntegration::QXcbIntegration(const QStringList ¶meters)
|
|||||||
|
|
||||||
QXcbIntegration::~QXcbIntegration()
|
QXcbIntegration::~QXcbIntegration()
|
||||||
{
|
{
|
||||||
#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX)
|
|
||||||
qDeleteAll(m_defaultContextInfos);
|
|
||||||
#endif
|
|
||||||
qDeleteAll(m_connections);
|
qDeleteAll(m_connections);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,14 +178,7 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont
|
|||||||
{
|
{
|
||||||
QXcbScreen *screen = static_cast<QXcbScreen *>(context->screen()->handle());
|
QXcbScreen *screen = static_cast<QXcbScreen *>(context->screen()->handle());
|
||||||
#if defined(XCB_USE_GLX)
|
#if defined(XCB_USE_GLX)
|
||||||
QOpenGLDefaultContextInfo *defaultContextInfo;
|
return new QGLXContext(screen, context->format(), context->shareHandle());
|
||||||
if (m_defaultContextInfos.contains(screen)) {
|
|
||||||
defaultContextInfo = m_defaultContextInfos.value(screen);
|
|
||||||
} else {
|
|
||||||
defaultContextInfo = QOpenGLDefaultContextInfo::create(screen);
|
|
||||||
m_defaultContextInfos.insert(screen, defaultContextInfo);
|
|
||||||
}
|
|
||||||
return new QGLXContext(screen, context->format(), context->shareHandle(), defaultContextInfo);
|
|
||||||
#elif defined(XCB_USE_EGL)
|
#elif defined(XCB_USE_EGL)
|
||||||
return new QEGLXcbPlatformContext(context->format(), context->shareHandle(),
|
return new QEGLXcbPlatformContext(context->format(), context->shareHandle(),
|
||||||
screen->connection()->egl_display(), screen->connection());
|
screen->connection()->egl_display(), screen->connection());
|
||||||
@ -293,21 +283,4 @@ QPlatformTheme *QXcbIntegration::createPlatformTheme(const QString &name) const
|
|||||||
return QGenericUnixTheme::createUnixTheme(name);
|
return QGenericUnixTheme::createUnixTheme(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Called by QXcbConnection prior to a QQnxScreen being deleted.
|
|
||||||
|
|
||||||
Destroys and cleans up any default OpenGL context info for this screen.
|
|
||||||
*/
|
|
||||||
void QXcbIntegration::removeDefaultOpenGLContextInfo(QXcbScreen *screen)
|
|
||||||
{
|
|
||||||
#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX)
|
|
||||||
if (!m_defaultContextInfos.contains(screen))
|
|
||||||
return;
|
|
||||||
QOpenGLDefaultContextInfo* info = m_defaultContextInfos.take(screen);
|
|
||||||
delete info;
|
|
||||||
#else
|
|
||||||
Q_UNUSED(screen);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -52,10 +52,6 @@ class QAbstractEventDispatcher;
|
|||||||
class QXcbNativeInterface;
|
class QXcbNativeInterface;
|
||||||
class QXcbScreen;
|
class QXcbScreen;
|
||||||
|
|
||||||
#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX)
|
|
||||||
class QOpenGLDefaultContextInfo;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class QXcbIntegration : public QPlatformIntegration
|
class QXcbIntegration : public QPlatformIntegration
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -97,8 +93,6 @@ public:
|
|||||||
QStringList themeNames() const;
|
QStringList themeNames() const;
|
||||||
QPlatformTheme *createPlatformTheme(const QString &name) const;
|
QPlatformTheme *createPlatformTheme(const QString &name) const;
|
||||||
|
|
||||||
void removeDefaultOpenGLContextInfo(QXcbScreen *screen);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QXcbConnection *> m_connections;
|
QList<QXcbConnection *> m_connections;
|
||||||
|
|
||||||
@ -108,10 +102,6 @@ private:
|
|||||||
QScopedPointer<QPlatformInputContext> m_inputContext;
|
QScopedPointer<QPlatformInputContext> m_inputContext;
|
||||||
QAbstractEventDispatcher *m_eventDispatcher;
|
QAbstractEventDispatcher *m_eventDispatcher;
|
||||||
|
|
||||||
#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX)
|
|
||||||
mutable QHash<QXcbScreen *, QOpenGLDefaultContextInfo *> m_defaultContextInfos;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef QT_NO_ACCESSIBILITY
|
#ifndef QT_NO_ACCESSIBILITY
|
||||||
QScopedPointer<QPlatformAccessibility> m_accessibility;
|
QScopedPointer<QPlatformAccessibility> m_accessibility;
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user