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/QPlatformIntegration>
52 #include <QtGui/private/qguiapplication_p.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 class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
152 QQuickRenderThreadSingleContextWindowManager()
153 : sg(QSGContext::createDefaultContext())
156 , allowMainThreadProcessingFlag(false)
158 , animationRunning(false)
159 , isPaintCompleted(false)
160 , isPostingSyncEvent(false)
161 , isRenderBlocked(false)
162 , isExternalUpdatePending(false)
163 , syncAlreadyHappened(false)
167 , isDeferredUpdatePosted(false)
168 , runToReleaseResources(false)
171 sg->moveToThread(this);
173 animationDriver = sg->createAnimationDriver(this);
174 animationDriver->install();
175 connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
176 connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
179 ~QQuickRenderThreadSingleContextWindowManager()
184 QSGContext *sceneGraphContext() const { return sg; }
186 void show(QQuickCanvas *canvas);
187 void hide(QQuickCanvas *canvas);
189 void canvasDestroyed(QQuickCanvas *canvas);
191 void paint(QQuickCanvas *canvas);
192 QImage grab(QQuickCanvas *canvas);
193 void resize(QQuickCanvas *canvas, const QSize &size);
194 void handleDeferredUpdate();
195 void maybeUpdate(QQuickCanvas *canvas);
197 void startRendering();
198 void stopRendering();
200 void exhaustSyncEvent();
201 void sync(bool guiAlreadyLocked);
204 void releaseResources();
205 void releaseResourcesInThread();
207 bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
209 bool event(QEvent *);
211 inline void lock() { mutex.lock(); }
212 inline void unlock() { mutex.unlock(); }
213 inline void wait() { condition.wait(&mutex); }
214 inline void wake() { condition.wakeOne(); }
220 QQuickCanvas *masterCanvas() {
221 QQuickCanvas *win = 0;
222 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
223 it != m_rendered_windows.constEnd() && !win; ++it) {
224 if (it.value()->isVisible)
231 void animationStarted();
232 void animationStopped();
233 void canvasVisibilityChanged();
236 void handleAddedWindows();
237 void handleAddedWindow(QQuickCanvas *canvas);
238 void handleRemovedWindows();
242 QAnimationDriver *animationDriver;
246 QWaitCondition condition;
248 bool allowMainThreadProcessingFlag;
251 uint animationRunning: 1;
252 uint isPaintCompleted : 1;
253 uint isPostingSyncEvent : 1;
254 uint isRenderBlocked : 1;
255 uint isExternalUpdatePending : 1;
256 uint syncAlreadyHappened : 1;
260 uint isDeferredUpdatePosted : 1;
261 uint runToReleaseResources : 1;
263 QQuickCanvas *canvasToGrab;
271 uint sizeWasChanged : 1;
275 QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
277 struct CanvasTracker {
278 QQuickCanvas *canvas;
280 uint toBeRemoved : 1;
283 QList<CanvasTracker> m_tracked_windows;
285 QList<QQuickCanvas *> m_removed_windows;
286 QList<QQuickCanvas *> m_added_windows;
290 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
293 QQuickTrivialWindowManager();
294 ~QQuickTrivialWindowManager()
299 void show(QQuickCanvas *canvas);
300 void hide(QQuickCanvas *canvas);
302 void canvasDestroyed(QQuickCanvas *canvas);
304 void releaseResources();
306 void renderCanvas(QQuickCanvas *canvas);
307 void paint(QQuickCanvas *canvas);
308 QImage grab(QQuickCanvas *canvas);
309 void resize(QQuickCanvas *canvas, const QSize &size);
311 void maybeUpdate(QQuickCanvas *canvas);
313 bool *allowMainThreadProcessing();
315 QSGContext *sceneGraphContext() const;
316 QQuickCanvas *masterCanvas() const;
318 bool event(QEvent *);
321 bool updatePending : 1;
325 QHash<QQuickCanvas *, CanvasData> m_windows;
336 QQuickWindowManager *QQuickWindowManager::instance()
338 static QQuickWindowManager *theInstance;
341 bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
342 if (qmlNoThreadedRenderer())
344 if (qmlFixedAnimationStep())
345 QUnifiedTimer::instance(true)->setConsistentTiming(true);
347 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
348 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
357 void QQuickRenderThreadSingleContextWindowManager::initialize()
359 Q_ASSERT(m_rendered_windows.size());
361 QQuickCanvas *win = masterCanvas();
365 gl = new QOpenGLContext();
366 // Pick up the surface format from one of them
367 gl->setFormat(win->requestedFormat());
369 if (!gl->makeCurrent(win))
370 qWarning("QQuickCanvas: makeCurrent() failed...");
372 Q_ASSERT(!sg->isReady());
378 This function is called when the canvas is created to register the canvas with
381 Called on GUI Thread.
384 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
387 printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
390 CanvasTracker tracker;
391 tracker.canvas = canvas;
392 tracker.isVisible = false;
393 tracker.toBeRemoved = false;
394 m_tracked_windows << tracker;
396 connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
397 connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
399 canvasVisibilityChanged();
403 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
406 printf(" RenderThread: adding canvas: %p\n", canvas);
409 CanvasData *data = new CanvasData;
410 data->sizeWasChanged = false;
411 data->windowSize = canvas->size();
412 data->isVisible = canvas->visible();
413 m_rendered_windows[canvas] = data;
415 isExternalUpdatePending = true;
420 Called on Render Thread
422 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
425 printf(" RenderThread: about to add %d\n", m_added_windows.size());
428 while (m_added_windows.size()) {
429 QQuickCanvas *canvas = m_added_windows.takeLast();
430 handleAddedWindow(canvas);
436 Called on the GUI Thread, from the canvas' destructor
439 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
442 printf("GUI: Canvas destroyed: %p\n", canvas);
453 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
456 printf("GUI: Canvas hidden: %p\n", canvas);
460 for (int i=0; i<m_tracked_windows.size(); ++i) {
461 if (m_tracked_windows.at(i).canvas == canvas) {
462 m_tracked_windows[i].toBeRemoved = true;
469 disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
470 disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
471 canvasVisibilityChanged();
472 m_tracked_windows.removeAt(position);
476 printf("GUI: Canvas removal completed... %p\n", canvas);
481 Called on Render Thread
483 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
486 printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
489 bool removedAnything = false;
490 while (m_removed_windows.size()) {
491 QQuickCanvas *canvas = m_removed_windows.takeLast();
493 printf(" RenderThread: removing %p\n", canvas);
496 QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
497 delete m_rendered_windows.take(canvas);
498 removedAnything = true;
501 // If a window is removed because it has been hidden it will take with it
502 // the gl context (at least on Mac) if bound, so disconnect the gl context
514 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
516 bool anyoneShowing = false;
517 QList<QQuickCanvas *> toAdd, toRemove;
519 // Not optimal, but also not frequently used...
520 for (int i=0; i<m_tracked_windows.size(); ++i) {
521 CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
522 QQuickCanvas *win = t.canvas;
524 Q_ASSERT(win->visible() || QQuickCanvasPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
525 bool canvasVisible = win->width() > 0 && win->height() > 0;
526 anyoneShowing |= (canvasVisible && !t.toBeRemoved);
528 if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
530 } else if (canvasVisible && !t.isVisible) {
533 t.isVisible = canvasVisible;
537 if (!anyoneShowing) {
542 m_added_windows << toAdd;
543 m_removed_windows << toRemove;
544 while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
552 } else if (anyoneShowing) {
553 Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
554 for (int i=0; i<toAdd.size(); ++i)
555 handleAddedWindow(toAdd.at(i));
562 void QQuickRenderThreadSingleContextWindowManager::run()
565 printf("QML Rendering Thread Started\n");
569 if (runToReleaseResources) {
570 releaseResourcesInThread();
571 runToReleaseResources = false;
579 // Wake GUI as it is waiting for the GL context to have appeared, as
580 // an indication that the render thread is now running.
587 while (!shouldExit) {
591 printf(" RenderThread: *** NEW FRAME ***\n");
594 isExternalUpdatePending = false;
595 handleAddedWindows();
598 isPostingSyncEvent = true;
601 printf(" RenderThread: aquired sync lock...\n");
603 allowMainThreadProcessingFlag = false;
604 QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
607 printf(" RenderThread: going to sleep...\n");
609 wake(); // In case the event got through all the way to wait() before this thread got to wait.
613 isPostingSyncEvent = false;
617 printf(" RenderThread: Doing locked sync\n");
619 #ifdef QQUICK_CANVAS_TIMING
620 if (qquick_canvas_timing)
624 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
625 it != m_rendered_windows.constEnd(); ++it) {
626 QQuickCanvas *canvas = it.key();
629 printf(" RenderThread: Syncing canvas: %p\n", canvas);
632 CanvasData *canvasData = it.value();
633 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
635 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
637 if (!canvasData->isVisible)
638 gl->makeCurrent(masterCanvas());
640 gl->makeCurrent(canvas);
642 if (canvasData->viewportSize != canvasData->windowSize) {
644 printf(" RenderThread: --- window has changed size...\n");
646 canvasData->viewportSize = canvasData->windowSize;
647 canvasData->sizeWasChanged = true;
648 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
651 canvasPrivate->syncSceneGraph();
655 // Wake GUI after sync to let it continue animating and event processing.
656 allowMainThreadProcessingFlag = true;
660 printf(" RenderThread: sync done\n");
662 #ifdef QQUICK_CANVAS_TIMING
663 if (qquick_canvas_timing)
664 syncTime = threadTimer.elapsed();
667 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
668 it != m_rendered_windows.constEnd(); ++it) {
669 QQuickCanvas *canvas = it.key();
670 CanvasData *canvasData = it.value();
671 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
674 printf(" RenderThread: Rendering canvas %p\n", canvas);
677 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
680 printf(" RenderThread: --- rendering at size %dx%d\n",
681 canvasData->viewportSize.width(), canvasData->viewportSize.height()
685 // We only need to re-makeCurrent when we have multiple surfaces.
686 if (m_rendered_windows.size() > 1)
687 gl->makeCurrent(canvas);
689 canvasPrivate->renderSceneGraph(canvasData->viewportSize);
690 #ifdef QQUICK_CANVAS_TIMING
691 if (qquick_canvas_timing)
692 renderTime = threadTimer.elapsed() - syncTime;
695 // The content of the target buffer is undefined after swap() so grab needs
696 // to happen before swap();
697 if (canvas == canvasToGrab) {
699 printf(" RenderThread: --- grabbing...\n");
701 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
706 printf(" RenderThread: --- wait for swap...\n");
709 if (canvasData->isVisible)
710 gl->swapBuffers(canvas);
712 canvasPrivate->fireFrameSwapped();
714 printf(" RenderThread: --- swap complete...\n");
719 #ifdef QQUICK_CANVAS_TIMING
720 if (qquick_canvas_timing) {
721 swapTime = threadTimer.elapsed() - renderTime;
722 qDebug() << "- Breakdown of frame time; sync:" << syncTime
723 << "ms render:" << renderTime << "ms swap:" << swapTime
724 << "ms total:" << swapTime + renderTime << "ms";
730 handleRemovedWindows();
732 isPaintCompleted = true;
735 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
736 it != m_rendered_windows.constEnd(); ++it) {
737 CanvasData *canvasData = it.value();
738 if (canvasData->sizeWasChanged) {
739 canvasData->renderedSize = canvasData->viewportSize;
740 canvasData->sizeWasChanged = false;
745 // Wake the GUI thread now that rendering is complete, to signal that painting
746 // is done, resizing is done or grabbing is completed. For grabbing, we're
747 // signalling this much later than needed (we could have done it before swap)
748 // but we don't want to lock an extra time.
751 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
753 printf(" RenderThread: nothing to do, going to sleep...\n");
755 isRenderBlocked = true;
757 isRenderBlocked = false;
762 QCoreApplication::processEvents();
764 // Process any "deleteLater" objects...
765 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
769 printf(" RenderThread: deleting all outstanding nodes\n");
772 m_removed_windows << m_rendered_windows.keys();
773 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 void QQuickRenderThreadSingleContextWindowManager::releaseResourcesInThread()
796 printf(" RenderThread: releasing resources...\n");
798 QQuickCanvas *canvas = masterCanvas();
799 QWindow *tmpSurface = 0;
802 gl->makeCurrent(canvas);
804 tmpSurface = new QWindow();
805 tmpSurface->setSurfaceType(QSurface::OpenGLSurface);
806 tmpSurface->resize(4, 4);
807 tmpSurface->create();
808 gl->makeCurrent(tmpSurface);
822 void QQuickRenderThreadSingleContextWindowManager::releaseResources()
825 printf("GUI: releasing resources\n");
829 if (!isRunning() && gl) {
830 runToReleaseResources = true;
833 while (isRunning()) {
839 printf("GUI: render thread running not releasing resources...\n");
846 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
848 Q_ASSERT(QThread::currentThread() == qApp->thread());
850 if (e->type() == QEvent_Sync) {
852 // If all canvases have been hidden, ignore the event
856 if (!syncAlreadyHappened)
859 syncAlreadyHappened = false;
861 if (animationRunning) {
863 printf("GUI: Advancing animations...\n");
866 animationDriver->advance();
869 printf("GUI: Animations advanced...\n");
874 } else if (e->type() == QEvent_DeferredUpdate) {
875 handleDeferredUpdate();
877 } else if (e->type() == QEvent::Timer) {
879 printf("GUI: Animations advanced via timer...\n");
881 animationDriver->advance();
884 return QThread::event(e);
889 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
891 if (isPostingSyncEvent) {
893 syncAlreadyHappened = true;
899 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
902 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
904 if (!guiAlreadyLocked)
907 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
908 it != m_rendered_windows.constEnd(); ++it) {
909 QQuickCanvasPrivate::get(it.key())->polishItems();
915 if (!guiAlreadyLocked)
923 Acquires the mutex for the GUI thread. The function uses the isGuiLocked
924 variable to keep track of how many recursion levels the gui is locked with.
925 We only actually acquire the mutex for the first level to avoid deadlocking
929 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
931 if (++isGuiLocked == 1)
935 printf("GUI: aquired lock... level=%d\n", isGuiLocked);
941 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
944 printf("GUI: releasing lock... level=%d\n", isGuiLocked);
947 if (--isGuiLocked == 0)
954 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
957 printf("GUI: animationStarted()\n");
961 animationTimer = startTimer(1000/60);
967 animationRunning = true;
977 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
980 printf("GUI: animationStopped()...\n");
984 killTimer(animationTimer);
990 animationRunning = false;
995 void QQuickRenderThreadSingleContextWindowManager::paint(QQuickCanvas *canvas)
999 printf("GUI: paint called: %p\n", canvas);
1005 isPaintCompleted = false;
1006 while (isRunning() && !isPaintCompleted) {
1007 if (isRenderBlocked)
1014 printf("GUI: paint done: %p\n", canvas);
1020 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
1023 printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
1026 // If the rendering thread is not running we do not need to do anything.
1027 // Also if the canvas is being resized to an invalid size, it will be removed
1028 // by the canvasVisibilityChanged slot as result of width/heightcChanged()
1029 if (!isRunning() || size.width() <= 0 || size.height() <= 0)
1035 CanvasData *canvasData = m_rendered_windows.value(canvas);
1037 canvasData->windowSize = size;
1038 while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
1039 if (isRenderBlocked)
1047 printf("GUI: Resize done: %p\n", canvas);
1053 void QQuickRenderThreadSingleContextWindowManager::startRendering()
1056 printf("GUI: Starting Render Thread\n");
1061 isPostingSyncEvent = false;
1062 syncAlreadyHappened = false;
1066 animationRunning = animationDriver->isRunning();
1067 start(); // Start the render thread...
1071 // Animations will now be driven from the rendering thread.
1072 if (animationTimer >= 0) {
1073 killTimer(animationTimer);
1074 animationTimer = -1;
1081 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1084 printf("GUI: stopping render thread\n");
1091 if (isRenderBlocked) {
1093 printf("GUI: waking up render thread\n");
1098 while (!hasExited) {
1100 printf("GUI: waiting for render thread to have exited..\n");
1108 printf("GUI: waiting for render thread to terminate..\n");
1110 // Actually wait for the thread to terminate. Otherwise we can delete it
1111 // too early and crash.
1115 printf("GUI: thread has terminated and we're all good..\n");
1118 // Activate timer to keep animations running
1119 if (animationDriver->isRunning())
1120 animationTimer = startTimer(1000/60);
1125 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1130 if (QThread::currentThread() != qApp->thread()) {
1131 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1136 printf("GUI: doing a pixelwise grab..\n");
1142 canvasToGrab = canvas;
1143 isPaintCompleted = false;
1144 while (isRunning() && !isPaintCompleted) {
1145 if (isRenderBlocked)
1150 QImage grabbed = grabContent;
1151 grabContent = QImage();
1159 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1162 printf("GUI: handling update to ourselves...\n");
1165 isDeferredUpdatePosted = false;
1168 isExternalUpdatePending = true;
1169 if (isRenderBlocked)
1174 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1176 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1177 "QQuickCanvas::update",
1178 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1181 isExternalUpdatePending = true;
1183 } else if (!isDeferredUpdatePosted) {
1185 printf("GUI: posting update to ourselves...\n");
1187 isDeferredUpdatePosted = true;
1188 QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1193 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1195 , eventPending(false)
1197 sg = QSGContext::createDefaultContext();
1201 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1204 data.updatePending = false;
1205 data.grabOnly = false;
1206 m_windows[canvas] = data;
1208 maybeUpdate(canvas);
1211 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1213 if (!m_windows.contains(canvas))
1216 m_windows.remove(canvas);
1217 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1218 cd->cleanupNodesOnShutdown();
1221 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1226 void QQuickTrivialWindowManager::releaseResources()
1228 if (m_windows.size() == 0 && gl) {
1229 QQuickCanvas *canvas = masterCanvas();
1230 QWindow *tmpSurface = 0;
1233 gl->makeCurrent(canvas);
1235 tmpSurface = new QWindow();
1236 tmpSurface->setSurfaceType(QSurface::OpenGLSurface);
1237 tmpSurface->resize(4, 4);
1238 tmpSurface->create();
1239 gl->makeCurrent(tmpSurface);
1250 QQuickCanvas *QQuickTrivialWindowManager::masterCanvas() const
1252 // Find a "proper surface" to bind...
1253 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1254 it != m_windows.constEnd(); ++it) {
1255 if (it.key()->visible())
1261 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1263 if (!m_windows.contains(canvas))
1266 CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1268 QQuickCanvas *window = canvas->visible() ? canvas : masterCanvas();
1274 gl = new QOpenGLContext();
1275 gl->setFormat(window->requestedFormat());
1277 if (!gl->makeCurrent(window))
1278 qWarning("QQuickCanvas: makeCurrent() failed...");
1281 gl->makeCurrent(window);
1284 bool alsoSwap = data.updatePending;
1285 data.updatePending = false;
1287 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1289 cd->syncSceneGraph();
1290 cd->renderSceneGraph(canvas->size());
1292 if (data.grabOnly) {
1293 grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1294 data.grabOnly = false;
1297 if (alsoSwap && canvas->visible()) {
1298 gl->swapBuffers(canvas);
1299 cd->fireFrameSwapped();
1302 // Might have been set during syncSceneGraph()
1303 if (data.updatePending)
1304 maybeUpdate(canvas);
1307 void QQuickTrivialWindowManager::paint(QQuickCanvas *canvas)
1309 if (!m_windows.contains(canvas))
1312 m_windows[canvas].updatePending = true;
1313 renderCanvas(canvas);
1316 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1318 if (!m_windows.contains(canvas))
1321 m_windows[canvas].grabOnly = true;
1323 renderCanvas(canvas);
1325 QImage grabbed = grabContent;
1326 grabContent = QImage();
1332 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1338 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1340 if (!m_windows.contains(canvas))
1343 m_windows[canvas].updatePending = true;
1345 if (!eventPending) {
1346 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1347 eventPending = true;
1353 bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1360 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1366 bool QQuickTrivialWindowManager::event(QEvent *e)
1368 if (e->type() == QEvent::User) {
1369 eventPending = false;
1370 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1371 it != m_windows.constEnd(); ++it) {
1372 const CanvasData &data = it.value();
1373 if (data.updatePending)
1374 renderCanvas(it.key());
1378 return QObject::event(e);
1381 #include "qquickwindowmanager.moc"