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.
|
which were deprecated in Qt 4 have been removed.
|
||||||
* Previously deprecated default value listBase parameter has been removed from
|
* Previously deprecated default value listBase parameter has been removed from
|
||||||
both QGLWidget::renderText() functions.
|
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
|
QtScript
|
||||||
--------
|
--------
|
||||||
|
@ -3126,6 +3126,19 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
|
|||||||
Q_UNUSED(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)
|
\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
|
In some very rare cases the underlying call may fail. If this
|
||||||
occurs an error message is output to stderr.
|
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
|
\fn void QGLContext::swapBuffers() const
|
||||||
|
|
||||||
Swaps the screen contents with an off-screen buffer. Only works if
|
Call this to finish a frame of OpenGL rendering, and make sure to
|
||||||
the context is in double buffer mode.
|
call makeCurrent() again before you begin a new frame.
|
||||||
|
|
||||||
\sa QGLFormat::setDoubleBuffer()
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ -3362,13 +3377,18 @@ void QGLContextPrivate::setCurrentContext(QGLContext *context)
|
|||||||
1. Call doneCurrent() in the main thread when the rendering is
|
1. Call doneCurrent() in the main thread when the rendering is
|
||||||
finished.
|
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().
|
makeCurrent() and then call swapBuffers().
|
||||||
|
|
||||||
4. Call doneCurrent() in the swapping thread and notify the main
|
5. Call doneCurrent() in the swapping thread.
|
||||||
thread that swapping is done.
|
|
||||||
|
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
|
Doing this will free up the main thread so that it can continue
|
||||||
with, for example, handling UI events or network requests. Even if
|
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
|
QGLWidgets can only be created in the main GUI thread. This means
|
||||||
a call to doneCurrent() is necessary to release the GL context
|
a call to doneCurrent() is necessary to release the GL context
|
||||||
from the main thread, before the widget can be drawn into by
|
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
|
paint events to a QGLWidget when the widget is resized, or parts
|
||||||
of it becomes exposed or needs redrawing. It is therefore
|
of it becomes exposed or needs redrawing. It is therefore
|
||||||
necessary to handle those events because the default
|
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.
|
Returns the context of this widget.
|
||||||
|
|
||||||
@ -4483,7 +4506,7 @@ QGLFormat QGLWidget::format() const
|
|||||||
return d->glcx->format();
|
return d->glcx->format();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QGLContext *QGLWidget::context() const
|
QGLContext *QGLWidget::context() const
|
||||||
{
|
{
|
||||||
Q_D(const QGLWidget);
|
Q_D(const QGLWidget);
|
||||||
return d->glcx;
|
return d->glcx;
|
||||||
|
@ -281,6 +281,8 @@ public:
|
|||||||
QGLFormat requestedFormat() const;
|
QGLFormat requestedFormat() const;
|
||||||
void setFormat(const QGLFormat& format);
|
void setFormat(const QGLFormat& format);
|
||||||
|
|
||||||
|
void moveToThread(QThread *thread);
|
||||||
|
|
||||||
virtual void makeCurrent();
|
virtual void makeCurrent();
|
||||||
virtual void doneCurrent();
|
virtual void doneCurrent();
|
||||||
|
|
||||||
@ -413,7 +415,7 @@ public:
|
|||||||
QGLFormat format() const;
|
QGLFormat format() const;
|
||||||
void setFormat(const QGLFormat& format);
|
void setFormat(const QGLFormat& format);
|
||||||
|
|
||||||
const QGLContext* context() const;
|
QGLContext* context() const;
|
||||||
void setContext(QGLContext* context, const QGLContext* shareContext = 0,
|
void setContext(QGLContext* context, const QGLContext* shareContext = 0,
|
||||||
bool deleteOldContext = true);
|
bool deleteOldContext = true);
|
||||||
|
|
||||||
|
@ -380,19 +380,18 @@ class Q_OPENGL_EXPORT QGLTextureDestroyer : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
QGLTextureDestroyer() : QObject() {
|
QGLTextureDestroyer() : QObject() {
|
||||||
qRegisterMetaType<GLuint>();
|
connect(this, SIGNAL(freeTexture(QGLContext *, QPlatformPixmap *, quint32)),
|
||||||
connect(this, SIGNAL(freeTexture(QGLContext *, QPlatformPixmap *, GLuint)),
|
this, SLOT(freeTexture_slot(QGLContext *, QPlatformPixmap *, quint32)));
|
||||||
this, SLOT(freeTexture_slot(QGLContext *, QPlatformPixmap *, GLuint)));
|
|
||||||
}
|
}
|
||||||
void emitFreeTexture(QGLContext *context, QPlatformPixmap *boundPixmap, GLuint id) {
|
void emitFreeTexture(QGLContext *context, QPlatformPixmap *boundPixmap, GLuint id) {
|
||||||
emit freeTexture(context, boundPixmap, id);
|
emit freeTexture(context, boundPixmap, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void freeTexture(QGLContext *context, QPlatformPixmap *boundPixmap, GLuint id);
|
void freeTexture(QGLContext *context, QPlatformPixmap *boundPixmap, quint32 id);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void freeTexture_slot(QGLContext *context, QPlatformPixmap *boundPixmap, GLuint id) {
|
void freeTexture_slot(QGLContext *context, QPlatformPixmap *boundPixmap, quint32 id) {
|
||||||
Q_UNUSED(boundPixmap);
|
Q_UNUSED(boundPixmap);
|
||||||
QGLShareContextScope scope(context);
|
QGLShareContextScope scope(context);
|
||||||
glDeleteTextures(1, &id);
|
glDeleteTextures(1, &id);
|
||||||
|
@ -186,9 +186,14 @@ void QGLContext::reset()
|
|||||||
d->initDone = false;
|
d->initDone = false;
|
||||||
QGLContextGroup::removeShare(this);
|
QGLContextGroup::removeShare(this);
|
||||||
if (d->guiGlContext) {
|
if (d->guiGlContext) {
|
||||||
if (d->ownContext)
|
if (QOpenGLContext::currentContext() == d->guiGlContext)
|
||||||
|
doneCurrent();
|
||||||
|
if (d->ownContext) {
|
||||||
|
if (d->guiGlContext->thread() == QThread::currentThread())
|
||||||
delete d->guiGlContext;
|
delete d->guiGlContext;
|
||||||
else
|
else
|
||||||
|
d->guiGlContext->deleteLater();
|
||||||
|
} else
|
||||||
d->guiGlContext->setQGLContextHandle(0,0);
|
d->guiGlContext->setQGLContextHandle(0,0);
|
||||||
d->guiGlContext = 0;
|
d->guiGlContext = 0;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include <private/qgl_p.h>
|
#include <private/qgl_p.h>
|
||||||
#include <private/qglpixelbuffer_p.h>
|
#include <private/qglpixelbuffer_p.h>
|
||||||
#include <private/qglframebufferobject_p.h>
|
#include <private/qglframebufferobject_p.h>
|
||||||
|
#include <qopenglfunctions.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@ -90,7 +91,7 @@ void QGLPaintDevice::beginPaint()
|
|||||||
|
|
||||||
if (m_previousFBO != m_thisFBO) {
|
if (m_previousFBO != m_thisFBO) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the default fbo for the context to m_thisFBO so that
|
// 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) {
|
if (ctx->d_ptr->current_fbo != m_thisFBO) {
|
||||||
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;
|
ctx->d_ptr->default_fbo = m_thisFBO;
|
||||||
@ -120,7 +121,7 @@ void QGLPaintDevice::endPaint()
|
|||||||
QGLContext *ctx = context();
|
QGLContext *ctx = context();
|
||||||
if (m_previousFBO != ctx->d_func()->current_fbo) {
|
if (m_previousFBO != ctx->d_func()->current_fbo) {
|
||||||
ctx->d_ptr->current_fbo = m_previousFBO;
|
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;
|
ctx->d_ptr->default_fbo = 0;
|
||||||
|
@ -100,6 +100,7 @@
|
|||||||
|
|
||||||
#include "gl2paintengineex/qpaintengineex_opengl2_p.h"
|
#include "gl2paintengineex/qpaintengineex_opengl2_p.h"
|
||||||
|
|
||||||
|
#include <qglframebufferobject.h>
|
||||||
#include <qglpixelbuffer.h>
|
#include <qglpixelbuffer.h>
|
||||||
#include <private/qglpixelbuffer_p.h>
|
#include <private/qglpixelbuffer_p.h>
|
||||||
#include <private/qfont_p.h>
|
#include <private/qfont_p.h>
|
||||||
@ -115,11 +116,23 @@ QGLContext* QGLPBufferGLPaintDevice::context() const
|
|||||||
return pbuf->d_func()->qctx;
|
return pbuf->d_func()->qctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QGLPBufferGLPaintDevice::endPaint() {
|
void QGLPBufferGLPaintDevice::beginPaint()
|
||||||
|
{
|
||||||
|
pbuf->makeCurrent();
|
||||||
|
QGLPaintDevice::beginPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QGLPBufferGLPaintDevice::endPaint()
|
||||||
|
{
|
||||||
glFlush();
|
glFlush();
|
||||||
QGLPaintDevice::endPaint();
|
QGLPaintDevice::endPaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QGLPBufferGLPaintDevice::setFbo(GLuint fbo)
|
||||||
|
{
|
||||||
|
m_thisFBO = fbo;
|
||||||
|
}
|
||||||
|
|
||||||
void QGLPBufferGLPaintDevice::setPBuffer(QGLPixelBuffer* pb)
|
void QGLPBufferGLPaintDevice::setPBuffer(QGLPixelBuffer* pb)
|
||||||
{
|
{
|
||||||
pbuf = pb;
|
pbuf = pb;
|
||||||
@ -221,6 +234,7 @@ bool QGLPixelBuffer::makeCurrent()
|
|||||||
format.setSamples(d->req_format.samples());
|
format.setSamples(d->req_format.samples());
|
||||||
d->fbo = new QOpenGLFramebufferObject(d->req_size, format);
|
d->fbo = new QOpenGLFramebufferObject(d->req_size, format);
|
||||||
d->fbo->bind();
|
d->fbo->bind();
|
||||||
|
d->glDevice.setFbo(d->fbo->handle());
|
||||||
glViewport(0, 0, d->req_size.width(), d->req_size.height());
|
glViewport(0, 0, d->req_size.width(), d->req_size.height());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -241,6 +255,15 @@ bool QGLPixelBuffer::doneCurrent()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the context of this pixelbuffer.
|
||||||
|
*/
|
||||||
|
QGLContext *QGLPixelBuffer::context() const
|
||||||
|
{
|
||||||
|
Q_D(const QGLPixelBuffer);
|
||||||
|
return d->qctx;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn GLuint QGLPixelBuffer::generateDynamicTexture() const
|
\fn GLuint QGLPixelBuffer::generateDynamicTexture() const
|
||||||
|
|
||||||
@ -366,6 +389,8 @@ QImage QGLPixelBuffer::toImage() const
|
|||||||
return QImage();
|
return QImage();
|
||||||
|
|
||||||
const_cast<QGLPixelBuffer *>(this)->makeCurrent();
|
const_cast<QGLPixelBuffer *>(this)->makeCurrent();
|
||||||
|
if (d->fbo)
|
||||||
|
d->fbo->bind();
|
||||||
return qt_gl_read_framebuffer(d->req_size, d->format.alpha(), true);
|
return qt_gl_read_framebuffer(d->req_size, d->format.alpha(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,7 +640,7 @@ GLuint QGLPixelBuffer::generateDynamicTexture() const
|
|||||||
|
|
||||||
bool QGLPixelBuffer::hasOpenGLPbuffers()
|
bool QGLPixelBuffer::hasOpenGLPbuffers()
|
||||||
{
|
{
|
||||||
return QOpenGLFramebufferObject::hasOpenGLFramebufferObjects();
|
return QGLFramebufferObject::hasOpenGLFramebufferObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -66,6 +66,8 @@ public:
|
|||||||
bool makeCurrent();
|
bool makeCurrent();
|
||||||
bool doneCurrent();
|
bool doneCurrent();
|
||||||
|
|
||||||
|
QGLContext *context() const;
|
||||||
|
|
||||||
GLuint generateDynamicTexture() const;
|
GLuint generateDynamicTexture() const;
|
||||||
bool bindToDynamicTexture(GLuint texture);
|
bool bindToDynamicTexture(GLuint texture);
|
||||||
void releaseFromDynamicTexture();
|
void releaseFromDynamicTexture();
|
||||||
|
@ -68,8 +68,10 @@ public:
|
|||||||
virtual QPaintEngine* paintEngine() const {return pbuf->paintEngine();}
|
virtual QPaintEngine* paintEngine() const {return pbuf->paintEngine();}
|
||||||
virtual QSize size() const {return pbuf->size();}
|
virtual QSize size() const {return pbuf->size();}
|
||||||
virtual QGLContext* context() const;
|
virtual QGLContext* context() const;
|
||||||
|
virtual void beginPaint();
|
||||||
virtual void endPaint();
|
virtual void endPaint();
|
||||||
void setPBuffer(QGLPixelBuffer* pb);
|
void setPBuffer(QGLPixelBuffer* pb);
|
||||||
|
void setFbo(GLuint fbo);
|
||||||
private:
|
private:
|
||||||
QGLPixelBuffer* pbuf;
|
QGLPixelBuffer* pbuf;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
CONFIG += testcase
|
CONFIG += testcase
|
||||||
TARGET = tst_qglthreads
|
TARGET = tst_qglthreads
|
||||||
requires(contains(QT_CONFIG,opengl))
|
requires(contains(QT_CONFIG,opengl))
|
||||||
QT += opengl widgets testlib
|
QT += opengl widgets testlib gui-private core-private
|
||||||
|
|
||||||
HEADERS += tst_qglthreads.h
|
HEADERS += tst_qglthreads.h
|
||||||
SOURCES += tst_qglthreads.cpp
|
SOURCES += tst_qglthreads.cpp
|
||||||
@ -10,5 +10,6 @@ x11 {
|
|||||||
LIBS += $$QMAKE_LIBS_X11
|
LIBS += $$QMAKE_LIBS_X11
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG+=insignificant_test # QTBUG-22560
|
|
||||||
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
|
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
|
||||||
|
|
||||||
|
win32:CONFIG+=insignificant_test # QTBUG-28264
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
#include <QtCore/QtCore>
|
#include <QtCore/QtCore>
|
||||||
#include <QtGui/QtGui>
|
#include <QtGui/QtGui>
|
||||||
|
#include <private/qguiapplication_p.h>
|
||||||
|
#include <qpa/qplatformintegration.h>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtOpenGL/QtOpenGL>
|
#include <QtOpenGL/QtOpenGL>
|
||||||
#include "tst_qglthreads.h"
|
#include "tst_qglthreads.h"
|
||||||
@ -74,7 +76,8 @@ class SwapThread : public QThread
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SwapThread(QGLWidget *widget)
|
SwapThread(QGLWidget *widget)
|
||||||
: m_widget(widget)
|
: m_context(widget->context())
|
||||||
|
, m_swapTriggered(false)
|
||||||
{
|
{
|
||||||
moveToThread(this);
|
moveToThread(this);
|
||||||
}
|
}
|
||||||
@ -84,25 +87,48 @@ public:
|
|||||||
time.start();
|
time.start();
|
||||||
while (time.elapsed() < RUNNING_TIME) {
|
while (time.elapsed() < RUNNING_TIME) {
|
||||||
lock();
|
lock();
|
||||||
wait();
|
waitForReadyToSwap();
|
||||||
|
|
||||||
m_widget->makeCurrent();
|
m_context->makeCurrent();
|
||||||
m_widget->swapBuffers();
|
m_context->swapBuffers();
|
||||||
m_widget->doneCurrent();
|
m_context->doneCurrent();
|
||||||
|
|
||||||
|
m_context->moveToThread(qApp->thread());
|
||||||
|
|
||||||
|
signalSwapDone();
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_swapTriggered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock() { m_mutex.lock(); }
|
void lock() { m_mutex.lock(); }
|
||||||
void unlock() { m_mutex.unlock(); }
|
void unlock() { m_mutex.unlock(); }
|
||||||
|
|
||||||
void wait() { m_wait_condition.wait(&m_mutex); }
|
void waitForSwapDone() { if (m_swapTriggered) m_swapDone.wait(&m_mutex); }
|
||||||
void notify() { m_wait_condition.wakeAll(); }
|
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:
|
private:
|
||||||
QGLWidget *m_widget;
|
QGLContext *m_context;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
QWaitCondition m_wait_condition;
|
QWaitCondition m_readyToSwap;
|
||||||
|
QWaitCondition m_swapDone;
|
||||||
|
|
||||||
|
bool m_swapTriggered;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ForegroundWidget : public QGLWidget
|
class ForegroundWidget : public QGLWidget
|
||||||
@ -117,6 +143,8 @@ public:
|
|||||||
void paintEvent(QPaintEvent *)
|
void paintEvent(QPaintEvent *)
|
||||||
{
|
{
|
||||||
m_thread->lock();
|
m_thread->lock();
|
||||||
|
m_thread->waitForSwapDone();
|
||||||
|
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
p.fillRect(rect(), QColor(rand() % 256, rand() % 256, rand() % 256));
|
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.drawText(rect(), Qt::AlignCenter, "This is an autotest");
|
||||||
p.end();
|
p.end();
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
m_thread->notify();
|
|
||||||
|
if (m_thread->isRunning()) {
|
||||||
|
context()->moveToThread(m_thread);
|
||||||
|
m_thread->signalReadyToSwap();
|
||||||
|
}
|
||||||
|
|
||||||
m_thread->unlock();
|
m_thread->unlock();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
@ -140,6 +173,8 @@ public:
|
|||||||
|
|
||||||
void tst_QGLThreads::swapInThread()
|
void tst_QGLThreads::swapInThread()
|
||||||
{
|
{
|
||||||
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
|
||||||
|
QSKIP("No platformsupport for ThreadedOpenGL");
|
||||||
QGLFormat format;
|
QGLFormat format;
|
||||||
format.setSwapInterval(1);
|
format.setSwapInterval(1);
|
||||||
ForegroundWidget widget(format);
|
ForegroundWidget widget(format);
|
||||||
@ -176,10 +211,12 @@ class CreateAndUploadThread : public QThread
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CreateAndUploadThread(QGLWidget *shareWidget)
|
CreateAndUploadThread(QGLWidget *shareWidget, QSemaphore *semaphore)
|
||||||
|
: m_semaphore(semaphore)
|
||||||
{
|
{
|
||||||
m_gl = new QGLWidget(0, shareWidget);
|
m_gl = new QGLWidget(0, shareWidget);
|
||||||
moveToThread(this);
|
moveToThread(this);
|
||||||
|
m_gl->context()->moveToThread(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
~CreateAndUploadThread()
|
~CreateAndUploadThread()
|
||||||
@ -203,6 +240,8 @@ public:
|
|||||||
p.end();
|
p.end();
|
||||||
m_gl->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
|
m_gl->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::InternalBindOption);
|
||||||
|
|
||||||
|
m_semaphore->acquire(1);
|
||||||
|
|
||||||
createdAndUploaded(image);
|
createdAndUploaded(image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,12 +251,18 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QGLWidget *m_gl;
|
QGLWidget *m_gl;
|
||||||
|
QSemaphore *m_semaphore;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextureDisplay : public QGLWidget
|
class TextureDisplay : public QGLWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
TextureDisplay(QSemaphore *semaphore)
|
||||||
|
: m_semaphore(semaphore)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *) {
|
void paintEvent(QPaintEvent *) {
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
for (int i=0; i<m_images.size(); ++i) {
|
for (int i=0; i<m_images.size(); ++i) {
|
||||||
@ -232,6 +277,8 @@ public slots:
|
|||||||
m_images << image;
|
m_images << image;
|
||||||
m_positions << QPoint(-rand() % width() / 2, -rand() % height() / 2);
|
m_positions << QPoint(-rand() % width() / 2, -rand() % height() / 2);
|
||||||
|
|
||||||
|
m_semaphore->release(1);
|
||||||
|
|
||||||
if (m_images.size() > 100) {
|
if (m_images.size() > 100) {
|
||||||
m_images.takeFirst();
|
m_images.takeFirst();
|
||||||
m_positions.takeFirst();
|
m_positions.takeFirst();
|
||||||
@ -241,12 +288,19 @@ public slots:
|
|||||||
private:
|
private:
|
||||||
QList <QImage> m_images;
|
QList <QImage> m_images;
|
||||||
QList <QPoint> m_positions;
|
QList <QPoint> m_positions;
|
||||||
|
|
||||||
|
QSemaphore *m_semaphore;
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QGLThreads::textureUploadInThread()
|
void tst_QGLThreads::textureUploadInThread()
|
||||||
{
|
{
|
||||||
TextureDisplay display;
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
|
||||||
CreateAndUploadThread thread(&display);
|
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)));
|
connect(&thread, SIGNAL(createdAndUploaded(QImage)), &display, SLOT(receiveImage(QImage)));
|
||||||
|
|
||||||
@ -362,10 +416,9 @@ public:
|
|||||||
time.start();
|
time.start();
|
||||||
failure = false;
|
failure = false;
|
||||||
|
|
||||||
m_widget->makeCurrent();
|
|
||||||
|
|
||||||
while (time.elapsed() < RUNNING_TIME && !failure) {
|
while (time.elapsed() < RUNNING_TIME && !failure) {
|
||||||
|
|
||||||
|
m_widget->makeCurrent();
|
||||||
|
|
||||||
m_widget->mutex.lock();
|
m_widget->mutex.lock();
|
||||||
QSize s = m_widget->newSize;
|
QSize s = m_widget->newSize;
|
||||||
@ -416,6 +469,8 @@ void tst_QGLThreads::renderInThread_data()
|
|||||||
|
|
||||||
void tst_QGLThreads::renderInThread()
|
void tst_QGLThreads::renderInThread()
|
||||||
{
|
{
|
||||||
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
|
||||||
|
QSKIP("No platformsupport for ThreadedOpenGL");
|
||||||
|
|
||||||
QFETCH(bool, resize);
|
QFETCH(bool, resize);
|
||||||
QFETCH(bool, update);
|
QFETCH(bool, update);
|
||||||
@ -428,6 +483,8 @@ void tst_QGLThreads::renderInThread()
|
|||||||
QVERIFY(QTest::qWaitForWindowExposed(&widget));
|
QVERIFY(QTest::qWaitForWindowExposed(&widget));
|
||||||
widget.doneCurrent();
|
widget.doneCurrent();
|
||||||
|
|
||||||
|
widget.context()->moveToThread(&thread);
|
||||||
|
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
int value = 10;
|
int value = 10;
|
||||||
@ -451,6 +508,7 @@ public:
|
|||||||
virtual ~Device() {}
|
virtual ~Device() {}
|
||||||
virtual QPaintDevice *realPaintDevice() = 0;
|
virtual QPaintDevice *realPaintDevice() = 0;
|
||||||
virtual void prepareDevice() {}
|
virtual void prepareDevice() {}
|
||||||
|
virtual void moveToThread(QThread *) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class GLWidgetWrapper : public Device
|
class GLWidgetWrapper : public Device
|
||||||
@ -463,6 +521,7 @@ public:
|
|||||||
widget.doneCurrent();
|
widget.doneCurrent();
|
||||||
}
|
}
|
||||||
QPaintDevice *realPaintDevice() { return &widget; }
|
QPaintDevice *realPaintDevice() { return &widget; }
|
||||||
|
void moveToThread(QThread *thread) { widget.context()->moveToThread(thread); }
|
||||||
|
|
||||||
ThreadSafeGLWidget widget;
|
ThreadSafeGLWidget widget;
|
||||||
};
|
};
|
||||||
@ -483,6 +542,7 @@ public:
|
|||||||
PixelBufferWrapper() { pbuffer = new QGLPixelBuffer(512, 512); }
|
PixelBufferWrapper() { pbuffer = new QGLPixelBuffer(512, 512); }
|
||||||
~PixelBufferWrapper() { delete pbuffer; }
|
~PixelBufferWrapper() { delete pbuffer; }
|
||||||
QPaintDevice *realPaintDevice() { return pbuffer; }
|
QPaintDevice *realPaintDevice() { return pbuffer; }
|
||||||
|
void moveToThread(QThread *thread) { pbuffer->context()->moveToThread(thread); }
|
||||||
|
|
||||||
QGLPixelBuffer *pbuffer;
|
QGLPixelBuffer *pbuffer;
|
||||||
};
|
};
|
||||||
@ -499,6 +559,7 @@ public:
|
|||||||
~FrameBufferObjectWrapper() { delete fbo; }
|
~FrameBufferObjectWrapper() { delete fbo; }
|
||||||
QPaintDevice *realPaintDevice() { return fbo; }
|
QPaintDevice *realPaintDevice() { return fbo; }
|
||||||
void prepareDevice() { widget.makeCurrent(); }
|
void prepareDevice() { widget.makeCurrent(); }
|
||||||
|
void moveToThread(QThread *thread) { widget.context()->moveToThread(thread); }
|
||||||
|
|
||||||
ThreadSafeGLWidget widget;
|
ThreadSafeGLWidget widget;
|
||||||
QGLFramebufferObject *fbo;
|
QGLFramebufferObject *fbo;
|
||||||
@ -545,6 +606,8 @@ public slots:
|
|||||||
QThread::msleep(20);
|
QThread::msleep(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device->moveToThread(qApp->thread());
|
||||||
|
|
||||||
fail = beginFailed;
|
fail = beginFailed;
|
||||||
QThread::currentThread()->quit();
|
QThread::currentThread()->quit();
|
||||||
}
|
}
|
||||||
@ -569,6 +632,7 @@ public:
|
|||||||
painters.append(new ThreadPainter(devices.at(i)));
|
painters.append(new ThreadPainter(devices.at(i)));
|
||||||
painters.at(i)->moveToThread(threads.at(i));
|
painters.at(i)->moveToThread(threads.at(i));
|
||||||
painters.at(i)->connect(threads.at(i), SIGNAL(started()), painters.at(i), SLOT(draw()));
|
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()
|
void tst_QGLThreads::painterOnGLWidgetInThread()
|
||||||
{
|
{
|
||||||
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
|
||||||
|
QSKIP("No platformsupport for ThreadedOpenGL");
|
||||||
if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
|
if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
|
||||||
(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
|
(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
|
||||||
QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 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()
|
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
|
#ifdef Q_WS_X11
|
||||||
QSKIP("Drawing text in threads onto X11 drawables currently crashes on some X11 servers.");
|
QSKIP("Drawing text in threads onto X11 drawables currently crashes on some X11 servers.");
|
||||||
#endif
|
#endif
|
||||||
@ -660,6 +729,8 @@ void tst_QGLThreads::painterOnPixmapInThread()
|
|||||||
*/
|
*/
|
||||||
void tst_QGLThreads::painterOnPboInThread()
|
void tst_QGLThreads::painterOnPboInThread()
|
||||||
{
|
{
|
||||||
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
|
||||||
|
QSKIP("No platformsupport for ThreadedOpenGL");
|
||||||
if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
|
if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
|
||||||
(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
|
(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
|
||||||
QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 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()
|
void tst_QGLThreads::painterOnFboInThread()
|
||||||
{
|
{
|
||||||
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
|
||||||
|
QSKIP("No platformsupport for ThreadedOpenGL");
|
||||||
if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
|
if (!((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) ||
|
||||||
(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
|
(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0))) {
|
||||||
QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");
|
QSKIP("The OpenGL based threaded QPainter tests requires OpenGL/ES 2.0.");
|
||||||
|
Loading…
Reference in New Issue
Block a user