~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to tests/auto/opengl/qglthreads/tst_qglthreads.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the test suite of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 2.1 as published by the Free Software
 
20
** Foundation and appearing in the file LICENSE.LGPL included in the
 
21
** packaging of this file.  Please review the following information to
 
22
** ensure the GNU Lesser General Public License version 2.1 requirements
 
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
24
**
 
25
** In addition, as a special exception, Digia gives you certain additional
 
26
** rights.  These rights are described in the Digia Qt LGPL Exception
 
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
28
**
 
29
** GNU General Public License Usage
 
30
** Alternatively, this file may be used under the terms of the GNU
 
31
** General Public License version 3.0 as published by the Free Software
 
32
** Foundation and appearing in the file LICENSE.GPL included in the
 
33
** packaging of this file.  Please review the following information to
 
34
** ensure the GNU General Public License version 3.0 requirements will be
 
35
** met: http://www.gnu.org/copyleft/gpl.html.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include <QtTest/QtTest>
 
43
#include <QtCore/QtCore>
 
44
#include <QtGui/QtGui>
 
45
#include <private/qguiapplication_p.h>
 
46
#include <qpa/qplatformintegration.h>
 
47
#include <QtWidgets/QApplication>
 
48
#include <QtOpenGL/QtOpenGL>
 
49
#include "tst_qglthreads.h"
 
50
 
 
51
#define RUNNING_TIME 5000
 
52
 
 
53
tst_QGLThreads::tst_QGLThreads(QObject *parent)
 
54
    : QObject(parent)
 
55
{
 
56
}
 
57
 
 
58
/*
 
59
 
 
60
   swapInThread
 
61
 
 
62
   The purpose of this testcase is to verify that it is possible to do rendering into
 
63
   a GL context from the GUI thread, then swap the contents in from a background thread.
 
64
 
 
65
   The usecase for this is to have the background thread do the waiting for vertical
 
66
   sync while the GUI thread is idle.
 
67
 
 
68
   Currently the locking is handled directly in the paintEvent(). For the actual usecase
 
69
   in Qt, the locking is done in the windowsurface before starting any drawing while
 
70
   unlocking is done after all drawing has been done.
 
71
 */
 
72
 
 
73
 
 
74
class SwapThread : public QThread
 
75
{
 
76
    Q_OBJECT
 
77
public:
 
78
    SwapThread(QGLWidget *widget)
 
79
        : m_context(widget->context())
 
80
        , m_swapTriggered(false)
 
81
    {
 
82
        moveToThread(this);
 
83
    }
 
84
 
 
85
    void run() {
 
86
        QTime time;
 
87
        time.start();
 
88
        while (time.elapsed() < RUNNING_TIME) {
 
89
            lock();
 
90
            waitForReadyToSwap();
 
91
 
 
92
            m_context->makeCurrent();
 
93
            m_context->swapBuffers();
 
94
            m_context->doneCurrent();
 
95
 
 
96
            m_context->moveToThread(qApp->thread());
 
97
 
 
98
            signalSwapDone();
 
99
            unlock();
 
100
        }
 
101
 
 
102
        m_swapTriggered = false;
 
103
    }
 
104
 
 
105
    void lock() { m_mutex.lock(); }
 
106
    void unlock() { m_mutex.unlock(); }
 
107
 
 
108
    void waitForSwapDone() { if (m_swapTriggered) m_swapDone.wait(&m_mutex); }
 
109
    void waitForReadyToSwap() { if (!m_swapTriggered) m_readyToSwap.wait(&m_mutex); }
 
110
 
 
111
    void signalReadyToSwap()
 
112
    {
 
113
        if (!isRunning())
 
114
            return;
 
115
        m_readyToSwap.wakeAll();
 
116
        m_swapTriggered = true;
 
117
    }
 
118
 
 
119
    void signalSwapDone()
 
120
    {
 
121
        m_swapTriggered = false;
 
122
        m_swapDone.wakeAll();
 
123
    }
 
124
 
 
125
private:
 
126
    QGLContext *m_context;
 
127
    QMutex m_mutex;
 
128
    QWaitCondition m_readyToSwap;
 
129
    QWaitCondition m_swapDone;
 
130
 
 
131
    bool m_swapTriggered;
 
132
};
 
133
 
 
134
class ForegroundWidget : public QGLWidget
 
135
{
 
136
public:
 
137
    ForegroundWidget(const QGLFormat &format)
 
138
        : QGLWidget(format), m_thread(0)
 
139
    {
 
140
        setAutoBufferSwap(false);
 
141
    }
 
142
 
 
143
    void paintEvent(QPaintEvent *)
 
144
    {
 
145
        m_thread->lock();
 
146
        m_thread->waitForSwapDone();
 
147
 
 
148
        makeCurrent();
 
149
        QPainter p(this);
 
150
        p.fillRect(rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
 
151
        p.setPen(Qt::red);
 
152
        p.setFont(QFont("SansSerif", 24));
 
153
        p.drawText(rect(), Qt::AlignCenter, "This is an autotest");
 
154
        p.end();
 
155
        doneCurrent();
 
156
 
 
157
        if (m_thread->isRunning()) {
 
158
            context()->moveToThread(m_thread);
 
159
            m_thread->signalReadyToSwap();
 
160
        }
 
161
 
 
162
        m_thread->unlock();
 
163
 
 
164
        update();
 
165
    }
 
166
 
 
167
    void setThread(SwapThread *thread) {
 
168
        m_thread = thread;
 
169
    }
 
170
 
 
171
    SwapThread *m_thread;
 
172
};
 
173
 
 
174
void tst_QGLThreads::swapInThread()
 
175
{
 
176
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
 
177
        QSKIP("No platformsupport for ThreadedOpenGL");
 
178
    QGLFormat format;
 
179
    format.setSwapInterval(1);
 
180
    ForegroundWidget widget(format);
 
181
    SwapThread thread(&widget);
 
182
    widget.setThread(&thread);
 
183
    widget.show();
 
184
 
 
185
    QVERIFY(QTest::qWaitForWindowExposed(&widget));
 
186
    thread.start();
 
187
 
 
188
    while (thread.isRunning()) {
 
189
        qApp->processEvents();
 
190
    }
 
191
 
 
192
    widget.hide();
 
193
 
 
194
    QVERIFY(true);
 
195
}
 
196
 
 
197
 
 
198
 
 
199
 
 
200
 
 
201
 
 
202
 
 
203
/*
 
204
   textureUploadInThread
 
205
 
 
206
   The purpose of this testcase is to verify that doing texture uploads in a background
 
207
   thread is possible and that it works.
 
208
 */
 
209
 
 
210
class CreateAndUploadThread : public QThread
 
211
{
 
212
    Q_OBJECT
 
213
public:
 
214
    CreateAndUploadThread(QGLWidget *shareWidget, QSemaphore *semaphore)
 
215
        : m_semaphore(semaphore)
 
216
    {
 
217
        m_gl = new QGLWidget(0, shareWidget);
 
218
        moveToThread(this);
 
219
        m_gl->context()->moveToThread(this);
 
220
    }
 
221
 
 
222
    ~CreateAndUploadThread()
 
223
    {
 
224
        delete m_gl;
 
225
    }
 
226
 
 
227
    void run() {
 
228
        m_gl->makeCurrent();
 
229
        QTime time;
 
230
        time.start();
 
231
        while (time.elapsed() < RUNNING_TIME) {
 
232
            int width = 400;
 
233
            int height = 300;
 
234
            QImage image(width, height, QImage::Format_RGB32);
 
235
            QPainter p(&image);
 
236
            p.fillRect(image.rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
 
237
            p.setPen(Qt::red);
 
238
            p.setFont(QFont("SansSerif", 24));
 
239
            p.drawText(image.rect(), Qt::AlignCenter, "This is an autotest");
 
240
            p.end();
 
241
            m_gl->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
 
242
 
 
243
            m_semaphore->acquire(1);
 
244
 
 
245
            createdAndUploaded(image);
 
246
        }
 
247
    }
 
248
 
 
249
signals:
 
250
    void createdAndUploaded(const QImage &image);
 
251
 
 
252
private:
 
253
    QGLWidget *m_gl;
 
254
    QSemaphore *m_semaphore;
 
255
};
 
256
 
 
257
class TextureDisplay : public QGLWidget
 
258
{
 
259
    Q_OBJECT
 
260
public:
 
261
    TextureDisplay(QSemaphore *semaphore)
 
262
        : m_semaphore(semaphore)
 
263
    {
 
264
    }
 
265
 
 
266
    void paintEvent(QPaintEvent *) {
 
267
        QPainter p(this);
 
268
        for (int i=0; i<m_images.size(); ++i) {
 
269
            p.drawImage(m_positions.at(i), m_images.at(i));
 
270
            m_positions[i] += QPoint(1, 1);
 
271
        }
 
272
        update();
 
273
    }
 
274
 
 
275
public slots:
 
276
    void receiveImage(const QImage &image) {
 
277
        m_images << image;
 
278
        m_positions << QPoint(-rand() % width() / 2, -rand() % height() / 2);
 
279
 
 
280
        m_semaphore->release(1);
 
281
 
 
282
        if (m_images.size() > 100) {
 
283
            m_images.takeFirst();
 
284
            m_positions.takeFirst();
 
285
        }
 
286
    }
 
287
 
 
288
private:
 
289
    QList <QImage> m_images;
 
290
    QList <QPoint> m_positions;
 
291
 
 
292
    QSemaphore *m_semaphore;
 
293
};
 
294
 
 
295
void tst_QGLThreads::textureUploadInThread()
 
296
{
 
297
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
 
298
        QSKIP("No platformsupport for ThreadedOpenGL");
 
299
 
 
300
    // prevent producer thread from queuing up too many images
 
301
    QSemaphore semaphore(100);
 
302
    TextureDisplay display(&semaphore);
 
303
    CreateAndUploadThread thread(&display, &semaphore);
 
304
 
 
305
    connect(&thread, SIGNAL(createdAndUploaded(QImage)), &display, SLOT(receiveImage(QImage)));
 
306
 
 
307
    display.show();
 
308
    QVERIFY(QTest::qWaitForWindowActive(&display));
 
309
 
 
310
    thread.start();
 
311
 
 
312
    while (thread.isRunning()) {
 
313
        qApp->processEvents();
 
314
    }
 
315
 
 
316
    QVERIFY(true);
 
317
}
 
318
 
 
319
 
 
320
 
 
321
 
 
322
 
 
323
 
 
324
/*
 
325
   renderInThread
 
326
 
 
327
   This test sets up a scene and renders it in a different thread.
 
328
   For simplicity, the scene is simply a bunch of rectangles, but
 
329
   if that works, we're in good shape..
 
330
 */
 
331
 
 
332
static inline float qrandom() { return (rand() % 100) / 100.f; }
 
333
 
 
334
void renderAScene(int w, int h)
 
335
{
 
336
#ifdef QT_OPENGL_ES_2
 
337
            QGLShaderProgram program;
 
338
            program.addShaderFromSourceCode(QGLShader::Vertex, "attribute highp vec2 pos; void main() { gl_Position = vec4(pos.xy, 1.0, 1.0); }");
 
339
            program.addShaderFromSourceCode(QGLShader::Fragment, "uniform lowp vec4 color; void main() { gl_FragColor = color; }");
 
340
            program.bindAttributeLocation("pos", 0);
 
341
            program.bind();
 
342
            int colorId = program.uniformLocation("color");
 
343
 
 
344
            glEnableVertexAttribArray(0);
 
345
 
 
346
            for (int i=0; i<1000; ++i) {
 
347
                GLfloat pos[] = {
 
348
                    (rand() % 100) / 100.,
 
349
                    (rand() % 100) / 100.,
 
350
                    (rand() % 100) / 100.,
 
351
                    (rand() % 100) / 100.,
 
352
                    (rand() % 100) / 100.,
 
353
                    (rand() % 100) / 100.
 
354
                };
 
355
 
 
356
                glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos);
 
357
                glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
 
358
            }
 
359
#else
 
360
            glViewport(0, 0, w, h);
 
361
 
 
362
            glMatrixMode(GL_PROJECTION);
 
363
            glLoadIdentity();
 
364
            glFrustum(0, w, h, 0, 1, 100);
 
365
            glTranslated(0, 0, -1);
 
366
 
 
367
            glMatrixMode(GL_MODELVIEW);
 
368
            glLoadIdentity();
 
369
 
 
370
            for (int i=0;i<1000; ++i) {
 
371
                glBegin(GL_TRIANGLES);
 
372
                glColor3f(qrandom(), qrandom(), qrandom());
 
373
                glVertex2f(qrandom() * w, qrandom() * h);
 
374
                glColor3f(qrandom(), qrandom(), qrandom());
 
375
                glVertex2f(qrandom() * w, qrandom() * h);
 
376
                glColor3f(qrandom(), qrandom(), qrandom());
 
377
                glVertex2f(qrandom() * w, qrandom() * h);
 
378
                glEnd();
 
379
            }
 
380
#endif
 
381
}
 
382
 
 
383
class ThreadSafeGLWidget : public QGLWidget
 
384
{
 
385
public:
 
386
    ThreadSafeGLWidget(QWidget *parent = 0) : QGLWidget(parent) {}
 
387
    void paintEvent(QPaintEvent *)
 
388
    {
 
389
        // ignored as we're anyway swapping as fast as we can
 
390
    };
 
391
 
 
392
    void resizeEvent(QResizeEvent *e)
 
393
    {
 
394
        mutex.lock();
 
395
        newSize = e->size();
 
396
        mutex.unlock();
 
397
    };
 
398
 
 
399
    QMutex mutex;
 
400
    QSize newSize;
 
401
};
 
402
 
 
403
class SceneRenderingThread : public QThread
 
404
{
 
405
    Q_OBJECT
 
406
public:
 
407
    SceneRenderingThread(ThreadSafeGLWidget *widget)
 
408
        : m_widget(widget)
 
409
    {
 
410
        moveToThread(this);
 
411
        m_size = widget->size();
 
412
    }
 
413
 
 
414
    void run() {
 
415
        QTime time;
 
416
        time.start();
 
417
        failure = false;
 
418
 
 
419
        while (time.elapsed() < RUNNING_TIME && !failure) {
 
420
 
 
421
            m_widget->makeCurrent();
 
422
 
 
423
            m_widget->mutex.lock();
 
424
            QSize s = m_widget->newSize;
 
425
            m_widget->mutex.unlock();
 
426
 
 
427
            if (s != m_size) {
 
428
                glViewport(0, 0, s.width(), s.height());
 
429
            }
 
430
 
 
431
            if (QGLContext::currentContext() != m_widget->context()) {
 
432
                failure = true;
 
433
                break;
 
434
            }
 
435
 
 
436
            glClear(GL_COLOR_BUFFER_BIT);
 
437
 
 
438
            int w = m_widget->width();
 
439
            int h = m_widget->height();
 
440
 
 
441
            renderAScene(w, h);
 
442
 
 
443
            int color;
 
444
            glReadPixels(w / 2, h / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color);
 
445
 
 
446
            m_widget->swapBuffers();
 
447
        }
 
448
 
 
449
        m_widget->doneCurrent();
 
450
    }
 
451
 
 
452
    bool failure;
 
453
 
 
454
private:
 
455
    ThreadSafeGLWidget *m_widget;
 
456
    QSize m_size;
 
457
};
 
458
 
 
459
void tst_QGLThreads::renderInThread_data()
 
460
{
 
461
    QTest::addColumn<bool>("resize");
 
462
    QTest::addColumn<bool>("update");
 
463
 
 
464
    QTest::newRow("basic") << false << false;
 
465
    QTest::newRow("with-resize") << true << false;
 
466
    QTest::newRow("with-update") << false << true;
 
467
    QTest::newRow("with-resize-and-update") << true << true;
 
468
}
 
469
 
 
470
void tst_QGLThreads::renderInThread()
 
471
{
 
472
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
 
473
        QSKIP("No platformsupport for ThreadedOpenGL");
 
474
 
 
475
    QFETCH(bool, resize);
 
476
    QFETCH(bool, update);
 
477
 
 
478
    ThreadSafeGLWidget widget;
 
479
    widget.resize(200, 200);
 
480
    SceneRenderingThread thread(&widget);
 
481
 
 
482
    widget.show();
 
483
    QVERIFY(QTest::qWaitForWindowExposed(&widget));
 
484
    widget.doneCurrent();
 
485
 
 
486
    widget.context()->moveToThread(&thread);
 
487
 
 
488
    thread.start();
 
489
 
 
490
    int value = 10;
 
491
    while (thread.isRunning()) {
 
492
        if (resize)
 
493
            widget.resize(200 + value, 200 + value);
 
494
        if (update)
 
495
            widget.update(100 + value, 100 + value, 20, 20);
 
496
        qApp->processEvents();
 
497
        value = -value;
 
498
 
 
499
        QThread::msleep(100);
 
500
    }
 
501
 
 
502
    QVERIFY(!thread.failure);
 
503
}
 
504
 
 
505
class Device
 
506
{
 
507
public:
 
508
    virtual ~Device() {}
 
509
    virtual QPaintDevice *realPaintDevice() = 0;
 
510
    virtual void prepareDevice() {}
 
511
    virtual void moveToThread(QThread *) {}
 
512
};
 
513
 
 
514
class GLWidgetWrapper : public Device
 
515
{
 
516
public:
 
517
    GLWidgetWrapper() {
 
518
        widget.resize(150, 150);
 
519
        widget.show();
 
520
        QTest::qWaitForWindowExposed(&widget);
 
521
        widget.doneCurrent();
 
522
    }
 
523
    QPaintDevice *realPaintDevice() { return &widget; }
 
524
    void moveToThread(QThread *thread) { widget.context()->moveToThread(thread); }
 
525
 
 
526
    ThreadSafeGLWidget widget;
 
527
};
 
528
 
 
529
class PixmapWrapper : public Device
 
530
{
 
531
public:
 
532
    PixmapWrapper() { pixmap = new QPixmap(512, 512); }
 
533
    ~PixmapWrapper() { delete pixmap; }
 
534
    QPaintDevice *realPaintDevice() { return pixmap; }
 
535
 
 
536
    QPixmap *pixmap;
 
537
};
 
538
 
 
539
class PixelBufferWrapper : public Device
 
540
{
 
541
public:
 
542
    PixelBufferWrapper() { pbuffer = new QGLPixelBuffer(512, 512); }
 
543
    ~PixelBufferWrapper() { delete pbuffer; }
 
544
    QPaintDevice *realPaintDevice() { return pbuffer; }
 
545
    void moveToThread(QThread *thread) { pbuffer->context()->moveToThread(thread); }
 
546
 
 
547
    QGLPixelBuffer *pbuffer;
 
548
};
 
549
 
 
550
 
 
551
class FrameBufferObjectWrapper : public Device
 
552
{
 
553
public:
 
554
    FrameBufferObjectWrapper() {
 
555
        widget.makeCurrent();
 
556
        fbo = new QGLFramebufferObject(512, 512);
 
557
        widget.doneCurrent();
 
558
    }
 
559
    ~FrameBufferObjectWrapper() { delete fbo; }
 
560
    QPaintDevice *realPaintDevice() { return fbo; }
 
561
    void prepareDevice() { widget.makeCurrent(); }
 
562
    void moveToThread(QThread *thread) { widget.context()->moveToThread(thread); }
 
563
 
 
564
    ThreadSafeGLWidget widget;
 
565
    QGLFramebufferObject *fbo;
 
566
};
 
567
 
 
568
 
 
569
class ThreadPainter : public QObject
 
570
{
 
571
    Q_OBJECT
 
572
public:
 
573
    ThreadPainter(Device *pd) : device(pd), fail(true) {
 
574
        pixmap = QPixmap(40, 40);
 
575
        pixmap.fill(Qt::green);
 
576
        QPainter p(&pixmap);
 
577
        p.drawLine(0, 0, 40, 40);
 
578
        p.drawLine(0, 40, 40, 0);
 
579
    }
 
580
 
 
581
public slots:
 
582
    void draw() {
 
583
        bool beginFailed = false;
 
584
        QTime time;
 
585
        time.start();
 
586
        int rotAngle = 10;
 
587
        device->prepareDevice();
 
588
        QPaintDevice *paintDevice = device->realPaintDevice();
 
589
        QSize s(paintDevice->width(), paintDevice->height());
 
590
        while (time.elapsed() < RUNNING_TIME) {
 
591
            QPainter p;
 
592
            if (!p.begin(paintDevice)) {
 
593
                beginFailed = true;
 
594
                break;
 
595
            }
 
596
            p.translate(s.width()/2, s.height()/2);
 
597
            p.rotate(rotAngle);
 
598
            p.translate(-s.width()/2, -s.height()/2);
 
599
            p.fillRect(0, 0, s.width(), s.height(), Qt::red);
 
600
            QRect rect(QPoint(0, 0), s);
 
601
            p.drawPixmap(10, 10, pixmap);
 
602
            p.drawTiledPixmap(50, 50, 100, 100, pixmap);
 
603
            p.drawText(rect.center(), "This is a piece of text");
 
604
            p.end();
 
605
            rotAngle += 2;
 
606
            QThread::msleep(20);
 
607
        }
 
608
 
 
609
        device->moveToThread(qApp->thread());
 
610
 
 
611
        fail = beginFailed;
 
612
        QThread::currentThread()->quit();
 
613
    }
 
614
 
 
615
    bool failed() { return fail; }
 
616
 
 
617
private:
 
618
    QPixmap pixmap;
 
619
    Device *device;
 
620
    bool fail;
 
621
};
 
622
 
 
623
template <class T>
 
624
class PaintThreadManager
 
625
{
 
626
public:
 
627
    PaintThreadManager(int count) : numThreads(count)
 
628
    {
 
629
        for (int i=0; i<numThreads; ++i) {
 
630
            devices.append(new T);
 
631
            threads.append(new QThread);
 
632
            painters.append(new ThreadPainter(devices.at(i)));
 
633
            painters.at(i)->moveToThread(threads.at(i));
 
634
            painters.at(i)->connect(threads.at(i), SIGNAL(started()), painters.at(i), SLOT(draw()));
 
635
            devices.at(i)->moveToThread(threads.at(i));
 
636
        }
 
637
    }
 
638
 
 
639
    ~PaintThreadManager() {
 
640
        qDeleteAll(threads);
 
641
        qDeleteAll(painters);
 
642
        qDeleteAll(devices);
 
643
    }
 
644
 
 
645
 
 
646
    void start() {
 
647
        for (int i=0; i<numThreads; ++i)
 
648
            threads.at(i)->start();
 
649
    }
 
650
 
 
651
    bool areRunning() {
 
652
        bool running = false;
 
653
        for (int i=0; i<numThreads; ++i){
 
654
            if (threads.at(i)->isRunning())
 
655
                running = true;
 
656
        }
 
657
 
 
658
        return running;
 
659
    }
 
660
 
 
661
    bool failed() {
 
662
        for (int i=0; i<numThreads; ++i) {
 
663
            if (painters.at(i)->failed())
 
664
                return true;
 
665
        }
 
666
 
 
667
        return false;
 
668
    }
 
669
 
 
670
private:
 
671
    QList<QThread *> threads;
 
672
    QList<Device *> devices;
 
673
    QList<ThreadPainter *> painters;
 
674
    int numThreads;
 
675
};
 
676
 
 
677
/*
 
678
   This test uses QPainter to draw onto different QGLWidgets in
 
679
   different threads at the same time. The ThreadSafeGLWidget is
 
680
   necessary to handle paint and resize events that might come from
 
681
   the main thread at any time while the test is running. The resize
 
682
   and paint events would cause makeCurrent() calls to be issued from
 
683
   within the QGLWidget while the widget's context was current in
 
684
   another thread, which would cause errors.
 
685
*/
 
686
void tst_QGLThreads::painterOnGLWidgetInThread()
 
687
{
 
688
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
 
689
        QSKIP("No platformsupport for ThreadedOpenGL");
 
690
    if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
 
691
          (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
 
692
        QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");
 
693
    }
 
694
 
 
695
    PaintThreadManager<GLWidgetWrapper> painterThreads(5);
 
696
    painterThreads.start();
 
697
 
 
698
    while (painterThreads.areRunning()) {
 
699
        qApp->processEvents();
 
700
        QThread::msleep(100);
 
701
    }
 
702
    QVERIFY(!painterThreads.failed());
 
703
}
 
704
 
 
705
/*
 
706
   This test uses QPainter to draw onto different QPixmaps in
 
707
   different threads at the same time.
 
708
*/
 
709
void tst_QGLThreads::painterOnPixmapInThread()
 
710
{
 
711
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)
 
712
        || !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps))
 
713
        QSKIP("No platformsupport for ThreadedOpenGL or ThreadedPixmaps");
 
714
#ifdef Q_WS_X11
 
715
    QSKIP("Drawing text in threads onto X11 drawables currently crashes on some X11 servers.");
 
716
#endif
 
717
    PaintThreadManager<PixmapWrapper> painterThreads(5);
 
718
    painterThreads.start();
 
719
 
 
720
    while (painterThreads.areRunning()) {
 
721
        qApp->processEvents();
 
722
        QThread::msleep(100);
 
723
    }
 
724
    QVERIFY(!painterThreads.failed());
 
725
}
 
726
 
 
727
/* This test uses QPainter to draw onto different QGLPixelBuffer
 
728
   objects in different threads at the same time.
 
729
*/
 
730
void tst_QGLThreads::painterOnPboInThread()
 
731
{
 
732
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
 
733
        QSKIP("No platformsupport for ThreadedOpenGL");
 
734
    if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
 
735
          (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
 
736
        QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");
 
737
    }
 
738
 
 
739
    if (!QGLPixelBuffer::hasOpenGLPbuffers()) {
 
740
        QSKIP("This system doesn't support pbuffers.");
 
741
    }
 
742
 
 
743
    PaintThreadManager<PixelBufferWrapper> painterThreads(5);
 
744
    painterThreads.start();
 
745
 
 
746
    while (painterThreads.areRunning()) {
 
747
        qApp->processEvents();
 
748
        QThread::msleep(100);
 
749
    }
 
750
    QVERIFY(!painterThreads.failed());
 
751
}
 
752
 
 
753
/* This test uses QPainter to draw onto different
 
754
   QGLFramebufferObjects (bound in a QGLWidget's context) in different
 
755
   threads at the same time.
 
756
*/
 
757
void tst_QGLThreads::painterOnFboInThread()
 
758
{
 
759
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
 
760
        QSKIP("No platformsupport for ThreadedOpenGL");
 
761
    if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
 
762
          (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
 
763
        QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");
 
764
    }
 
765
 
 
766
    if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) {
 
767
        QSKIP("This system doesn't support framebuffer objects.");
 
768
    }
 
769
 
 
770
    PaintThreadManager<FrameBufferObjectWrapper> painterThreads(5);
 
771
    painterThreads.start();
 
772
 
 
773
    while (painterThreads.areRunning()) {
 
774
        qApp->processEvents();
 
775
        QThread::msleep(100);
 
776
    }
 
777
    QVERIFY(!painterThreads.failed());
 
778
}
 
779
 
 
780
int main(int argc, char **argv)
 
781
{
 
782
    QApplication::setAttribute(Qt::AA_X11InitThreads);
 
783
    QApplication app(argc, argv);
 
784
    QTEST_DISABLE_KEYPAD_NAVIGATION \
 
785
 
 
786
    tst_QGLThreads tc;
 
787
    return QTest::qExec(&tc, argc, argv);
 
788
}
 
789
 
 
790
#include "tst_qglthreads.moc"