Merge master <-> api_changes
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickwindowmanager.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickwindowmanager_p.h"
43
44 #include <QtCore/QCoreApplication>
45 #include <QtCore/QTime>
46 #include <QtCore/QMutex>
47 #include <QtCore/QWaitCondition>
48 #include <QtCore/private/qabstractanimation_p.h>
49
50 #include <QtGui/QOpenGLContext>
51 #include <QtGui/private/qguiapplication_p.h>
52 #include <QtGui/qplatformintegration_qpa.h>
53
54 #include <QtQml/private/qqmlglobal_p.h>
55
56 #include <QtQuick/QQuickCanvas>
57 #include <QtQuick/private/qquickcanvas_p.h>
58 #include <QtQuick/private/qsgcontext_p.h>
59
60 QT_BEGIN_NAMESPACE
61
62 const QEvent::Type QEvent_Sync = QEvent::Type(QEvent::User);
63 const QEvent::Type QEvent_DeferredUpdate = QEvent::Type(QEvent::User + 1);
64
65
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;
70 static int syncTime;
71 static int renderTime;
72 static int swapTime;
73 #endif
74
75 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
76
77
78
79 /*!
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
86  */
87
88 /*
89   Threaded Rendering
90   ==================
91
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.
96
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
99   the system to work.
100
101   Variables that are used:
102
103   Private::animationRunning: This is true while the animations are running, and only
104   written to inside locks.
105
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.
109
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
113   complete.
114
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.
122
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.
125
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.
130
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
133   swapping happens.
134
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()
138
139   RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
140   after shouldExit has been set to true.
141  */
142
143 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP);
144 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
145
146 //#define THREAD_DEBUG
147
148 QQuickWindowManager::~QQuickWindowManager()
149 {
150 }
151
152 class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
153 {
154     Q_OBJECT
155 public:
156     QQuickRenderThreadSingleContextWindowManager()
157         : sg(QSGContext::createDefaultContext())
158         , gl(0)
159         , animationTimer(-1)
160         , allowMainThreadProcessingFlag(false)
161         , isGuiLocked(0)
162         , animationRunning(false)
163         , isPaintCompleted(false)
164         , isPostingSyncEvent(false)
165         , isRenderBlocked(false)
166         , isExternalUpdatePending(false)
167         , syncAlreadyHappened(false)
168         , inSync(false)
169         , shouldExit(false)
170         , hasExited(false)
171         , isDeferredUpdatePosted(false)
172         , canvasToGrab(0)
173     {
174         sg->moveToThread(this);
175
176         animationDriver = sg->createAnimationDriver(this);
177         animationDriver->install();
178         connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
179         connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
180     }
181
182     QSGContext *sceneGraphContext() const { return sg; }
183
184     void releaseResources() { }
185
186     void show(QQuickCanvas *canvas);
187     void hide(QQuickCanvas *canvas);
188
189     void canvasDestroyed(QQuickCanvas *canvas);
190
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);
196     void wakeup();
197
198     void startRendering();
199     void stopRendering();
200
201     void exhaustSyncEvent();
202     void sync(bool guiAlreadyLocked);
203
204     void initialize();
205
206     volatile bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
207
208     bool event(QEvent *);
209
210     inline void lock() { mutex.lock(); }
211     inline void unlock() { mutex.unlock(); }
212     inline void wait() { condition.wait(&mutex); }
213     inline void wake() { condition.wakeOne(); }
214     void lockInGui();
215     void unlockInGui();
216
217     void run();
218
219     QQuickCanvas *masterCanvas() {
220         QQuickCanvas *win = 0;
221         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
222             it != m_rendered_windows.constEnd() && !win; ++it) {
223             if (it.value()->isVisible)
224                 win = it.key();
225         }
226         return win;
227     }
228
229 public slots:
230     void animationStarted();
231     void animationStopped();
232     void canvasVisibilityChanged();
233
234 private:
235     void handleAddedWindows();
236     void handleAddedWindow(QQuickCanvas *canvas);
237     void handleRemovedWindows();
238
239     QSGContext *sg;
240     QOpenGLContext *gl;
241     QAnimationDriver *animationDriver;
242     int animationTimer;
243
244     QMutex mutex;
245     QWaitCondition condition;
246
247     volatile bool allowMainThreadProcessingFlag;
248
249     int isGuiLocked;
250     uint animationRunning: 1;
251     uint isPaintCompleted : 1;
252     uint isPostingSyncEvent : 1;
253     uint isRenderBlocked : 1;
254     uint isExternalUpdatePending : 1;
255     uint syncAlreadyHappened : 1;
256     uint inSync : 1;
257     uint shouldExit : 1;
258     uint hasExited : 1;
259     uint isDeferredUpdatePosted : 1;
260
261     QQuickCanvas *canvasToGrab;
262     QImage grabContent;
263
264     struct CanvasData {
265         QSize renderedSize;
266         QSize windowSize;
267         QSize viewportSize;
268
269         uint sizeWasChanged : 1;
270         uint isVisible : 1;
271     };
272
273     QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
274
275     struct CanvasTracker {
276         QQuickCanvas *canvas;
277         uint isVisible : 1;
278         uint toBeRemoved : 1;
279     };
280
281     QList<CanvasTracker> m_tracked_windows;
282
283     QList<QQuickCanvas *> m_removed_windows;
284     QList<QQuickCanvas *> m_added_windows;
285 };
286
287
288 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
289 {
290 public:
291     QQuickTrivialWindowManager();
292
293     void show(QQuickCanvas *canvas);
294     void hide(QQuickCanvas *canvas);
295
296     void canvasDestroyed(QQuickCanvas *canvas);
297
298     void initializeGL();
299     void renderCanvas(QQuickCanvas *canvas);
300     void paint(QQuickCanvas *canvas);
301     QImage grab(QQuickCanvas *canvas);
302     void resize(QQuickCanvas *canvas, const QSize &size);
303     void wakeup();
304
305     void maybeUpdate(QQuickCanvas *canvas);
306
307     void releaseResources() { }
308
309     volatile bool *allowMainThreadProcessing();
310
311     QSGContext *sceneGraphContext() const;
312
313     bool event(QEvent *);
314
315     struct CanvasData {
316         bool updatePending : 1;
317         bool grabOnly : 1;
318     };
319
320     QHash<QQuickCanvas *, CanvasData> m_windows;
321
322     QOpenGLContext *gl;
323     QSGContext *sg;
324
325     QImage grabContent;
326
327     bool eventPending;
328 };
329
330
331 QQuickWindowManager *QQuickWindowManager::instance()
332 {
333     static QQuickWindowManager *theInstance;
334
335     if (!theInstance) {
336         bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL);
337         if (qmlNoThreadedRenderer())
338             fancy = false;
339         if (qmlFixedAnimationStep())
340             QUnifiedTimer::instance(true)->setConsistentTiming(true);
341         theInstance = fancy
342                 ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
343                 : (QQuickWindowManager*) new QQuickTrivialWindowManager;
344     }
345     return theInstance;
346 }
347
348
349
350
351
352 void QQuickRenderThreadSingleContextWindowManager::initialize()
353 {
354     Q_ASSERT(m_rendered_windows.size());
355
356     QQuickCanvas *win = masterCanvas();
357     if (!win)
358         return;
359
360     gl = new QOpenGLContext();
361     // Pick up the surface format from one of them
362     gl->setFormat(win->requestedFormat());
363     gl->create();
364     if (!gl->makeCurrent(win))
365         qWarning("QQuickCanvas: makeCurrent() failed...");
366
367     Q_ASSERT(!sg->isReady());
368     sg->initialize(gl);
369 }
370
371
372 /*!
373     This function is called when the canvas is created to register the canvas with
374     the window manager.
375
376     Called on GUI Thread.
377  */
378
379 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
380 {
381 #ifdef THREAD_DEBUG
382     printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
383 #endif
384
385     CanvasTracker tracker;
386     tracker.canvas = canvas;
387     tracker.isVisible = false;
388     tracker.toBeRemoved = false;
389     m_tracked_windows << tracker;
390
391     connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
392     connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
393
394     canvasVisibilityChanged();
395 }
396
397
398 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
399 {
400 #ifdef THREAD_DEBUG
401     printf("                RenderThread: adding canvas: %p\n", canvas);
402 #endif
403
404     CanvasData *data = new CanvasData;
405     data->sizeWasChanged = false;
406     data->windowSize = canvas->size();
407     data->isVisible = canvas->visible();
408     m_rendered_windows[canvas] = data;
409
410     isExternalUpdatePending = true;
411 }
412
413
414 /*!
415     Called on Render Thread
416  */
417 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
418 {
419 #ifdef THREAD_DEBUG
420     printf("                RenderThread: about to add %d\n", m_added_windows.size());
421 #endif
422
423     while (m_added_windows.size()) {
424         QQuickCanvas *canvas = m_added_windows.takeLast();
425         handleAddedWindow(canvas);
426     }
427 }
428
429
430 /*!
431     Called on the GUI Thread, from the canvas' destructor
432  */
433
434 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
435 {
436 #ifdef THREAD_DEBUG
437     printf("GUI: Canvas destroyed: %p\n", canvas);
438 #endif
439
440     hide(canvas);
441 }
442
443
444 /*!
445     Called on GUI Thread
446  */
447
448 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
449 {
450 #ifdef THREAD_DEBUG
451     printf("GUI: Canvas hidden: %p\n", canvas);
452 #endif
453
454     int position = -1;
455     for (int i=0; i<m_tracked_windows.size(); ++i) {
456         if (m_tracked_windows.at(i).canvas == canvas) {
457             m_tracked_windows[i].toBeRemoved = true;
458             position = i;
459             break;
460         }
461     }
462
463     if (position >= 0) {
464         disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
465         disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
466         canvasVisibilityChanged();
467         m_tracked_windows.removeAt(position);
468     }
469
470 #ifdef THREAD_DEBUG
471     printf("GUI: Canvas removal completed... %p\n", canvas);
472 #endif
473 }
474
475 /*!
476     Called on Render Thread
477  */
478 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
479 {
480 #ifdef THREAD_DEBUG
481     printf("                RenderThread: about to remove %d\n", m_removed_windows.size());
482 #endif
483
484     bool removedAnything = false;
485     while (m_removed_windows.size()) {
486         QQuickCanvas *canvas = m_removed_windows.takeLast();
487 #ifdef THREAD_DEBUG
488     printf("                RenderThread: removing %p\n", canvas);
489 #endif
490
491         QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
492         delete m_rendered_windows.take(canvas);
493         removedAnything = true;
494     }
495
496     // If a window is removed because it has been hidden it will take with it
497     // the gl context (at least on Mac) if bound, so disconnect the gl context
498     // from anything
499     if (removedAnything)
500         gl->doneCurrent();
501 }
502
503
504
505 /*!
506     Called on GUI Thread
507  */
508
509 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
510 {
511     bool anyoneShowing = false;
512     QList<QQuickCanvas *> toAdd, toRemove;
513
514     // Not optimal, but also not frequently used...
515     for (int i=0; i<m_tracked_windows.size(); ++i) {
516         CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
517         QQuickCanvas *win = t.canvas;
518
519         Q_ASSERT(win->visible() || QQuickCanvasPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
520         bool canvasVisible = win->width() > 0 && win->height() > 0;
521         anyoneShowing |= (canvasVisible && !t.toBeRemoved);
522
523         if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
524             toRemove << win;
525         } else if (canvasVisible && !t.isVisible) {
526             toAdd << win;
527         }
528         t.isVisible = canvasVisible;
529     }
530
531     if (isRunning()) {
532         if (!anyoneShowing) {
533             stopRendering();
534         } else {
535             lockInGui();
536             exhaustSyncEvent();
537             m_added_windows << toAdd;
538             m_removed_windows << toRemove;
539             while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
540                 if (isRenderBlocked)
541                     wake();
542                 wait();
543             }
544             unlockInGui();
545         }
546
547     } else if (anyoneShowing) {
548         Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
549         for (int i=0; i<toAdd.size(); ++i)
550             handleAddedWindow(toAdd.at(i));
551         startRendering();
552     }
553
554 }
555
556
557 void QQuickRenderThreadSingleContextWindowManager::run()
558 {
559 #ifdef THREAD_DEBUG
560     printf("QML Rendering Thread Started\n");
561 #endif
562
563     lock();
564     Q_ASSERT(!gl);
565     initialize();
566     // Wake GUI as it is waiting for the GL context to have appeared, as
567     // an indication that the render thread is now running.
568     wake();
569     unlock();
570
571     if (!gl)
572         return;
573
574     while (!shouldExit) {
575         lock();
576
577 #ifdef THREAD_DEBUG
578         printf("                RenderThread: *** NEW FRAME ***\n");
579 #endif
580
581         isExternalUpdatePending = false;
582         handleAddedWindows();
583
584         if (!isGuiLocked) {
585             isPostingSyncEvent = true;
586
587 #ifdef THREAD_DEBUG
588             printf("                RenderThread: aquired sync lock...\n");
589 #endif
590             allowMainThreadProcessingFlag = false;
591             QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
592
593 #ifdef THREAD_DEBUG
594             printf("                RenderThread: going to sleep...\n");
595 #endif
596             wake(); // In case the event got through all the way to wait() before this thread got to wait.
597             wait();
598
599
600             isPostingSyncEvent = false;
601         }
602
603 #ifdef THREAD_DEBUG
604         printf("                RenderThread: Doing locked sync\n");
605 #endif
606 #ifdef QQUICK_CANVAS_TIMING
607         if (qquick_canvas_timing)
608             threadTimer.start();
609 #endif
610         inSync = true;
611         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
612              it != m_rendered_windows.constEnd(); ++it) {
613             QQuickCanvas *canvas = it.key();
614
615 #ifdef THREAD_DEBUG
616             printf("                RenderThread: Syncing canvas: %p\n", canvas);
617 #endif
618
619             CanvasData *canvasData = it.value();
620             QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
621
622             Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
623
624             if (!canvasData->isVisible)
625                 gl->makeCurrent(masterCanvas());
626             else
627                 gl->makeCurrent(canvas);
628
629             if (canvasData->viewportSize != canvasData->windowSize) {
630 #ifdef THREAD_DEBUG
631                 printf("                RenderThread: --- window has changed size...\n");
632 #endif
633                 canvasData->viewportSize = canvasData->windowSize;
634                 canvasData->sizeWasChanged = true;
635                 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
636             }
637
638             canvasPrivate->syncSceneGraph();
639         }
640         inSync = false;
641
642         // Wake GUI after sync to let it continue animating and event processing.
643         allowMainThreadProcessingFlag = true;
644         wake();
645         unlock();
646 #ifdef THREAD_DEBUG
647         printf("                RenderThread: sync done\n");
648 #endif
649 #ifdef QQUICK_CANVAS_TIMING
650         if (qquick_canvas_timing)
651             syncTime = threadTimer.elapsed();
652 #endif
653
654         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
655              it != m_rendered_windows.constEnd(); ++it) {
656             QQuickCanvas *canvas = it.key();
657             CanvasData *canvasData = it.value();
658             QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
659
660 #ifdef THREAD_DEBUG
661             printf("                RenderThread: Rendering canvas %p\n", canvas);
662 #endif
663
664             Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
665
666 #ifdef THREAD_DEBUG
667             printf("                RenderThread: --- rendering at size %dx%d\n",
668                    canvasData->viewportSize.width(), canvasData->viewportSize.height()
669                    );
670 #endif
671
672             // We only need to re-makeCurrent when we have multiple surfaces.
673             if (m_rendered_windows.size() > 1)
674                 gl->makeCurrent(canvas);
675
676             canvasPrivate->renderSceneGraph(canvasData->viewportSize);
677 #ifdef QQUICK_CANVAS_TIMING
678             if (qquick_canvas_timing)
679                 renderTime = threadTimer.elapsed() - syncTime;
680 #endif
681
682             // The content of the target buffer is undefined after swap() so grab needs
683             // to happen before swap();
684             if (canvas == canvasToGrab) {
685 #ifdef THREAD_DEBUG
686                 printf("                RenderThread: --- grabbing...\n");
687 #endif
688                 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
689                 canvasToGrab = 0;
690             }
691
692 #ifdef THREAD_DEBUG
693             printf("                RenderThread: --- wait for swap...\n");
694 #endif
695
696             if (canvasData->isVisible)
697                 gl->swapBuffers(canvas);
698
699             canvasPrivate->fireFrameSwapped();
700 #ifdef THREAD_DEBUG
701             printf("                RenderThread: --- swap complete...\n");
702 #endif
703
704         }
705
706 #ifdef QQUICK_CANVAS_TIMING
707             if (qquick_canvas_timing) {
708                 swapTime = threadTimer.elapsed() - renderTime;
709                 qDebug() << "- Breakdown of frame time; sync:" << syncTime
710                          << "ms render:" << renderTime << "ms swap:" << swapTime
711                          << "ms total:" << swapTime + renderTime << "ms";
712             }
713 #endif
714
715         lock();
716
717         handleRemovedWindows();
718
719         isPaintCompleted = true;
720
721         // Update sizes...
722         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
723              it != m_rendered_windows.constEnd(); ++it) {
724             CanvasData *canvasData = it.value();
725             if (canvasData->sizeWasChanged) {
726                 canvasData->renderedSize = canvasData->viewportSize;
727                 canvasData->sizeWasChanged = false;
728             }
729         }
730
731
732         // Wake the GUI thread now that rendering is complete, to signal that painting
733         // is done, resizing is done or grabbing is completed. For grabbing, we're
734         // signalling this much later than needed (we could have done it before swap)
735         // but we don't want to lock an extra time.
736         wake();
737
738         if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
739 #ifdef THREAD_DEBUG
740             printf("                RenderThread: nothing to do, going to sleep...\n");
741 #endif
742             isRenderBlocked = true;
743             wait();
744             isRenderBlocked = false;
745         }
746
747         unlock();
748
749         QCoreApplication::processEvents();
750
751         // Process any "deleteLater" objects...
752         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
753     }
754
755 #ifdef THREAD_DEBUG
756     printf("                RenderThread: deleting all outstanding nodes\n");
757 #endif
758
759     m_removed_windows << m_rendered_windows.keys();
760     handleRemovedWindows();
761
762     sg->invalidate();
763
764     gl->doneCurrent();
765     delete gl;
766     gl = 0;
767
768 #ifdef THREAD_DEBUG
769     printf("                RenderThread: render loop exited... Good Night!\n");
770 #endif
771
772     lock();
773     hasExited = true;
774
775 #ifdef THREAD_DEBUG
776     printf("                RenderThread: waking GUI for final sleep..\n");
777 #endif
778     wake();
779     unlock();
780
781 #ifdef THREAD_DEBUG
782     printf("                RenderThread: All done...\n");
783 #endif
784 }
785
786 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
787 {
788     Q_ASSERT(QThread::currentThread() == qApp->thread());
789
790     if (e->type() == QEvent_Sync) {
791
792         // If all canvases have been hidden, ignore the event
793         if (!isRunning())
794             return true;
795
796         if (!syncAlreadyHappened)
797             sync(false);
798
799         syncAlreadyHappened = false;
800
801         if (animationRunning) {
802 #ifdef THREAD_DEBUG
803             printf("GUI: Advancing animations...\n");
804 #endif
805
806             animationDriver->advance();
807
808 #ifdef THREAD_DEBUG
809             printf("GUI: Animations advanced...\n");
810 #endif
811         }
812
813         return true;
814     } else if (e->type() == QEvent_DeferredUpdate) {
815         handleDeferredUpdate();
816
817     } else if (e->type() == QEvent::Timer) {
818 #ifdef THREAD_DEBUG
819             printf("GUI: Animations advanced via timer...\n");
820 #endif
821         animationDriver->advance();
822     }
823
824     return QThread::event(e);
825 }
826
827
828
829 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
830 {
831     if (isPostingSyncEvent) {
832         sync(true);
833         syncAlreadyHappened = true;
834     }
835 }
836
837
838
839 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
840 {
841 #ifdef THREAD_DEBUG
842     printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
843 #endif
844     if (!guiAlreadyLocked)
845         lockInGui();
846
847     for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
848          it != m_rendered_windows.constEnd(); ++it) {
849         QQuickCanvasPrivate::get(it.key())->polishItems();
850     }
851
852     wake();
853     wait();
854
855     if (!guiAlreadyLocked)
856         unlockInGui();
857 }
858
859
860
861
862 /*!
863     Acquires the mutex for the GUI thread. The function uses the isGuiLocked
864     variable to keep track of how many recursion levels the gui is locked with.
865     We only actually acquire the mutex for the first level to avoid deadlocking
866     ourselves.
867  */
868
869 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
870 {
871     if (++isGuiLocked == 1)
872         lock();
873
874 #ifdef THREAD_DEBUG
875     printf("GUI: aquired lock... level=%d\n", isGuiLocked);
876 #endif
877 }
878
879
880
881 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
882 {
883 #ifdef THREAD_DEBUG
884     printf("GUI: releasing lock... level=%d\n", isGuiLocked);
885 #endif
886
887     if (--isGuiLocked == 0)
888         unlock();
889 }
890
891
892
893
894 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
895 {
896 #ifdef THREAD_DEBUG
897     printf("GUI: animationStarted()\n");
898 #endif
899
900     if (!isRunning()) {
901         animationTimer = startTimer(1000/60);
902         return;
903     }
904
905     lockInGui();
906
907     animationRunning = true;
908
909     if (isRenderBlocked)
910         wake();
911
912     unlockInGui();
913 }
914
915
916
917 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
918 {
919 #ifdef THREAD_DEBUG
920     printf("GUI: animationStopped()...\n");
921 #endif
922
923     if (!isRunning()) {
924         killTimer(animationTimer);
925         animationTimer = -1;
926         return;
927     }
928
929     lockInGui();
930     animationRunning = false;
931     unlockInGui();
932 }
933
934
935 void QQuickRenderThreadSingleContextWindowManager::paint(QQuickCanvas *canvas)
936 {
937     Q_UNUSED(canvas);
938 #ifdef THREAD_DEBUG
939     printf("GUI: paint called: %p\n", canvas);
940 #endif
941
942     lockInGui();
943     exhaustSyncEvent();
944
945     isPaintCompleted = false;
946     while (isRunning() && !isPaintCompleted) {
947         if (isRenderBlocked)
948             wake();
949         wait();
950     }
951     unlockInGui();
952
953 #ifdef THREAD_DEBUG
954     printf("GUI: paint done: %p\n", canvas);
955 #endif
956 }
957
958
959
960 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
961 {
962 #ifdef THREAD_DEBUG
963     printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
964 #endif
965
966     // If the rendering thread is not running we do not need to do anything.
967     // Also if the canvas is being resized to an invalid size, it will be removed
968     // by the canvasVisibilityChanged slot as result of width/heightcChanged()
969     if (!isRunning() || size.width() <= 0 || size.height() <= 0)
970         return;
971
972     lockInGui();
973     exhaustSyncEvent();
974
975     CanvasData *canvasData = m_rendered_windows.value(canvas);
976     if (canvasData) {
977         canvasData->windowSize = size;
978         while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
979             if (isRenderBlocked)
980                 wake();
981             wait();
982         }
983     }
984     unlockInGui();
985
986 #ifdef THREAD_DEBUG
987     printf("GUI: Resize done: %p\n", canvas);
988 #endif
989 }
990
991
992
993 void QQuickRenderThreadSingleContextWindowManager::startRendering()
994 {
995 #ifdef THREAD_DEBUG
996     printf("GUI: Starting Render Thread\n");
997 #endif
998     hasExited = false;
999     shouldExit = false;
1000     isGuiLocked = 0;
1001     isPostingSyncEvent = false;
1002     syncAlreadyHappened = false;
1003     inSync = false;
1004
1005     lockInGui();
1006     animationRunning = animationDriver->isRunning();
1007     start(); // Start the render thread...
1008     wait();
1009     unlockInGui();
1010
1011     // Animations will now be driven from the rendering thread.
1012     if (animationTimer >= 0) {
1013         killTimer(animationTimer);
1014         animationTimer = -1;
1015     }
1016
1017
1018 }
1019
1020
1021
1022 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1023 {
1024 #ifdef THREAD_DEBUG
1025     printf("GUI: stopping render thread\n");
1026 #endif
1027
1028     lockInGui();
1029     exhaustSyncEvent();
1030     shouldExit = true;
1031
1032     if (isRenderBlocked) {
1033 #ifdef THREAD_DEBUG
1034         printf("GUI: waking up render thread\n");
1035 #endif
1036         wake();
1037     }
1038
1039     while (!hasExited) {
1040 #ifdef THREAD_DEBUG
1041         printf("GUI: waiting for render thread to have exited..\n");
1042 #endif
1043         wait();
1044     }
1045
1046     unlockInGui();
1047
1048 #ifdef THREAD_DEBUG
1049     printf("GUI: waiting for render thread to terminate..\n");
1050 #endif
1051     // Actually wait for the thread to terminate.  Otherwise we can delete it
1052     // too early and crash.
1053     QThread::wait();
1054
1055 #ifdef THREAD_DEBUG
1056     printf("GUI: thread has terminated and we're all good..\n");
1057 #endif
1058
1059     // Activate timer to keep animations running
1060     if (animationDriver->isRunning())
1061         animationTimer = startTimer(1000/60);
1062 }
1063
1064
1065
1066 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1067 {
1068     if (!isRunning())
1069         return QImage();
1070
1071     if (QThread::currentThread() != qApp->thread()) {
1072         qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1073         return QImage();
1074     }
1075
1076 #ifdef THREAD_DEBUG
1077     printf("GUI: doing a pixelwise grab..\n");
1078 #endif
1079
1080     lockInGui();
1081     exhaustSyncEvent();
1082
1083     canvasToGrab = canvas;
1084     isPaintCompleted = false;
1085     while (isRunning() && !isPaintCompleted) {
1086         if (isRenderBlocked)
1087             wake();
1088         wait();
1089     }
1090
1091     QImage grabbed = grabContent;
1092     grabContent = QImage();
1093
1094     unlockInGui();
1095
1096     return grabbed;
1097 }
1098
1099
1100 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1101 {
1102 #ifdef THREAD_DEBUG
1103     printf("GUI: handling update to ourselves...\n");
1104 #endif
1105
1106     isDeferredUpdatePosted = false;
1107
1108     lockInGui();
1109     isExternalUpdatePending = true;
1110     if (isRenderBlocked)
1111         wake();
1112     unlockInGui();
1113 }
1114
1115 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1116 {
1117     Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1118                "QQuickCanvas::update",
1119                "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1120
1121     if (inSync) {
1122         isExternalUpdatePending = true;
1123
1124     } else if (!isDeferredUpdatePosted) {
1125 #ifdef THREAD_DEBUG
1126         printf("GUI: posting update to ourselves...\n");
1127 #endif
1128         isDeferredUpdatePosted = true;
1129         QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1130     }
1131
1132 }
1133
1134 void QQuickRenderThreadSingleContextWindowManager::wakeup()
1135 {
1136     lockInGui();
1137     if (isRenderBlocked)
1138         wake();
1139     unlockInGui();
1140 }
1141
1142 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1143     : gl(0)
1144     , eventPending(false)
1145 {
1146     sg = QSGContext::createDefaultContext();
1147 }
1148
1149
1150 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1151 {
1152     CanvasData data;
1153     data.updatePending = false;
1154     data.grabOnly = false;
1155     m_windows[canvas] = data;
1156
1157     maybeUpdate(canvas);
1158 }
1159
1160 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1161 {
1162     if (!m_windows.contains(canvas))
1163         return;
1164
1165     m_windows.remove(canvas);
1166     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1167     cd->cleanupNodesOnShutdown();
1168
1169     if (m_windows.size() == 0) {
1170         sg->invalidate();
1171         delete gl;
1172         gl = 0;
1173     }
1174 }
1175
1176 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1177 {
1178     hide(canvas);
1179 }
1180
1181 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1182 {
1183     if (!m_windows.contains(canvas))
1184         return;
1185
1186     CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1187
1188     QQuickCanvas *masterCanvas = 0;
1189     if (!canvas->visible()) {
1190         // Find a "proper surface" to bind...
1191         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1192              it != m_windows.constEnd() && !masterCanvas; ++it) {
1193             if (it.key()->visible())
1194                 masterCanvas = it.key();
1195         }
1196     } else {
1197         masterCanvas = canvas;
1198     }
1199
1200     if (!masterCanvas)
1201         return;
1202
1203     if (!gl) {
1204         gl = new QOpenGLContext();
1205         gl->setFormat(masterCanvas->requestedFormat());
1206         gl->create();
1207         if (!gl->makeCurrent(masterCanvas))
1208             qWarning("QQuickCanvas: makeCurrent() failed...");
1209         sg->initialize(gl);
1210     } else {
1211         gl->makeCurrent(masterCanvas);
1212     }
1213
1214     bool alsoSwap = data.updatePending;
1215     data.updatePending = false;
1216
1217     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1218     cd->polishItems();
1219     cd->syncSceneGraph();
1220     cd->renderSceneGraph(canvas->size());
1221
1222     if (data.grabOnly) {
1223         grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1224         data.grabOnly = false;
1225     }
1226
1227     if (alsoSwap && canvas->visible()) {
1228         gl->swapBuffers(canvas);
1229         cd->fireFrameSwapped();
1230     }
1231
1232     // Might have been set during syncSceneGraph()
1233     if (data.updatePending)
1234         maybeUpdate(canvas);
1235 }
1236
1237 void QQuickTrivialWindowManager::paint(QQuickCanvas *canvas)
1238 {
1239     if (!m_windows.contains(canvas))
1240         return;
1241
1242     m_windows[canvas].updatePending = true;
1243     renderCanvas(canvas);
1244 }
1245
1246 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1247 {
1248     if (!m_windows.contains(canvas))
1249         return QImage();
1250
1251     m_windows[canvas].grabOnly = true;
1252
1253     renderCanvas(canvas);
1254
1255     QImage grabbed = grabContent;
1256     grabContent = QImage();
1257     return grabbed;
1258 }
1259
1260
1261
1262 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1263 {
1264 }
1265
1266
1267
1268 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1269 {
1270     if (!m_windows.contains(canvas))
1271         return;
1272
1273     m_windows[canvas].updatePending = true;
1274
1275     if (!eventPending) {
1276         QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1277         eventPending = true;
1278     }
1279 }
1280
1281 void QQuickTrivialWindowManager::wakeup()
1282 {
1283 }
1284
1285 volatile bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1286 {
1287     return 0;
1288 }
1289
1290
1291
1292 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1293 {
1294     return sg;
1295 }
1296
1297
1298 bool QQuickTrivialWindowManager::event(QEvent *e)
1299 {
1300     if (e->type() == QEvent::User) {
1301         eventPending = false;
1302         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1303              it != m_windows.constEnd(); ++it) {
1304             const CanvasData &data = it.value();
1305             if (data.updatePending)
1306                 renderCanvas(it.key());
1307         }
1308         return true;
1309     }
1310     return QObject::event(e);
1311 }
1312
1313 #include "qquickwindowmanager.moc"
1314
1315 QT_END_NAMESPACE