1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickwindowmanager_p.h"
44 #include <QtCore/QCoreApplication>
45 #include <QtCore/QTime>
46 #include <QtCore/QMutex>
47 #include <QtCore/QWaitCondition>
48 #include <QtCore/private/qabstractanimation_p.h>
50 #include <QtGui/QOpenGLContext>
51 #include <QtGui/private/qguiapplication_p.h>
52 #include <QtGui/qplatformintegration_qpa.h>
54 #include <QtQml/private/qqmlglobal_p.h>
56 #include <QtQuick/QQuickCanvas>
57 #include <QtQuick/private/qquickcanvas_p.h>
58 #include <QtQuick/private/qsgcontext_p.h>
62 const QEvent::Type QEvent_Sync = QEvent::Type(QEvent::User);
63 const QEvent::Type QEvent_DeferredUpdate = QEvent::Type(QEvent::User + 1);
66 #define QQUICK_CANVAS_TIMING
67 #ifdef QQUICK_CANVAS_TIMING
68 static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty();
69 static QTime threadTimer;
71 static int renderTime;
75 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
80 expectations for this manager to work:
81 - one opengl context to render multiple windows
82 - OpenGL pipeline will not block for vsync in swap
83 - OpenGL pipeline will block based on a full buffer queue.
84 - Multiple screens can share the OpenGL context
85 - Animations are advanced for all windows once per swap
92 The threaded rendering uses a number of different variables to track potential
93 states used to handle resizing, initial paint, grabbing and driving animations
94 while ALWAYS keeping the GL context in the rendering thread and keeping the
95 overhead of normal one-shot paints and vblank driven animations at a minimum.
97 Resize, initial show and grab suffer slightly in this model as they are locked
98 to the rendering in the rendering thread, but this is a necessary evil for
101 Variables that are used:
103 Private::animationRunning: This is true while the animations are running, and only
104 written to inside locks.
106 RenderThread::isGuiLocked: This is used to indicate that the GUI thread owns the
107 lock. This variable is an integer to allow for recursive calls to lockInGui()
108 without using a recursive mutex. See isPostingSyncEvent.
110 RenderThread::isPaintComplete: This variable is cleared when rendering starts and
111 set once rendering is complete. It is monitored in the paintEvent(),
112 resizeEvent() and grab() functions to force them to wait for rendering to
115 RenderThread::isPostingSyncEvent: This variable is set in the render thread just
116 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
117 in the case where render thread waits while waiting for GUI to pick up the sync
118 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
119 When this happens, we use the
120 exhaustSyncEvent() function to do the sync right there and mark the coming
121 sync event to be discarded. There can only ever be one sync incoming.
123 RenderThread::isRenderBlock: This variable is true when animations are not
124 running and the render thread has gone to sleep, waiting for more to do.
126 RenderThread::isExternalUpdatePending: This variable is set to false when
127 a new render pass is started and to true in maybeUpdate(). It is an
128 indication to the render thread that another render pass needs to take
129 place, rather than the render thread going to sleep after completing its swap.
131 RenderThread::doGrab: This variable is set by the grab() function and
132 tells the renderer to do a grab after rendering is complete and before
135 RenderThread::shouldExit: This variable is used to determine if the render
136 thread should do a nother pass. It is typically set as a result of show()
137 and unset as a result of hide() or during shutdown()
139 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
140 after shouldExit has been set to true.
143 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP);
144 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
146 //#define THREAD_DEBUG
148 QQuickWindowManager::~QQuickWindowManager()
152 class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
156 QQuickRenderThreadSingleContextWindowManager()
157 : sg(QSGContext::createDefaultContext())
160 , allowMainThreadProcessingFlag(false)
162 , animationRunning(false)
163 , isPaintCompleted(false)
164 , isPostingSyncEvent(false)
165 , isRenderBlocked(false)
166 , isExternalUpdatePending(false)
167 , syncAlreadyHappened(false)
171 , isDeferredUpdatePosted(false)
174 sg->moveToThread(this);
176 animationDriver = sg->createAnimationDriver(this);
177 animationDriver->install();
178 connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
179 connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
182 QSGContext *sceneGraphContext() const { return sg; }
184 void releaseResources() { }
186 void show(QQuickCanvas *canvas);
187 void hide(QQuickCanvas *canvas);
189 void canvasDestroyed(QQuickCanvas *canvas);
191 void exposureChanged(QQuickCanvas *canvas);
192 QImage grab(QQuickCanvas *canvas);
193 void resize(QQuickCanvas *canvas, const QSize &size);
194 void handleDeferredUpdate();
195 void maybeUpdate(QQuickCanvas *canvas);
198 void startRendering();
199 void stopRendering();
201 void exhaustSyncEvent();
202 void sync(bool guiAlreadyLocked);
206 volatile bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
208 bool event(QEvent *);
210 inline void lock() { mutex.lock(); }
211 inline void unlock() { mutex.unlock(); }
212 inline void wait() { condition.wait(&mutex); }
213 inline void wake() { condition.wakeOne(); }
219 QQuickCanvas *masterCanvas() {
220 QQuickCanvas *win = 0;
221 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
222 it != m_rendered_windows.constEnd() && !win; ++it) {
223 if (it.value()->isVisible)
230 void animationStarted();
231 void animationStopped();
232 void canvasVisibilityChanged();
235 void handleAddedWindows();
236 void handleAddedWindow(QQuickCanvas *canvas);
237 void handleRemovedWindows();
241 QAnimationDriver *animationDriver;
245 QWaitCondition condition;
247 volatile bool allowMainThreadProcessingFlag;
250 uint animationRunning: 1;
251 uint isPaintCompleted : 1;
252 uint isPostingSyncEvent : 1;
253 uint isRenderBlocked : 1;
254 uint isExternalUpdatePending : 1;
255 uint syncAlreadyHappened : 1;
259 uint isDeferredUpdatePosted : 1;
261 QQuickCanvas *canvasToGrab;
269 uint sizeWasChanged : 1;
273 QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
275 struct CanvasTracker {
276 QQuickCanvas *canvas;
278 uint toBeRemoved : 1;
281 QList<CanvasTracker> m_tracked_windows;
283 QList<QQuickCanvas *> m_removed_windows;
284 QList<QQuickCanvas *> m_added_windows;
288 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
291 QQuickTrivialWindowManager();
293 void show(QQuickCanvas *canvas);
294 void hide(QQuickCanvas *canvas);
296 void canvasDestroyed(QQuickCanvas *canvas);
299 void renderCanvas(QQuickCanvas *canvas);
300 void exposureChanged(QQuickCanvas *canvas);
301 QImage grab(QQuickCanvas *canvas);
302 void resize(QQuickCanvas *canvas, const QSize &size);
305 void maybeUpdate(QQuickCanvas *canvas);
307 void releaseResources() { }
309 volatile bool *allowMainThreadProcessing();
311 QSGContext *sceneGraphContext() const;
313 bool event(QEvent *);
316 bool updatePending : 1;
320 QHash<QQuickCanvas *, CanvasData> m_windows;
331 QQuickWindowManager *QQuickWindowManager::instance()
333 static QQuickWindowManager *theInstance;
337 theInstance = QSGContext::createWindowManager();
339 bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL);
340 if (qmlNoThreadedRenderer())
343 if (qmlFixedAnimationStep())
344 QUnifiedTimer::instance(true)->setConsistentTiming(true);
348 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
349 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
359 void QQuickRenderThreadSingleContextWindowManager::initialize()
361 Q_ASSERT(m_rendered_windows.size());
363 QQuickCanvas *win = masterCanvas();
367 gl = new QOpenGLContext();
368 // Pick up the surface format from one of them
369 gl->setFormat(win->requestedFormat());
371 if (!gl->makeCurrent(win))
372 qWarning("QQuickCanvas: makeCurrent() failed...");
374 Q_ASSERT(!sg->isReady());
380 This function is called when the canvas is created to register the canvas with
383 Called on GUI Thread.
386 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
389 printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
392 CanvasTracker tracker;
393 tracker.canvas = canvas;
394 tracker.isVisible = false;
395 tracker.toBeRemoved = false;
396 m_tracked_windows << tracker;
398 connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
399 connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
401 canvasVisibilityChanged();
405 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
408 printf(" RenderThread: adding canvas: %p\n", canvas);
411 CanvasData *data = new CanvasData;
412 data->sizeWasChanged = false;
413 data->windowSize = canvas->size();
414 data->isVisible = canvas->visible();
415 m_rendered_windows[canvas] = data;
417 isExternalUpdatePending = true;
422 Called on Render Thread
424 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
427 printf(" RenderThread: about to add %d\n", m_added_windows.size());
430 while (m_added_windows.size()) {
431 QQuickCanvas *canvas = m_added_windows.takeLast();
432 handleAddedWindow(canvas);
438 Called on the GUI Thread, from the canvas' destructor
441 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
444 printf("GUI: Canvas destroyed: %p\n", canvas);
455 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
458 printf("GUI: Canvas hidden: %p\n", canvas);
462 for (int i=0; i<m_tracked_windows.size(); ++i) {
463 if (m_tracked_windows.at(i).canvas == canvas) {
464 m_tracked_windows[i].toBeRemoved = true;
471 disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
472 disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
473 canvasVisibilityChanged();
474 m_tracked_windows.removeAt(position);
478 printf("GUI: Canvas removal completed... %p\n", canvas);
483 Called on Render Thread
485 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
488 printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
491 bool removedAnything = false;
492 while (m_removed_windows.size()) {
493 QQuickCanvas *canvas = m_removed_windows.takeLast();
495 printf(" RenderThread: removing %p\n", canvas);
498 QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
499 delete m_rendered_windows.take(canvas);
500 removedAnything = true;
503 // If a window is removed because it has been hidden it will take with it
504 // the gl context (at least on Mac) if bound, so disconnect the gl context
516 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
518 bool anyoneShowing = false;
519 QList<QQuickCanvas *> toAdd, toRemove;
521 // Not optimal, but also not frequently used...
522 for (int i=0; i<m_tracked_windows.size(); ++i) {
523 CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
524 QQuickCanvas *win = t.canvas;
526 Q_ASSERT(win->visible() || QQuickCanvasPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
527 bool canvasVisible = win->width() > 0 && win->height() > 0;
528 anyoneShowing |= (canvasVisible && !t.toBeRemoved);
530 if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
532 } else if (canvasVisible && !t.isVisible) {
535 t.isVisible = canvasVisible;
539 if (!anyoneShowing) {
544 m_added_windows << toAdd;
545 m_removed_windows << toRemove;
546 while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
554 } else if (anyoneShowing) {
555 Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
556 for (int i=0; i<toAdd.size(); ++i)
557 handleAddedWindow(toAdd.at(i));
564 void QQuickRenderThreadSingleContextWindowManager::run()
567 printf("QML Rendering Thread Started\n");
573 // Wake GUI as it is waiting for the GL context to have appeared, as
574 // an indication that the render thread is now running.
581 while (!shouldExit) {
585 printf(" RenderThread: *** NEW FRAME ***\n");
588 isExternalUpdatePending = false;
589 handleAddedWindows();
592 isPostingSyncEvent = true;
595 printf(" RenderThread: aquired sync lock...\n");
597 allowMainThreadProcessingFlag = false;
598 QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
601 printf(" RenderThread: going to sleep...\n");
603 wake(); // In case the event got through all the way to wait() before this thread got to wait.
607 isPostingSyncEvent = false;
611 printf(" RenderThread: Doing locked sync\n");
613 #ifdef QQUICK_CANVAS_TIMING
614 if (qquick_canvas_timing)
618 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
619 it != m_rendered_windows.constEnd(); ++it) {
620 QQuickCanvas *canvas = it.key();
623 printf(" RenderThread: Syncing canvas: %p\n", canvas);
626 CanvasData *canvasData = it.value();
627 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
629 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
631 if (!canvasData->isVisible)
632 gl->makeCurrent(masterCanvas());
634 gl->makeCurrent(canvas);
636 if (canvasData->viewportSize != canvasData->windowSize) {
638 printf(" RenderThread: --- window has changed size...\n");
640 canvasData->viewportSize = canvasData->windowSize;
641 canvasData->sizeWasChanged = true;
642 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
645 canvasPrivate->syncSceneGraph();
649 // Wake GUI after sync to let it continue animating and event processing.
650 allowMainThreadProcessingFlag = true;
654 printf(" RenderThread: sync done\n");
656 #ifdef QQUICK_CANVAS_TIMING
657 if (qquick_canvas_timing)
658 syncTime = threadTimer.elapsed();
661 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
662 it != m_rendered_windows.constEnd(); ++it) {
663 QQuickCanvas *canvas = it.key();
664 CanvasData *canvasData = it.value();
665 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
668 printf(" RenderThread: Rendering canvas %p\n", canvas);
671 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
674 printf(" RenderThread: --- rendering at size %dx%d\n",
675 canvasData->viewportSize.width(), canvasData->viewportSize.height()
679 // We only need to re-makeCurrent when we have multiple surfaces.
680 if (m_rendered_windows.size() > 1)
681 gl->makeCurrent(canvas);
683 canvasPrivate->renderSceneGraph(canvasData->viewportSize);
684 #ifdef QQUICK_CANVAS_TIMING
685 if (qquick_canvas_timing)
686 renderTime = threadTimer.elapsed() - syncTime;
689 // The content of the target buffer is undefined after swap() so grab needs
690 // to happen before swap();
691 if (canvas == canvasToGrab) {
693 printf(" RenderThread: --- grabbing...\n");
695 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
700 printf(" RenderThread: --- wait for swap...\n");
703 if (canvasData->isVisible)
704 gl->swapBuffers(canvas);
706 canvasPrivate->fireFrameSwapped();
708 printf(" RenderThread: --- swap complete...\n");
713 #ifdef QQUICK_CANVAS_TIMING
714 if (qquick_canvas_timing) {
715 swapTime = threadTimer.elapsed() - renderTime;
716 qDebug() << "- Breakdown of frame time; sync:" << syncTime
717 << "ms render:" << renderTime << "ms swap:" << swapTime
718 << "ms total:" << swapTime + renderTime << "ms";
724 handleRemovedWindows();
726 isPaintCompleted = true;
729 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
730 it != m_rendered_windows.constEnd(); ++it) {
731 CanvasData *canvasData = it.value();
732 if (canvasData->sizeWasChanged) {
733 canvasData->renderedSize = canvasData->viewportSize;
734 canvasData->sizeWasChanged = false;
739 // Wake the GUI thread now that rendering is complete, to signal that painting
740 // is done, resizing is done or grabbing is completed. For grabbing, we're
741 // signalling this much later than needed (we could have done it before swap)
742 // but we don't want to lock an extra time.
745 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
747 printf(" RenderThread: nothing to do, going to sleep...\n");
749 isRenderBlocked = true;
751 isRenderBlocked = false;
756 QCoreApplication::processEvents();
758 // Process any "deleteLater" objects...
759 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
763 printf(" RenderThread: deleting all outstanding nodes\n");
766 m_removed_windows << m_rendered_windows.keys();
767 handleRemovedWindows();
776 printf(" RenderThread: render loop exited... Good Night!\n");
783 printf(" RenderThread: waking GUI for final sleep..\n");
789 printf(" RenderThread: All done...\n");
793 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
795 Q_ASSERT(QThread::currentThread() == qApp->thread());
797 if (e->type() == QEvent_Sync) {
799 // If all canvases have been hidden, ignore the event
803 if (!syncAlreadyHappened)
806 syncAlreadyHappened = false;
808 if (animationRunning) {
810 printf("GUI: Advancing animations...\n");
813 animationDriver->advance();
816 printf("GUI: Animations advanced...\n");
821 } else if (e->type() == QEvent_DeferredUpdate) {
822 handleDeferredUpdate();
824 } else if (e->type() == QEvent::Timer) {
826 printf("GUI: Animations advanced via timer...\n");
828 animationDriver->advance();
831 return QThread::event(e);
836 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
838 if (isPostingSyncEvent) {
840 syncAlreadyHappened = true;
846 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
849 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
851 if (!guiAlreadyLocked)
854 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
855 it != m_rendered_windows.constEnd(); ++it) {
856 QQuickCanvasPrivate::get(it.key())->polishItems();
862 if (!guiAlreadyLocked)
870 Acquires the mutex for the GUI thread. The function uses the isGuiLocked
871 variable to keep track of how many recursion levels the gui is locked with.
872 We only actually acquire the mutex for the first level to avoid deadlocking
876 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
878 if (++isGuiLocked == 1)
882 printf("GUI: aquired lock... level=%d\n", isGuiLocked);
888 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
891 printf("GUI: releasing lock... level=%d\n", isGuiLocked);
894 if (--isGuiLocked == 0)
901 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
904 printf("GUI: animationStarted()\n");
908 animationTimer = startTimer(1000/60);
914 animationRunning = true;
924 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
927 printf("GUI: animationStopped()...\n");
931 killTimer(animationTimer);
937 animationRunning = false;
942 void QQuickRenderThreadSingleContextWindowManager::exposureChanged(QQuickCanvas *canvas)
946 printf("GUI: exposure changed: %p\n", canvas);
950 printf("GUI: exposure changed done: %p\n", canvas);
956 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
959 printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
962 // If the rendering thread is not running we do not need to do anything.
963 // Also if the canvas is being resized to an invalid size, it will be removed
964 // by the canvasVisibilityChanged slot as result of width/heightcChanged()
965 if (!isRunning() || size.width() <= 0 || size.height() <= 0)
971 CanvasData *canvasData = m_rendered_windows.value(canvas);
973 canvasData->windowSize = size;
974 while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
983 printf("GUI: Resize done: %p\n", canvas);
989 void QQuickRenderThreadSingleContextWindowManager::startRendering()
992 printf("GUI: Starting Render Thread\n");
997 isPostingSyncEvent = false;
998 syncAlreadyHappened = false;
1002 animationRunning = animationDriver->isRunning();
1003 start(); // Start the render thread...
1007 // Animations will now be driven from the rendering thread.
1008 if (animationTimer >= 0) {
1009 killTimer(animationTimer);
1010 animationTimer = -1;
1018 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1021 printf("GUI: stopping render thread\n");
1028 if (isRenderBlocked) {
1030 printf("GUI: waking up render thread\n");
1035 while (!hasExited) {
1037 printf("GUI: waiting for render thread to have exited..\n");
1045 printf("GUI: waiting for render thread to terminate..\n");
1047 // Actually wait for the thread to terminate. Otherwise we can delete it
1048 // too early and crash.
1052 printf("GUI: thread has terminated and we're all good..\n");
1055 // Activate timer to keep animations running
1056 if (animationDriver->isRunning())
1057 animationTimer = startTimer(1000/60);
1062 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1067 if (QThread::currentThread() != qApp->thread()) {
1068 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1073 printf("GUI: doing a pixelwise grab..\n");
1079 canvasToGrab = canvas;
1080 isPaintCompleted = false;
1081 while (isRunning() && !isPaintCompleted) {
1082 if (isRenderBlocked)
1087 QImage grabbed = grabContent;
1088 grabContent = QImage();
1096 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1099 printf("GUI: handling update to ourselves...\n");
1102 isDeferredUpdatePosted = false;
1105 isExternalUpdatePending = true;
1106 if (isRenderBlocked)
1111 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1113 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1114 "QQuickCanvas::update",
1115 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1118 isExternalUpdatePending = true;
1120 } else if (!isDeferredUpdatePosted) {
1122 printf("GUI: posting update to ourselves...\n");
1124 isDeferredUpdatePosted = true;
1125 QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1130 void QQuickRenderThreadSingleContextWindowManager::wakeup()
1133 if (isRenderBlocked)
1138 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1140 , eventPending(false)
1142 sg = QSGContext::createDefaultContext();
1146 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1149 data.updatePending = false;
1150 data.grabOnly = false;
1151 m_windows[canvas] = data;
1153 maybeUpdate(canvas);
1156 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1158 if (!m_windows.contains(canvas))
1161 m_windows.remove(canvas);
1162 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1163 cd->cleanupNodesOnShutdown();
1165 if (m_windows.size() == 0) {
1172 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1177 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1179 if (!m_windows.contains(canvas))
1182 CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1184 QQuickCanvas *masterCanvas = 0;
1185 if (!canvas->visible()) {
1186 // Find a "proper surface" to bind...
1187 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1188 it != m_windows.constEnd() && !masterCanvas; ++it) {
1189 if (it.key()->visible())
1190 masterCanvas = it.key();
1193 masterCanvas = canvas;
1200 gl = new QOpenGLContext();
1201 gl->setFormat(masterCanvas->requestedFormat());
1203 if (!gl->makeCurrent(masterCanvas))
1204 qWarning("QQuickCanvas: makeCurrent() failed...");
1207 gl->makeCurrent(masterCanvas);
1210 bool alsoSwap = data.updatePending;
1211 data.updatePending = false;
1213 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1215 cd->syncSceneGraph();
1216 cd->renderSceneGraph(canvas->size());
1218 if (data.grabOnly) {
1219 grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1220 data.grabOnly = false;
1223 if (alsoSwap && canvas->visible()) {
1224 gl->swapBuffers(canvas);
1225 cd->fireFrameSwapped();
1228 // Might have been set during syncSceneGraph()
1229 if (data.updatePending)
1230 maybeUpdate(canvas);
1233 void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *canvas)
1237 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1239 if (!m_windows.contains(canvas))
1242 m_windows[canvas].grabOnly = true;
1244 renderCanvas(canvas);
1246 QImage grabbed = grabContent;
1247 grabContent = QImage();
1253 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1259 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1261 if (!m_windows.contains(canvas))
1264 m_windows[canvas].updatePending = true;
1266 if (!eventPending) {
1267 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1268 eventPending = true;
1272 void QQuickTrivialWindowManager::wakeup()
1276 volatile bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1283 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1289 bool QQuickTrivialWindowManager::event(QEvent *e)
1291 if (e->type() == QEvent::User) {
1292 eventPending = false;
1293 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1294 it != m_windows.constEnd(); ++it) {
1295 const CanvasData &data = it.value();
1296 if (data.updatePending)
1297 renderCanvas(it.key());
1301 return QObject::event(e);
1304 #include "qquickwindowmanager.moc"