From 926ddffa547a9f37a67554cce810045c074172b7 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Sat, 27 Aug 2011 09:04:20 +0200 Subject: [PATCH] separated out render thread code MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I9e440ef172055f626c908d4dd733d0243feb77c9 Reviewed-on: http://codereview.qt.nokia.com/3700 Reviewed-by: Qt Sanity Bot Reviewed-by: Samuel Rødal --- src/declarative/items/qsgcanvas.cpp | 176 ++++++++++++++++++++--------------- src/declarative/items/qsgcanvas.h | 4 +- src/declarative/items/qsgcanvas_p.h | 79 +++++++++++++--- 3 files changed, 169 insertions(+), 90 deletions(-) diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index 0d4001a..bc642e4 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -178,28 +178,38 @@ void QSGCanvas::resizeEvent(QResizeEvent *e) d->thread->resize(size()); } +void QSGCanvas::animationStarted() +{ + d_func()->thread->animationStarted(); +} + +void QSGCanvas::animationStopped() +{ + d_func()->thread->animationStopped(); +} + void QSGCanvas::showEvent(QShowEvent *e) { Q_D(QSGCanvas); if (d->vsyncAnimations) { if (!d->animationDriver) { d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), d->thread, SLOT(animationStarted()), Qt::DirectConnection); - connect(d->animationDriver, SIGNAL(stopped()), d->thread, SLOT(animationStopped()), Qt::DirectConnection); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection); + connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection); } d->animationDriver->install(); } if (!d->thread->isRunning()) { - d->thread->windowSize = size(); - d->thread->startRenderThread(); + d->thread->setWindowSize(size()); + d->thread->startRendering(); } } void QSGCanvas::hideEvent(QHideEvent *e) { Q_D(QSGCanvas); - d->thread->stopRenderThread(); + d->thread->stopRendering(); } @@ -334,8 +344,6 @@ QSGCanvasPrivate::QSGCanvasPrivate() , mouseGrabberItem(0) , dirtyItemList(0) , context(0) - , animationRunning(false) - , renderThreadAwakened(false) , vsyncAnimations(false) , thread(0) , animationDriver(0) @@ -369,7 +377,7 @@ void QSGCanvasPrivate::init(QSGCanvas *c) thread->d = this; context = QSGContext::createDefaultContext(); - context->moveToThread(thread); + thread->moveCanvasToThread(context); } void QSGCanvasPrivate::sceneMouseEventForTransform(QGraphicsSceneMouseEvent &sceneEvent, @@ -775,7 +783,7 @@ QSGCanvas::~QSGCanvas() Q_D(QSGCanvas); if (d->thread->isRunning()) { - d->thread->stopRenderThread(); + d->thread->stopRendering(); delete d->thread; d->thread = 0; } @@ -831,27 +839,6 @@ bool QSGCanvas::event(QEvent *e) { Q_D(QSGCanvas); - if (e->type() == QEvent::User) { - if (!d->thread->syncAlreadyHappened) - d->thread->sync(false); - - d->thread->syncAlreadyHappened = false; - - if (d->animationRunning && d->animationDriver) { -#ifdef THREAD_DEBUG - qDebug("GUI: Advancing animations...\n"); -#endif - - d->animationDriver->advance(); - -#ifdef THREAD_DEBUG - qDebug("GUI: Animations advanced...\n"); -#endif - } - - return true; - } - switch (e->type()) { case QEvent::TouchBegin: @@ -1775,26 +1762,8 @@ void QSGCanvas::maybeUpdate() { Q_D(QSGCanvas); - if (d->thread && d->thread->isRunning()) { - Q_ASSERT_X(QThread::currentThread() == QApplication::instance()->thread() || d->thread->inSync, - "QSGCanvas::update", - "Function can only be called from GUI thread or during QSGItem::updatePaintNode()"); - - if (d->thread->inSync) { - d->thread->isExternalUpdatePending = true; - - } else if (!d->renderThreadAwakened) { -#ifdef THREAD_DEBUG - printf("GUI: doing update...\n"); -#endif - d->renderThreadAwakened = true; - d->thread->lockInGui(); - d->thread->isExternalUpdatePending = true; - if (d->thread->isRenderBlocked) - d->thread->wake(); - d->thread->unlockInGui(); - } - } + if (d->thread && d->thread->isRunning()) + d->thread->maybeUpdate(); } /*! @@ -1876,20 +1845,26 @@ QImage QSGCanvas::grabFrameBuffer() } + +void QSGCanvasRenderLoop::createGLContext() +{ + gl = new QGuiGLContext(); + gl->create(); +} + + void QSGCanvasRenderThread::run() { #ifdef THREAD_DEBUG qDebug("QML Rendering Thread Started"); #endif - if (!guiContext) { - guiContext = new QGuiGLContext(); - guiContext->create(); - guiContext->makeCurrent(renderer); - - d->initializeSceneGraph(); + if (!glContext()) { + createGLContext(); + makeCurrent(); + initializeSceneGraph(); } else { - guiContext->makeCurrent(renderer); + makeCurrent(); } while (!shouldExit) { @@ -1916,7 +1891,7 @@ void QSGCanvasRenderThread::run() #ifdef THREAD_DEBUG printf(" RenderThread: aquired sync lock...\n"); #endif - QApplication::postEvent(renderer, new QEvent(QEvent::User)); + QApplication::postEvent(this, new QEvent(QEvent::User)); #ifdef THREAD_DEBUG printf(" RenderThread: going to sleep...\n"); #endif @@ -1929,7 +1904,7 @@ void QSGCanvasRenderThread::run() printf(" RenderThread: Doing locked sync\n"); #endif inSync = true; - d->syncSceneGraph(); + syncSceneGraph(); inSync = false; // Wake GUI after sync to let it continue animating and event processing. @@ -1945,7 +1920,7 @@ void QSGCanvasRenderThread::run() printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height()); #endif - d->renderSceneGraph(windowSize); + renderSceneGraph(windowSize); // The content of the target buffer is undefined after swap() so grab needs // to happen before swap(); @@ -1961,8 +1936,7 @@ void QSGCanvasRenderThread::run() printf(" RenderThread: wait for swap...\n"); #endif - guiContext->swapBuffers(renderer); - emit renderer->frameSwapped();//notify compositor that frame has been swapped + swapBuffers(); #ifdef THREAD_DEBUG printf(" RenderThread: swap complete...\n"); #endif @@ -1978,7 +1952,7 @@ void QSGCanvasRenderThread::run() // but we don't want to lock an extra time. wake(); - if (!d->animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) { + if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) { #ifdef THREAD_DEBUG printf(" RenderThread: nothing to do, going to sleep...\n"); #endif @@ -1997,7 +1971,7 @@ void QSGCanvasRenderThread::run() printf(" RenderThread: render loop exited... Good Night!\n"); #endif - guiContext->doneCurrent(); + doneCurrent(); lock(); hasExited = true; @@ -2014,6 +1988,36 @@ void QSGCanvasRenderThread::run() +bool QSGCanvasRenderThread::event(QEvent *e) +{ + Q_ASSERT(QThread::currentThread() == qApp->thread()); + + if (e->type() == QEvent::User) { + if (!syncAlreadyHappened) + sync(false); + + syncAlreadyHappened = false; + + if (animationRunning && animationDriver()) { +#ifdef THREAD_DEBUG + qDebug("GUI: Advancing animations...\n"); +#endif + + animationDriver()->advance(); + +#ifdef THREAD_DEBUG + qDebug("GUI: Animations advanced...\n"); +#endif + } + + return true; + } + + return QThread::event(e); +} + + + void QSGCanvasRenderThread::exhaustSyncEvent() { if (isGuiBlockPending) { @@ -2030,17 +2034,17 @@ void QSGCanvasRenderThread::sync(bool guiAlreadyLocked) printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event"); #endif if (!guiAlreadyLocked) - d->thread->lockInGui(); + lockInGui(); - d->renderThreadAwakened = false; + renderThreadAwakened = false; - d->polishItems(); + polishItems(); - d->thread->wake(); - d->thread->wait(); + wake(); + wait(); if (!guiAlreadyLocked) - d->thread->unlockInGui(); + unlockInGui(); } @@ -2090,7 +2094,7 @@ void QSGCanvasRenderThread::animationStarted() lockInGui(); - d->animationRunning = true; + animationRunning = true; if (isRenderBlocked) wake(); @@ -2107,7 +2111,7 @@ void QSGCanvasRenderThread::animationStopped() #endif lockInGui(); - d->animationRunning = false; + animationRunning = false; unlockInGui(); } @@ -2158,7 +2162,7 @@ void QSGCanvasRenderThread::resize(const QSize &size) -void QSGCanvasRenderThread::startRenderThread() +void QSGCanvasRenderThread::startRendering() { #ifdef THREAD_DEBUG printf("GUI: Starting Render Thread\n"); @@ -2172,7 +2176,7 @@ void QSGCanvasRenderThread::startRenderThread() -void QSGCanvasRenderThread::stopRenderThread() +void QSGCanvasRenderThread::stopRendering() { #ifdef THREAD_DEBUG printf("GUI: stopping render thread\n"); @@ -2247,6 +2251,30 @@ QImage QSGCanvasRenderThread::grab() } + +void QSGCanvasRenderThread::maybeUpdate() +{ + Q_ASSERT_X(QThread::currentThread() == QApplication::instance()->thread() || inSync, + "QSGCanvas::update", + "Function can only be called from GUI thread or during QSGItem::updatePaintNode()"); + + if (inSync) { + isExternalUpdatePending = true; + + } else if (!renderThreadAwakened) { +#ifdef THREAD_DEBUG + printf("GUI: doing update...\n"); +#endif + renderThreadAwakened = true; + lockInGui(); + isExternalUpdatePending = true; + if (isRenderBlocked) + wake(); + unlockInGui(); + } +} + + #include "moc_qsgcanvas.cpp" QT_END_NAMESPACE diff --git a/src/declarative/items/qsgcanvas.h b/src/declarative/items/qsgcanvas.h index 86efdcb..be6b173 100644 --- a/src/declarative/items/qsgcanvas.h +++ b/src/declarative/items/qsgcanvas.h @@ -115,10 +115,12 @@ protected: private Q_SLOTS: void sceneGraphChanged(); void maybeUpdate(); + void animationStarted(); + void animationStopped(); private: friend class QSGItem; - friend class QSGCanvasRenderThread; + friend class QSGCanvasRenderLoop; Q_DISABLE_COPY(QSGCanvas) }; diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h index bf65e95..7daa142 100644 --- a/src/declarative/items/qsgcanvas_p.h +++ b/src/declarative/items/qsgcanvas_p.h @@ -82,7 +82,7 @@ public: class QSGCanvasPrivate; class QTouchEvent; -class QSGCanvasRenderThread; +class QSGCanvasRenderLoop; class QSGCanvasPrivate : public QWindowPrivate { @@ -161,12 +161,9 @@ public: QSGContext *context; - uint animationRunning: 1; - uint renderThreadAwakened : 1; - uint vsyncAnimations : 1; - QSGCanvasRenderThread *thread; + QSGCanvasRenderLoop *thread; QSize widgetSize; QSize viewportSize; @@ -177,15 +174,64 @@ public: QHash itemForTouchPointId; }; +class QSGCanvasRenderLoop +{ +public: + QSGCanvasRenderLoop() + : d(0) + , renderer(0) + , gl(0) + { + } + virtual ~QSGCanvasRenderLoop() + { + delete gl; + } + + friend class QSGCanvasPrivate; + + virtual void paint() = 0; + virtual void resize(const QSize &size) = 0; + virtual void startRendering() = 0; + virtual void stopRendering() = 0; + virtual QImage grab() = 0; + virtual void setWindowSize(const QSize &size) = 0; + virtual void maybeUpdate() = 0; + virtual bool isRunning() const = 0; + virtual void animationStarted() = 0; + virtual void animationStopped() = 0; + virtual void moveCanvasToThread(QSGContext *) { } + +protected: + void initializeSceneGraph() { d->initializeSceneGraph(); } + void syncSceneGraph() { d->syncSceneGraph(); } + void renderSceneGraph(const QSize &size) { d->renderSceneGraph(size); } + void polishItems() { d->polishItems(); } + QAnimationDriver *animationDriver() const { return d->animationDriver; } + + inline QGuiGLContext *glContext() const { return gl; } + void createGLContext(); + void makeCurrent() { gl->makeCurrent(renderer); } + void doneCurrent() { gl->doneCurrent(); } + void swapBuffers() { + gl->swapBuffers(renderer); + emit renderer->frameSwapped(); + } + +private: + QSGCanvasPrivate *d; + QSGCanvas *renderer; + QGuiGLContext *gl; +}; -class QSGCanvasRenderThread : public QThread +class QSGCanvasRenderThread : public QThread, public QSGCanvasRenderLoop { Q_OBJECT public: QSGCanvasRenderThread() : mutex(QMutex::NonRecursive) - , guiContext(0) + , animationRunning(false) , isGuiBlocked(0) , isPaintCompleted(false) , isGuiBlockPending(false) @@ -196,6 +242,7 @@ public: , doGrab(false) , shouldExit(false) , hasExited(false) + , renderThreadAwakened(false) {} inline void lock() { mutex.lock(); } @@ -208,10 +255,16 @@ public: void paint(); void resize(const QSize &size); - void startRenderThread(); - void stopRenderThread(); + void startRendering(); + void stopRendering(); void exhaustSyncEvent(); void sync(bool guiAlreadyLocked); + bool isRunning() const { return QThread::isRunning(); } + void setWindowSize(const QSize &size) { windowSize = size; } + void maybeUpdate(); + void moveCanvasToThread(QSGCanvas *c) { c->moveToThread(this); } + + bool event(QEvent *); QImage grab(); @@ -226,11 +279,7 @@ public: QSize windowSize; QSize renderedSize; - QSGCanvas *renderer; - QSGCanvasPrivate *d; - - QGuiGLContext *guiContext; - + uint animationRunning: 1; int isGuiBlocked; uint isPaintCompleted : 1; uint isGuiBlockPending : 1; @@ -238,10 +287,10 @@ public: uint isExternalUpdatePending : 1; uint syncAlreadyHappened : 1; uint inSync : 1; - uint doGrab : 1; uint shouldExit : 1; uint hasExited : 1; + uint renderThreadAwakened : 1; QImage grabContent; -- 1.7.2.5