1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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())
155 , allowMainThreadProcessingFlag(false)
157 , animationRunning(false)
158 , isPaintCompleted(false)
159 , isPostingSyncEvent(false)
160 , isRenderBlocked(false)
161 , isExternalUpdatePending(false)
162 , syncAlreadyHappened(false)
166 , isDeferredUpdatePosted(false)
169 sg->moveToThread(this);
171 animationDriver = sg->createAnimationDriver(this);
172 animationDriver->install();
173 connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
174 connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
177 QSGContext *sceneGraphContext() const { return sg; }
179 void show(QQuickCanvas *canvas);
180 void hide(QQuickCanvas *canvas);
182 void canvasDestroyed(QQuickCanvas *canvas);
184 void paint(QQuickCanvas *canvas);
185 QImage grab(QQuickCanvas *canvas);
186 void resize(QQuickCanvas *canvas, const QSize &size);
187 void handleDeferredUpdate();
188 void maybeUpdate(QQuickCanvas *canvas);
190 void startRendering();
191 void stopRendering();
193 void exhaustSyncEvent();
194 void sync(bool guiAlreadyLocked);
198 bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
200 bool event(QEvent *);
202 inline void lock() { mutex.lock(); }
203 inline void unlock() { mutex.unlock(); }
204 inline void wait() { condition.wait(&mutex); }
205 inline void wake() { condition.wakeOne(); }
212 void animationStarted();
213 void animationStopped();
214 void canvasVisibilityChanged();
217 void handleAddedWindows();
218 void handleAddedWindow(QQuickCanvas *canvas);
219 void handleRemovedWindows();
223 QAnimationDriver *animationDriver;
227 QWaitCondition condition;
229 bool allowMainThreadProcessingFlag;
232 uint animationRunning: 1;
233 uint isPaintCompleted : 1;
234 uint isPostingSyncEvent : 1;
235 uint isRenderBlocked : 1;
236 uint isExternalUpdatePending : 1;
237 uint syncAlreadyHappened : 1;
241 uint isDeferredUpdatePosted : 1;
243 QQuickCanvas *canvasToGrab;
251 uint sizeWasChanged : 1;
254 QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
256 struct CanvasTracker {
257 QQuickCanvas *canvas;
259 uint toBeRemoved : 1;
262 QList<CanvasTracker> m_tracked_windows;
264 QList<QQuickCanvas *> m_removed_windows;
265 QList<QQuickCanvas *> m_added_windows;
269 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
272 QQuickTrivialWindowManager();
274 void show(QQuickCanvas *canvas);
275 void hide(QQuickCanvas *canvas);
277 void canvasDestroyed(QQuickCanvas *canvas);
279 void renderCanvas(QQuickCanvas *canvas);
280 void paint(QQuickCanvas *canvas);
281 QImage grab(QQuickCanvas *canvas);
282 void resize(QQuickCanvas *canvas, const QSize &size);
284 void maybeUpdate(QQuickCanvas *canvas);
286 bool *allowMainThreadProcessing();
288 QSGContext *sceneGraphContext() const;
290 bool event(QEvent *);
293 bool updatePending : 1;
297 QHash<QQuickCanvas *, CanvasData> m_windows;
308 QQuickWindowManager *QQuickWindowManager::instance()
310 static QQuickWindowManager *theInstance;
313 bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
314 if (qmlNoThreadedRenderer())
316 if (qmlFixedAnimationStep())
317 QUnifiedTimer::instance(true)->setConsistentTiming(true);
319 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
320 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
329 void QQuickRenderThreadSingleContextWindowManager::initialize()
331 Q_ASSERT(m_rendered_windows.size());
332 QQuickCanvas *win = m_rendered_windows.constBegin().key();
334 gl = new QOpenGLContext();
335 // Pick up the surface format from one of them
336 gl->setFormat(win->requestedFormat());
338 if (!gl->makeCurrent(win))
339 qWarning("QQuickCanvas: makeCurrent() failed...");
341 Q_ASSERT(!sg->isReady());
347 This function is called when the canvas is created to register the canvas with
350 Called on GUI Thread.
353 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
356 printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
359 CanvasTracker tracker;
360 tracker.canvas = canvas;
361 tracker.isVisible = false;
362 tracker.toBeRemoved = false;
363 m_tracked_windows << tracker;
365 connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
366 connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
368 canvasVisibilityChanged();
372 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
375 printf(" RenderThread: adding canvas: %p\n", canvas);
378 CanvasData *data = new CanvasData;
379 data->sizeWasChanged = false;
380 data->windowSize = canvas->size();
381 m_rendered_windows[canvas] = data;
383 isExternalUpdatePending = true;
388 Called on Render Thread
390 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
393 printf(" RenderThread: about to add %d\n", m_added_windows.size());
396 while (m_added_windows.size()) {
397 QQuickCanvas *canvas = m_added_windows.takeLast();
398 handleAddedWindow(canvas);
404 Called on the GUI Thread, from the canvas' destructor
407 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
410 printf("GUI: Canvas destroyed: %p\n", canvas);
421 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
424 printf("GUI: Canvas hidden: %p\n", canvas);
428 for (int i=0; i<m_tracked_windows.size(); ++i) {
429 if (m_tracked_windows.at(i).canvas == canvas) {
430 m_tracked_windows[i].toBeRemoved = true;
437 disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
438 disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
439 canvasVisibilityChanged();
440 m_tracked_windows.removeAt(position);
444 printf("GUI: Canvas removal completed... %p\n", canvas);
449 Called on Render Thread
451 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
454 printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
457 bool removedAnything = false;
458 while (m_removed_windows.size()) {
459 QQuickCanvas *canvas = m_removed_windows.takeLast();
461 printf(" RenderThread: removing %p\n", canvas);
464 QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
465 delete m_rendered_windows.take(canvas);
466 removedAnything = true;
469 // If a window is removed because it has been hidden it will take with it
470 // the gl context (at least on Mac) if bound, so disconnect the gl context
482 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
484 bool anyoneShowing = false;
485 QList<QQuickCanvas *> toAdd, toRemove;
487 // Not optimal, but also not frequently used...
488 for (int i=0; i<m_tracked_windows.size(); ++i) {
489 CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
490 QQuickCanvas *win = t.canvas;
492 Q_ASSERT(win->visible() || t.toBeRemoved);
493 bool canvasVisible = win->width() > 0 && win->height() > 0;
494 anyoneShowing |= (canvasVisible && !t.toBeRemoved);
496 if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
498 } else if (canvasVisible && !t.isVisible) {
501 t.isVisible = canvasVisible;
505 if (!anyoneShowing) {
510 m_added_windows << toAdd;
511 m_removed_windows << toRemove;
512 while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
520 } else if (anyoneShowing) {
521 Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
522 for (int i=0; i<toAdd.size(); ++i)
523 handleAddedWindow(toAdd.at(i));
530 void QQuickRenderThreadSingleContextWindowManager::run()
533 printf("QML Rendering Thread Started\n");
539 // Wake GUI as it is waiting for the GL context to have appeared, as
540 // an indication that the render thread is now running.
544 while (!shouldExit) {
548 printf(" RenderThread: *** NEW FRAME ***\n");
551 isExternalUpdatePending = false;
552 handleAddedWindows();
555 isPostingSyncEvent = true;
558 printf(" RenderThread: aquired sync lock...\n");
560 allowMainThreadProcessingFlag = false;
561 QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
564 printf(" RenderThread: going to sleep...\n");
566 wake(); // In case the event got through all the way to wait() before this thread got to wait.
570 isPostingSyncEvent = false;
574 printf(" RenderThread: Doing locked sync\n");
576 #ifdef QQUICK_CANVAS_TIMING
577 if (qquick_canvas_timing)
581 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
582 it != m_rendered_windows.constEnd(); ++it) {
583 QQuickCanvas *canvas = it.key();
586 printf(" RenderThread: Syncing canvas: %p\n", canvas);
589 CanvasData *canvasData = it.value();
590 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
592 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
594 gl->makeCurrent(canvas);
596 if (canvasData->viewportSize != canvasData->windowSize) {
598 printf(" RenderThread: --- window has changed size...\n");
600 canvasData->viewportSize = canvasData->windowSize;
601 canvasData->sizeWasChanged = true;
602 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
605 canvasPrivate->syncSceneGraph();
609 // Wake GUI after sync to let it continue animating and event processing.
610 allowMainThreadProcessingFlag = true;
614 printf(" RenderThread: sync done\n");
616 #ifdef QQUICK_CANVAS_TIMING
617 if (qquick_canvas_timing)
618 syncTime = threadTimer.elapsed();
621 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
622 it != m_rendered_windows.constEnd(); ++it) {
623 QQuickCanvas *canvas = it.key();
624 CanvasData *canvasData = it.value();
625 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
628 printf(" RenderThread: Rendering canvas %p\n", canvas);
631 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
634 printf(" RenderThread: --- rendering at size %dx%d\n",
635 canvasData->viewportSize.width(), canvasData->viewportSize.height()
639 // We only need to re-makeCurrent when we have multiple surfaces.
640 if (m_rendered_windows.size() > 1)
641 gl->makeCurrent(canvas);
643 canvasPrivate->renderSceneGraph(canvasData->viewportSize);
644 #ifdef QQUICK_CANVAS_TIMING
645 if (qquick_canvas_timing)
646 renderTime = threadTimer.elapsed() - syncTime;
649 // The content of the target buffer is undefined after swap() so grab needs
650 // to happen before swap();
651 if (canvas == canvasToGrab) {
653 printf(" RenderThread: --- grabbing...\n");
655 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
660 printf(" RenderThread: --- wait for swap...\n");
663 gl->swapBuffers(canvas);
664 canvasPrivate->fireFrameSwapped();
666 printf(" RenderThread: --- swap complete...\n");
671 #ifdef QQUICK_CANVAS_TIMING
672 if (qquick_canvas_timing) {
673 swapTime = threadTimer.elapsed() - renderTime;
674 qDebug() << "- Breakdown of frame time; sync:" << syncTime
675 << "ms render:" << renderTime << "ms swap:" << swapTime
676 << "ms total:" << swapTime + renderTime << "ms";
682 handleRemovedWindows();
684 isPaintCompleted = true;
687 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
688 it != m_rendered_windows.constEnd(); ++it) {
689 CanvasData *canvasData = it.value();
690 if (canvasData->sizeWasChanged) {
691 canvasData->renderedSize = canvasData->viewportSize;
692 canvasData->sizeWasChanged = false;
697 // Wake the GUI thread now that rendering is complete, to signal that painting
698 // is done, resizing is done or grabbing is completed. For grabbing, we're
699 // signalling this much later than needed (we could have done it before swap)
700 // but we don't want to lock an extra time.
703 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
705 printf(" RenderThread: nothing to do, going to sleep...\n");
707 isRenderBlocked = true;
709 isRenderBlocked = false;
714 QCoreApplication::processEvents();
716 // Process any "deleteLater" objects...
717 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
721 printf(" RenderThread: deleting all outstanding nodes\n");
724 m_removed_windows << m_rendered_windows.keys();
725 handleRemovedWindows();
734 printf(" RenderThread: render loop exited... Good Night!\n");
741 printf(" RenderThread: waking GUI for final sleep..\n");
747 printf(" RenderThread: All done...\n");
751 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
753 Q_ASSERT(QThread::currentThread() == qApp->thread());
755 if (e->type() == QEvent_Sync) {
757 // If all canvases have been hidden, ignore the event
761 if (!syncAlreadyHappened)
764 syncAlreadyHappened = false;
766 if (animationRunning) {
768 printf("GUI: Advancing animations...\n");
771 animationDriver->advance();
774 printf("GUI: Animations advanced...\n");
779 } else if (e->type() == QEvent_DeferredUpdate) {
780 handleDeferredUpdate();
782 } else if (e->type() == QEvent::Timer) {
784 printf("GUI: Animations advanced via timer...\n");
786 animationDriver->advance();
789 return QThread::event(e);
794 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
796 if (isPostingSyncEvent) {
798 syncAlreadyHappened = true;
804 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
807 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
809 if (!guiAlreadyLocked)
812 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
813 it != m_rendered_windows.constEnd(); ++it) {
814 QQuickCanvasPrivate::get(it.key())->polishItems();
820 if (!guiAlreadyLocked)
828 Acquires the mutex for the GUI thread. The function uses the isGuiLocked
829 variable to keep track of how many recursion levels the gui is locked with.
830 We only actually acquire the mutex for the first level to avoid deadlocking
834 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
836 if (++isGuiLocked == 1)
840 printf("GUI: aquired lock... level=%d\n", isGuiLocked);
846 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
849 printf("GUI: releasing lock... level=%d\n", isGuiLocked);
852 if (--isGuiLocked == 0)
859 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
862 printf("GUI: animationStarted()\n");
866 animationTimer = startTimer(1000/60);
872 animationRunning = true;
882 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
885 printf("GUI: animationStopped()...\n");
889 killTimer(animationTimer);
895 animationRunning = false;
900 void QQuickRenderThreadSingleContextWindowManager::paint(QQuickCanvas *canvas)
904 printf("GUI: paint called: %p\n", canvas);
910 isPaintCompleted = false;
911 while (isRunning() && !isPaintCompleted) {
919 printf("GUI: paint done: %p\n", canvas);
925 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
928 printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
931 // If the rendering thread is not running we do not need to do anything.
932 // Also if the canvas is being resized to an invalid size, it will be removed
933 // by the canvasVisibilityChanged slot as result of width/heightcChanged()
934 if (!isRunning() || size.width() <= 0 || size.height() <= 0)
940 CanvasData *canvasData = m_rendered_windows.value(canvas);
942 canvasData->windowSize = size;
943 while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
952 printf("GUI: Resize done: %p\n", canvas);
958 void QQuickRenderThreadSingleContextWindowManager::startRendering()
961 printf("GUI: Starting Render Thread\n");
966 isPostingSyncEvent = false;
967 syncAlreadyHappened = false;
971 start(); // Start the render thread...
975 // Animations will now be driven from the rendering thread.
976 if (animationTimer >= 0) {
977 killTimer(animationTimer);
986 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
989 printf("GUI: stopping render thread\n");
996 if (isRenderBlocked) {
998 printf("GUI: waking up render thread\n");
1003 while (!hasExited) {
1005 printf("GUI: waiting for render thread to have exited..\n");
1013 printf("GUI: waiting for render thread to terminate..\n");
1015 // Actually wait for the thread to terminate. Otherwise we can delete it
1016 // too early and crash.
1020 printf("GUI: thread has terminated and we're all good..\n");
1023 // Activate timer to keep animations running
1024 if (animationDriver->isRunning())
1025 animationTimer = startTimer(1000/60);
1030 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1035 if (QThread::currentThread() != qApp->thread()) {
1036 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1041 printf("GUI: doing a pixelwise grab..\n");
1047 canvasToGrab = canvas;
1048 isPaintCompleted = false;
1049 while (isRunning() && !isPaintCompleted) {
1050 if (isRenderBlocked)
1055 QImage grabbed = grabContent;
1056 grabContent = QImage();
1064 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1067 printf("GUI: handling update to ourselves...\n");
1070 isDeferredUpdatePosted = false;
1073 isExternalUpdatePending = true;
1074 if (isRenderBlocked)
1079 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1081 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1082 "QQuickCanvas::update",
1083 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1086 isExternalUpdatePending = true;
1088 } else if (!isDeferredUpdatePosted) {
1090 printf("GUI: posting update to ourselves...\n");
1092 isDeferredUpdatePosted = true;
1093 QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1098 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1100 , eventPending(false)
1102 sg = QSGContext::createDefaultContext();
1106 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1109 data.updatePending = false;
1110 data.grabOnly = false;
1111 m_windows[canvas] = data;
1113 maybeUpdate(canvas);
1116 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1118 if (!m_windows.contains(canvas))
1121 m_windows.remove(canvas);
1122 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1123 cd->cleanupNodesOnShutdown();
1125 if (m_windows.size() == 0) {
1132 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1137 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1139 if (!m_windows.contains(canvas))
1142 CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1145 gl = new QOpenGLContext();
1146 gl->setFormat(canvas->requestedFormat());
1148 if (!gl->makeCurrent(canvas))
1149 qWarning("QQuickCanvas: makeCurrent() failed...");
1152 gl->makeCurrent(canvas);
1155 bool alsoSwap = data.updatePending;
1156 data.updatePending = false;
1158 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1160 cd->syncSceneGraph();
1161 cd->renderSceneGraph(canvas->size());
1163 if (data.grabOnly) {
1164 grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1165 data.grabOnly = false;
1169 gl->swapBuffers(canvas);
1170 cd->fireFrameSwapped();
1173 // Might have been set during syncSceneGraph()
1174 if (data.updatePending)
1175 maybeUpdate(canvas);
1178 void QQuickTrivialWindowManager::paint(QQuickCanvas *canvas)
1180 if (!m_windows.contains(canvas))
1183 m_windows[canvas].updatePending = true;
1184 renderCanvas(canvas);
1187 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1189 if (!m_windows.contains(canvas))
1192 m_windows[canvas].grabOnly = true;
1194 renderCanvas(canvas);
1196 QImage grabbed = grabContent;
1197 grabContent = QImage();
1203 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1209 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1211 if (!m_windows.contains(canvas))
1214 m_windows[canvas].updatePending = true;
1216 if (!eventPending) {
1217 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1218 eventPending = true;
1224 bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1231 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1237 bool QQuickTrivialWindowManager::event(QEvent *e)
1239 if (e->type() == QEvent::User) {
1240 eventPending = false;
1241 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1242 it != m_windows.constEnd(); ++it) {
1243 const CanvasData &data = it.value();
1244 if (data.updatePending)
1245 renderCanvas(it.key());
1249 return QObject::event(e);
1252 #include "qquickwindowmanager.moc"