Added explicit threading API to QtOpenGL.
authorSamuel Rødal <samuel.rodal@digia.com>
Mon, 26 Nov 2012 11:57:09 +0000 (12:57 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 30 Nov 2012 17:28:04 +0000 (18:28 +0100)
Since QtOpenGL/QGLContext is implemented in terms of
QtGui/QOpenGLContext which has stricter requirements about how it's
supposed to be used, we need to apply these requirements to QGLContext
as well.

This change adds QGLContext::moveToThread(QThread *) and documents it as
a necessity for making a context current on another thread.

Also introduces QGLPixelbuffer::context() to access the QGLContext of a
pixelbuffer, and made QGLWidget::context() return a non-const
QGLContext, since there's no good reason why it shouldn't, and it leads
to less const_cast clutter.

We could have introduced a backdoor in QOpenGLContext instead, making it
loosen its requirements, but that would have made it harder / impossible
to fully support threaded OpenGL in all the platforms.

Task-number: QTBUG-22560
Change-Id: Ibb6f65f342e7c963e80cc42ab5664c5f1cab30b0
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>
dist/changes-5.0.0
src/opengl/qgl.cpp
src/opengl/qgl.h
src/opengl/qgl_p.h
src/opengl/qgl_qpa.cpp
src/opengl/qglpaintdevice.cpp
src/opengl/qglpixelbuffer.cpp
src/opengl/qglpixelbuffer.h
src/opengl/qglpixelbuffer_p.h
tests/auto/opengl/qglthreads/qglthreads.pro
tests/auto/opengl/qglthreads/tst_qglthreads.cpp

index 056b750..c76f216 100644 (file)
@@ -676,6 +676,15 @@ QtOpenGL
   which were deprecated in Qt 4 have been removed.
 * Previously deprecated default value listBase parameter has been removed from
   both QGLWidget::renderText() functions.
+* In order to ensure support on more platforms, stricter requirements have been
+  introduced for doing threaded OpenGL. First, you must call makeCurrent() at
+  least once per swapBuffers() call, so that the platform has a chance to
+  synchronize resizes to the OpenGL surface. Second, before doing makeCurrent()
+  or swapBuffers() in a separate thread, you must call
+  QGLContext::moveToThread(QThread *) to explicitly let Qt know in which thread
+  a QGLContext is currently being used. You also need to make sure that the
+  context is not current in the current thread before moving it to a different
+  thread.
 
 QtScript
 --------
index c94c8f5..583ecb6 100644 (file)
@@ -3127,6 +3127,19 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
 }
 
 /*!
+    Moves the QGLContext to the given \a thread.
+
+    Enables calling swapBuffers() and makeCurrent() on the context in
+    the given thread.
+*/
+void QGLContext::moveToThread(QThread *thread)
+{
+    Q_D(QGLContext);
+    if (d->guiGlContext)
+        d->guiGlContext->moveToThread(thread);
+}
+
+/*!
     \fn bool QGLContext::chooseContext(const QGLContext* shareContext = 0)
 
     This semi-internal function is called by create(). It creates a
@@ -3195,16 +3208,18 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
 
     In some very rare cases the underlying call may fail. If this
     occurs an error message is output to stderr.
+
+    If you call this from a thread other than the main UI thread,
+    make sure you've first pushed the context to the relevant thread
+    from the UI thread using moveToThread().
 */
 
 
 /*!
     \fn void QGLContext::swapBuffers() const
 
-    Swaps the screen contents with an off-screen buffer. Only works if
-    the context is in double buffer mode.
-
-    \sa QGLFormat::setDoubleBuffer()
+    Call this to finish a frame of OpenGL rendering, and make sure to
+    call makeCurrent() again before you begin a new frame.
 */
 
 
@@ -3362,13 +3377,18 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
     1. Call doneCurrent() in the main thread when the rendering is
     finished.
 
-    2. Notify the swapping thread that it can grab the context.
+    2. Call QGLContext::moveToThread(swapThread) to transfer ownership
+    of the context to the swapping thread.
 
-    3. Make the rendering context current in the swapping thread with
+    3. Notify the swapping thread that it can grab the context.
+
+    4. Make the rendering context current in the swapping thread with
     makeCurrent() and then call swapBuffers().
 
-    4. Call doneCurrent() in the swapping thread and notify the main
-    thread that swapping is done.
+    5. Call doneCurrent() in the swapping thread.
+
+    6. Call QGLContext::moveToThread(qApp->thread()) and notify the
+    main thread that swapping is done.
 
     Doing this will free up the main thread so that it can continue
     with, for example, handling UI events or network requests. Even if
@@ -3400,7 +3420,10 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
     QGLWidgets can only be created in the main GUI thread. This means
     a call to doneCurrent() is necessary to release the GL context
     from the main thread, before the widget can be drawn into by
-    another thread. Also, the main GUI thread will dispatch resize and
+    another thread. You then need to call QGLContext::moveToThread()
+    to transfer ownership of the context to the thread in which you
+    want to make it current.
+    Also, the main GUI thread will dispatch resize and
     paint events to a QGLWidget when the widget is resized, or parts
     of it becomes exposed or needs redrawing. It is therefore
     necessary to handle those events because the default
@@ -3749,7 +3772,7 @@ void QGLWidget::setFormat(const QGLFormat &format)
 
 
 /*!
-    \fn const QGLContext *QGLWidget::context() const
+    \fn QGLContext *QGLWidget::context() const
 
     Returns the context of this widget.
 
@@ -4483,7 +4506,7 @@ QGLFormat QGLWidget::format() const
     return d->glcx->format();
 }
 
-const QGLContext *QGLWidget::context() const
+QGLContext *QGLWidget::context() const
 {
     Q_D(const QGLWidget);
     return d->glcx;
index ab2fd8d..1d21b42 100644 (file)
@@ -281,6 +281,8 @@ public:
     QGLFormat requestedFormat() const;
     void setFormat(const QGLFormat& format);
 
+    void moveToThread(QThread *thread);
+
     virtual void makeCurrent();
     virtual void doneCurrent();
 
@@ -413,7 +415,7 @@ public:
     QGLFormat format() const;
     void setFormat(const QGLFormat& format);
 
-    const QGLContext* context() const;
+    QGLContext* context() const;
     void setContext(QGLContext* context, const QGLContext* shareContext = 0,
                     bool deleteOldContext = true);
 
index df099ea..904f3c1 100644 (file)
@@ -380,19 +380,18 @@ class Q_OPENGL_EXPORT QGLTextureDestroyer : public QObject
     Q_OBJECT
 public:
     QGLTextureDestroyer() : QObject() {
-        qRegisterMetaType<GLuint>();
-        connect(this, SIGNAL(freeTexture(QGLContext *, QPlatformPixmap *, GLuint)),
-                this, SLOT(freeTexture_slot(QGLContext *, QPlatformPixmap *, GLuint)));
+        connect(this, SIGNAL(freeTexture(QGLContext *, QPlatformPixmap *, quint32)),
+                this, SLOT(freeTexture_slot(QGLContext *, QPlatformPixmap *, quint32)));
     }
     void emitFreeTexture(QGLContext *context, QPlatformPixmap *boundPixmap, GLuint id) {
         emit freeTexture(context, boundPixmap, id);
     }
 
 Q_SIGNALS:
-    void freeTexture(QGLContext *context, QPlatformPixmap *boundPixmap, GLuint id);
+    void freeTexture(QGLContext *context, QPlatformPixmap *boundPixmap, quint32 id);
 
 private slots:
-    void freeTexture_slot(QGLContext *context, QPlatformPixmap *boundPixmap, GLuint id) {
+    void freeTexture_slot(QGLContext *context, QPlatformPixmap *boundPixmap, quint32 id) {
         Q_UNUSED(boundPixmap);
         QGLShareContextScope scope(context);
         glDeleteTextures(1, &id);
index f52bece..ba07f61 100644 (file)
@@ -186,9 +186,14 @@ void QGLContext::reset()
     d->initDone = false;
     QGLContextGroup::removeShare(this);
     if (d->guiGlContext) {
-        if (d->ownContext)
-            delete d->guiGlContext;
-        else
+        if (QOpenGLContext::currentContext() == d->guiGlContext)
+            doneCurrent();
+        if (d->ownContext) {
+            if (d->guiGlContext->thread() == QThread::currentThread())
+                delete d->guiGlContext;
+            else
+                d->guiGlContext->deleteLater();
+        } else
             d->guiGlContext->setQGLContextHandle(0,0);
         d->guiGlContext = 0;
     }
index 5aa125f..e870e45 100644 (file)
@@ -43,6 +43,7 @@
 #include <private/qgl_p.h>
 #include <private/qglpixelbuffer_p.h>
 #include <private/qglframebufferobject_p.h>
+#include <qopenglfunctions.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -90,7 +91,7 @@ void QGLPaintDevice::beginPaint()
 
     if (m_previousFBO != m_thisFBO) {
         ctx->d_ptr->current_fbo = m_thisFBO;
-        glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO);
+        ctx->contextHandle()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO);
     }
 
     // Set the default fbo for the context to m_thisFBO so that
@@ -108,7 +109,7 @@ void QGLPaintDevice::ensureActiveTarget()
 
     if (ctx->d_ptr->current_fbo != m_thisFBO) {
         ctx->d_ptr->current_fbo = m_thisFBO;
-        glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO);
+        ctx->contextHandle()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_thisFBO);
     }
 
     ctx->d_ptr->default_fbo = m_thisFBO;
@@ -120,7 +121,7 @@ void QGLPaintDevice::endPaint()
     QGLContext *ctx = context();
     if (m_previousFBO != ctx->d_func()->current_fbo) {
         ctx->d_ptr->current_fbo = m_previousFBO;
-        glBindFramebuffer(GL_FRAMEBUFFER, m_previousFBO);
+        ctx->contextHandle()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_previousFBO);
     }
 
     ctx->d_ptr->default_fbo = 0;
index a5e748a..dd13ef2 100644 (file)
 
 #include "gl2paintengineex/qpaintengineex_opengl2_p.h"
 
+#include <qglframebufferobject.h>
 #include <qglpixelbuffer.h>
 #include <private/qglpixelbuffer_p.h>
 #include <private/qfont_p.h>
@@ -115,11 +116,23 @@ QGLContext* QGLPBufferGLPaintDevice::context() const
     return pbuf->d_func()->qctx;
 }
 
-void QGLPBufferGLPaintDevice::endPaint() {
+void QGLPBufferGLPaintDevice::beginPaint()
+{
+    pbuf->makeCurrent();
+    QGLPaintDevice::beginPaint();
+}
+
+void QGLPBufferGLPaintDevice::endPaint()
+{
     glFlush();
     QGLPaintDevice::endPaint();
 }
 
+void QGLPBufferGLPaintDevice::setFbo(GLuint fbo)
+{
+    m_thisFBO = fbo;
+}
+
 void QGLPBufferGLPaintDevice::setPBuffer(QGLPixelBuffer* pb)
 {
     pbuf = pb;
@@ -221,6 +234,7 @@ bool QGLPixelBuffer::makeCurrent()
             format.setSamples(d->req_format.samples());
         d->fbo = new QOpenGLFramebufferObject(d->req_size, format);
         d->fbo->bind();
+        d->glDevice.setFbo(d->fbo->handle());
         glViewport(0, 0, d->req_size.width(), d->req_size.height());
     }
     return true;
@@ -242,6 +256,15 @@ bool QGLPixelBuffer::doneCurrent()
 }
 
 /*!
+    Returns the context of this pixelbuffer.
+*/
+QGLContext *QGLPixelBuffer::context() const
+{
+    Q_D(const QGLPixelBuffer);
+    return d->qctx;
+}
+
+/*!
     \fn GLuint QGLPixelBuffer::generateDynamicTexture() const
 
     Generates and binds a 2D GL texture that is the same size as the
@@ -366,6 +389,8 @@ QImage QGLPixelBuffer::toImage() const
         return QImage();
 
     const_cast<QGLPixelBuffer *>(this)->makeCurrent();
+    if (d->fbo)
+        d->fbo->bind();
     return qt_gl_read_framebuffer(d->req_size, d->format.alpha(), true);
 }
 
@@ -615,7 +640,7 @@ GLuint QGLPixelBuffer::generateDynamicTexture() const
 
 bool QGLPixelBuffer::hasOpenGLPbuffers()
 {
-    return QOpenGLFramebufferObject::hasOpenGLFramebufferObjects();
+    return QGLFramebufferObject::hasOpenGLFramebufferObjects();
 }
 
 QT_END_NAMESPACE
index 58cd470..f597a48 100644 (file)
@@ -66,6 +66,8 @@ public:
     bool makeCurrent();
     bool doneCurrent();
 
+    QGLContext *context() const;
+
     GLuint generateDynamicTexture() const;
     bool bindToDynamicTexture(GLuint texture);
     void releaseFromDynamicTexture();
index fd20cee..caa2e2f 100644 (file)
@@ -68,8 +68,10 @@ public:
     virtual QPaintEngine* paintEngine() const {return pbuf->paintEngine();}
     virtual QSize size() const {return pbuf->size();}
     virtual QGLContext* context() const;
+    virtual void beginPaint();
     virtual void endPaint();
     void setPBuffer(QGLPixelBuffer* pb);
+    void setFbo(GLuint fbo);
 private:
     QGLPixelBuffer* pbuf;
 };
index 754f494..d5cbd0d 100644 (file)
@@ -1,7 +1,7 @@
 CONFIG += testcase
 TARGET = tst_qglthreads
 requires(contains(QT_CONFIG,opengl))
-QT += opengl widgets testlib
+QT += opengl widgets testlib gui-private core-private
 
 HEADERS += tst_qglthreads.h
 SOURCES += tst_qglthreads.cpp
@@ -10,5 +10,6 @@ x11 {
     LIBS += $$QMAKE_LIBS_X11
 }
 
-CONFIG+=insignificant_test # QTBUG-22560
 DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+
+win32:CONFIG+=insignificant_test # QTBUG-28264
index 267e326..8535f17 100644 (file)
@@ -42,6 +42,8 @@
 #include <QtTest/QtTest>
 #include <QtCore/QtCore>
 #include <QtGui/QtGui>
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
 #include <QtWidgets/QApplication>
 #include <QtOpenGL/QtOpenGL>
 #include "tst_qglthreads.h"
@@ -74,7 +76,8 @@ class SwapThread : public QThread
     Q_OBJECT
 public:
     SwapThread(QGLWidget *widget)
-        : m_widget(widget)
+        : m_context(widget->context())
+        , m_swapTriggered(false)
     {
         moveToThread(this);
     }
@@ -84,25 +87,48 @@ public:
         time.start();
         while (time.elapsed() < RUNNING_TIME) {
             lock();
-            wait();
+            waitForReadyToSwap();
 
-            m_widget->makeCurrent();
-            m_widget->swapBuffers();
-            m_widget->doneCurrent();
+            m_context->makeCurrent();
+            m_context->swapBuffers();
+            m_context->doneCurrent();
+
+            m_context->moveToThread(qApp->thread());
+
+            signalSwapDone();
             unlock();
         }
+
+        m_swapTriggered = false;
     }
 
     void lock() { m_mutex.lock(); }
     void unlock() { m_mutex.unlock(); }
 
-    void wait() { m_wait_condition.wait(&m_mutex); }
-    void notify() { m_wait_condition.wakeAll(); }
+    void waitForSwapDone() { if (m_swapTriggered) m_swapDone.wait(&m_mutex); }
+    void waitForReadyToSwap() { if (!m_swapTriggered) m_readyToSwap.wait(&m_mutex); }
+
+    void signalReadyToSwap()
+    {
+        if (!isRunning())
+            return;
+        m_readyToSwap.wakeAll();
+        m_swapTriggered = true;
+    }
+
+    void signalSwapDone()
+    {
+        m_swapTriggered = false;
+        m_swapDone.wakeAll();
+    }
 
 private:
-    QGLWidget *m_widget;
+    QGLContext *m_context;
     QMutex m_mutex;
-    QWaitCondition m_wait_condition;
+    QWaitCondition m_readyToSwap;
+    QWaitCondition m_swapDone;
+
+    bool m_swapTriggered;
 };
 
 class ForegroundWidget : public QGLWidget
@@ -117,6 +143,8 @@ public:
     void paintEvent(QPaintEvent *)
     {
         m_thread->lock();
+        m_thread->waitForSwapDone();
+
         makeCurrent();
         QPainter p(this);
         p.fillRect(rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
@@ -125,7 +153,12 @@ public:
         p.drawText(rect(), Qt::AlignCenter, "This is an autotest");
         p.end();
         doneCurrent();
-        m_thread->notify();
+
+        if (m_thread->isRunning()) {
+            context()->moveToThread(m_thread);
+            m_thread->signalReadyToSwap();
+        }
+
         m_thread->unlock();
 
         update();
@@ -140,6 +173,8 @@ public:
 
 void tst_QGLThreads::swapInThread()
 {
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+        QSKIP("No platformsupport for ThreadedOpenGL");
     QGLFormat format;
     format.setSwapInterval(1);
     ForegroundWidget widget(format);
@@ -176,10 +211,12 @@ class CreateAndUploadThread : public QThread
 {
     Q_OBJECT
 public:
-    CreateAndUploadThread(QGLWidget *shareWidget)
+    CreateAndUploadThread(QGLWidget *shareWidget, QSemaphore *semaphore)
+        : m_semaphore(semaphore)
     {
         m_gl = new QGLWidget(0, shareWidget);
         moveToThread(this);
+        m_gl->context()->moveToThread(this);
     }
 
     ~CreateAndUploadThread()
@@ -203,6 +240,8 @@ public:
             p.end();
             m_gl->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
 
+            m_semaphore->acquire(1);
+
             createdAndUploaded(image);
         }
     }
@@ -212,12 +251,18 @@ signals:
 
 private:
     QGLWidget *m_gl;
+    QSemaphore *m_semaphore;
 };
 
 class TextureDisplay : public QGLWidget
 {
     Q_OBJECT
 public:
+    TextureDisplay(QSemaphore *semaphore)
+        : m_semaphore(semaphore)
+    {
+    }
+
     void paintEvent(QPaintEvent *) {
         QPainter p(this);
         for (int i=0; i<m_images.size(); ++i) {
@@ -232,6 +277,8 @@ public slots:
         m_images << image;
         m_positions << QPoint(-rand() % width() / 2, -rand() % height() / 2);
 
+        m_semaphore->release(1);
+
         if (m_images.size() > 100) {
             m_images.takeFirst();
             m_positions.takeFirst();
@@ -241,12 +288,19 @@ public slots:
 private:
     QList <QImage> m_images;
     QList <QPoint> m_positions;
+
+    QSemaphore *m_semaphore;
 };
 
 void tst_QGLThreads::textureUploadInThread()
 {
-    TextureDisplay display;
-    CreateAndUploadThread thread(&display);
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+        QSKIP("No platformsupport for ThreadedOpenGL");
+
+    // prevent producer thread from queuing up too many images
+    QSemaphore semaphore(100);
+    TextureDisplay display(&semaphore);
+    CreateAndUploadThread thread(&display, &semaphore);
 
     connect(&thread, SIGNAL(createdAndUploaded(QImage)), &display, SLOT(receiveImage(QImage)));
 
@@ -362,10 +416,9 @@ public:
         time.start();
         failure = false;
 
-        m_widget->makeCurrent();
-
         while (time.elapsed() < RUNNING_TIME && !failure) {
 
+            m_widget->makeCurrent();
 
             m_widget->mutex.lock();
             QSize s = m_widget->newSize;
@@ -416,6 +469,8 @@ void tst_QGLThreads::renderInThread_data()
 
 void tst_QGLThreads::renderInThread()
 {
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+        QSKIP("No platformsupport for ThreadedOpenGL");
 
     QFETCH(bool, resize);
     QFETCH(bool, update);
@@ -428,6 +483,8 @@ void tst_QGLThreads::renderInThread()
     QVERIFY(QTest::qWaitForWindowExposed(&widget));
     widget.doneCurrent();
 
+    widget.context()->moveToThread(&thread);
+
     thread.start();
 
     int value = 10;
@@ -451,6 +508,7 @@ public:
     virtual ~Device() {}
     virtual QPaintDevice *realPaintDevice() = 0;
     virtual void prepareDevice() {}
+    virtual void moveToThread(QThread *) {}
 };
 
 class GLWidgetWrapper : public Device
@@ -463,6 +521,7 @@ public:
         widget.doneCurrent();
     }
     QPaintDevice *realPaintDevice() { return &widget; }
+    void moveToThread(QThread *thread) { widget.context()->moveToThread(thread); }
 
     ThreadSafeGLWidget widget;
 };
@@ -483,6 +542,7 @@ public:
     PixelBufferWrapper() { pbuffer = new QGLPixelBuffer(512, 512); }
     ~PixelBufferWrapper() { delete pbuffer; }
     QPaintDevice *realPaintDevice() { return pbuffer; }
+    void moveToThread(QThread *thread) { pbuffer->context()->moveToThread(thread); }
 
     QGLPixelBuffer *pbuffer;
 };
@@ -499,6 +559,7 @@ public:
     ~FrameBufferObjectWrapper() { delete fbo; }
     QPaintDevice *realPaintDevice() { return fbo; }
     void prepareDevice() { widget.makeCurrent(); }
+    void moveToThread(QThread *thread) { widget.context()->moveToThread(thread); }
 
     ThreadSafeGLWidget widget;
     QGLFramebufferObject *fbo;
@@ -545,6 +606,8 @@ public slots:
             QThread::msleep(20);
         }
 
+        device->moveToThread(qApp->thread());
+
         fail = beginFailed;
         QThread::currentThread()->quit();
     }
@@ -569,6 +632,7 @@ public:
             painters.append(new ThreadPainter(devices.at(i)));
             painters.at(i)->moveToThread(threads.at(i));
             painters.at(i)->connect(threads.at(i), SIGNAL(started()), painters.at(i), SLOT(draw()));
+            devices.at(i)->moveToThread(threads.at(i));
         }
     }
 
@@ -621,6 +685,8 @@ private:
 */
 void tst_QGLThreads::painterOnGLWidgetInThread()
 {
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+        QSKIP("No platformsupport for ThreadedOpenGL");
     if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
           (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
         QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");
@@ -642,6 +708,9 @@ void tst_QGLThreads::painterOnGLWidgetInThread()
 */
 void tst_QGLThreads::painterOnPixmapInThread()
 {
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)
+        || !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps))
+        QSKIP("No platformsupport for ThreadedOpenGL or ThreadedPixmaps");
 #ifdef Q_WS_X11
     QSKIP("Drawing text in threads onto X11 drawables currently crashes on some X11 servers.");
 #endif
@@ -660,6 +729,8 @@ void tst_QGLThreads::painterOnPixmapInThread()
 */
 void tst_QGLThreads::painterOnPboInThread()
 {
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+        QSKIP("No platformsupport for ThreadedOpenGL");
     if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
           (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
         QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");
@@ -685,6 +756,8 @@ void tst_QGLThreads::painterOnPboInThread()
 */
 void tst_QGLThreads::painterOnFboInThread()
 {
+    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+        QSKIP("No platformsupport for ThreadedOpenGL");
     if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
           (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
         QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");