1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickwindowmanager_p.h"
44 #include <QtCore/QCoreApplication>
45 #include <QtCore/QTime>
46 #include <QtCore/QMutex>
47 #include <QtCore/QWaitCondition>
48 #include <QtCore/private/qabstractanimation_p.h>
50 #include <QtGui/QOpenGLContext>
51 #include <QtGui/private/qguiapplication_p.h>
52 #include <qpa/qplatformintegration.h>
54 #include <QtQml/private/qqmlglobal_p.h>
56 #include <QtQuick/QQuickWindow>
57 #include <QtQuick/private/qquickwindow_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_RENDER_TIMING
67 #ifdef QQUICK_RENDER_TIMING
68 static bool qquick_render_timing = !qgetenv("QML_RENDER_TIMING").isEmpty();
69 static QTime threadTimer;
71 static int renderTime;
75 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
80 expectations for this manager to work:
81 - one opengl context to render multiple windows
82 - OpenGL pipeline will not block for vsync in swap
83 - OpenGL pipeline will block based on a full buffer queue.
84 - Multiple screens can share the OpenGL context
85 - Animations are advanced for all windows once per swap
92 The threaded rendering uses a number of different variables to track potential
93 states used to handle resizing, initial paint, grabbing and driving animations
94 while ALWAYS keeping the GL context in the rendering thread and keeping the
95 overhead of normal one-shot paints and vblank driven animations at a minimum.
97 Resize, initial show and grab suffer slightly in this model as they are locked
98 to the rendering in the rendering thread, but this is a necessary evil for
101 Variables that are used:
103 Private::animationRunning: This is true while the animations are running, and only
104 written to inside locks.
106 RenderThread::isGuiLocked: This is used to indicate that the GUI thread owns the
107 lock. This variable is an integer to allow for recursive calls to lockInGui()
108 without using a recursive mutex. See isPostingSyncEvent.
110 RenderThread::isPostingSyncEvent: This variable is set in the render thread just
111 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
112 in the case where render thread waits while waiting for GUI to pick up the sync
113 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
114 When this happens, we use the
115 exhaustSyncEvent() function to do the sync right there and mark the coming
116 sync event to be discarded. There can only ever be one sync incoming.
118 RenderThread::isRenderBlock: This variable is true when animations are not
119 running and the render thread has gone to sleep, waiting for more to do.
121 RenderThread::isExternalUpdatePending: This variable is set to false when
122 a new render pass is started and to true in maybeUpdate(). It is an
123 indication to the render thread that another render pass needs to take
124 place, rather than the render thread going to sleep after completing its swap.
126 RenderThread::doGrab: This variable is set by the grab() function and
127 tells the renderer to do a grab after rendering is complete and before
130 RenderThread::shouldExit: This variable is used to determine if the render
131 thread should do a nother pass. It is typically set as a result of show()
132 and unset as a result of hide() or during shutdown()
134 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
135 after shouldExit has been set to true.
138 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
139 DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk
141 //#define THREAD_DEBUG
143 QQuickWindowManager::~QQuickWindowManager()
147 class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
151 QQuickRenderThreadSingleContextWindowManager()
152 : sg(QSGContext::createDefaultContext())
155 , allowMainThreadProcessingFlag(false)
157 , animationRunning(false)
158 , isPostingSyncEvent(false)
159 , isRenderBlocked(false)
160 , isExternalUpdatePending(false)
161 , syncAlreadyHappened(false)
165 , isDeferredUpdatePosted(false)
168 sg->moveToThread(this);
170 animationDriver = sg->createAnimationDriver(this);
171 animationDriver->install();
172 connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
173 connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
176 QSGContext *sceneGraphContext() const { return sg; }
178 void releaseResources() { }
180 void show(QQuickWindow *window);
181 void hide(QQuickWindow *window);
183 void windowDestroyed(QQuickWindow *window);
185 void exposureChanged(QQuickWindow *window);
186 QImage grab(QQuickWindow *window);
187 void resize(QQuickWindow *window, const QSize &size);
188 void handleDeferredUpdate();
189 void maybeUpdate(QQuickWindow *window);
190 void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation
193 void startRendering();
194 void stopRendering();
196 void exhaustSyncEvent();
197 void sync(bool guiAlreadyLocked);
201 volatile 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 QQuickWindow *masterWindow() {
215 QQuickWindow *win = 0;
216 for (QHash<QQuickWindow *, WindowData *>::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 windowVisibilityChanged();
230 void handleAddedWindows();
231 void handleAddedWindow(QQuickWindow *window);
232 void handleRemovedWindows(bool clearGLContext = true);
236 QAnimationDriver *animationDriver;
240 QWaitCondition condition;
242 volatile bool allowMainThreadProcessingFlag;
245 uint animationRunning: 1;
246 uint isPostingSyncEvent : 1;
247 uint isRenderBlocked : 1;
248 uint isExternalUpdatePending : 1;
249 uint syncAlreadyHappened : 1;
253 uint isDeferredUpdatePosted : 1;
255 QQuickWindow *windowToGrab;
263 uint sizeWasChanged : 1;
267 QHash<QQuickWindow *, WindowData *> m_rendered_windows;
269 struct WindowTracker {
270 QQuickWindow *window;
272 uint toBeRemoved : 1;
275 QList<WindowTracker> m_tracked_windows;
277 QList<QQuickWindow *> m_removed_windows;
278 QList<QQuickWindow *> m_added_windows;
282 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
285 QQuickTrivialWindowManager();
287 void show(QQuickWindow *window);
288 void hide(QQuickWindow *window);
290 void windowDestroyed(QQuickWindow *window);
293 void renderWindow(QQuickWindow *window);
294 void exposureChanged(QQuickWindow *window);
295 QImage grab(QQuickWindow *window);
296 void resize(QQuickWindow *window, const QSize &size);
299 void maybeUpdate(QQuickWindow *window);
300 void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation.
302 void releaseResources() { }
304 volatile bool *allowMainThreadProcessing();
306 QSGContext *sceneGraphContext() const;
308 bool event(QEvent *);
311 bool updatePending : 1;
315 QHash<QQuickWindow *, WindowData> m_windows;
326 QQuickWindowManager *QQuickWindowManager::instance()
328 static QQuickWindowManager *theInstance;
332 theInstance = QSGContext::createWindowManager();
334 bool bufferQueuing = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL);
335 bool fancy = bufferQueuing
336 && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
337 if (qmlNoThreadedRenderer())
339 else if (qmlForceThreadedRenderer())
342 // Enable fixed animation steps...
343 QByteArray fixed = qgetenv("QML_FIXED_ANIMATION_STEP");
344 bool fixedAnimationSteps = bufferQueuing;
346 fixedAnimationSteps = false;
347 else if (fixed.length())
348 fixedAnimationSteps = true;
349 if (fixedAnimationSteps)
350 QUnifiedTimer::instance(true)->setConsistentTiming(true);
354 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
355 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
365 void QQuickRenderThreadSingleContextWindowManager::initialize()
367 Q_ASSERT(m_rendered_windows.size());
369 QQuickWindow *win = masterWindow();
373 gl = new QOpenGLContext();
374 // Pick up the surface format from one of them
375 gl->setFormat(win->requestedFormat());
377 if (!gl->makeCurrent(win))
378 qWarning("QQuickWindow: makeCurrent() failed...");
380 Q_ASSERT(!sg->isReady());
386 This function is called when the window is created to register the window with
389 Called on GUI Thread.
392 void QQuickRenderThreadSingleContextWindowManager::show(QQuickWindow *window)
395 printf("GUI: Window added to windowing system, %p, %dx%d\n", window, window->width(), window->height());
398 WindowTracker tracker;
399 tracker.window = window;
400 tracker.isVisible = false;
401 tracker.toBeRemoved = false;
402 m_tracked_windows << tracker;
404 connect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection);
405 connect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection);
407 windowVisibilityChanged();
411 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickWindow *window)
414 printf(" RenderThread: adding window: %p\n", window);
417 WindowData *data = new WindowData;
418 data->sizeWasChanged = false;
419 data->windowSize = window->size();
420 data->isVisible = window->isVisible();
421 m_rendered_windows[window] = data;
423 isExternalUpdatePending = true;
428 Called on Render Thread
430 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
433 printf(" RenderThread: about to add %d\n", m_added_windows.size());
436 while (m_added_windows.size()) {
437 QQuickWindow *window = m_added_windows.takeLast();
438 handleAddedWindow(window);
444 Called on the GUI Thread, from the window' destructor
447 void QQuickRenderThreadSingleContextWindowManager::windowDestroyed(QQuickWindow *window)
450 printf("GUI: Window destroyed: %p\n", window);
461 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickWindow *window)
464 printf("GUI: Window hidden: %p\n", window);
468 for (int i=0; i<m_tracked_windows.size(); ++i) {
469 if (m_tracked_windows.at(i).window == window) {
470 m_tracked_windows[i].toBeRemoved = true;
477 disconnect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged()));
478 disconnect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged()));
479 windowVisibilityChanged();
480 m_tracked_windows.removeAt(position);
484 printf("GUI: Window removal completed... %p\n", window);
489 Called on Render Thread
491 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows(bool clearGLContext)
494 printf(" RenderThread: about to remove %d\n", m_removed_windows.size());
497 bool removedAnything = false;
498 while (m_removed_windows.size()) {
499 QQuickWindow *window = m_removed_windows.takeLast();
501 printf(" RenderThread: removing %p\n", window);
504 QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
505 delete m_rendered_windows.take(window);
506 removedAnything = true;
509 // If a window is removed because it has been hidden it will take with it
510 // the gl context (at least on Mac) if bound, so disconnect the gl context
512 if (removedAnything && clearGLContext)
522 void QQuickRenderThreadSingleContextWindowManager::windowVisibilityChanged()
524 bool anyoneShowing = false;
525 QList<QQuickWindow *> toAdd, toRemove;
527 // Not optimal, but also not frequently used...
528 for (int i=0; i<m_tracked_windows.size(); ++i) {
529 WindowTracker &t = const_cast<WindowTracker &>(m_tracked_windows.at(i));
530 QQuickWindow *win = t.window;
532 Q_ASSERT(win->isVisible() || QQuickWindowPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
533 bool windowVisible = win->width() > 0 && win->height() > 0;
534 anyoneShowing |= (windowVisible && !t.toBeRemoved);
536 if ((!windowVisible && t.isVisible) || t.toBeRemoved) {
538 } else if (windowVisible && !t.isVisible) {
541 t.isVisible = windowVisible;
545 if (!anyoneShowing) {
550 m_added_windows << toAdd;
551 m_removed_windows << toRemove;
552 while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
560 } else if (anyoneShowing) {
561 Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
562 for (int i=0; i<toAdd.size(); ++i)
563 handleAddedWindow(toAdd.at(i));
570 void QQuickRenderThreadSingleContextWindowManager::run()
573 printf("QML Rendering Thread Started\n");
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_RENDER_TIMING
620 if (qquick_render_timing)
624 for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
625 it != m_rendered_windows.constEnd(); ++it) {
626 QQuickWindow *window = it.key();
629 printf(" RenderThread: Syncing window: %p\n", window);
632 WindowData *windowData = it.value();
633 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window);
635 Q_ASSERT(windowData->windowSize.width() > 0 && windowData->windowSize.height() > 0);
637 if (!windowData->isVisible)
638 gl->makeCurrent(masterWindow());
640 gl->makeCurrent(window);
642 if (windowData->viewportSize != windowData->windowSize) {
644 printf(" RenderThread: --- window has changed size...\n");
646 windowData->viewportSize = windowData->windowSize;
647 windowData->sizeWasChanged = true;
648 glViewport(0, 0, windowData->viewportSize.width(), windowData->viewportSize.height());
651 windowPrivate->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_RENDER_TIMING
663 if (qquick_render_timing)
664 syncTime = threadTimer.elapsed();
667 for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
668 it != m_rendered_windows.constEnd(); ++it) {
669 QQuickWindow *window = it.key();
670 WindowData *windowData = it.value();
671 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window);
674 printf(" RenderThread: Rendering window %p\n", window);
677 Q_ASSERT(windowData->windowSize.width() > 0 && windowData->windowSize.height() > 0);
680 printf(" RenderThread: --- rendering at size %dx%d\n",
681 windowData->viewportSize.width(), windowData->viewportSize.height()
685 // We only need to re-makeCurrent when we have multiple surfaces.
686 if (m_rendered_windows.size() > 1)
687 gl->makeCurrent(window);
689 windowPrivate->renderSceneGraph(windowData->viewportSize);
690 #ifdef QQUICK_RENDER_TIMING
691 if (qquick_render_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 (window == windowToGrab) {
699 printf(" RenderThread: --- grabbing...\n");
701 grabContent = qt_gl_read_framebuffer(windowData->windowSize, false, false);
706 printf(" RenderThread: --- wait for swap...\n");
709 if (windowData->isVisible && window->isExposed())
710 gl->swapBuffers(window);
712 windowPrivate->fireFrameSwapped();
714 printf(" RenderThread: --- swap complete...\n");
719 #ifdef QQUICK_RENDER_TIMING
720 if (qquick_render_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();
733 for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
734 it != m_rendered_windows.constEnd(); ++it) {
735 WindowData *windowData = it.value();
736 if (windowData->sizeWasChanged) {
737 windowData->renderedSize = windowData->viewportSize;
738 windowData->sizeWasChanged = false;
743 // Wake the GUI thread now that rendering is complete, to signal that painting
744 // is done, resizing is done or grabbing is completed. For grabbing, we're
745 // signalling this much later than needed (we could have done it before swap)
746 // but we don't want to lock an extra time.
749 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !windowToGrab) {
751 printf(" RenderThread: nothing to do, going to sleep...\n");
753 isRenderBlocked = true;
755 isRenderBlocked = false;
760 QCoreApplication::processEvents();
762 // Process any "deleteLater" objects...
763 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
767 printf(" RenderThread: deleting all outstanding nodes\n");
770 m_removed_windows << m_rendered_windows.keys();
771 handleRemovedWindows(false);
780 printf(" RenderThread: render loop exited... Good Night!\n");
787 printf(" RenderThread: waking GUI for final sleep..\n");
793 printf(" RenderThread: All done...\n");
797 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
799 Q_ASSERT(QThread::currentThread() == qApp->thread());
801 if (e->type() == QEvent_Sync) {
803 // If all windows have been hidden, ignore the event
807 if (!syncAlreadyHappened)
810 syncAlreadyHappened = false;
812 if (animationRunning) {
814 printf("GUI: Advancing animations...\n");
817 animationDriver->advance();
820 printf("GUI: Animations advanced...\n");
825 } else if (e->type() == QEvent_DeferredUpdate) {
826 handleDeferredUpdate();
828 } else if (e->type() == QEvent::Timer) {
830 printf("GUI: Animations advanced via timer...\n");
832 animationDriver->advance();
835 return QThread::event(e);
840 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
842 if (isPostingSyncEvent) {
844 syncAlreadyHappened = true;
850 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
853 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
855 if (!guiAlreadyLocked)
858 for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
859 it != m_rendered_windows.constEnd(); ++it) {
860 QQuickWindowPrivate::get(it.key())->polishItems();
866 if (!guiAlreadyLocked)
874 Acquires the mutex for the GUI thread. The function uses the isGuiLocked
875 variable to keep track of how many recursion levels the gui is locked with.
876 We only actually acquire the mutex for the first level to avoid deadlocking
880 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
882 if (++isGuiLocked == 1)
886 printf("GUI: aquired lock... level=%d\n", isGuiLocked);
892 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
895 printf("GUI: releasing lock... level=%d\n", isGuiLocked);
898 if (--isGuiLocked == 0)
905 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
908 printf("GUI: animationStarted()\n");
912 animationTimer = startTimer(1000/60);
918 animationRunning = true;
928 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
931 printf("GUI: animationStopped()...\n");
935 killTimer(animationTimer);
941 animationRunning = false;
946 void QQuickRenderThreadSingleContextWindowManager::exposureChanged(QQuickWindow *window)
950 printf("GUI: exposure changed: %p\n", window);
953 if (window->isExposed())
957 printf("GUI: exposure changed done: %p\n", window);
963 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickWindow *window, const QSize &size)
966 printf("GUI: Resize Event: %p = %dx%d\n", window, size.width(), size.height());
969 // If the rendering thread is not running we do not need to do anything.
970 // Also if the window is being resized to an invalid size, it will be removed
971 // by the windowVisibilityChanged slot as result of width/heightcChanged()
972 if (!isRunning() || size.width() <= 0 || size.height() <= 0)
978 WindowData *windowData = m_rendered_windows.value(window);
980 windowData->windowSize = size;
981 while (isRunning() && windowData->renderedSize != size && size.width() > 0 && size.height() > 0) {
990 printf("GUI: Resize done: %p\n", window);
996 void QQuickRenderThreadSingleContextWindowManager::startRendering()
999 printf("GUI: Starting Render Thread\n");
1004 isPostingSyncEvent = false;
1005 syncAlreadyHappened = false;
1009 animationRunning = animationDriver->isRunning();
1010 start(); // Start the render thread...
1014 // Animations will now be driven from the rendering thread.
1015 if (animationTimer >= 0) {
1016 killTimer(animationTimer);
1017 animationTimer = -1;
1025 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1028 printf("GUI: stopping render thread\n");
1035 if (isRenderBlocked) {
1037 printf("GUI: waking up render thread\n");
1042 while (!hasExited) {
1044 printf("GUI: waiting for render thread to have exited..\n");
1052 printf("GUI: waiting for render thread to terminate..\n");
1054 // Actually wait for the thread to terminate. Otherwise we can delete it
1055 // too early and crash.
1059 printf("GUI: thread has terminated and we're all good..\n");
1062 // Activate timer to keep animations running
1063 if (animationDriver->isRunning())
1064 animationTimer = startTimer(1000/60);
1069 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickWindow *window)
1074 if (QThread::currentThread() != qApp->thread()) {
1075 qWarning("QQuickWindow::grabFrameBuffer: can only be called from the GUI thread");
1080 printf("GUI: doing a pixelwise grab..\n");
1086 windowToGrab = window;
1087 while (isRunning() && windowToGrab) {
1088 if (isRenderBlocked)
1093 QImage grabbed = grabContent;
1094 grabContent = QImage();
1102 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1105 printf("GUI: handling update to ourselves...\n");
1108 isDeferredUpdatePosted = false;
1111 isExternalUpdatePending = true;
1112 if (isRenderBlocked)
1117 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickWindow *)
1119 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1120 "QQuickWindow::update",
1121 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1124 isExternalUpdatePending = true;
1126 } else if (!isDeferredUpdatePosted) {
1128 printf("GUI: posting update to ourselves...\n");
1130 isDeferredUpdatePosted = true;
1131 QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1136 void QQuickRenderThreadSingleContextWindowManager::wakeup()
1139 isExternalUpdatePending = true;
1140 if (isRenderBlocked)
1145 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1147 , eventPending(false)
1149 sg = QSGContext::createDefaultContext();
1153 void QQuickTrivialWindowManager::show(QQuickWindow *window)
1156 data.updatePending = false;
1157 data.grabOnly = false;
1158 m_windows[window] = data;
1160 maybeUpdate(window);
1163 void QQuickTrivialWindowManager::hide(QQuickWindow *window)
1165 if (!m_windows.contains(window))
1168 m_windows.remove(window);
1169 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
1170 cd->cleanupNodesOnShutdown();
1172 if (m_windows.size() == 0) {
1179 void QQuickTrivialWindowManager::windowDestroyed(QQuickWindow *window)
1184 void QQuickTrivialWindowManager::renderWindow(QQuickWindow *window)
1186 if (!window->isExposed() || !m_windows.contains(window))
1189 WindowData &data = const_cast<WindowData &>(m_windows[window]);
1191 QQuickWindow *masterWindow = 0;
1192 if (!window->isVisible()) {
1193 // Find a "proper surface" to bind...
1194 for (QHash<QQuickWindow *, WindowData>::const_iterator it = m_windows.constBegin();
1195 it != m_windows.constEnd() && !masterWindow; ++it) {
1196 if (it.key()->isVisible())
1197 masterWindow = it.key();
1200 masterWindow = window;
1207 gl = new QOpenGLContext();
1208 gl->setFormat(masterWindow->requestedFormat());
1210 if (!gl->makeCurrent(masterWindow))
1211 qWarning("QQuickWindow: makeCurrent() failed...");
1214 gl->makeCurrent(masterWindow);
1217 bool alsoSwap = data.updatePending;
1218 data.updatePending = false;
1220 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
1222 cd->syncSceneGraph();
1223 cd->renderSceneGraph(window->size());
1225 if (data.grabOnly) {
1226 grabContent = qt_gl_read_framebuffer(window->size(), false, false);
1227 data.grabOnly = false;
1230 if (alsoSwap && window->isVisible()) {
1231 gl->swapBuffers(window);
1232 cd->fireFrameSwapped();
1235 // Might have been set during syncSceneGraph()
1236 if (data.updatePending)
1237 maybeUpdate(window);
1240 void QQuickTrivialWindowManager::exposureChanged(QQuickWindow *window)
1242 if (window->isExposed())
1243 maybeUpdate(window);
1246 QImage QQuickTrivialWindowManager::grab(QQuickWindow *window)
1248 if (!m_windows.contains(window))
1251 m_windows[window].grabOnly = true;
1253 renderWindow(window);
1255 QImage grabbed = grabContent;
1256 grabContent = QImage();
1262 void QQuickTrivialWindowManager::resize(QQuickWindow *, const QSize &)
1268 void QQuickTrivialWindowManager::maybeUpdate(QQuickWindow *window)
1270 if (!m_windows.contains(window))
1273 m_windows[window].updatePending = true;
1275 if (!eventPending) {
1276 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1277 eventPending = true;
1281 void QQuickTrivialWindowManager::wakeup()
1285 volatile bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1292 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1298 bool QQuickTrivialWindowManager::event(QEvent *e)
1300 if (e->type() == QEvent::User) {
1301 eventPending = false;
1302 for (QHash<QQuickWindow *, WindowData>::const_iterator it = m_windows.constBegin();
1303 it != m_windows.constEnd(); ++it) {
1304 const WindowData &data = it.value();
1305 if (data.updatePending)
1306 renderWindow(it.key());
1310 return QObject::event(e);
1313 #include "qquickwindowmanager.moc"