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)
, minor(0)
, swapInterval(1) // default to vsync
, colorSpace(QSurfaceFormat::DefaultColorSpace)
{
}
@ -91,7 +92,8 @@ public:
profile(other->profile),
major(other->major),
minor(other->minor),
swapInterval(other->swapInterval)
swapInterval(other->swapInterval),
colorSpace(other->colorSpace)
{
}
@ -110,6 +112,7 @@ public:
int major;
int minor;
int swapInterval;
QSurfaceFormat::ColorSpace colorSpace;
};
/*!
@ -124,6 +127,12 @@ public:
contains surface configuration parameters such as OpenGL profile and
version for rendering, whether or not to enable stereo buffers, and swap
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.
*/
/*!
\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.
@ -736,6 +761,47 @@ int QSurfaceFormat::swapInterval() const
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)
/*!
@ -841,6 +907,7 @@ QDebug operator<<(QDebug dbg, const QSurfaceFormat &f)
<< ", samples " << d->numSamples
<< ", swapBehavior " << d->swapBehavior
<< ", swapInterval " << d->swapInterval
<< ", colorSpace " << d->colorSpace
<< ", profile " << d->profile
<< ')';

View File

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

View File

@ -76,7 +76,11 @@ enum {
#undef FontChange
#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;
@ -120,6 +124,10 @@ QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit)
<< GLX_SAMPLES_ARB
<< format.samples();
if ((flags & QGLX_SUPPORTS_SRGB) && format.colorSpace() == QSurfaceFormat::sRGBColorSpace)
spec << GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
<< True;
spec << GLX_DRAWABLE_TYPE
<< drawableBit
@ -179,14 +187,14 @@ template <class 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;
GLXFBConfig config = 0;
do {
const QVector<int> spec = qglx_buildSpec(format, drawableBit);
const QVector<int> spec = qglx_buildSpec(format, drawableBit, flags);
int confcount = 0;
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++) {
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));
const int actualRed = qPopulationCount(visual->red_mask);
@ -228,28 +243,28 @@ GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format
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);
XVisualInfo *visualInfo = 0;
GLXFBConfig config = qglx_findConfig(display, screen, *format, false, drawableBit);
GLXFBConfig config = qglx_findConfig(display, screen, *format, false, drawableBit, flags);
if (config)
visualInfo = glXGetVisualFromFBConfig(display, config);
if (visualInfo) {
qglx_surfaceFormatFromGLXFBConfig(format, display, config);
qglx_surfaceFormatFromGLXFBConfig(format, display, config, flags);
return visualInfo;
}
// attempt to fall back to glXChooseVisual
do {
QVector<int> attribs = qglx_buildSpec(*format, drawableBit);
QVector<int> attribs = qglx_buildSpec(*format, drawableBit, flags);
visualInfo = glXChooseVisual(display, screen, attribs.data());
if (visualInfo) {
qglx_surfaceFormatFromVisualInfo(format, display, visualInfo);
qglx_surfaceFormatFromVisualInfo(format, display, visualInfo, flags);
return visualInfo;
}
} while (qglx_reduceFormat(format));
@ -257,7 +272,7 @@ XVisualInfo *qglx_findVisualInfo(Display *display, int screen, QSurfaceFormat *f
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 greenSize = 0;
@ -268,6 +283,7 @@ void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display,
int sampleBuffers = 0;
int sampleCount = 0;
int stereo = 0;
int srgbCapable = 0;
glXGetFBConfigAttrib(display, config, GLX_RED_SIZE, &redSize);
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_SAMPLES_ARB, &sampleBuffers);
glXGetFBConfigAttrib(display, config, GLX_STEREO, &stereo);
if (flags & QGLX_SUPPORTS_SRGB)
glXGetFBConfigAttrib(display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable);
format->setRedBufferSize(redSize);
format->setGreenBufferSize(greenSize);
@ -288,11 +306,12 @@ void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display,
glXGetFBConfigAttrib(display, config, GLX_SAMPLES_ARB, &sampleCount);
format->setSamples(sampleCount);
}
format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace);
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 greenSize = 0;
@ -303,6 +322,7 @@ void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display,
int sampleBuffers = 0;
int sampleCount = 0;
int stereo = 0;
int srgbCapable = 0;
glXGetConfig(display, visualInfo, GLX_RED_SIZE, &redSize);
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_SAMPLES_ARB, &sampleBuffers);
glXGetConfig(display, visualInfo, GLX_STEREO, &stereo);
if (flags & QGLX_SUPPORTS_SRGB)
glXGetConfig(display, visualInfo, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgbCapable);
format->setRedBufferSize(redSize);
format->setGreenBufferSize(greenSize);
@ -323,6 +345,7 @@ void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display,
glXGetConfig(display, visualInfo, GLX_SAMPLES_ARB, &sampleCount);
format->setSamples(sampleCount);
}
format->setColorSpace(srgbCapable ? QSurfaceFormat::sRGBColorSpace : QSurfaceFormat::DefaultColorSpace);
format->setStereo(stereo);
}
@ -391,5 +414,10 @@ bool qglx_reduceFormat(QSurfaceFormat *format)
return true;
}
if (format->colorSpace() == QSurfaceFormat::sRGBColorSpace) {
format->setColorSpace(QSurfaceFormat::DefaultColorSpace);
return true;
}
return false;
}

View File

@ -57,11 +57,16 @@
#include <X11/Xlib.h>
#include <GL/glx.h>
QVector<int> qglx_buildSpec(const QSurfaceFormat &format, int drawableBit = GLX_WINDOW_BIT);
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);
void qglx_surfaceFormatFromGLXFBConfig(QSurfaceFormat *format, Display *display, GLXFBConfig config);
void qglx_surfaceFormatFromVisualInfo(QSurfaceFormat *format, Display *display, XVisualInfo *visualInfo);
enum QGlxFlags
{
QGLX_SUPPORTS_SRGB = 0x01
};
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);
#endif // QGLXCONVENIENCE_H

View File

@ -41,6 +41,7 @@
#include "qxcbscreen.h"
#include <QtGlxSupport/private/qglxconvenience_p.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
@ -58,13 +59,27 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
QXcbScreen *scr = xcbScreen();
if (!scr)
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) {
qWarning() << "No XVisualInfo for format" << m_format;
return Q_NULLPTR;
}
const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid);
XFree(visualInfo);
qDebug(lcQpaGl) << "Got format:" << m_format;
return xcb_visualtype;
}