Added explicit threading API to QtOpenGL.
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>
This commit is contained in:
parent
3ee48926e6
commit
6385a182f0
9
dist/changes-5.0.0
vendored
9
dist/changes-5.0.0
vendored
@ -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
|
||||
--------
|
||||
|
@ -3126,6 +3126,19 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
|
||||
Q_UNUSED(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)
|
||||
|
||||
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -186,9 +186,14 @@ void QGLContext::reset()
|
||||
d->initDone = false;
|
||||
QGLContextGroup::removeShare(this);
|
||||
if (d->guiGlContext) {
|
||||
if (d->ownContext)
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -100,6 +100,7 @@
|
||||
|
||||
#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;
|
||||
@ -241,6 +255,15 @@ bool QGLPixelBuffer::doneCurrent()
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the context of this pixelbuffer.
|
||||
*/
|
||||
QGLContext *QGLPixelBuffer::context() const
|
||||
{
|
||||
Q_D(const QGLPixelBuffer);
|
||||
return d->qctx;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn GLuint QGLPixelBuffer::generateDynamicTexture() const
|
||||
|
||||
@ -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
|
||||
|
@ -66,6 +66,8 @@ public:
|
||||
bool makeCurrent();
|
||||
bool doneCurrent();
|
||||
|
||||
QGLContext *context() const;
|
||||
|
||||
GLuint generateDynamicTexture() const;
|
||||
bool bindToDynamicTexture(GLuint texture);
|
||||
void releaseFromDynamicTexture();
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
|
Loading…
Reference in New Issue
Block a user