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:
Fredrik Höglund 2013-02-04 15:59:22 +01:00 committed by The Qt Project
parent 1b08e0307d
commit 4b54c55305
5 changed files with 48 additions and 204 deletions

View File

@ -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;
}
} }
} }

View File

@ -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);

View File

@ -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);
} }

View File

@ -121,9 +121,6 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters)
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

View File

@ -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