XCB: Correctly report the created OpenGL context version and profile

This commit fixes the xcb qpa plugin such that it now correctly reports
the version and profile of the created OpenGL context in the
QOpenGLSurfaceFormat. To do this we have to create a temporary X window
so that we can make our new context current.

We also handle the buggy nVidia drivers which incorrectly report 0 for
the GL_CONTEXT_PROFILE_MASK query.

The reduced format is also copied back from qglx_findVisualInfo.

Change-Id: I6f34fe1c6130aebbb6b40c36df4acc216069d2b1
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
This commit is contained in:
Sean Harmer 2012-09-18 17:05:01 +01:00 committed by The Qt Project
parent 61d853797b
commit aad58ac87e
4 changed files with 107 additions and 8 deletions

View File

@ -118,4 +118,38 @@ void QPlatformOpenGLContext::setContext(QOpenGLContext *context)
d->context = context;
}
bool QPlatformOpenGLContext::parseOpenGLVersion(const QString& versionString, int &major, int &minor)
{
bool majorOk = false;
bool minorOk = false;
QStringList parts = versionString.split(QLatin1Char(' '));
if (versionString.startsWith(QLatin1String("OpenGL ES"))) {
if (parts.size() >= 3) {
QStringList versionParts = parts.at(2).split(QLatin1Char('.'));
if (versionParts.size() >= 2) {
major = versionParts.at(0).toInt(&majorOk);
minor = versionParts.at(1).toInt(&minorOk);
} else {
qWarning("Unrecognized OpenGL ES version");
}
} else {
// If < 3 parts to the name, it is an unrecognised OpenGL ES
qWarning("Unrecognised OpenGL ES version");
}
} else {
// Not OpenGL ES, but regular OpenGL, the version numbers are first in the string
QStringList versionParts = parts.at(0).split(QLatin1Char('.'));
if (versionParts.size() >= 2) {
major = versionParts.at(0).toInt(&majorOk);
minor = versionParts.at(1).toInt(&minorOk);
} else {
qWarning("Unrecognized OpenGL version");
}
}
if (!majorOk || !minorOk)
qWarning("Unrecognized OpenGL version");
return (majorOk && minorOk);
}
QT_END_NAMESPACE

View File

@ -88,6 +88,8 @@ public:
QOpenGLContext *context() const;
static bool parseOpenGLVersion(const QString& versionString, int &major, int &minor);
private:
friend class QOpenGLContext;

View File

@ -210,6 +210,8 @@ XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *f
attribs.append(XNone);
visualInfo = glXChooseVisual(display, screen, attribs.data());
if (visualInfo)
*format = reducedFormat;
reducedFormat = qglx_reduceSurfaceFormat(reducedFormat, &reduced);
}

View File

@ -74,7 +74,6 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXC
#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
#endif
QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share)
: QPlatformOpenGLContext()
, m_screen(screen)
@ -86,18 +85,22 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
m_shareContext = static_cast<const QGLXContext*>(share)->glxContext();
GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen),screen->screenNumber(),format);
XVisualInfo *visualInfo = 0;
Window window = 0; // Temporary window used to query OpenGL context
if (config) {
// Resolve entry point for glXCreateContextAttribsARB
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
// Use glXCreateContextAttribsARB if is available
if (glXCreateContextAttribsARB != 0) {
QVector<int> contextAttributes;
contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << m_format.majorVersion()
<< GLX_CONTEXT_MINOR_VERSION_ARB << m_format.minorVersion();
// If asking for OpenGL 3.2 or newer we should also specify a profile
if (m_format.majorVersion() > 3 || (m_format.majorVersion() == 3 && m_format.minorVersion() > 1)) {
if (m_format.profile() == QSurfaceFormat::CoreProfile) {
contextAttributes << GLX_CONTEXT_FLAGS_ARB << GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
@ -129,14 +132,17 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
}
}
if (m_context) {
// Get the basic surface format details
if (m_context)
m_format = qglx_surfaceFormatFromGLXFBConfig(DISPLAY_FROM_XCB(screen), config, m_context);
m_format.setMajorVersion(format.majorVersion());
m_format.setMinorVersion(format.minorVersion());
m_format.setProfile(format.profile());
}
// Used for creating the temporary window
visualInfo = glXGetVisualFromFBConfig(DISPLAY_FROM_XCB(screen), config);
if (!visualInfo)
qFatal("Could not initialize GLX");
} else {
XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(m_screen), screen->screenNumber(), &m_format);
// Note that m_format gets updated with the used surface format
visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(screen), screen->screenNumber(), &m_format);
if (!visualInfo)
qFatal("Could not initialize GLX");
m_context = glXCreateContext(DISPLAY_FROM_XCB(screen), visualInfo, m_shareContext, true);
@ -145,8 +151,63 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
m_shareContext = 0;
m_context = glXCreateContext(DISPLAY_FROM_XCB(screen), visualInfo, 0, true);
}
XFree(visualInfo);
}
// Create a temporary window so that we can make the new context current
Colormap cmap = XCreateColormap(DISPLAY_FROM_XCB(screen), screen->root(), visualInfo->visual, AllocNone);
XSetWindowAttributes a;
a.background_pixel = WhitePixel(DISPLAY_FROM_XCB(screen), screen->screenNumber());
a.border_pixel = BlackPixel(DISPLAY_FROM_XCB(screen), screen->screenNumber());
a.colormap = cmap;
window = XCreateWindow(DISPLAY_FROM_XCB(screen), screen->root(),
0, 0, 100, 100,
0, visualInfo->depth, InputOutput, visualInfo->visual,
CWBackPixel|CWBorderPixel|CWColormap, &a);
XFree(visualInfo);
// Query the OpenGL version and profile
if (m_context && window) {
glXMakeCurrent(DISPLAY_FROM_XCB(screen), window, m_context);
int major = 0, minor = 0;
QString versionString(QLatin1String(reinterpret_cast<const char*>(glGetString(GL_VERSION))));
if (parseOpenGLVersion(versionString, major, minor)) {
m_format.setMajorVersion(major);
m_format.setMinorVersion(minor);
}
// If we have OpenGL 3.2 or newer we also need to query the profile in use
if (m_format.majorVersion() > 3 || (m_format.majorVersion() == 3 && m_format.minorVersion() > 1)) {
// nVidia drivers have a bug where querying GL_CONTEXT_PROFILE_MASK always returns 0.
// In this case let's assume that we got the profile that we asked for since nvidia implements
// both Core and Compatibility profiles
if (versionString.contains(QStringLiteral("NVIDIA"))) {
m_format.setProfile(format.profile());
} else {
GLint profileMask;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profileMask);
switch (profileMask) {
case GLX_CONTEXT_CORE_PROFILE_BIT_ARB:
m_format.setProfile(QSurfaceFormat::CoreProfile);
break;
case GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB:
default:
m_format.setProfile(QSurfaceFormat::CompatibilityProfile);
}
}
} else {
m_format.setProfile(QSurfaceFormat::NoProfile);
}
// Make our context non-current
glXMakeCurrent(DISPLAY_FROM_XCB(screen), 0, 0);
}
// Destroy our temporary window
XDestroyWindow(DISPLAY_FROM_XCB(screen), window);
}
QGLXContext::~QGLXContext()