1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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>
53 #include <QtDeclarative/private/qdeclarativeglobal_p.h>
55 #include <QtQuick/QQuickCanvas>
56 #include <QtQuick/private/qquickcanvas_p.h>
57 #include <QtQuick/private/qsgcontext_p.h>
61 const QEvent::Type QEvent_Sync = QEvent::Type(QEvent::User);
62 const QEvent::Type QEvent_DeferredUpdate = QEvent::Type(QEvent::User + 1);
65 #define QQUICK_CANVAS_TIMING
66 #ifdef QQUICK_CANVAS_TIMING
67 static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty();
68 static QTime threadTimer;
70 static int renderTime;
74 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
79 expectations for this manager to work:
80 - one opengl context to render multiple windows
81 - OpenGL pipeline will not block for vsync in swap
82 - OpenGL pipeline will block based on a full buffer queue.
83 - Multiple screens can share the OpenGL context
84 - Animations are advanced for all windows once per swap
91 The threaded rendering uses a number of different variables to track potential
92 states used to handle resizing, initial paint, grabbing and driving animations
93 while ALWAYS keeping the GL context in the rendering thread and keeping the
94 overhead of normal one-shot paints and vblank driven animations at a minimum.
96 Resize, initial show and grab suffer slightly in this model as they are locked
97 to the rendering in the rendering thread, but this is a necessary evil for
100 Variables that are used:
102 Private::animationRunning: This is true while the animations are running, and only
103 written to inside locks.
105 RenderThread::isGuiLocked: This is used to indicate that the GUI thread owns the
106 lock. This variable is an integer to allow for recursive calls to lockInGui()
107 without using a recursive mutex. See isPostingSyncEvent.
109 RenderThread::isPaintComplete: This variable is cleared when rendering starts and
110 set once rendering is complete. It is monitored in the paintEvent(),
111 resizeEvent() and grab() functions to force them to wait for rendering to
114 RenderThread::isPostingSyncEvent: This variable is set in the render thread just
115 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
116 in the case where render thread waits while waiting for GUI to pick up the sync
117 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
118 When this happens, we use the
119 exhaustSyncEvent() function to do the sync right there and mark the coming
120 sync event to be discarded. There can only ever be one sync incoming.
122 RenderThread::isRenderBlock: This variable is true when animations are not
123 running and the render thread has gone to sleep, waiting for more to do.
125 RenderThread::isExternalUpdatePending: This variable is set to false when
126 a new render pass is started and to true in maybeUpdate(). It is an
127 indication to the render thread that another render pass needs to take
128 place, rather than the render thread going to sleep after completing its swap.
130 RenderThread::doGrab: This variable is set by the grab() function and
131 tells the renderer to do a grab after rendering is complete and before
134 RenderThread::shouldExit: This variable is used to determine if the render
135 thread should do a nother pass. It is typically set as a result of show()
136 and unset as a result of hide() or during shutdown()
138 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
139 after shouldExit has been set to true.
142 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP);
143 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
145 //#define THREAD_DEBUG
147 class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
151 QQuickRenderThreadSingleContextWindowManager()
152 : sg(QSGContext::createDefaultContext())
156 , animationRunning(false)
157 , isPaintCompleted(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 show(QQuickCanvas *canvas);
179 void hide(QQuickCanvas *canvas);
181 void canvasDestroyed(QQuickCanvas *canvas);
183 void paint(QQuickCanvas *canvas);
184 QImage grab(QQuickCanvas *canvas);
185 void resize(QQuickCanvas *canvas, const QSize &size);
186 void handleDeferredUpdate();
187 void maybeUpdate(QQuickCanvas *canvas);
189 void startRendering();
190 void stopRendering();
192 void exhaustSyncEvent();
193 void sync(bool guiAlreadyLocked);
197 bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
199 bool event(QEvent *);
201 inline void lock() { mutex.lock(); }
202 inline void unlock() { mutex.unlock(); }
203 inline void wait() { condition.wait(&mutex); }
204 inline void wake() { condition.wakeOne(); }
211 void animationStarted();
212 void animationStopped();
213 void canvasVisibilityChanged();
216 void handleAddedWindows();
217 void handleAddedWindow(QQuickCanvas *canvas);
218 void handleRemovedWindows();
222 QAnimationDriver *animationDriver;
226 QWaitCondition condition;
228 bool allowMainThreadProcessingFlag;
231 uint animationRunning: 1;
232 uint isPaintCompleted : 1;
233 uint isPostingSyncEvent : 1;
234 uint isRenderBlocked : 1;
235 uint isExternalUpdatePending : 1;
236 uint syncAlreadyHappened : 1;
240 uint isDeferredUpdatePosted : 1;
242 QQuickCanvas *canvasToGrab;
250 uint sizeWasChanged : 1;
253 QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
255 struct CanvasTracker {
256 QQuickCanvas *canvas;
258 uint toBeRemoved : 1;
261 QList<CanvasTracker> m_tracked_windows;
263 QList<QQuickCanvas *> m_removed_windows;
264 QList<QQuickCanvas *> m_added_windows;
268 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
271 QQuickTrivialWindowManager();
273 void show(QQuickCanvas *canvas);
274 void hide(QQuickCanvas *canvas);
276 void canvasDestroyed(QQuickCanvas *canvas);
278 void renderCanvas(QQuickCanvas *canvas);
279 void paint(QQuickCanvas *canvas);
280 QImage grab(QQuickCanvas *canvas);
281 void resize(QQuickCanvas *canvas, const QSize &size);
283 void maybeUpdate(QQuickCanvas *canvas);
285 bool *allowMainThreadProcessing();
287 QSGContext *sceneGraphContext() const;
289 bool event(QEvent *);
292 bool updatePending : 1;
296 QHash<QQuickCanvas *, CanvasData> m_windows;
307 QQuickWindowManager *QQuickWindowManager::instance()
309 static QQuickWindowManager *theInstance;
312 bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
313 if (qmlNoThreadedRenderer())
315 if (qmlFixedAnimationStep())
316 QUnifiedTimer::instance(true)->setConsistentTiming(true);
318 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
319 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
328 void QQuickRenderThreadSingleContextWindowManager::initialize()
330 Q_ASSERT(m_rendered_windows.size());
331 QQuickCanvas *win = m_rendered_windows.constBegin().key();
333 gl = new QOpenGLContext();
334 // Pick up the surface format from one of them
335 gl->setFormat(win->requestedFormat());
337 gl->makeCurrent(win);
339 Q_ASSERT(!sg->isReady());
345 This function is called when the canvas is created to register the canvas with
348 Called on GUI Thread.
351 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
354 printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
357 CanvasTracker tracker;
358 tracker.canvas = canvas;
359 tracker.isVisible = false;
360 tracker.toBeRemoved = false;
361 m_tracked_windows << tracker;
363 connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
364 connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
366 canvasVisibilityChanged();
370 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
373 printf(" RenderThread: adding canvas: %p\n", canvas);
376 CanvasData *data = new CanvasData;
377 data->sizeWasChanged = false;
378 data->windowSize = canvas->size();
379 m_rendered_windows[canvas] = data;
381 isExternalUpdatePending = true;
386 Called on Render Thread
388 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
391 printf(" RenderThread: about to add %d\n", m_added_windows.size());
394 while (m_added_windows.size()) {
395 QQuickCanvas *canvas = m_added_windows.takeLast();
396 handleAddedWindow(canvas);
402 Called on the GUI Thread, from the canvas' destructor
405 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
408 printf("GUI: Canvas destroyed: %p\n", canvas);
419 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
422 printf("GUI: Canvas hidden: %p\n", canvas);
426 for (int i=0; i<m_tracked_windows.size(); ++i) {
427 if (m_tracked_windows.at(i).canvas == canvas) {
428 m_tracked_windows[i].toBeRemoved = true;
435 disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
436 disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
437 canvasVisibilityChanged();
438 m_tracked_windows.removeAt(position);
442 printf("GUI: Canvas removal completed... %p\n", canvas);
447 Called on Render Thread
449 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
452 printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
455 bool removedAnything = false;
456 while (m_removed_windows.size()) {
457 QQuickCanvas *canvas = m_removed_windows.takeLast();
459 printf(" RenderThread: removing %p\n", canvas);
462 QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
463 delete m_rendered_windows.take(canvas);
464 removedAnything = true;
467 // If a window is removed because it has been hidden it will take with it
468 // the gl context (at least on Mac) if bound, so disconnect the gl context
480 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
482 bool anyoneShowing = false;
483 QList<QQuickCanvas *> toAdd, toRemove;
485 // Not optimal, but also not frequently used...
486 for (int i=0; i<m_tracked_windows.size(); ++i) {
487 CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
488 QQuickCanvas *win = t.canvas;
490 Q_ASSERT(win->visible() || t.toBeRemoved);
491 bool canvasVisible = win->width() > 0 && win->height() > 0;
492 anyoneShowing |= (canvasVisible && !t.toBeRemoved);
494 if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
496 } else if (canvasVisible && !t.isVisible) {
499 t.isVisible = canvasVisible;
503 if (!anyoneShowing) {
508 m_added_windows << toAdd;
509 m_removed_windows << toRemove;
510 while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
518 } else if (anyoneShowing) {
519 Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
520 for (int i=0; i<toAdd.size(); ++i)
521 handleAddedWindow(toAdd.at(i));
528 void QQuickRenderThreadSingleContextWindowManager::run()
531 printf("QML Rendering Thread Started\n");
537 // Wake GUI as it is waiting for the GL context to have appeared, as
538 // an indication that the render thread is now running.
542 while (!shouldExit) {
546 printf(" RenderThread: *** NEW FRAME ***\n");
549 isExternalUpdatePending = false;
550 handleAddedWindows();
553 isPostingSyncEvent = true;
556 printf(" RenderThread: aquired sync lock...\n");
558 allowMainThreadProcessingFlag = false;
559 QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
562 printf(" RenderThread: going to sleep...\n");
564 wake(); // In case the event got through all the way to wait() before this thread got to wait.
568 isPostingSyncEvent = false;
572 printf(" RenderThread: Doing locked sync\n");
574 #ifdef QQUICK_CANVAS_TIMING
575 if (qquick_canvas_timing)
579 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
580 it != m_rendered_windows.constEnd(); ++it) {
581 QQuickCanvas *canvas = it.key();
584 printf(" RenderThread: Syncing canvas: %p\n", canvas);
587 CanvasData *canvasData = it.value();
588 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
590 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
592 gl->makeCurrent(canvas);
594 if (canvasData->viewportSize != canvasData->windowSize) {
596 printf(" RenderThread: --- window has changed size...\n");
598 canvasData->viewportSize = canvasData->windowSize;
599 canvasData->sizeWasChanged = true;
600 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
603 canvasPrivate->syncSceneGraph();
607 // Wake GUI after sync to let it continue animating and event processing.
608 allowMainThreadProcessingFlag = true;
612 printf(" RenderThread: sync done\n");
614 #ifdef QQUICK_CANVAS_TIMING
615 if (qquick_canvas_timing)
616 syncTime = threadTimer.elapsed();
619 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
620 it != m_rendered_windows.constEnd(); ++it) {
621 QQuickCanvas *canvas = it.key();
622 CanvasData *canvasData = it.value();
623 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
626 printf(" RenderThread: Rendering canvas %p\n", canvas);
629 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
632 printf(" RenderThread: --- rendering at size %dx%d\n",
633 canvasData->viewportSize.width(), canvasData->viewportSize.height()
637 // We only need to re-makeCurrent when we have multiple surfaces.
638 if (m_rendered_windows.size() > 1)
639 gl->makeCurrent(canvas);
641 canvasPrivate->renderSceneGraph(canvasData->viewportSize);
642 #ifdef QQUICK_CANVAS_TIMING
643 if (qquick_canvas_timing)
644 renderTime = threadTimer.elapsed() - syncTime;
647 // The content of the target buffer is undefined after swap() so grab needs
648 // to happen before swap();
649 if (canvas == canvasToGrab) {
651 printf(" RenderThread: --- grabbing...\n");
653 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
658 printf(" RenderThread: --- wait for swap...\n");
661 gl->swapBuffers(canvas);
662 canvasPrivate->fireFrameSwapped();
664 printf(" RenderThread: --- swap complete...\n");
669 #ifdef QQUICK_CANVAS_TIMING
670 if (qquick_canvas_timing) {
671 swapTime = threadTimer.elapsed() - renderTime;
672 qDebug() << "- Breakdown of frame time; sync:" << syncTime
673 << "ms render:" << renderTime << "ms swap:" << swapTime
674 << "ms total:" << swapTime + renderTime << "ms";
680 handleRemovedWindows();
682 isPaintCompleted = true;
685 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
686 it != m_rendered_windows.constEnd(); ++it) {
687 CanvasData *canvasData = it.value();
688 if (canvasData->sizeWasChanged) {
689 canvasData->renderedSize = canvasData->viewportSize;
690 canvasData->sizeWasChanged = false;
695 // Wake the GUI thread now that rendering is complete, to signal that painting
696 // is done, resizing is done or grabbing is completed. For grabbing, we're
697 // signalling this much later than needed (we could have done it before swap)
698 // but we don't want to lock an extra time.
701 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
703 printf(" RenderThread: nothing to do, going to sleep...\n");
705 isRenderBlocked = true;
707 isRenderBlocked = false;
712 QCoreApplication::processEvents();
714 // Process any "deleteLater" objects...
715 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
719 printf(" RenderThread: deleting all outstanding nodes\n");
722 m_removed_windows << m_rendered_windows.keys();
723 handleRemovedWindows();
732 printf(" RenderThread: render loop exited... Good Night!\n");
739 printf(" RenderThread: waking GUI for final sleep..\n");
745 printf(" RenderThread: All done...\n");
749 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
751 Q_ASSERT(QThread::currentThread() == qApp->thread());
753 if (e->type() == QEvent_Sync) {
755 // If all canvases have been hidden, ignore the event
759 if (!syncAlreadyHappened)
762 syncAlreadyHappened = false;
764 if (animationRunning) {
766 printf("GUI: Advancing animations...\n");
769 animationDriver->advance();
772 printf("GUI: Animations advanced...\n");
777 } else if (e->type() == QEvent_DeferredUpdate) {
778 handleDeferredUpdate();
780 } else if (e->type() == QEvent::Timer) {
782 printf("GUI: Animations advanced via timer...\n");
784 animationDriver->advance();
787 return QThread::event(e);
792 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
794 if (isPostingSyncEvent) {
796 syncAlreadyHappened = true;
802 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
805 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
807 if (!guiAlreadyLocked)
810 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
811 it != m_rendered_windows.constEnd(); ++it) {
812 QQuickCanvasPrivate::get(it.key())->polishItems();
818 if (!guiAlreadyLocked)
826 Acquires the mutex for the GUI thread. The function uses the isGuiLocked
827 variable to keep track of how many recursion levels the gui is locked with.
828 We only actually acquire the mutex for the first level to avoid deadlocking
832 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
834 if (++isGuiLocked == 1)
838 printf("GUI: aquired lock... level=%d\n", isGuiLocked);
844 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
847 printf("GUI: releasing lock... level=%d\n", isGuiLocked);
850 if (--isGuiLocked == 0)
857 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
860 printf("GUI: animationStarted()\n");
864 animationTimer = startTimer(1000/60);
870 animationRunning = true;
880 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
883 printf("GUI: animationStopped()...\n");
887 killTimer(animationTimer);
893 animationRunning = false;
898 void QQuickRenderThreadSingleContextWindowManager::paint(QQuickCanvas *canvas)
902 printf("GUI: paint called: %p\n", canvas);
908 isPaintCompleted = false;
909 while (isRunning() && !isPaintCompleted) {
917 printf("GUI: paint done: %p\n", canvas);
923 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
926 printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
929 // If the rendering thread is not running we do not need to do anything.
930 // Also if the canvas is being resized to an invalid size, it will be removed
931 // by the canvasVisibilityChanged slot as result of width/heightcChanged()
932 if (!isRunning() || size.width() <= 0 || size.height() <= 0)
938 CanvasData *canvasData = m_rendered_windows.value(canvas);
940 canvasData->windowSize = size;
941 while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
950 printf("GUI: Resize done: %p\n", canvas);
956 void QQuickRenderThreadSingleContextWindowManager::startRendering()
959 printf("GUI: Starting Render Thread\n");
964 isPostingSyncEvent = false;
965 syncAlreadyHappened = false;
969 start(); // Start the render thread...
973 // Animations will now be driven from the rendering thread.
974 if (animationTimer >= 0) {
975 killTimer(animationTimer);
984 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
987 printf("GUI: stopping render thread\n");
994 if (isRenderBlocked) {
996 printf("GUI: waking up render thread\n");
1001 while (!hasExited) {
1003 printf("GUI: waiting for render thread to have exited..\n");
1011 printf("GUI: waiting for render thread to terminate..\n");
1013 // Actually wait for the thread to terminate. Otherwise we can delete it
1014 // too early and crash.
1018 printf("GUI: thread has terminated and we're all good..\n");
1021 // Activate timer to keep animations running
1022 if (animationDriver->isRunning())
1023 animationTimer = startTimer(1000/60);
1028 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1033 if (QThread::currentThread() != qApp->thread()) {
1034 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1039 printf("GUI: doing a pixelwise grab..\n");
1045 canvasToGrab = canvas;
1046 isPaintCompleted = false;
1047 while (isRunning() && !isPaintCompleted) {
1048 if (isRenderBlocked)
1053 QImage grabbed = grabContent;
1054 grabContent = QImage();
1062 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1065 printf("GUI: handling update to ourselves...\n");
1068 isDeferredUpdatePosted = false;
1071 isExternalUpdatePending = true;
1072 if (isRenderBlocked)
1077 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1079 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1080 "QQuickCanvas::update",
1081 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1084 isExternalUpdatePending = true;
1086 } else if (!isDeferredUpdatePosted) {
1088 printf("GUI: posting update to ourselves...\n");
1090 isDeferredUpdatePosted = true;
1091 QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1096 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1098 , eventPending(false)
1100 sg = QSGContext::createDefaultContext();
1104 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1107 data.updatePending = false;
1108 data.grabOnly = false;
1109 m_windows[canvas] = data;
1111 maybeUpdate(canvas);
1114 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1116 if (!m_windows.contains(canvas))
1119 m_windows.remove(canvas);
1120 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1121 cd->cleanupNodesOnShutdown();
1123 if (m_windows.size() == 0) {
1130 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1135 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1137 if (!m_windows.contains(canvas))
1140 CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1143 gl = new QOpenGLContext();
1144 gl->setFormat(canvas->requestedFormat());
1146 gl->makeCurrent(canvas);
1149 gl->makeCurrent(canvas);
1152 bool alsoSwap = data.updatePending;
1153 data.updatePending = false;
1155 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1157 cd->syncSceneGraph();
1158 cd->renderSceneGraph(canvas->size());
1160 if (data.grabOnly) {
1161 grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1162 data.grabOnly = false;
1166 gl->swapBuffers(canvas);
1167 cd->fireFrameSwapped();
1170 // Might have been set during syncSceneGraph()
1171 if (data.updatePending)
1172 maybeUpdate(canvas);
1175 void QQuickTrivialWindowManager::paint(QQuickCanvas *canvas)
1177 if (!m_windows.contains(canvas))
1180 m_windows[canvas].updatePending = true;
1181 renderCanvas(canvas);
1184 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1186 if (!m_windows.contains(canvas))
1189 m_windows[canvas].grabOnly = true;
1191 renderCanvas(canvas);
1193 QImage grabbed = grabContent;
1194 grabContent = QImage();
1200 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1206 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1208 if (!m_windows.contains(canvas))
1211 m_windows[canvas].updatePending = true;
1213 if (!eventPending) {
1214 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1215 eventPending = true;
1221 bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1228 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1234 bool QQuickTrivialWindowManager::event(QEvent *e)
1236 if (e->type() == QEvent::User) {
1237 eventPending = false;
1238 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1239 it != m_windows.constEnd(); ++it) {
1240 const CanvasData &data = it.value();
1241 if (data.updatePending)
1242 renderCanvas(it.key());
1246 return QObject::event(e);
1249 #include "qquickwindowmanager.moc"