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::isPostingSyncEvent: This variable is set in the render thread just
111 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
112 in the case where render thread waits while waiting for GUI to pick up the sync
113 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
114 When this happens, we use the
115 exhaustSyncEvent() function to do the sync right there and mark the coming
116 sync event to be discarded. There can only ever be one sync incoming.
118 RenderThread::isRenderBlock: This variable is true when animations are not
119 running and the render thread has gone to sleep, waiting for more to do.
121 RenderThread::isExternalUpdatePending: This variable is set to false when
122 a new render pass is started and to true in maybeUpdate(). It is an
123 indication to the render thread that another render pass needs to take
124 place, rather than the render thread going to sleep after completing its swap.
126 RenderThread::doGrab: This variable is set by the grab() function and
127 tells the renderer to do a grab after rendering is complete and before
130 RenderThread::shouldExit: This variable is used to determine if the render
131 thread should do a nother pass. It is typically set as a result of show()
132 and unset as a result of hide() or during shutdown()
134 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
135 after shouldExit has been set to true.
138 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP);
139 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
141 //#define THREAD_DEBUG
143 QQuickWindowManager::~QQuickWindowManager()
147 class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
151 QQuickRenderThreadSingleContextWindowManager()
152 : sg(QSGContext::createDefaultContext())
155 , allowMainThreadProcessingFlag(false)
157 , animationRunning(false)
158 , isPostingSyncEvent(false)
159 , isRenderBlocked(false)
160 , isExternalUpdatePending(false)
161 , syncAlreadyHappened(false)
165 , isDeferredUpdatePosted(false)
168 sg->moveToThread(this);
170 animationDriver = sg->createAnimationDriver(this);
171 animationDriver->install();
172 connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
173 connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
176 QSGContext *sceneGraphContext() const { return sg; }
178 void releaseResources() { }
180 void show(QQuickCanvas *canvas);
181 void hide(QQuickCanvas *canvas);
183 void canvasDestroyed(QQuickCanvas *canvas);
185 void exposureChanged(QQuickCanvas *canvas);
186 QImage grab(QQuickCanvas *canvas);
187 void resize(QQuickCanvas *canvas, const QSize &size);
188 void handleDeferredUpdate();
189 void maybeUpdate(QQuickCanvas *canvas);
192 void startRendering();
193 void stopRendering();
195 void exhaustSyncEvent();
196 void sync(bool guiAlreadyLocked);
200 volatile bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
202 bool event(QEvent *);
204 inline void lock() { mutex.lock(); }
205 inline void unlock() { mutex.unlock(); }
206 inline void wait() { condition.wait(&mutex); }
207 inline void wake() { condition.wakeOne(); }
213 QQuickCanvas *masterCanvas() {
214 QQuickCanvas *win = 0;
215 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
216 it != m_rendered_windows.constEnd() && !win; ++it) {
217 if (it.value()->isVisible)
224 void animationStarted();
225 void animationStopped();
226 void canvasVisibilityChanged();
229 void handleAddedWindows();
230 void handleAddedWindow(QQuickCanvas *canvas);
231 void handleRemovedWindows(bool clearGLContext = true);
235 QAnimationDriver *animationDriver;
239 QWaitCondition condition;
241 volatile bool allowMainThreadProcessingFlag;
244 uint animationRunning: 1;
245 uint isPostingSyncEvent : 1;
246 uint isRenderBlocked : 1;
247 uint isExternalUpdatePending : 1;
248 uint syncAlreadyHappened : 1;
252 uint isDeferredUpdatePosted : 1;
254 QQuickCanvas *canvasToGrab;
262 uint sizeWasChanged : 1;
266 QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
268 struct CanvasTracker {
269 QQuickCanvas *canvas;
271 uint toBeRemoved : 1;
274 QList<CanvasTracker> m_tracked_windows;
276 QList<QQuickCanvas *> m_removed_windows;
277 QList<QQuickCanvas *> m_added_windows;
281 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
284 QQuickTrivialWindowManager();
286 void show(QQuickCanvas *canvas);
287 void hide(QQuickCanvas *canvas);
289 void canvasDestroyed(QQuickCanvas *canvas);
292 void renderCanvas(QQuickCanvas *canvas);
293 void exposureChanged(QQuickCanvas *canvas);
294 QImage grab(QQuickCanvas *canvas);
295 void resize(QQuickCanvas *canvas, const QSize &size);
298 void maybeUpdate(QQuickCanvas *canvas);
300 void releaseResources() { }
302 volatile bool *allowMainThreadProcessing();
304 QSGContext *sceneGraphContext() const;
306 bool event(QEvent *);
309 bool updatePending : 1;
313 QHash<QQuickCanvas *, CanvasData> m_windows;
324 QQuickWindowManager *QQuickWindowManager::instance()
326 static QQuickWindowManager *theInstance;
330 theInstance = QSGContext::createWindowManager();
332 bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL);
333 if (qmlNoThreadedRenderer())
336 if (qmlFixedAnimationStep())
337 QUnifiedTimer::instance(true)->setConsistentTiming(true);
341 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
342 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
352 void QQuickRenderThreadSingleContextWindowManager::initialize()
354 Q_ASSERT(m_rendered_windows.size());
356 QQuickCanvas *win = masterCanvas();
360 gl = new QOpenGLContext();
361 // Pick up the surface format from one of them
362 gl->setFormat(win->requestedFormat());
364 if (!gl->makeCurrent(win))
365 qWarning("QQuickCanvas: makeCurrent() failed...");
367 Q_ASSERT(!sg->isReady());
373 This function is called when the canvas is created to register the canvas with
376 Called on GUI Thread.
379 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
382 printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
385 CanvasTracker tracker;
386 tracker.canvas = canvas;
387 tracker.isVisible = false;
388 tracker.toBeRemoved = false;
389 m_tracked_windows << tracker;
391 connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
392 connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
394 canvasVisibilityChanged();
398 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
401 printf(" RenderThread: adding canvas: %p\n", canvas);
404 CanvasData *data = new CanvasData;
405 data->sizeWasChanged = false;
406 data->windowSize = canvas->size();
407 data->isVisible = canvas->isVisible();
408 m_rendered_windows[canvas] = data;
410 isExternalUpdatePending = true;
415 Called on Render Thread
417 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
420 printf(" RenderThread: about to add %d\n", m_added_windows.size());
423 while (m_added_windows.size()) {
424 QQuickCanvas *canvas = m_added_windows.takeLast();
425 handleAddedWindow(canvas);
431 Called on the GUI Thread, from the canvas' destructor
434 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
437 printf("GUI: Canvas destroyed: %p\n", canvas);
448 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
451 printf("GUI: Canvas hidden: %p\n", canvas);
455 for (int i=0; i<m_tracked_windows.size(); ++i) {
456 if (m_tracked_windows.at(i).canvas == canvas) {
457 m_tracked_windows[i].toBeRemoved = true;
464 disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
465 disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
466 canvasVisibilityChanged();
467 m_tracked_windows.removeAt(position);
471 printf("GUI: Canvas removal completed... %p\n", canvas);
476 Called on Render Thread
478 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows(bool clearGLContext)
481 printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
484 bool removedAnything = false;
485 while (m_removed_windows.size()) {
486 QQuickCanvas *canvas = m_removed_windows.takeLast();
488 printf(" RenderThread: removing %p\n", canvas);
491 QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
492 delete m_rendered_windows.take(canvas);
493 removedAnything = true;
496 // If a window is removed because it has been hidden it will take with it
497 // the gl context (at least on Mac) if bound, so disconnect the gl context
499 if (removedAnything && clearGLContext)
509 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
511 bool anyoneShowing = false;
512 QList<QQuickCanvas *> toAdd, toRemove;
514 // Not optimal, but also not frequently used...
515 for (int i=0; i<m_tracked_windows.size(); ++i) {
516 CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
517 QQuickCanvas *win = t.canvas;
519 Q_ASSERT(win->isVisible() || QQuickCanvasPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
520 bool canvasVisible = win->width() > 0 && win->height() > 0;
521 anyoneShowing |= (canvasVisible && !t.toBeRemoved);
523 if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
525 } else if (canvasVisible && !t.isVisible) {
528 t.isVisible = canvasVisible;
532 if (!anyoneShowing) {
537 m_added_windows << toAdd;
538 m_removed_windows << toRemove;
539 while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
547 } else if (anyoneShowing) {
548 Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
549 for (int i=0; i<toAdd.size(); ++i)
550 handleAddedWindow(toAdd.at(i));
557 void QQuickRenderThreadSingleContextWindowManager::run()
560 printf("QML Rendering Thread Started\n");
566 // Wake GUI as it is waiting for the GL context to have appeared, as
567 // an indication that the render thread is now running.
574 while (!shouldExit) {
578 printf(" RenderThread: *** NEW FRAME ***\n");
581 isExternalUpdatePending = false;
582 handleAddedWindows();
585 isPostingSyncEvent = true;
588 printf(" RenderThread: aquired sync lock...\n");
590 allowMainThreadProcessingFlag = false;
591 QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
594 printf(" RenderThread: going to sleep...\n");
596 wake(); // In case the event got through all the way to wait() before this thread got to wait.
600 isPostingSyncEvent = false;
604 printf(" RenderThread: Doing locked sync\n");
606 #ifdef QQUICK_CANVAS_TIMING
607 if (qquick_canvas_timing)
611 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
612 it != m_rendered_windows.constEnd(); ++it) {
613 QQuickCanvas *canvas = it.key();
616 printf(" RenderThread: Syncing canvas: %p\n", canvas);
619 CanvasData *canvasData = it.value();
620 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
622 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
624 if (!canvasData->isVisible)
625 gl->makeCurrent(masterCanvas());
627 gl->makeCurrent(canvas);
629 if (canvasData->viewportSize != canvasData->windowSize) {
631 printf(" RenderThread: --- window has changed size...\n");
633 canvasData->viewportSize = canvasData->windowSize;
634 canvasData->sizeWasChanged = true;
635 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
638 canvasPrivate->syncSceneGraph();
642 // Wake GUI after sync to let it continue animating and event processing.
643 allowMainThreadProcessingFlag = true;
647 printf(" RenderThread: sync done\n");
649 #ifdef QQUICK_CANVAS_TIMING
650 if (qquick_canvas_timing)
651 syncTime = threadTimer.elapsed();
654 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
655 it != m_rendered_windows.constEnd(); ++it) {
656 QQuickCanvas *canvas = it.key();
657 CanvasData *canvasData = it.value();
658 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
661 printf(" RenderThread: Rendering canvas %p\n", canvas);
664 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
667 printf(" RenderThread: --- rendering at size %dx%d\n",
668 canvasData->viewportSize.width(), canvasData->viewportSize.height()
672 // We only need to re-makeCurrent when we have multiple surfaces.
673 if (m_rendered_windows.size() > 1)
674 gl->makeCurrent(canvas);
676 canvasPrivate->renderSceneGraph(canvasData->viewportSize);
677 #ifdef QQUICK_CANVAS_TIMING
678 if (qquick_canvas_timing)
679 renderTime = threadTimer.elapsed() - syncTime;
682 // The content of the target buffer is undefined after swap() so grab needs
683 // to happen before swap();
684 if (canvas == canvasToGrab) {
686 printf(" RenderThread: --- grabbing...\n");
688 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
693 printf(" RenderThread: --- wait for swap...\n");
696 if (canvasData->isVisible)
697 gl->swapBuffers(canvas);
699 canvasPrivate->fireFrameSwapped();
701 printf(" RenderThread: --- swap complete...\n");
706 #ifdef QQUICK_CANVAS_TIMING
707 if (qquick_canvas_timing) {
708 swapTime = threadTimer.elapsed() - renderTime;
709 qDebug() << "- Breakdown of frame time; sync:" << syncTime
710 << "ms render:" << renderTime << "ms swap:" << swapTime
711 << "ms total:" << swapTime + renderTime << "ms";
717 handleRemovedWindows();
720 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
721 it != m_rendered_windows.constEnd(); ++it) {
722 CanvasData *canvasData = it.value();
723 if (canvasData->sizeWasChanged) {
724 canvasData->renderedSize = canvasData->viewportSize;
725 canvasData->sizeWasChanged = false;
730 // Wake the GUI thread now that rendering is complete, to signal that painting
731 // is done, resizing is done or grabbing is completed. For grabbing, we're
732 // signalling this much later than needed (we could have done it before swap)
733 // but we don't want to lock an extra time.
736 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
738 printf(" RenderThread: nothing to do, going to sleep...\n");
740 isRenderBlocked = true;
742 isRenderBlocked = false;
747 QCoreApplication::processEvents();
749 // Process any "deleteLater" objects...
750 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
754 printf(" RenderThread: deleting all outstanding nodes\n");
757 m_removed_windows << m_rendered_windows.keys();
758 handleRemovedWindows(false);
767 printf(" RenderThread: render loop exited... Good Night!\n");
774 printf(" RenderThread: waking GUI for final sleep..\n");
780 printf(" RenderThread: All done...\n");
784 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
786 Q_ASSERT(QThread::currentThread() == qApp->thread());
788 if (e->type() == QEvent_Sync) {
790 // If all canvases have been hidden, ignore the event
794 if (!syncAlreadyHappened)
797 syncAlreadyHappened = false;
799 if (animationRunning) {
801 printf("GUI: Advancing animations...\n");
804 animationDriver->advance();
807 printf("GUI: Animations advanced...\n");
812 } else if (e->type() == QEvent_DeferredUpdate) {
813 handleDeferredUpdate();
815 } else if (e->type() == QEvent::Timer) {
817 printf("GUI: Animations advanced via timer...\n");
819 animationDriver->advance();
822 return QThread::event(e);
827 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
829 if (isPostingSyncEvent) {
831 syncAlreadyHappened = true;
837 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
840 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
842 if (!guiAlreadyLocked)
845 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
846 it != m_rendered_windows.constEnd(); ++it) {
847 QQuickCanvasPrivate::get(it.key())->polishItems();
853 if (!guiAlreadyLocked)
861 Acquires the mutex for the GUI thread. The function uses the isGuiLocked
862 variable to keep track of how many recursion levels the gui is locked with.
863 We only actually acquire the mutex for the first level to avoid deadlocking
867 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
869 if (++isGuiLocked == 1)
873 printf("GUI: aquired lock... level=%d\n", isGuiLocked);
879 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
882 printf("GUI: releasing lock... level=%d\n", isGuiLocked);
885 if (--isGuiLocked == 0)
892 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
895 printf("GUI: animationStarted()\n");
899 animationTimer = startTimer(1000/60);
905 animationRunning = true;
915 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
918 printf("GUI: animationStopped()...\n");
922 killTimer(animationTimer);
928 animationRunning = false;
933 void QQuickRenderThreadSingleContextWindowManager::exposureChanged(QQuickCanvas *canvas)
937 printf("GUI: exposure changed: %p\n", canvas);
941 printf("GUI: exposure changed done: %p\n", canvas);
947 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
950 printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
953 // If the rendering thread is not running we do not need to do anything.
954 // Also if the canvas is being resized to an invalid size, it will be removed
955 // by the canvasVisibilityChanged slot as result of width/heightcChanged()
956 if (!isRunning() || size.width() <= 0 || size.height() <= 0)
962 CanvasData *canvasData = m_rendered_windows.value(canvas);
964 canvasData->windowSize = size;
965 while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
974 printf("GUI: Resize done: %p\n", canvas);
980 void QQuickRenderThreadSingleContextWindowManager::startRendering()
983 printf("GUI: Starting Render Thread\n");
988 isPostingSyncEvent = false;
989 syncAlreadyHappened = false;
993 animationRunning = animationDriver->isRunning();
994 start(); // Start the render thread...
998 // Animations will now be driven from the rendering thread.
999 if (animationTimer >= 0) {
1000 killTimer(animationTimer);
1001 animationTimer = -1;
1009 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1012 printf("GUI: stopping render thread\n");
1019 if (isRenderBlocked) {
1021 printf("GUI: waking up render thread\n");
1026 while (!hasExited) {
1028 printf("GUI: waiting for render thread to have exited..\n");
1036 printf("GUI: waiting for render thread to terminate..\n");
1038 // Actually wait for the thread to terminate. Otherwise we can delete it
1039 // too early and crash.
1043 printf("GUI: thread has terminated and we're all good..\n");
1046 // Activate timer to keep animations running
1047 if (animationDriver->isRunning())
1048 animationTimer = startTimer(1000/60);
1053 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1058 if (QThread::currentThread() != qApp->thread()) {
1059 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1064 printf("GUI: doing a pixelwise grab..\n");
1070 canvasToGrab = canvas;
1071 while (isRunning() && canvasToGrab) {
1072 if (isRenderBlocked)
1077 QImage grabbed = grabContent;
1078 grabContent = QImage();
1086 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1089 printf("GUI: handling update to ourselves...\n");
1092 isDeferredUpdatePosted = false;
1095 isExternalUpdatePending = true;
1096 if (isRenderBlocked)
1101 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1103 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1104 "QQuickCanvas::update",
1105 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1108 isExternalUpdatePending = true;
1110 } else if (!isDeferredUpdatePosted) {
1112 printf("GUI: posting update to ourselves...\n");
1114 isDeferredUpdatePosted = true;
1115 QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1120 void QQuickRenderThreadSingleContextWindowManager::wakeup()
1123 isExternalUpdatePending = true;
1124 if (isRenderBlocked)
1129 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1131 , eventPending(false)
1133 sg = QSGContext::createDefaultContext();
1137 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1140 data.updatePending = false;
1141 data.grabOnly = false;
1142 m_windows[canvas] = data;
1144 maybeUpdate(canvas);
1147 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1149 if (!m_windows.contains(canvas))
1152 m_windows.remove(canvas);
1153 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1154 cd->cleanupNodesOnShutdown();
1156 if (m_windows.size() == 0) {
1163 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1168 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1170 if (!m_windows.contains(canvas))
1173 CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1175 QQuickCanvas *masterCanvas = 0;
1176 if (!canvas->isVisible()) {
1177 // Find a "proper surface" to bind...
1178 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1179 it != m_windows.constEnd() && !masterCanvas; ++it) {
1180 if (it.key()->isVisible())
1181 masterCanvas = it.key();
1184 masterCanvas = canvas;
1191 gl = new QOpenGLContext();
1192 gl->setFormat(masterCanvas->requestedFormat());
1194 if (!gl->makeCurrent(masterCanvas))
1195 qWarning("QQuickCanvas: makeCurrent() failed...");
1198 gl->makeCurrent(masterCanvas);
1201 bool alsoSwap = data.updatePending;
1202 data.updatePending = false;
1204 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1206 cd->syncSceneGraph();
1207 cd->renderSceneGraph(canvas->size());
1209 if (data.grabOnly) {
1210 grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1211 data.grabOnly = false;
1214 if (alsoSwap && canvas->isVisible()) {
1215 gl->swapBuffers(canvas);
1216 cd->fireFrameSwapped();
1219 // Might have been set during syncSceneGraph()
1220 if (data.updatePending)
1221 maybeUpdate(canvas);
1224 void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *)
1228 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1230 if (!m_windows.contains(canvas))
1233 m_windows[canvas].grabOnly = true;
1235 renderCanvas(canvas);
1237 QImage grabbed = grabContent;
1238 grabContent = QImage();
1244 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1250 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1252 if (!m_windows.contains(canvas))
1255 m_windows[canvas].updatePending = true;
1257 if (!eventPending) {
1258 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1259 eventPending = true;
1263 void QQuickTrivialWindowManager::wakeup()
1267 volatile bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1274 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1280 bool QQuickTrivialWindowManager::event(QEvent *e)
1282 if (e->type() == QEvent::User) {
1283 eventPending = false;
1284 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1285 it != m_windows.constEnd(); ++it) {
1286 const CanvasData &data = it.value();
1287 if (data.updatePending)
1288 renderCanvas(it.key());
1292 return QObject::event(e);
1295 #include "qquickwindowmanager.moc"