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 QtDeclarative 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>
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)
167 , runToReleaseResources(false)
170 sg->moveToThread(this);
172 animationDriver = sg->createAnimationDriver(this);
173 animationDriver->install();
174 connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
175 connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
178 QSGContext *sceneGraphContext() const { return sg; }
180 void show(QQuickCanvas *canvas);
181 void hide(QQuickCanvas *canvas);
183 void canvasDestroyed(QQuickCanvas *canvas);
185 void paint(QQuickCanvas *canvas);
186 QImage grab(QQuickCanvas *canvas);
187 void resize(QQuickCanvas *canvas, const QSize &size);
188 void handleDeferredUpdate();
189 void maybeUpdate(QQuickCanvas *canvas);
191 void startRendering();
192 void stopRendering();
194 void exhaustSyncEvent();
195 void sync(bool guiAlreadyLocked);
198 void releaseResources();
199 void releaseResourcesInThread();
201 bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
203 bool event(QEvent *);
205 inline void lock() { mutex.lock(); }
206 inline void unlock() { mutex.unlock(); }
207 inline void wait() { condition.wait(&mutex); }
208 inline void wake() { condition.wakeOne(); }
214 QQuickCanvas *masterCanvas() {
215 QQuickCanvas *win = 0;
216 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
217 it != m_rendered_windows.constEnd() && !win; ++it) {
218 if (it.value()->isVisible)
225 void animationStarted();
226 void animationStopped();
227 void canvasVisibilityChanged();
230 void handleAddedWindows();
231 void handleAddedWindow(QQuickCanvas *canvas);
232 void handleRemovedWindows();
236 QAnimationDriver *animationDriver;
240 QWaitCondition condition;
242 bool allowMainThreadProcessingFlag;
245 uint animationRunning: 1;
246 uint isPaintCompleted : 1;
247 uint isPostingSyncEvent : 1;
248 uint isRenderBlocked : 1;
249 uint isExternalUpdatePending : 1;
250 uint syncAlreadyHappened : 1;
254 uint isDeferredUpdatePosted : 1;
255 uint runToReleaseResources : 1;
257 QQuickCanvas *canvasToGrab;
265 uint sizeWasChanged : 1;
269 QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
271 struct CanvasTracker {
272 QQuickCanvas *canvas;
274 uint toBeRemoved : 1;
277 QList<CanvasTracker> m_tracked_windows;
279 QList<QQuickCanvas *> m_removed_windows;
280 QList<QQuickCanvas *> m_added_windows;
284 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
287 QQuickTrivialWindowManager();
289 void show(QQuickCanvas *canvas);
290 void hide(QQuickCanvas *canvas);
292 void canvasDestroyed(QQuickCanvas *canvas);
294 void releaseResources();
296 void renderCanvas(QQuickCanvas *canvas);
297 void paint(QQuickCanvas *canvas);
298 QImage grab(QQuickCanvas *canvas);
299 void resize(QQuickCanvas *canvas, const QSize &size);
301 void maybeUpdate(QQuickCanvas *canvas);
303 bool *allowMainThreadProcessing();
305 QSGContext *sceneGraphContext() const;
306 QQuickCanvas *masterCanvas() const;
308 bool event(QEvent *);
311 bool updatePending : 1;
315 QHash<QQuickCanvas *, CanvasData> m_windows;
326 QQuickWindowManager *QQuickWindowManager::instance()
328 static QQuickWindowManager *theInstance;
331 bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
332 if (qmlNoThreadedRenderer())
334 if (qmlFixedAnimationStep())
335 QUnifiedTimer::instance(true)->setConsistentTiming(true);
337 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
338 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
347 void QQuickRenderThreadSingleContextWindowManager::initialize()
349 Q_ASSERT(m_rendered_windows.size());
351 QQuickCanvas *win = masterCanvas();
355 gl = new QOpenGLContext();
356 // Pick up the surface format from one of them
357 gl->setFormat(win->requestedFormat());
359 if (!gl->makeCurrent(win))
360 qWarning("QQuickCanvas: makeCurrent() failed...");
362 Q_ASSERT(!sg->isReady());
368 This function is called when the canvas is created to register the canvas with
371 Called on GUI Thread.
374 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
377 printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
380 CanvasTracker tracker;
381 tracker.canvas = canvas;
382 tracker.isVisible = false;
383 tracker.toBeRemoved = false;
384 m_tracked_windows << tracker;
386 connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
387 connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
389 canvasVisibilityChanged();
393 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
396 printf(" RenderThread: adding canvas: %p\n", canvas);
399 CanvasData *data = new CanvasData;
400 data->sizeWasChanged = false;
401 data->windowSize = canvas->size();
402 data->isVisible = canvas->visible();
403 m_rendered_windows[canvas] = data;
405 isExternalUpdatePending = true;
410 Called on Render Thread
412 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
415 printf(" RenderThread: about to add %d\n", m_added_windows.size());
418 while (m_added_windows.size()) {
419 QQuickCanvas *canvas = m_added_windows.takeLast();
420 handleAddedWindow(canvas);
426 Called on the GUI Thread, from the canvas' destructor
429 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
432 printf("GUI: Canvas destroyed: %p\n", canvas);
443 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
446 printf("GUI: Canvas hidden: %p\n", canvas);
450 for (int i=0; i<m_tracked_windows.size(); ++i) {
451 if (m_tracked_windows.at(i).canvas == canvas) {
452 m_tracked_windows[i].toBeRemoved = true;
459 disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
460 disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
461 canvasVisibilityChanged();
462 m_tracked_windows.removeAt(position);
466 printf("GUI: Canvas removal completed... %p\n", canvas);
471 Called on Render Thread
473 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
476 printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
479 bool removedAnything = false;
480 while (m_removed_windows.size()) {
481 QQuickCanvas *canvas = m_removed_windows.takeLast();
483 printf(" RenderThread: removing %p\n", canvas);
486 QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
487 delete m_rendered_windows.take(canvas);
488 removedAnything = true;
491 // If a window is removed because it has been hidden it will take with it
492 // the gl context (at least on Mac) if bound, so disconnect the gl context
504 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
506 bool anyoneShowing = false;
507 QList<QQuickCanvas *> toAdd, toRemove;
509 // Not optimal, but also not frequently used...
510 for (int i=0; i<m_tracked_windows.size(); ++i) {
511 CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
512 QQuickCanvas *win = t.canvas;
514 Q_ASSERT(win->visible() || QQuickCanvasPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
515 bool canvasVisible = win->width() > 0 && win->height() > 0;
516 anyoneShowing |= (canvasVisible && !t.toBeRemoved);
518 if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
520 } else if (canvasVisible && !t.isVisible) {
523 t.isVisible = canvasVisible;
527 if (!anyoneShowing) {
532 m_added_windows << toAdd;
533 m_removed_windows << toRemove;
534 while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
542 } else if (anyoneShowing) {
543 Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
544 for (int i=0; i<toAdd.size(); ++i)
545 handleAddedWindow(toAdd.at(i));
552 void QQuickRenderThreadSingleContextWindowManager::run()
555 printf("QML Rendering Thread Started\n");
559 if (runToReleaseResources) {
560 releaseResourcesInThread();
561 runToReleaseResources = false;
569 // Wake GUI as it is waiting for the GL context to have appeared, as
570 // an indication that the render thread is now running.
577 while (!shouldExit) {
581 printf(" RenderThread: *** NEW FRAME ***\n");
584 isExternalUpdatePending = false;
585 handleAddedWindows();
588 isPostingSyncEvent = true;
591 printf(" RenderThread: aquired sync lock...\n");
593 allowMainThreadProcessingFlag = false;
594 QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
597 printf(" RenderThread: going to sleep...\n");
599 wake(); // In case the event got through all the way to wait() before this thread got to wait.
603 isPostingSyncEvent = false;
607 printf(" RenderThread: Doing locked sync\n");
609 #ifdef QQUICK_CANVAS_TIMING
610 if (qquick_canvas_timing)
614 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
615 it != m_rendered_windows.constEnd(); ++it) {
616 QQuickCanvas *canvas = it.key();
619 printf(" RenderThread: Syncing canvas: %p\n", canvas);
622 CanvasData *canvasData = it.value();
623 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
625 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
627 if (!canvasData->isVisible)
628 gl->makeCurrent(masterCanvas());
630 gl->makeCurrent(canvas);
632 if (canvasData->viewportSize != canvasData->windowSize) {
634 printf(" RenderThread: --- window has changed size...\n");
636 canvasData->viewportSize = canvasData->windowSize;
637 canvasData->sizeWasChanged = true;
638 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
641 canvasPrivate->syncSceneGraph();
645 // Wake GUI after sync to let it continue animating and event processing.
646 allowMainThreadProcessingFlag = true;
650 printf(" RenderThread: sync done\n");
652 #ifdef QQUICK_CANVAS_TIMING
653 if (qquick_canvas_timing)
654 syncTime = threadTimer.elapsed();
657 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
658 it != m_rendered_windows.constEnd(); ++it) {
659 QQuickCanvas *canvas = it.key();
660 CanvasData *canvasData = it.value();
661 QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
664 printf(" RenderThread: Rendering canvas %p\n", canvas);
667 Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
670 printf(" RenderThread: --- rendering at size %dx%d\n",
671 canvasData->viewportSize.width(), canvasData->viewportSize.height()
675 // We only need to re-makeCurrent when we have multiple surfaces.
676 if (m_rendered_windows.size() > 1)
677 gl->makeCurrent(canvas);
679 canvasPrivate->renderSceneGraph(canvasData->viewportSize);
680 #ifdef QQUICK_CANVAS_TIMING
681 if (qquick_canvas_timing)
682 renderTime = threadTimer.elapsed() - syncTime;
685 // The content of the target buffer is undefined after swap() so grab needs
686 // to happen before swap();
687 if (canvas == canvasToGrab) {
689 printf(" RenderThread: --- grabbing...\n");
691 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
696 printf(" RenderThread: --- wait for swap...\n");
699 if (canvasData->isVisible)
700 gl->swapBuffers(canvas);
702 canvasPrivate->fireFrameSwapped();
704 printf(" RenderThread: --- swap complete...\n");
709 #ifdef QQUICK_CANVAS_TIMING
710 if (qquick_canvas_timing) {
711 swapTime = threadTimer.elapsed() - renderTime;
712 qDebug() << "- Breakdown of frame time; sync:" << syncTime
713 << "ms render:" << renderTime << "ms swap:" << swapTime
714 << "ms total:" << swapTime + renderTime << "ms";
720 handleRemovedWindows();
722 isPaintCompleted = true;
725 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
726 it != m_rendered_windows.constEnd(); ++it) {
727 CanvasData *canvasData = it.value();
728 if (canvasData->sizeWasChanged) {
729 canvasData->renderedSize = canvasData->viewportSize;
730 canvasData->sizeWasChanged = false;
735 // Wake the GUI thread now that rendering is complete, to signal that painting
736 // is done, resizing is done or grabbing is completed. For grabbing, we're
737 // signalling this much later than needed (we could have done it before swap)
738 // but we don't want to lock an extra time.
741 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
743 printf(" RenderThread: nothing to do, going to sleep...\n");
745 isRenderBlocked = true;
747 isRenderBlocked = false;
752 QCoreApplication::processEvents();
754 // Process any "deleteLater" objects...
755 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
759 printf(" RenderThread: deleting all outstanding nodes\n");
762 m_removed_windows << m_rendered_windows.keys();
763 handleRemovedWindows();
766 printf(" RenderThread: render loop exited... Good Night!\n");
773 printf(" RenderThread: waking GUI for final sleep..\n");
779 printf(" RenderThread: All done...\n");
783 void QQuickRenderThreadSingleContextWindowManager::releaseResourcesInThread()
786 printf(" RenderThread: releasing resources...\n");
789 QQuickCanvas *canvas = masterCanvas();
790 QWindow *tmpSurface = 0;
793 gl->makeCurrent(canvas);
795 tmpSurface = new QWindow();
796 tmpSurface->setSurfaceType(QSurface::OpenGLSurface);
797 tmpSurface->resize(4, 4);
798 tmpSurface->create();
799 gl->makeCurrent(tmpSurface);
813 void QQuickRenderThreadSingleContextWindowManager::releaseResources()
816 printf("GUI: releasing resources\n");
820 if (!isRunning() && gl) {
821 runToReleaseResources = true;
830 printf("GUI: render thread running not releasing resources...\n");
837 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
839 Q_ASSERT(QThread::currentThread() == qApp->thread());
841 if (e->type() == QEvent_Sync) {
843 // If all canvases have been hidden, ignore the event
847 if (!syncAlreadyHappened)
850 syncAlreadyHappened = false;
852 if (animationRunning) {
854 printf("GUI: Advancing animations...\n");
857 animationDriver->advance();
860 printf("GUI: Animations advanced...\n");
865 } else if (e->type() == QEvent_DeferredUpdate) {
866 handleDeferredUpdate();
868 } else if (e->type() == QEvent::Timer) {
870 printf("GUI: Animations advanced via timer...\n");
872 animationDriver->advance();
875 return QThread::event(e);
880 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
882 if (isPostingSyncEvent) {
884 syncAlreadyHappened = true;
890 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
893 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
895 if (!guiAlreadyLocked)
898 for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
899 it != m_rendered_windows.constEnd(); ++it) {
900 QQuickCanvasPrivate::get(it.key())->polishItems();
906 if (!guiAlreadyLocked)
914 Acquires the mutex for the GUI thread. The function uses the isGuiLocked
915 variable to keep track of how many recursion levels the gui is locked with.
916 We only actually acquire the mutex for the first level to avoid deadlocking
920 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
922 if (++isGuiLocked == 1)
926 printf("GUI: aquired lock... level=%d\n", isGuiLocked);
932 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
935 printf("GUI: releasing lock... level=%d\n", isGuiLocked);
938 if (--isGuiLocked == 0)
945 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
948 printf("GUI: animationStarted()\n");
952 animationTimer = startTimer(1000/60);
958 animationRunning = true;
968 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
971 printf("GUI: animationStopped()...\n");
975 killTimer(animationTimer);
981 animationRunning = false;
986 void QQuickRenderThreadSingleContextWindowManager::paint(QQuickCanvas *canvas)
990 printf("GUI: paint called: %p\n", canvas);
996 isPaintCompleted = false;
997 while (isRunning() && !isPaintCompleted) {
1005 printf("GUI: paint done: %p\n", canvas);
1011 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
1014 printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
1017 // If the rendering thread is not running we do not need to do anything.
1018 // Also if the canvas is being resized to an invalid size, it will be removed
1019 // by the canvasVisibilityChanged slot as result of width/heightcChanged()
1020 if (!isRunning() || size.width() <= 0 || size.height() <= 0)
1026 CanvasData *canvasData = m_rendered_windows.value(canvas);
1028 canvasData->windowSize = size;
1029 while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
1030 if (isRenderBlocked)
1038 printf("GUI: Resize done: %p\n", canvas);
1044 void QQuickRenderThreadSingleContextWindowManager::startRendering()
1047 printf("GUI: Starting Render Thread\n");
1052 isPostingSyncEvent = false;
1053 syncAlreadyHappened = false;
1057 animationRunning = animationDriver->isRunning();
1058 start(); // Start the render thread...
1062 // Animations will now be driven from the rendering thread.
1063 if (animationTimer >= 0) {
1064 killTimer(animationTimer);
1065 animationTimer = -1;
1072 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1075 printf("GUI: stopping render thread\n");
1082 if (isRenderBlocked) {
1084 printf("GUI: waking up render thread\n");
1089 while (!hasExited) {
1091 printf("GUI: waiting for render thread to have exited..\n");
1099 printf("GUI: waiting for render thread to terminate..\n");
1101 // Actually wait for the thread to terminate. Otherwise we can delete it
1102 // too early and crash.
1106 printf("GUI: thread has terminated and we're all good..\n");
1109 // Activate timer to keep animations running
1110 if (animationDriver->isRunning())
1111 animationTimer = startTimer(1000/60);
1116 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1121 if (QThread::currentThread() != qApp->thread()) {
1122 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1127 printf("GUI: doing a pixelwise grab..\n");
1133 canvasToGrab = canvas;
1134 isPaintCompleted = false;
1135 while (isRunning() && !isPaintCompleted) {
1136 if (isRenderBlocked)
1141 QImage grabbed = grabContent;
1142 grabContent = QImage();
1150 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1153 printf("GUI: handling update to ourselves...\n");
1156 isDeferredUpdatePosted = false;
1159 isExternalUpdatePending = true;
1160 if (isRenderBlocked)
1165 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1167 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1168 "QQuickCanvas::update",
1169 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1172 isExternalUpdatePending = true;
1174 } else if (!isDeferredUpdatePosted) {
1176 printf("GUI: posting update to ourselves...\n");
1178 isDeferredUpdatePosted = true;
1179 QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1184 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1186 , eventPending(false)
1188 sg = QSGContext::createDefaultContext();
1192 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1195 data.updatePending = false;
1196 data.grabOnly = false;
1197 m_windows[canvas] = data;
1199 maybeUpdate(canvas);
1202 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1204 if (!m_windows.contains(canvas))
1207 m_windows.remove(canvas);
1208 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1209 cd->cleanupNodesOnShutdown();
1212 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1217 void QQuickTrivialWindowManager::releaseResources()
1219 if (m_windows.size() == 0) {
1220 QQuickCanvas *canvas = masterCanvas();
1221 QWindow *tmpSurface = 0;
1224 gl->makeCurrent(canvas);
1226 tmpSurface = new QWindow();
1227 tmpSurface->setSurfaceType(QSurface::OpenGLSurface);
1228 tmpSurface->resize(4, 4);
1229 tmpSurface->create();
1230 gl->makeCurrent(tmpSurface);
1241 QQuickCanvas *QQuickTrivialWindowManager::masterCanvas() const
1243 // Find a "proper surface" to bind...
1244 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1245 it != m_windows.constEnd(); ++it) {
1246 if (it.key()->visible())
1252 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1254 if (!m_windows.contains(canvas))
1257 CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1259 QQuickCanvas *window = canvas->visible() ? canvas : masterCanvas();
1265 gl = new QOpenGLContext();
1266 gl->setFormat(window->requestedFormat());
1268 if (!gl->makeCurrent(window))
1269 qWarning("QQuickCanvas: makeCurrent() failed...");
1272 gl->makeCurrent(window);
1275 bool alsoSwap = data.updatePending;
1276 data.updatePending = false;
1278 QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1280 cd->syncSceneGraph();
1281 cd->renderSceneGraph(canvas->size());
1283 if (data.grabOnly) {
1284 grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1285 data.grabOnly = false;
1288 if (alsoSwap && canvas->visible()) {
1289 gl->swapBuffers(canvas);
1290 cd->fireFrameSwapped();
1293 // Might have been set during syncSceneGraph()
1294 if (data.updatePending)
1295 maybeUpdate(canvas);
1298 void QQuickTrivialWindowManager::paint(QQuickCanvas *canvas)
1300 if (!m_windows.contains(canvas))
1303 m_windows[canvas].updatePending = true;
1304 renderCanvas(canvas);
1307 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1309 if (!m_windows.contains(canvas))
1312 m_windows[canvas].grabOnly = true;
1314 renderCanvas(canvas);
1316 QImage grabbed = grabContent;
1317 grabContent = QImage();
1323 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1329 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1331 if (!m_windows.contains(canvas))
1334 m_windows[canvas].updatePending = true;
1336 if (!eventPending) {
1337 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1338 eventPending = true;
1344 bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1351 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1357 bool QQuickTrivialWindowManager::event(QEvent *e)
1359 if (e->type() == QEvent::User) {
1360 eventPending = false;
1361 for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1362 it != m_windows.constEnd(); ++it) {
1363 const CanvasData &data = it.value();
1364 if (data.updatePending)
1365 renderCanvas(it.key());
1369 return QObject::event(e);
1372 #include "qquickwindowmanager.moc"