tests/auto/qglthreads/tst_qglthreads.cpp
changeset 30 5dc02b23752f
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the test suite of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QtTest/QtTest>
       
    43 #include <QtCore/QtCore>
       
    44 #include <QtGui/QtGui>
       
    45 #include <QtOpenGL/QtOpenGL>
       
    46 #include "tst_qglthreads.h"
       
    47 
       
    48 #ifdef Q_WS_X11
       
    49 #include <private/qt_x11_p.h>
       
    50 #endif
       
    51 
       
    52 #define RUNNING_TIME 5000
       
    53 
       
    54 tst_QGLThreads::tst_QGLThreads(QObject *parent)
       
    55     : QObject(parent)
       
    56 {
       
    57 }
       
    58 
       
    59 
       
    60 
       
    61 /*
       
    62 
       
    63    swapInThread
       
    64 
       
    65    The purpose of this testcase is to verify that it is possible to do rendering into
       
    66    a GL context from the GUI thread, then swap the contents in from a background thread.
       
    67 
       
    68    The usecase for this is to have the background thread do the waiting for vertical
       
    69    sync while the GUI thread is idle.
       
    70 
       
    71    Currently the locking is handled directly in the paintEvent(). For the actual usecase
       
    72    in Qt, the locking is done in the windowsurface before starting any drawing while
       
    73    unlocking is done after all drawing has been done.
       
    74  */
       
    75 
       
    76 
       
    77 class SwapThread : public QThread
       
    78 {
       
    79     Q_OBJECT
       
    80 public:
       
    81     SwapThread(QGLWidget *widget)
       
    82         : m_widget(widget)
       
    83     {
       
    84         moveToThread(this);
       
    85     }
       
    86 
       
    87     void run() {
       
    88         QTime time;
       
    89         time.start();
       
    90         while (time.elapsed() < RUNNING_TIME) {
       
    91             lock();
       
    92             wait();
       
    93 
       
    94             m_widget->makeCurrent();
       
    95             m_widget->swapBuffers();
       
    96             m_widget->doneCurrent();
       
    97             unlock();
       
    98         }
       
    99     }
       
   100 
       
   101     void lock() { m_mutex.lock(); }
       
   102     void unlock() { m_mutex.unlock(); }
       
   103 
       
   104     void wait() { m_wait_condition.wait(&m_mutex); }
       
   105     void notify() { m_wait_condition.wakeAll(); }
       
   106 
       
   107 private:
       
   108     QGLWidget *m_widget;
       
   109     QMutex m_mutex;
       
   110     QWaitCondition m_wait_condition;
       
   111 };
       
   112 
       
   113 class ForegroundWidget : public QGLWidget
       
   114 {
       
   115 public:
       
   116     ForegroundWidget(const QGLFormat &format)
       
   117         : QGLWidget(format), m_thread(0)
       
   118     {
       
   119         setAutoBufferSwap(false);
       
   120     }
       
   121 
       
   122     void paintEvent(QPaintEvent *)
       
   123     {
       
   124         m_thread->lock();
       
   125         makeCurrent();
       
   126         QPainter p(this);
       
   127         p.fillRect(rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
       
   128         p.setPen(Qt::red);
       
   129         p.setFont(QFont("SansSerif", 24));
       
   130         p.drawText(rect(), Qt::AlignCenter, "This is an autotest");
       
   131         p.end();
       
   132         doneCurrent();
       
   133         m_thread->notify();
       
   134         m_thread->unlock();
       
   135 
       
   136         update();
       
   137     }
       
   138 
       
   139     void setThread(SwapThread *thread) {
       
   140         m_thread = thread;
       
   141     }
       
   142 
       
   143     SwapThread *m_thread;
       
   144 };
       
   145 
       
   146 void tst_QGLThreads::swapInThread()
       
   147 {
       
   148 #ifdef Q_OS_MAC
       
   149     QSKIP("OpenGL threading tests are currently disabled on mac as they were causing reboots", SkipAll);
       
   150 #endif
       
   151 
       
   152     QGLFormat format;
       
   153     format.setSwapInterval(1);
       
   154     ForegroundWidget widget(format);
       
   155     SwapThread thread(&widget);
       
   156     widget.setThread(&thread);
       
   157     widget.show();
       
   158 
       
   159     QTest::qWaitForWindowShown(&widget);
       
   160     thread.start();
       
   161 
       
   162     while (thread.isRunning()) {
       
   163         qApp->processEvents();
       
   164     }
       
   165 
       
   166     widget.hide();
       
   167 
       
   168     QVERIFY(true);
       
   169 }
       
   170 
       
   171 
       
   172 
       
   173 
       
   174 
       
   175 
       
   176 
       
   177 /*
       
   178    textureUploadInThread
       
   179 
       
   180    The purpose of this testcase is to verify that doing texture uploads in a background
       
   181    thread is possible and that it works.
       
   182  */
       
   183 
       
   184 class CreateAndUploadThread : public QThread
       
   185 {
       
   186     Q_OBJECT
       
   187 public:
       
   188     CreateAndUploadThread(QGLWidget *shareWidget)
       
   189     {
       
   190         m_gl = new QGLWidget(0, shareWidget);
       
   191         moveToThread(this);
       
   192     }
       
   193 
       
   194     ~CreateAndUploadThread()
       
   195     {
       
   196         delete m_gl;
       
   197     }
       
   198 
       
   199     void run() {
       
   200         m_gl->makeCurrent();
       
   201         QTime time;
       
   202         time.start();
       
   203         while (time.elapsed() < RUNNING_TIME) {
       
   204             QImage image(400, 300, QImage::Format_RGB32);
       
   205             QPainter p(&image);
       
   206             p.fillRect(image.rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
       
   207             p.setPen(Qt::red);
       
   208             p.setFont(QFont("SansSerif", 24));
       
   209             p.drawText(image.rect(), Qt::AlignCenter, "This is an autotest");
       
   210             p.end();
       
   211             m_gl->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
       
   212             createdAndUploaded(image);
       
   213         }
       
   214     }
       
   215 
       
   216 signals:
       
   217     void createdAndUploaded(const QImage &image);
       
   218 
       
   219 private:
       
   220     QGLWidget *m_gl;
       
   221 };
       
   222 
       
   223 class TextureDisplay : public QGLWidget
       
   224 {
       
   225     Q_OBJECT
       
   226 public:
       
   227     void paintEvent(QPaintEvent *) {
       
   228         QPainter p(this);
       
   229         for (int i=0; i<m_images.size(); ++i) {
       
   230             p.drawImage(m_positions.at(i), m_images.at(i));
       
   231             m_positions[i] += QPoint(1, 1);
       
   232         }
       
   233         update();
       
   234     }
       
   235 
       
   236 public slots:
       
   237     void receiveImage(const QImage &image) {
       
   238         m_images << image;
       
   239         m_positions << QPoint(-rand() % width() / 2, -rand() % height() / 2);
       
   240 
       
   241         if (m_images.size() > 100) {
       
   242             m_images.takeFirst();
       
   243             m_positions.takeFirst();
       
   244         }
       
   245     }
       
   246 
       
   247 private:
       
   248     QList <QImage> m_images;
       
   249     QList <QPoint> m_positions;
       
   250 };
       
   251 
       
   252 void tst_QGLThreads::textureUploadInThread()
       
   253 {
       
   254 #ifdef Q_OS_MAC
       
   255     QSKIP("OpenGL threading tests are currently disabled on mac as they were causing reboots", SkipAll);
       
   256 #endif
       
   257 
       
   258     TextureDisplay display;
       
   259     CreateAndUploadThread thread(&display);
       
   260 
       
   261     connect(&thread, SIGNAL(createdAndUploaded(QImage)), &display, SLOT(receiveImage(QImage)));
       
   262 
       
   263     display.show();
       
   264     QTest::qWaitForWindowShown(&display);
       
   265 
       
   266     thread.start();
       
   267 
       
   268     while (thread.isRunning()) {
       
   269         qApp->processEvents();
       
   270     }
       
   271 
       
   272     QVERIFY(true);
       
   273 }
       
   274 
       
   275 
       
   276 
       
   277 
       
   278 
       
   279 
       
   280 /*
       
   281    renderInThread
       
   282 
       
   283    This test sets up a scene and renders it in a different thread.
       
   284    For simplicity, the scene is simply a bunch of rectangles, but
       
   285    if that works, we're in good shape..
       
   286  */
       
   287 
       
   288 static inline float qrandom() { return (rand() % 100) / 100.f; }
       
   289 
       
   290 void renderAScene(int w, int h)
       
   291 {
       
   292 #ifdef QT_OPENGL_ES_2
       
   293             QGLShaderProgram program;
       
   294             program.addShaderFromSourceCode(QGLShader::Vertex, "attribute highp vec2 pos; void main() { gl_Position = vec4(pos.xy, 1.0, 1.0); }");
       
   295             program.addShaderFromSourceCode(QGLShader::Fragment, "uniform lowp vec4 color; void main() { gl_FragColor = color; }");
       
   296             program.bindAttributeLocation("pos", 0);
       
   297             program.bind();
       
   298             int colorId = program.uniformLocation("color");
       
   299 
       
   300             glEnableVertexAttribArray(0);
       
   301 
       
   302             for (int i=0; i<1000; ++i) {
       
   303                 GLfloat pos[] = {
       
   304                     (rand() % 100) / 100.,
       
   305                     (rand() % 100) / 100.,
       
   306                     (rand() % 100) / 100.,
       
   307                     (rand() % 100) / 100.,
       
   308                     (rand() % 100) / 100.,
       
   309                     (rand() % 100) / 100.
       
   310                 };
       
   311 
       
   312                 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos);
       
   313                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
       
   314             }
       
   315 #else
       
   316             glViewport(0, 0, w, h);
       
   317 
       
   318             glMatrixMode(GL_PROJECTION);
       
   319             glLoadIdentity();
       
   320             glFrustum(0, w, h, 0, 1, 100);
       
   321             glTranslated(0, 0, -1);
       
   322 
       
   323             glMatrixMode(GL_MODELVIEW);
       
   324             glLoadIdentity();
       
   325 
       
   326             for (int i=0;i<1000; ++i) {
       
   327                 glBegin(GL_TRIANGLES);
       
   328                 glColor3f(qrandom(), qrandom(), qrandom());
       
   329                 glVertex2f(qrandom() * w, qrandom() * h);
       
   330                 glColor3f(qrandom(), qrandom(), qrandom());
       
   331                 glVertex2f(qrandom() * w, qrandom() * h);
       
   332                 glColor3f(qrandom(), qrandom(), qrandom());
       
   333                 glVertex2f(qrandom() * w, qrandom() * h);
       
   334                 glEnd();
       
   335             }
       
   336 #endif
       
   337 }
       
   338 
       
   339 class ThreadSafeGLWidget : public QGLWidget
       
   340 {
       
   341 public:
       
   342     void paintEvent(QPaintEvent *)
       
   343     {
       
   344         // ignored as we're anyway swapping as fast as we can
       
   345     };
       
   346 
       
   347     void resizeEvent(QResizeEvent *e)
       
   348     {
       
   349         mutex.lock();
       
   350         newSize = e->size();
       
   351         mutex.unlock();
       
   352     };
       
   353 
       
   354     QMutex mutex;
       
   355     QSize newSize;
       
   356 };
       
   357 
       
   358 class SceneRenderingThread : public QThread
       
   359 {
       
   360     Q_OBJECT
       
   361 public:
       
   362     SceneRenderingThread(ThreadSafeGLWidget *widget)
       
   363         : m_widget(widget)
       
   364     {
       
   365         moveToThread(this);
       
   366         m_size = widget->size();
       
   367     }
       
   368 
       
   369     void run() {
       
   370         QTime time;
       
   371         time.start();
       
   372         failure = false;
       
   373 
       
   374         m_widget->makeCurrent();
       
   375 
       
   376         while (time.elapsed() < RUNNING_TIME && !failure) {
       
   377 
       
   378 
       
   379             m_widget->mutex.lock();
       
   380             QSize s = m_widget->newSize;
       
   381             m_widget->mutex.unlock();
       
   382 
       
   383             if (s != m_size) {
       
   384                 glViewport(0, 0, s.width(), s.height());
       
   385             }
       
   386 
       
   387             if (QGLContext::currentContext() != m_widget->context()) {
       
   388                 failure = true;
       
   389                 break;
       
   390             }
       
   391 
       
   392             glClear(GL_COLOR_BUFFER_BIT);
       
   393 
       
   394             int w = m_widget->width();
       
   395             int h = m_widget->height();
       
   396 
       
   397             renderAScene(w, h);
       
   398 
       
   399             int color;
       
   400             glReadPixels(w / 2, h / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color);
       
   401 
       
   402             m_widget->swapBuffers();
       
   403         }
       
   404 
       
   405         m_widget->doneCurrent();
       
   406     }
       
   407 
       
   408     bool failure;
       
   409 
       
   410 private:
       
   411     ThreadSafeGLWidget *m_widget;
       
   412     QSize m_size;
       
   413 };
       
   414 
       
   415 void tst_QGLThreads::renderInThread_data()
       
   416 {
       
   417     QTest::addColumn<bool>("resize");
       
   418     QTest::addColumn<bool>("update");
       
   419 
       
   420     QTest::newRow("basic") << false << false;
       
   421     QTest::newRow("with-resize") << true << false;
       
   422     QTest::newRow("with-update") << false << true;
       
   423     QTest::newRow("with-resize-and-update") << true << true;
       
   424 }
       
   425 
       
   426 void tst_QGLThreads::renderInThread()
       
   427 {
       
   428 #ifdef Q_OS_MAC
       
   429     QSKIP("OpenGL threading tests are currently disabled on mac as they were causing reboots", SkipAll);
       
   430 #endif
       
   431 
       
   432     QFETCH(bool, resize);
       
   433     QFETCH(bool, update);
       
   434 
       
   435     ThreadSafeGLWidget widget;
       
   436     widget.resize(200, 200);
       
   437     SceneRenderingThread thread(&widget);
       
   438 
       
   439     widget.show();
       
   440     QTest::qWaitForWindowShown(&widget);
       
   441     widget.doneCurrent();
       
   442 
       
   443     thread.start();
       
   444 
       
   445     int value = 10;
       
   446     while (thread.isRunning()) {
       
   447         if (resize)
       
   448             widget.resize(200 + value, 200 + value);
       
   449         if (update)
       
   450             widget.update(100 + value, 100 + value, 20, 20);
       
   451         qApp->processEvents();
       
   452         value = -value;
       
   453 
       
   454 #ifdef Q_WS_WIN
       
   455         Sleep(100);
       
   456 #else
       
   457         usleep(100 * 1000);
       
   458 #endif
       
   459     }
       
   460 
       
   461     QVERIFY(!thread.failure);
       
   462 }
       
   463 
       
   464 
       
   465 
       
   466 
       
   467 int main(int argc, char **argv)
       
   468 {
       
   469 #ifdef Q_WS_X11
       
   470     XInitThreads();
       
   471 #endif
       
   472 
       
   473     QApplication app(argc, argv);
       
   474     QTEST_DISABLE_KEYPAD_NAVIGATION \
       
   475 
       
   476     tst_QGLThreads tc;
       
   477     return QTest::qExec(&tc, argc, argv);
       
   478 }
       
   479 
       
   480 #include "tst_qglthreads.moc"