Add swapInterval to QSurfaceFormat

Implement swap interval support for EGL, GLX and WGL.

The environment variable QT_QPA_EGLFS_SWAPINTERVAL is renamed to
QT_QPA_EGL_SWAPINTERVAL and can be used to override the applications'
setting of the swap interval.

Task-number: QTBUG-31939

Change-Id: I644325d5d3306b7604bffd7efccda3c00ed37d36
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>
This commit is contained in:
Laszlo Agocs 2013-11-06 16:00:23 +01:00 committed by The Qt Project
parent c784ec00fa
commit 5dd94b75e3
14 changed files with 144 additions and 38 deletions

4
dist/changes-5.3.0 vendored
View File

@ -29,3 +29,7 @@ QtCore
QtGui
-----
- Added setSwapInterval() to QSurfaceFormat. Platforms that support
setting the swap interval are now defaulting to the value of 1,
meaning vsync is enabled.

View File

@ -72,6 +72,7 @@ public:
, profile(QSurfaceFormat::NoProfile)
, major(2)
, minor(0)
, swapInterval(1) // default to vsync
{
}
@ -89,7 +90,8 @@ public:
renderableType(other->renderableType),
profile(other->profile),
major(other->major),
minor(other->minor)
minor(other->minor),
swapInterval(other->swapInterval)
{
}
@ -107,6 +109,7 @@ public:
QSurfaceFormat::OpenGLContextProfile profile;
int major;
int minor;
int swapInterval;
};
/*!
@ -675,6 +678,46 @@ void QSurfaceFormat::setVersion(int major, int minor)
}
}
/*!
Sets the preferred swap interval. The swap interval specifies the
minimum number of video frames that are displayed before a buffer
swap occurs. This can be used to sync the GL drawing into a window
to the vertical refresh of the screen.
Setting an \a interval value of 0 will turn the vertical refresh
syncing off, any value higher than 0 will turn the vertical
syncing on. Setting \a interval to a higher value, for example 10,
results in having 10 vertical retraces between every buffer swap.
The default interval is 1.
Changing the swap interval may not be supported by the underlying
platform. In this case, the request will be silently ignored.
\since 5.3
\sa swapInterval()
*/
void QSurfaceFormat::setSwapInterval(int interval)
{
if (d->swapInterval != interval) {
detach();
d->swapInterval = interval;
}
}
/*!
Returns the swap interval.
\since 5.3
\sa setSwapInterval()
*/
int QSurfaceFormat::swapInterval() const
{
return d->swapInterval;
}
/*!
Returns \c true if all the options of the two QSurfaceFormat objects
\a a and \a b are equal.
@ -694,7 +737,8 @@ bool operator==(const QSurfaceFormat& a, const QSurfaceFormat& b)
&& a.d->swapBehavior == b.d->swapBehavior
&& a.d->profile == b.d->profile
&& a.d->major == b.d->major
&& a.d->minor == b.d->minor);
&& a.d->minor == b.d->minor
&& a.d->swapInterval == b.d->swapInterval);
}
/*!
@ -724,6 +768,7 @@ QDebug operator<<(QDebug dbg, const QSurfaceFormat &f)
<< ", stencilBufferSize " << d->stencilSize
<< ", samples " << d->numSamples
<< ", swapBehavior " << d->swapBehavior
<< ", swapInterval " << d->swapInterval
<< ", profile " << d->profile
<< ')';

View File

@ -135,6 +135,9 @@ public:
bool testOption(FormatOption option) const;
QSurfaceFormat::FormatOptions options() const;
int swapInterval() const;
void setSwapInterval(int interval);
private:
QSurfaceFormatPrivate *d;

View File

@ -350,6 +350,7 @@ QSurfaceFormat q_glFormatFromConfig(EGLDisplay display, const EGLConfig config,
format.setStencilBufferSize(stencilSize);
format.setSamples(sampleCount);
format.setStereo(false); // EGL doesn't support stereo buffers
format.setSwapInterval(referenceFormat.swapInterval());
// Clear the EGL error state because some of the above may
// have errored out because the attribute is not applicable

View File

@ -64,6 +64,9 @@ QEGLPlatformContext::QEGLPlatformContext(const QSurfaceFormat &format, QPlatform
: m_eglDisplay(display)
, m_eglApi(eglApi)
, m_eglConfig(q_configFromGLFormat(display, format))
, m_swapInterval(-1)
, m_swapIntervalEnvChecked(false)
, m_swapIntervalFromEnv(-1)
{
init(format, share);
}
@ -136,6 +139,27 @@ bool QEGLPlatformContext::makeCurrent(QPlatformSurface *surface)
}
#endif
if (ok) {
if (!m_swapIntervalEnvChecked) {
m_swapIntervalEnvChecked = true;
if (qEnvironmentVariableIsSet("QT_QPA_EGL_SWAPINTERVAL")) {
QByteArray swapIntervalString = qgetenv("QT_QPA_EGL_SWAPINTERVAL");
bool ok;
const int swapInterval = swapIntervalString.toInt(&ok);
if (ok)
m_swapIntervalFromEnv = swapInterval;
}
}
const int requestedSwapInterval = m_swapIntervalFromEnv >= 0
? m_swapIntervalFromEnv
: surface->format().swapInterval();
if (requestedSwapInterval >= 0 && m_swapInterval != requestedSwapInterval) {
m_swapInterval = requestedSwapInterval;
eglSwapInterval(eglDisplay(), m_swapInterval);
}
}
return ok;
}

View File

@ -80,6 +80,9 @@ private:
EGLenum m_eglApi;
EGLConfig m_eglConfig;
QSurfaceFormat m_format;
int m_swapInterval;
bool m_swapIntervalEnvChecked;
int m_swapIntervalFromEnv;
};
#endif //QEGLPLATFORMCONTEXT_H

View File

@ -54,29 +54,13 @@ QT_BEGIN_NAMESPACE
QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
EGLDisplay display, EGLenum eglApi)
: QEGLPlatformContext(QEglFSHooks::hooks()->surfaceFormatFor(format), share, display,
QEglFSIntegration::chooseConfig(display, QEglFSHooks::hooks()->surfaceFormatFor(format)), eglApi),
m_swapIntervalSet(false)
QEglFSIntegration::chooseConfig(display, QEglFSHooks::hooks()->surfaceFormatFor(format)), eglApi)
{
}
bool QEglFSContext::makeCurrent(QPlatformSurface *surface)
{
bool success = QEGLPlatformContext::makeCurrent(surface);
if (success && !m_swapIntervalSet) {
m_swapIntervalSet = true;
int swapInterval = 1;
QByteArray swapIntervalString = qgetenv("QT_QPA_EGLFS_SWAPINTERVAL");
if (!swapIntervalString.isEmpty()) {
bool ok;
swapInterval = swapIntervalString.toInt(&ok);
if (!ok)
swapInterval = 1;
}
eglSwapInterval(eglDisplay(), swapInterval);
}
return success;
return QEGLPlatformContext::makeCurrent(surface);
}
EGLSurface QEglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface)

View File

@ -55,9 +55,6 @@ public:
bool makeCurrent(QPlatformSurface *surface);
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface);
void swapBuffers(QPlatformSurface *surface);
private:
bool m_swapIntervalSet;
};
QT_END_NAMESPACE

View File

@ -126,7 +126,7 @@ void QEglFSWindow::create()
EGLDisplay display = static_cast<QEglFSScreen *>(screen)->display();
QSurfaceFormat platformFormat = QEglFSHooks::hooks()->surfaceFormatFor(window()->requestedFormat());
m_config = QEglFSIntegration::chooseConfig(display, platformFormat);
m_format = q_glFormatFromConfig(display, m_config);
m_format = q_glFormatFromConfig(display, m_config, platformFormat);
resetSurface();

View File

@ -880,7 +880,9 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex
m_staticContext(staticContext),
m_context(context),
m_renderingContext(0),
m_pixelFormat(0), m_extensionsUsed(false)
m_pixelFormat(0),
m_extensionsUsed(false),
m_swapInterval(-1)
{
QSurfaceFormat format = context->format();
if (format.renderableType() == QSurfaceFormat::DefaultRenderableType)
@ -977,11 +979,9 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex
QWindowsOpenGLContextFormat::current().apply(&m_obtainedFormat);
if (requestedAdditional.swapInterval != -1 && m_staticContext->wglSwapInternalExt) {
m_staticContext->wglSwapInternalExt(requestedAdditional.swapInterval);
if (m_staticContext->wglGetSwapInternalExt)
obtainedSwapInternal = m_staticContext->wglGetSwapInternalExt();
}
if (m_staticContext->wglGetSwapInternalExt)
obtainedSwapInternal = m_staticContext->wglGetSwapInternalExt();
wglMakeCurrent(0, 0);
} while (false);
if (hdc)
@ -1085,7 +1085,19 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface)
window->setFlag(QWindowsWindow::OpenGLDoubleBuffered);
}
m_windowContexts.append(newContext);
return wglMakeCurrent(newContext.hdc, newContext.renderingContext);
bool success = wglMakeCurrent(newContext.hdc, newContext.renderingContext);
// Set the swap interval
if (m_staticContext->wglSwapInternalExt) {
const int interval = surface->format().swapInterval();
if (interval >= 0 && m_swapInterval != interval) {
m_swapInterval = interval;
m_staticContext->wglSwapInternalExt(interval);
}
}
return success;
}
void QWindowsGLContext::doneCurrent()

View File

@ -64,11 +64,10 @@ enum QWindowsGLFormatFlags
// Additional format information for Windows.
struct QWindowsOpenGLAdditionalFormat
{
QWindowsOpenGLAdditionalFormat(unsigned formatFlagsIn = 0, unsigned pixmapDepthIn = 0, unsigned swapIntervalIn = -1) :
formatFlags(formatFlagsIn), pixmapDepth(pixmapDepthIn), swapInterval(swapIntervalIn) {}
QWindowsOpenGLAdditionalFormat(unsigned formatFlagsIn = 0, unsigned pixmapDepthIn = 0) :
formatFlags(formatFlagsIn), pixmapDepth(pixmapDepthIn) { }
unsigned formatFlags; // QWindowsGLFormatFlags.
unsigned pixmapDepth; // for QWindowsGLRenderToPixmap
int swapInterval;
};
// Per-window data for active OpenGL contexts.
@ -178,6 +177,7 @@ private:
PIXELFORMATDESCRIPTOR m_obtainedPixelFormatDescriptor;
int m_pixelFormat;
bool m_extensionsUsed;
int m_swapInterval;
};
QT_END_NAMESPACE

View File

@ -167,6 +167,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
, m_shareContext(0)
, m_format(format)
, m_isPBufferCurrent(false)
, m_swapInterval(-1)
{
if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType)
m_format.setRenderableType(QSurfaceFormat::OpenGL);
@ -326,19 +327,50 @@ QGLXContext::~QGLXContext()
bool QGLXContext::makeCurrent(QPlatformSurface *surface)
{
bool success = false;
Q_ASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface);
Display *dpy = DISPLAY_FROM_XCB(m_screen);
GLXDrawable glxDrawable = 0;
QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass();
if (surfaceClass == QSurface::Window) {
m_isPBufferCurrent = false;
QXcbWindow *window = static_cast<QXcbWindow *>(surface);
return glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), window->xcb_window(), m_context);
glxDrawable = window->xcb_window();
success = glXMakeCurrent(dpy, glxDrawable, m_context);
} else if (surfaceClass == QSurface::Offscreen) {
m_isPBufferCurrent = true;
QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface);
return glXMakeContextCurrent(DISPLAY_FROM_XCB(m_screen), pbuffer->pbuffer(), pbuffer->pbuffer(), m_context);
glxDrawable = pbuffer->pbuffer();
success = glXMakeContextCurrent(dpy, glxDrawable, glxDrawable, m_context);
}
return false;
if (success) {
int interval = surface->format().swapInterval();
if (interval >= 0 && m_swapInterval != interval) {
m_swapInterval = interval;
typedef void (*qt_glXSwapIntervalEXT)(Display *, GLXDrawable, int);
typedef void (*qt_glXSwapIntervalMESA)(unsigned int);
static qt_glXSwapIntervalEXT glXSwapIntervalEXT = 0;
static qt_glXSwapIntervalMESA glXSwapIntervalMESA = 0;
static bool resolved = false;
if (!resolved) {
resolved = true;
QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(dpy,
m_screen->screenNumber())).split(' ');
if (glxExt.contains("GLX_EXT_swap_control"))
glXSwapIntervalEXT = (qt_glXSwapIntervalEXT) getProcAddress("glXSwapIntervalEXT");
if (glxExt.contains("GLX_MESA_swap_control"))
glXSwapIntervalMESA = (qt_glXSwapIntervalMESA) getProcAddress("glXSwapIntervalMESA");
}
if (glXSwapIntervalEXT)
glXSwapIntervalEXT(dpy, glxDrawable, interval);
else if (glXSwapIntervalMESA)
glXSwapIntervalMESA(interval);
}
}
return success;
}
void QGLXContext::doneCurrent()

View File

@ -78,6 +78,7 @@ private:
GLXContext m_shareContext;
QSurfaceFormat m_format;
bool m_isPBufferCurrent;
int m_swapInterval;
};

View File

@ -293,7 +293,7 @@ void QXcbWindow::create()
#elif defined(XCB_USE_EGL)
EGLDisplay eglDisplay = connection()->egl_display();
EGLConfig eglConfig = q_configFromGLFormat(eglDisplay, m_format, true);
m_format = q_glFormatFromConfig(eglDisplay, eglConfig);
m_format = q_glFormatFromConfig(eglDisplay, eglConfig, m_format);
VisualID id = QXlibEglIntegration::getCompatibleVisualId(DISPLAY_FROM_XCB(this), eglDisplay, eglConfig);