summaryrefslogtreecommitdiffstats
path: root/tests/auto/opengl/qglthreads
diff options
context:
space:
mode:
authorJo Asplin <jo.asplin@nokia.com>2011-09-08 15:12:01 +0200
committerJo Asplin <jo.asplin@nokia.com>2011-09-09 10:32:38 +0200
commit0bfa24106d1465dccabcf7ad38c9cffdc2229f29 (patch)
tree8a7d4e516b2c5aa6ca54ee8dc799ea4e17dde479 /tests/auto/opengl/qglthreads
parent0a208dd4135fc16d05f9fd8c417d99e3f39c39fb (diff)
Moved autotests in category 'opengl' into new directory structure
Task-number: QTBUG-21356 Change-Id: I496680aa670af3409e50e1c254ef2c557b414d33 Reviewed-on: http://codereview.qt-project.org/4452 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Rohan McGovern <rohan.mcgovern@nokia.com>
Diffstat (limited to 'tests/auto/opengl/qglthreads')
-rw-r--r--tests/auto/opengl/qglthreads/qglthreads.pro14
-rw-r--r--tests/auto/opengl/qglthreads/tst_qglthreads.cpp758
-rw-r--r--tests/auto/opengl/qglthreads/tst_qglthreads.h65
3 files changed, 837 insertions, 0 deletions
diff --git a/tests/auto/opengl/qglthreads/qglthreads.pro b/tests/auto/opengl/qglthreads/qglthreads.pro
new file mode 100644
index 0000000000..d2fd31411f
--- /dev/null
+++ b/tests/auto/opengl/qglthreads/qglthreads.pro
@@ -0,0 +1,14 @@
+load(qttest_p4)
+requires(contains(QT_CONFIG,opengl))
+QT += opengl widgets
+
+win32:!wince*: DEFINES += QT_NO_EGL
+
+HEADERS += tst_qglthreads.h
+SOURCES += tst_qglthreads.cpp
+
+x11 {
+ LIBS += $$QMAKE_LIBS_X11
+}
+
+CONFIG+=insignificant_test
diff --git a/tests/auto/opengl/qglthreads/tst_qglthreads.cpp b/tests/auto/opengl/qglthreads/tst_qglthreads.cpp
new file mode 100644
index 0000000000..ee05e406c8
--- /dev/null
+++ b/tests/auto/opengl/qglthreads/tst_qglthreads.cpp
@@ -0,0 +1,758 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtCore/QtCore>
+#include <QtGui/QtGui>
+#include <QtWidgets/QApplication>
+#include <QtOpenGL/QtOpenGL>
+#include "tst_qglthreads.h"
+
+#ifdef Q_OS_SYMBIAN
+#define RUNNING_TIME 2000 // save GPU mem by running shorter time.
+#else
+#define RUNNING_TIME 5000
+#endif
+
+
+
+
+tst_QGLThreads::tst_QGLThreads(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*
+
+ swapInThread
+
+ The purpose of this testcase is to verify that it is possible to do rendering into
+ a GL context from the GUI thread, then swap the contents in from a background thread.
+
+ The usecase for this is to have the background thread do the waiting for vertical
+ sync while the GUI thread is idle.
+
+ Currently the locking is handled directly in the paintEvent(). For the actual usecase
+ in Qt, the locking is done in the windowsurface before starting any drawing while
+ unlocking is done after all drawing has been done.
+ */
+
+
+class SwapThread : public QThread
+{
+ Q_OBJECT
+public:
+ SwapThread(QGLWidget *widget)
+ : m_widget(widget)
+ {
+ moveToThread(this);
+ }
+
+ void run() {
+ QTime time;
+ time.start();
+ while (time.elapsed() < RUNNING_TIME) {
+ lock();
+ wait();
+
+ m_widget->makeCurrent();
+ m_widget->swapBuffers();
+ m_widget->doneCurrent();
+ unlock();
+ }
+ }
+
+ void lock() { m_mutex.lock(); }
+ void unlock() { m_mutex.unlock(); }
+
+ void wait() { m_wait_condition.wait(&m_mutex); }
+ void notify() { m_wait_condition.wakeAll(); }
+
+private:
+ QGLWidget *m_widget;
+ QMutex m_mutex;
+ QWaitCondition m_wait_condition;
+};
+
+class ForegroundWidget : public QGLWidget
+{
+public:
+ ForegroundWidget(const QGLFormat &format)
+ : QGLWidget(format), m_thread(0)
+ {
+ setAutoBufferSwap(false);
+ }
+
+ void paintEvent(QPaintEvent *)
+ {
+ m_thread->lock();
+ makeCurrent();
+ QPainter p(this);
+ p.fillRect(rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
+ p.setPen(Qt::red);
+ p.setFont(QFont("SansSerif", 24));
+ p.drawText(rect(), Qt::AlignCenter, "This is an autotest");
+ p.end();
+ doneCurrent();
+ m_thread->notify();
+ m_thread->unlock();
+
+ update();
+ }
+
+ void setThread(SwapThread *thread) {
+ m_thread = thread;
+ }
+
+ SwapThread *m_thread;
+};
+
+void tst_QGLThreads::swapInThread()
+{
+#ifdef Q_OS_MAC
+ QSKIP("OpenGL threading tests are currently disabled on mac as they were causing reboots", SkipAll);
+#endif
+
+ QGLFormat format;
+ format.setSwapInterval(1);
+ ForegroundWidget widget(format);
+ SwapThread thread(&widget);
+ widget.setThread(&thread);
+ widget.show();
+
+ QTest::qWaitForWindowShown(&widget);
+ thread.start();
+
+ while (thread.isRunning()) {
+ qApp->processEvents();
+ }
+
+ widget.hide();
+
+ QVERIFY(true);
+}
+
+
+
+
+
+
+
+/*
+ textureUploadInThread
+
+ The purpose of this testcase is to verify that doing texture uploads in a background
+ thread is possible and that it works.
+ */
+
+class CreateAndUploadThread : public QThread
+{
+ Q_OBJECT
+public:
+ CreateAndUploadThread(QGLWidget *shareWidget)
+ {
+ m_gl = new QGLWidget(0, shareWidget);
+ moveToThread(this);
+ }
+
+ ~CreateAndUploadThread()
+ {
+ delete m_gl;
+ }
+
+ void run() {
+ m_gl->makeCurrent();
+ QTime time;
+ time.start();
+ while (time.elapsed() < RUNNING_TIME) {
+ int width = 400;
+ int height = 300;
+#ifdef Q_OS_SYMBIAN
+ // GPU mem is very scarce resource on Symbian currently.
+ // That's why we create only small textures.
+ width = 50;
+ height = 20;
+#endif
+ QImage image(width, height, QImage::Format_RGB32);
+ QPainter p(&image);
+ p.fillRect(image.rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
+ p.setPen(Qt::red);
+ p.setFont(QFont("SansSerif", 24));
+ p.drawText(image.rect(), Qt::AlignCenter, "This is an autotest");
+ p.end();
+ m_gl->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
+
+ createdAndUploaded(image);
+ }
+ }
+
+signals:
+ void createdAndUploaded(const QImage &image);
+
+private:
+ QGLWidget *m_gl;
+};
+
+class TextureDisplay : public QGLWidget
+{
+ Q_OBJECT
+public:
+ void paintEvent(QPaintEvent *) {
+ QPainter p(this);
+ for (int i=0; i<m_images.size(); ++i) {
+ p.drawImage(m_positions.at(i), m_images.at(i));
+ m_positions[i] += QPoint(1, 1);
+ }
+ update();
+ }
+
+public slots:
+ void receiveImage(const QImage &image) {
+ m_images << image;
+ m_positions << QPoint(-rand() % width() / 2, -rand() % height() / 2);
+
+ if (m_images.size() > 100) {
+ m_images.takeFirst();
+ m_positions.takeFirst();
+ }
+ }
+
+private:
+ QList <QImage> m_images;
+ QList <QPoint> m_positions;
+};
+
+void tst_QGLThreads::textureUploadInThread()
+{
+#ifdef Q_OS_MAC
+ QSKIP("OpenGL threading tests are currently disabled on mac as they were causing reboots", SkipAll);
+#endif
+
+ TextureDisplay display;
+ CreateAndUploadThread thread(&display);
+
+ connect(&thread, SIGNAL(createdAndUploaded(QImage)), &display, SLOT(receiveImage(QImage)));
+
+ display.show();
+ QTest::qWaitForWindowShown(&display);
+
+ thread.start();
+
+ while (thread.isRunning()) {
+ qApp->processEvents();
+ }
+
+ QVERIFY(true);
+}
+
+
+
+
+
+
+/*
+ renderInThread
+
+ This test sets up a scene and renders it in a different thread.
+ For simplicity, the scene is simply a bunch of rectangles, but
+ if that works, we're in good shape..
+ */
+
+static inline float qrandom() { return (rand() % 100) / 100.f; }
+
+void renderAScene(int w, int h)
+{
+#ifdef QT_OPENGL_ES_2
+ QGLShaderProgram program;
+ program.addShaderFromSourceCode(QGLShader::Vertex, "attribute highp vec2 pos; void main() { gl_Position = vec4(pos.xy, 1.0, 1.0); }");
+ program.addShaderFromSourceCode(QGLShader::Fragment, "uniform lowp vec4 color; void main() { gl_FragColor = color; }");
+ program.bindAttributeLocation("pos", 0);
+ program.bind();
+ int colorId = program.uniformLocation("color");
+
+ glEnableVertexAttribArray(0);
+
+ for (int i=0; i<1000; ++i) {
+ GLfloat pos[] = {
+ (rand() % 100) / 100.,
+ (rand() % 100) / 100.,
+ (rand() % 100) / 100.,
+ (rand() % 100) / 100.,
+ (rand() % 100) / 100.,
+ (rand() % 100) / 100.
+ };
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
+ }
+#else
+ glViewport(0, 0, w, h);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustum(0, w, h, 0, 1, 100);
+ glTranslated(0, 0, -1);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ for (int i=0;i<1000; ++i) {
+ glBegin(GL_TRIANGLES);
+ glColor3f(qrandom(), qrandom(), qrandom());
+ glVertex2f(qrandom() * w, qrandom() * h);
+ glColor3f(qrandom(), qrandom(), qrandom());
+ glVertex2f(qrandom() * w, qrandom() * h);
+ glColor3f(qrandom(), qrandom(), qrandom());
+ glVertex2f(qrandom() * w, qrandom() * h);
+ glEnd();
+ }
+#endif
+}
+
+class ThreadSafeGLWidget : public QGLWidget
+{
+public:
+ ThreadSafeGLWidget(QWidget *parent = 0) : QGLWidget(parent) {}
+ void paintEvent(QPaintEvent *)
+ {
+ // ignored as we're anyway swapping as fast as we can
+ };
+
+ void resizeEvent(QResizeEvent *e)
+ {
+ mutex.lock();
+ newSize = e->size();
+ mutex.unlock();
+ };
+
+ QMutex mutex;
+ QSize newSize;
+};
+
+class SceneRenderingThread : public QThread
+{
+ Q_OBJECT
+public:
+ SceneRenderingThread(ThreadSafeGLWidget *widget)
+ : m_widget(widget)
+ {
+ moveToThread(this);
+ m_size = widget->size();
+ }
+
+ void run() {
+ QTime time;
+ time.start();
+ failure = false;
+
+ m_widget->makeCurrent();
+
+ while (time.elapsed() < RUNNING_TIME && !failure) {
+
+
+ m_widget->mutex.lock();
+ QSize s = m_widget->newSize;
+ m_widget->mutex.unlock();
+
+ if (s != m_size) {
+ glViewport(0, 0, s.width(), s.height());
+ }
+
+ if (QGLContext::currentContext() != m_widget->context()) {
+ failure = true;
+ break;
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ int w = m_widget->width();
+ int h = m_widget->height();
+
+ renderAScene(w, h);
+
+ int color;
+ glReadPixels(w / 2, h / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color);
+
+ m_widget->swapBuffers();
+ }
+
+ m_widget->doneCurrent();
+ }
+
+ bool failure;
+
+private:
+ ThreadSafeGLWidget *m_widget;
+ QSize m_size;
+};
+
+void tst_QGLThreads::renderInThread_data()
+{
+ QTest::addColumn<bool>("resize");
+ QTest::addColumn<bool>("update");
+
+ QTest::newRow("basic") << false << false;
+ QTest::newRow("with-resize") << true << false;
+ QTest::newRow("with-update") << false << true;
+ QTest::newRow("with-resize-and-update") << true << true;
+}
+
+void tst_QGLThreads::renderInThread()
+{
+#ifdef Q_OS_MAC
+ QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll);
+#endif
+
+#ifdef Q_OS_SYMBIAN
+ QSKIP("OpenGL threading tests are disabled on Symbian as accessing RWindow from a secondary thread is not supported", SkipAll);
+#endif
+
+ QFETCH(bool, resize);
+ QFETCH(bool, update);
+
+ ThreadSafeGLWidget widget;
+ widget.resize(200, 200);
+ SceneRenderingThread thread(&widget);
+
+ widget.show();
+ QTest::qWaitForWindowShown(&widget);
+ widget.doneCurrent();
+
+ thread.start();
+
+ int value = 10;
+ while (thread.isRunning()) {
+ if (resize)
+ widget.resize(200 + value, 200 + value);
+ if (update)
+ widget.update(100 + value, 100 + value, 20, 20);
+ qApp->processEvents();
+ value = -value;
+
+ QThread::msleep(100);
+ }
+
+ QVERIFY(!thread.failure);
+}
+
+class Device
+{
+public:
+ virtual ~Device() {}
+ virtual QPaintDevice *realPaintDevice() = 0;
+ virtual void prepareDevice() {}
+};
+
+class GLWidgetWrapper : public Device
+{
+public:
+ GLWidgetWrapper() {
+ widget.resize(150, 150);
+ widget.show();
+ QTest::qWaitForWindowShown(&widget);
+ widget.doneCurrent();
+ }
+ QPaintDevice *realPaintDevice() { return &widget; }
+
+ ThreadSafeGLWidget widget;
+};
+
+class PixmapWrapper : public Device
+{
+public:
+ PixmapWrapper() { pixmap = new QPixmap(512, 512); }
+ ~PixmapWrapper() { delete pixmap; }
+ QPaintDevice *realPaintDevice() { return pixmap; }
+
+ QPixmap *pixmap;
+};
+
+class PixelBufferWrapper : public Device
+{
+public:
+ PixelBufferWrapper() { pbuffer = new QGLPixelBuffer(512, 512); }
+ ~PixelBufferWrapper() { delete pbuffer; }
+ QPaintDevice *realPaintDevice() { return pbuffer; }
+
+ QGLPixelBuffer *pbuffer;
+};
+
+
+class FrameBufferObjectWrapper : public Device
+{
+public:
+ FrameBufferObjectWrapper() {
+ widget.makeCurrent();
+ fbo = new QGLFramebufferObject(512, 512);
+ widget.doneCurrent();
+ }
+ ~FrameBufferObjectWrapper() { delete fbo; }
+ QPaintDevice *realPaintDevice() { return fbo; }
+ void prepareDevice() { widget.makeCurrent(); }
+
+ ThreadSafeGLWidget widget;
+ QGLFramebufferObject *fbo;
+};
+
+
+class ThreadPainter : public QObject
+{
+ Q_OBJECT
+public:
+ ThreadPainter(Device *pd) : device(pd), fail(true) {
+ pixmap = QPixmap(40, 40);
+ pixmap.fill(Qt::green);
+ QPainter p(&pixmap);
+ p.drawLine(0, 0, 40, 40);
+ p.drawLine(0, 40, 40, 0);
+ }
+
+public slots:
+ void draw() {
+ bool beginFailed = false;
+ QTime time;
+ time.start();
+ int rotAngle = 10;
+ device->prepareDevice();
+ QPaintDevice *paintDevice = device->realPaintDevice();
+ QSize s(paintDevice->width(), paintDevice->height());
+ while (time.elapsed() < RUNNING_TIME) {
+ QPainter p;
+ if (!p.begin(paintDevice)) {
+ beginFailed = true;
+ break;
+ }
+ p.translate(s.width()/2, s.height()/2);
+ p.rotate(rotAngle);
+ p.translate(-s.width()/2, -s.height()/2);
+ p.fillRect(0, 0, s.width(), s.height(), Qt::red);
+ QRect rect(QPoint(0, 0), s);
+ p.drawPixmap(10, 10, pixmap);
+ p.drawTiledPixmap(50, 50, 100, 100, pixmap);
+ p.drawText(rect.center(), "This is a piece of text");
+ p.end();
+ rotAngle += 2;
+ QThread::msleep(20);
+ }
+
+ fail = beginFailed;
+ QThread::currentThread()->quit();
+ }
+
+ bool failed() { return fail; }
+
+private:
+ QPixmap pixmap;
+ Device *device;
+ bool fail;
+};
+
+template <class T>
+class PaintThreadManager
+{
+public:
+ PaintThreadManager(int count) : numThreads(count)
+ {
+ for (int i=0; i<numThreads; ++i) {
+ devices.append(new T);
+ threads.append(new QThread);
+ 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()));
+ }
+ }
+
+ ~PaintThreadManager() {
+ qDeleteAll(threads);
+ qDeleteAll(painters);
+ qDeleteAll(devices);
+ }
+
+
+ void start() {
+ for (int i=0; i<numThreads; ++i)
+ threads.at(i)->start();
+ }
+
+ bool areRunning() {
+ bool running = false;
+ for (int i=0; i<numThreads; ++i){
+ if (threads.at(i)->isRunning())
+ running = true;
+ }
+
+ return running;
+ }
+
+ bool failed() {
+ for (int i=0; i<numThreads; ++i) {
+ if (painters.at(i)->failed())
+ return true;
+ }
+
+ return false;
+ }
+
+private:
+ QList<QThread *> threads;
+ QList<Device *> devices;
+ QList<ThreadPainter *> painters;
+ int numThreads;
+};
+
+/*
+ This test uses QPainter to draw onto different QGLWidgets in
+ different threads at the same time. The ThreadSafeGLWidget is
+ necessary to handle paint and resize events that might come from
+ the main thread at any time while the test is running. The resize
+ and paint events would cause makeCurrent() calls to be issued from
+ within the QGLWidget while the widget's context was current in
+ another thread, which would cause errors.
+*/
+void tst_QGLThreads::painterOnGLWidgetInThread()
+{
+#ifdef Q_OS_MAC
+ QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll);
+#endif
+ 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.", SkipAll);
+ }
+
+ PaintThreadManager<GLWidgetWrapper> painterThreads(5);
+ painterThreads.start();
+
+ while (painterThreads.areRunning()) {
+ qApp->processEvents();
+ QThread::msleep(100);
+ }
+ QVERIFY(!painterThreads.failed());
+}
+
+/*
+ This test uses QPainter to draw onto different QPixmaps in
+ different threads at the same time.
+*/
+void tst_QGLThreads::painterOnPixmapInThread()
+{
+#ifdef Q_WS_X11
+ QSKIP("Drawing text in threads onto X11 drawables currently crashes on some X11 servers.", SkipAll);
+#endif
+ PaintThreadManager<PixmapWrapper> painterThreads(5);
+ painterThreads.start();
+
+ while (painterThreads.areRunning()) {
+ qApp->processEvents();
+ QThread::msleep(100);
+ }
+ QVERIFY(!painterThreads.failed());
+}
+
+/* This test uses QPainter to draw onto different QGLPixelBuffer
+ objects in different threads at the same time.
+*/
+void tst_QGLThreads::painterOnPboInThread()
+{
+#ifdef Q_OS_MAC
+ QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll);
+#endif
+ 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.", SkipAll);
+ return;
+ }
+
+ if (!QGLPixelBuffer::hasOpenGLPbuffers()) {
+ QSKIP("This system doesn't support pbuffers.", SkipAll);
+ return;
+ }
+
+ PaintThreadManager<PixelBufferWrapper> painterThreads(5);
+ painterThreads.start();
+
+ while (painterThreads.areRunning()) {
+ qApp->processEvents();
+ QThread::msleep(100);
+ }
+ QVERIFY(!painterThreads.failed());
+}
+
+/* This test uses QPainter to draw onto different
+ QGLFramebufferObjects (bound in a QGLWidget's context) in different
+ threads at the same time.
+*/
+void tst_QGLThreads::painterOnFboInThread()
+{
+#ifdef Q_OS_MAC
+ QSKIP("OpenGL threading tests are currently disabled on Mac as they were causing reboots", SkipAll);
+#endif
+ 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.", SkipAll);
+ return;
+ }
+
+ if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) {
+ QSKIP("This system doesn't support framebuffer objects.", SkipAll);
+ return;
+ }
+
+ PaintThreadManager<FrameBufferObjectWrapper> painterThreads(5);
+ painterThreads.start();
+
+ while (painterThreads.areRunning()) {
+ qApp->processEvents();
+ QThread::msleep(100);
+ }
+ QVERIFY(!painterThreads.failed());
+}
+
+int main(int argc, char **argv)
+{
+ QApplication::setAttribute(Qt::AA_X11InitThreads);
+ QApplication app(argc, argv);
+ QTEST_DISABLE_KEYPAD_NAVIGATION \
+
+ tst_QGLThreads tc;
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_qglthreads.moc"
diff --git a/tests/auto/opengl/qglthreads/tst_qglthreads.h b/tests/auto/opengl/qglthreads/tst_qglthreads.h
new file mode 100644
index 0000000000..7972638085
--- /dev/null
+++ b/tests/auto/opengl/qglthreads/tst_qglthreads.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TST_QGLTHREADS_H
+#define TST_QGLTHREADS_H
+
+#include <QObject>
+
+class tst_QGLThreads : public QObject
+{
+Q_OBJECT
+public:
+ explicit tst_QGLThreads(QObject *parent = 0);
+
+private slots:
+ void swapInThread();
+ void textureUploadInThread();
+
+ void renderInThread_data();
+ void renderInThread();
+ void painterOnGLWidgetInThread();
+ void painterOnPixmapInThread();
+ void painterOnPboInThread();
+ void painterOnFboInThread();
+};
+
+#endif // TST_QGLTHREADS_H