Support windows with sRGB-capable default framebuffers in QSurfaceFormat

Backend implementation is done for GLX for now. WGL and EGL may follow
later on.

[ChangeLog][QtGui] Added support for requesting OpenGL windows with
sRGB-capable default framebuffers. While this is implicit on some
platforms, QSurfaceFormat now has the necessary flags to request
such windows in a cross-platform manner.

Task-number: QTBUG-50987
Change-Id: I4df1f786e41e63396f46920a81afdf5ecb5eedea
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2017-02-20 14:48:56 +01:00
parent d5078161aa
commit 931e4ae665
5 changed files with 141 additions and 17 deletions

View File

@ -73,6 +73,7 @@ public:
, major(2) , major(2)
, minor(0) , minor(0)
, swapInterval(1) // default to vsync , swapInterval(1) // default to vsync
, colorSpace(QSurfaceFormat::DefaultColorSpace)
{ {
} }
@ -91,7 +92,8 @@ public:
profile(other->profile), profile(other->profile),
major(other->major), major(other->major),
minor(other->minor), minor(other->minor),
swapInterval(other->swapInterval) swapInterval(other->swapInterval),
colorSpace(other->colorSpace)
{ {
} }
@ -110,6 +112,7 @@ public:
int major; int major;
int minor; int minor;
int swapInterval; int swapInterval;
QSurfaceFormat::ColorSpace colorSpace;
}; };
/*! /*!
@ -124,6 +127,12 @@ public:
contains surface configuration parameters such as OpenGL profile and contains surface configuration parameters such as OpenGL profile and
version for rendering, whether or not to enable stereo buffers, and swap version for rendering, whether or not to enable stereo buffers, and swap
behaviour. behaviour.
\note When troubleshooting context or window format issues, it can be
helpful to enable the logging category \c{qt.qpa.gl}. Depending on the
platform, this may print useful debug information when it comes to OpenGL
initialization and the native visual or framebuffer configurations which
QSurfaceFormat gets mapped to.
*/ */
/*! /*!
@ -198,6 +207,22 @@ public:
\value CompatibilityProfile Functionality from earlier OpenGL versions is available. \value CompatibilityProfile Functionality from earlier OpenGL versions is available.
*/ */
/*!
\enum QSurfaceFormat::ColorSpace
This enum is used to specify the preferred color space, controlling if the
window's associated default framebuffer is able to do updates and blending
in a given encoding instead of the standard linear operations.
\value DefaultColorSpace The default, unspecified color space.
\value sRGBColorSpace When \c{GL_ARB_framebuffer_sRGB} or
\c{GL_EXT_framebuffer_sRGB} is supported by the platform and this value is
set, the window will be created with an sRGB-capable default
framebuffer. Note that some platforms may return windows with a sRGB-capable
default framebuffer even when not requested explicitly.
*/
/*! /*!
Constructs a default initialized QSurfaceFormat. Constructs a default initialized QSurfaceFormat.
@ -736,6 +761,47 @@ int QSurfaceFormat::swapInterval() const
return d->swapInterval; return d->swapInterval;
} }
/*!
Sets the preferred \a colorSpace.
For example, this allows requesting windows with default framebuffers that
are sRGB-capable on platforms that support it.
\note When the requested color space is not supported by the platform, the
request is ignored. Query the QSurfaceFormat after window creation to verify
if the color space request could be honored or not.
\note This setting controls if the default framebuffer of the window is
capable of updates and blending in a given color space. It does not change
applications' output by itself. The applications' rendering code will still
have to opt in via the appropriate OpenGL calls to enable updates and
blending to be performed in the given color space instead of using the
standard linear operations.
\since 5.10
\sa colorSpace()
*/
void QSurfaceFormat::setColorSpace(ColorSpace colorSpace)
{
if (d->colorSpace != colorSpace) {
detach();
d->colorSpace = colorSpace;
}
}
/*!
\return the color space.
\since 5.10
\sa setColorSpace()
*/
QSurfaceFormat::ColorSpace QSurfaceFormat::colorSpace() const
{
return d->colorSpace;
}
Q_GLOBAL_STATIC(QSurfaceFormat, qt_default_surface_format) Q_GLOBAL_STATIC(QSurfaceFormat, qt_default_surface_format)
/*! /*!
@ -841,6 +907,7 @@ QDebug operator<<(QDebug dbg, const QSurfaceFormat &f)
<< ", samples " << d->numSamples << ", samples " << d->numSamples
<< ", swapBehavior " << d->swapBehavior << ", swapBehavior " << d->swapBehavior
<< ", swapInterval " << d->swapInterval << ", swapInterval " << d->swapInterval
<< ", colorSpace " << d->colorSpace
<< ", profile " << d->profile << ", profile " << d->profile
<< ')'; << ')';

View File

@ -85,6 +85,12 @@ public:
}; };
Q_ENUM(OpenGLContextProfile) Q_ENUM(OpenGLContextProfile)
enum ColorSpace {
DefaultColorSpace,
sRGBColorSpace
};
Q_ENUM(ColorSpace)
QSurfaceFormat(); QSurfaceFormat();
/*implicit*/ QSurfaceFormat(FormatOptions options); /*implicit*/ QSurfaceFormat(FormatOptions options);
QSurfaceFormat(const QSurfaceFormat &other); QSurfaceFormat(const QSurfaceFormat &other);
@ -145,6 +151,9 @@ public:
int swapInterval() const; int swapInterval() const;
void setSwapInterval(int interval); void setSwapInterval(int interval);
ColorSpace colorSpace() const;
void setColorSpace(ColorSpace colorSpace);
static void setDefaultFormat(const QSurfaceFormat &format); static void setDefaultFormat(const QSurfaceFormat &format);
static QSurfaceFormat defaultFormat(); static QSurfaceFormat defaultFormat();

View File

@ -76,7 +76,11 @@ enum {
#undef FontChange #undef FontChange
#endif #endif
QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit) #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
#endif
QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit, int flags)
{ {
QVector<int> spec; QVector<int> spec;
@ -120,6 +124,10 @@ QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit)
<< GLX_SAMPLES_ARB << GLX_SAMPLES_ARB
<< format.samples(); << format.samples();
if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace)
spec << GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
<< True;
spec << GLX_DRAWABLE_TYPE spec << GLX_DRAWABLE_TYPE
<< drawableBit << drawableBit
@ -179,14 +187,14 @@ template <class T>
using QXlibArrayPointer = QScopedArrayPointer<T, QXlibScopedPointerDeleter<T>>; using QXlibArrayPointer = QScopedArrayPointer<T, QXlibScopedPointerDeleter<T>>;
} }
GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format, bool highestPixelFormat, int drawableBit) GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format, bool highestPixelFormat, int drawableBit, int flags)
{ {
QXcbSoftwareOpenGLEnforcer softwareOpenGLEnforcer; QXcbSoftwareOpenGLEnforcer softwareOpenGLEnforcer;
GLXFBConfig config = 0; GLXFBConfig config = 0;
do { do {
const QVector<int> spec = qglx_buildSpec(format, drawableBit); const QVector<int> spec = qglx_buildSpec(format, drawableBit, flags);
int confcount = 0; int confcount = 0;
QXlibArrayPointer<GLXFBConfig> configs(glXChooseFBConfig(display, screen, spec.constData(), &confcount)); QXlibArrayPointer<GLXFBConfig> configs(glXChooseFBConfig(display, screen, spec.constData(), &confcount));
@ -205,6 +213,13 @@ GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format
for (int i = 0; i < confcount; i++) { for (int i = 0; i < confcount; i++) {
GLXFBConfig candidate = configs[i]; GLXFBConfig candidate = configs[i];
if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace) {
int srgbCapable = 0;
glXGetFBConfigAttrib(display, candidate, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable);
if (!srgbCapable)
continue;
}
QXlibPointer<XVisualInfo> visual(glXGetVisualFromFBConfig(display, candidate)); QXlibPointer<XVisualInfo> visual(glXGetVisualFromFBConfig(display, candidate));
const int actualRed = qPopulationCount(visual->red_mask); const int actualRed = qPopulationCount(visual->red_mask);
@ -228,28 +243,28 @@ GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format
return config; return config;
} }
XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit) XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit, int flags)
{ {
Q_ASSERT(format); Q_ASSERT(format);
XVisualInfo *visualInfo = 0; XVisualInfo *visualInfo = 0;
GLXFBConfig config = qglx_findConfig(display, screen, *format, false, drawableBit); GLXFBConfig config = qglx_findConfig(display, screen, *format, false, drawableBit, flags);
if (config) if (config)
visualInfo = glXGetVisualFromFBConfig(display, config); visualInfo = glXGetVisualFromFBConfig(display, config);
if (visualInfo) { if (visualInfo) {
qglx_surfaceFormatFromGLXFBConfig(format, display, config); qglx_surfaceFormatFromGLXFBConfig(format, display, config, flags);
return visualInfo; return visualInfo;
} }
// attempt to fall back to glXChooseVisual // attempt to fall back to glXChooseVisual
do { do {
QVector<int> attribs = qglx_buildSpec(*format, drawableBit); QVector<int> attribs = qglx_buildSpec(*format, drawableBit, flags);
visualInfo = glXChooseVisual(display, screen, attribs.data()); visualInfo = glXChooseVisual(display, screen, attribs.data());
if (visualInfo) { if (visualInfo) {
qglx_surfaceFormatFromVisualInfo(format, display, visualInfo); qglx_surfaceFormatFromVisualInfo(format, display, visualInfo, flags);
return visualInfo; return visualInfo;
} }
} while (qglx_reduceFormat(format)); } while (qglx_reduceFormat(format));
@ -257,7 +272,7 @@ XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *f
return visualInfo; return visualInfo;
} }
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config) void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, int flags)
{ {
int redSize = 0; int redSize = 0;
int greenSize = 0; int greenSize = 0;
@ -268,6 +283,7 @@ void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display,
int sampleBuffers = 0; int sampleBuffers = 0;
int sampleCount = 0; int sampleCount = 0;
int stereo = 0; int stereo = 0;
int srgbCapable = 0;
glXGetFBConfigAttrib(display, config, GLX_RED_SIZE, &redSize); glXGetFBConfigAttrib(display, config, GLX_RED_SIZE, &redSize);
glXGetFBConfigAttrib(display, config, GLX_GREEN_SIZE, &greenSize); glXGetFBConfigAttrib(display, config, GLX_GREEN_SIZE, &greenSize);
@ -277,6 +293,8 @@ void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display,
glXGetFBConfigAttrib(display, config, GLX_STENCIL_SIZE, &stencilSize); glXGetFBConfigAttrib(display, config, GLX_STENCIL_SIZE, &stencilSize);
glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleBuffers); glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleBuffers);
glXGetFBConfigAttrib(display, config, GLX_STEREO, &stereo); glXGetFBConfigAttrib(display, config, GLX_STEREO, &stereo);
if (flags & QGLX_SUPPORTS_SRGB)
glXGetFBConfigAttrib(display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable);
format->setRedBufferSize(redSize); format->setRedBufferSize(redSize);
format->setGreenBufferSize(greenSize); format->setGreenBufferSize(greenSize);
@ -288,11 +306,12 @@ void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display,
glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleCount); glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleCount);
format->setSamples(sampleCount); format->setSamples(sampleCount);
} }
format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace);
format->setStereo(stereo); format->setStereo(stereo);
} }
void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo) void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo, int flags)
{ {
int redSize = 0; int redSize = 0;
int greenSize = 0; int greenSize = 0;
@ -303,6 +322,7 @@ void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display,
int sampleBuffers = 0; int sampleBuffers = 0;
int sampleCount = 0; int sampleCount = 0;
int stereo = 0; int stereo = 0;
int srgbCapable = 0;
glXGetConfig(display, visualInfo, GLX_RED_SIZE, &redSize); glXGetConfig(display, visualInfo, GLX_RED_SIZE, &redSize);
glXGetConfig(display, visualInfo, GLX_GREEN_SIZE, &greenSize); glXGetConfig(display, visualInfo, GLX_GREEN_SIZE, &greenSize);
@ -312,6 +332,8 @@ void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display,
glXGetConfig(display, visualInfo, GLX_STENCIL_SIZE, &stencilSize); glXGetConfig(display, visualInfo, GLX_STENCIL_SIZE, &stencilSize);
glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleBuffers); glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleBuffers);
glXGetConfig(display, visualInfo, GLX_STEREO, &stereo); glXGetConfig(display, visualInfo, GLX_STEREO, &stereo);
if (flags & QGLX_SUPPORTS_SRGB)
glXGetConfig(display, visualInfo, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable);
format->setRedBufferSize(redSize); format->setRedBufferSize(redSize);
format->setGreenBufferSize(greenSize); format->setGreenBufferSize(greenSize);
@ -323,6 +345,7 @@ void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display,
glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleCount); glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleCount);
format->setSamples(sampleCount); format->setSamples(sampleCount);
} }
format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace);
format->setStereo(stereo); format->setStereo(stereo);
} }
@ -391,5 +414,10 @@ bool qglx_reduceFormat(QSurfaceFormat *format)
return true; return true;
} }
if (format->colorSpace() == QSurfaceFormat::sRGBColorSpace) {
format->setColorSpace(QSurfaceFormat::DefaultColorSpace);
return true;
}
return false; return false;
} }

View File

@ -57,11 +57,16 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <GL/glx.h> #include <GL/glx.h>
QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit = GLX_WINDOW_BIT); enum QGlxFlags
XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit = GLX_WINDOW_BIT); {
GLXFBConfig qglx_findConfig(Display *display, int screen, QSurfaceFormat format, bool highestPixelFormat = false, int drawableBit = GLX_WINDOW_BIT); QGLX_SUPPORTS_SRGB = 0x01
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config); };
void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo);
QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit = GLX_WINDOW_BIT, int flags = 0);
XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *format, int drawableBit = GLX_WINDOW_BIT, int flags = 0);
GLXFBConfig qglx_findConfig(Display *display, int screen, QSurfaceFormat format, bool highestPixelFormat = false, int drawableBit = GLX_WINDOW_BIT, int flags = 0);
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config, int flags = 0);
void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo, int flags = 0);
bool qglx_reduceFormat(QSurfaceFormat *format); bool qglx_reduceFormat(QSurfaceFormat *format);
#endif // QGLXCONVENIENCE_H #endif // QGLXCONVENIENCE_H

View File

@ -41,6 +41,7 @@
#include "qxcbscreen.h" #include "qxcbscreen.h"
#include <QtGlxSupport/private/qglxconvenience_p.h> #include <QtGlxSupport/private/qglxconvenience_p.h>
#include <QDebug>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -58,13 +59,27 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
QXcbScreen *scr = xcbScreen(); QXcbScreen *scr = xcbScreen();
if (!scr) if (!scr)
return Q_NULLPTR; return Q_NULLPTR;
XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(scr), scr->screenNumber(), &m_format);
qDebug(lcQpaGl) << "Requested format before FBConfig/Visual selection:" << m_format;
const char *glxExts = glXQueryExtensionsString(DISPLAY_FROM_XCB(scr), scr->screenNumber());
int flags = 0;
if (glxExts) {
qCDebug(lcQpaGl, "Available GLX extensions: %s", glxExts);
if (strstr(glxExts, "GLX_EXT_framebuffer_sRGB") || strstr(glxExts, "GLX_ARB_framebuffer_sRGB"))
flags |= QGLX_SUPPORTS_SRGB;
}
XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(scr), scr->screenNumber(), &m_format, GLX_WINDOW_BIT, flags);
if (!visualInfo) { if (!visualInfo) {
qWarning() << "No XVisualInfo for format" << m_format; qWarning() << "No XVisualInfo for format" << m_format;
return Q_NULLPTR; return Q_NULLPTR;
} }
const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid); const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid);
XFree(visualInfo); XFree(visualInfo);
qDebug(lcQpaGl) << "Got format:" << m_format;
return xcb_visualtype; return xcb_visualtype;
} }