12c5dbd9321b6cbcac44b1dd1e87d593544bdf6c
[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 exposureChanged(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 exposureChanged(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
337         theInstance = QSGContext::createWindowManager();
338
339         bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL);
340         if (qmlNoThreadedRenderer())
341             fancy = false;
342
343         if (qmlFixedAnimationStep())
344             QUnifiedTimer::instance(true)->setConsistentTiming(true);
345
346         if (!theInstance) {
347             theInstance = fancy
348                     ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
349                     : (QQuickWindowManager*) new QQuickTrivialWindowManager;
350         }
351     }
352     return theInstance;
353 }
354
355
356
357
358
359 void QQuickRenderThreadSingleContextWindowManager::initialize()
360 {
361     Q_ASSERT(m_rendered_windows.size());
362
363     QQuickCanvas *win = masterCanvas();
364     if (!win)
365         return;
366
367     gl = new QOpenGLContext();
368     // Pick up the surface format from one of them
369     gl->setFormat(win->requestedFormat());
370     gl->create();
371     if (!gl->makeCurrent(win))
372         qWarning("QQuickCanvas: makeCurrent() failed...");
373
374     Q_ASSERT(!sg->isReady());
375     sg->initialize(gl);
376 }
377
378
379 /*!
380     This function is called when the canvas is created to register the canvas with
381     the window manager.
382
383     Called on GUI Thread.
384  */
385
386 void QQuickRenderThreadSingleContextWindowManager::show(QQuickCanvas *canvas)
387 {
388 #ifdef THREAD_DEBUG
389     printf("GUI: Canvas added to windowing system, %p, %dx%d\n", canvas, canvas->width(), canvas->height());
390 #endif
391
392     CanvasTracker tracker;
393     tracker.canvas = canvas;
394     tracker.isVisible = false;
395     tracker.toBeRemoved = false;
396     m_tracked_windows << tracker;
397
398     connect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
399     connect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()), Qt::DirectConnection);
400
401     canvasVisibilityChanged();
402 }
403
404
405 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickCanvas *canvas)
406 {
407 #ifdef THREAD_DEBUG
408     printf("                RenderThread: adding canvas: %p\n", canvas);
409 #endif
410
411     CanvasData *data = new CanvasData;
412     data->sizeWasChanged = false;
413     data->windowSize = canvas->size();
414     data->isVisible = canvas->visible();
415     m_rendered_windows[canvas] = data;
416
417     isExternalUpdatePending = true;
418 }
419
420
421 /*!
422     Called on Render Thread
423  */
424 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
425 {
426 #ifdef THREAD_DEBUG
427     printf("                RenderThread: about to add %d\n", m_added_windows.size());
428 #endif
429
430     while (m_added_windows.size()) {
431         QQuickCanvas *canvas = m_added_windows.takeLast();
432         handleAddedWindow(canvas);
433     }
434 }
435
436
437 /*!
438     Called on the GUI Thread, from the canvas' destructor
439  */
440
441 void QQuickRenderThreadSingleContextWindowManager::canvasDestroyed(QQuickCanvas *canvas)
442 {
443 #ifdef THREAD_DEBUG
444     printf("GUI: Canvas destroyed: %p\n", canvas);
445 #endif
446
447     hide(canvas);
448 }
449
450
451 /*!
452     Called on GUI Thread
453  */
454
455 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas)
456 {
457 #ifdef THREAD_DEBUG
458     printf("GUI: Canvas hidden: %p\n", canvas);
459 #endif
460
461     int position = -1;
462     for (int i=0; i<m_tracked_windows.size(); ++i) {
463         if (m_tracked_windows.at(i).canvas == canvas) {
464             m_tracked_windows[i].toBeRemoved = true;
465             position = i;
466             break;
467         }
468     }
469
470     if (position >= 0) {
471         disconnect(canvas, SIGNAL(widthChanged(int)), this, SLOT(canvasVisibilityChanged()));
472         disconnect(canvas, SIGNAL(heightChanged(int)), this, SLOT(canvasVisibilityChanged()));
473         canvasVisibilityChanged();
474         m_tracked_windows.removeAt(position);
475     }
476
477 #ifdef THREAD_DEBUG
478     printf("GUI: Canvas removal completed... %p\n", canvas);
479 #endif
480 }
481
482 /*!
483     Called on Render Thread
484  */
485 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows()
486 {
487 #ifdef THREAD_DEBUG
488     printf("                RenderThread: about to remove %d\n", m_removed_windows.size());
489 #endif
490
491     bool removedAnything = false;
492     while (m_removed_windows.size()) {
493         QQuickCanvas *canvas = m_removed_windows.takeLast();
494 #ifdef THREAD_DEBUG
495     printf("                RenderThread: removing %p\n", canvas);
496 #endif
497
498         QQuickCanvasPrivate::get(canvas)->cleanupNodesOnShutdown();
499         delete m_rendered_windows.take(canvas);
500         removedAnything = true;
501     }
502
503     // If a window is removed because it has been hidden it will take with it
504     // the gl context (at least on Mac) if bound, so disconnect the gl context
505     // from anything
506     if (removedAnything)
507         gl->doneCurrent();
508 }
509
510
511
512 /*!
513     Called on GUI Thread
514  */
515
516 void QQuickRenderThreadSingleContextWindowManager::canvasVisibilityChanged()
517 {
518     bool anyoneShowing = false;
519     QList<QQuickCanvas *> toAdd, toRemove;
520
521     // Not optimal, but also not frequently used...
522     for (int i=0; i<m_tracked_windows.size(); ++i) {
523         CanvasTracker &t = const_cast<CanvasTracker &>(m_tracked_windows.at(i));
524         QQuickCanvas *win = t.canvas;
525
526         Q_ASSERT(win->visible() || QQuickCanvasPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
527         bool canvasVisible = win->width() > 0 && win->height() > 0;
528         anyoneShowing |= (canvasVisible && !t.toBeRemoved);
529
530         if ((!canvasVisible && t.isVisible) || t.toBeRemoved) {
531             toRemove << win;
532         } else if (canvasVisible && !t.isVisible) {
533             toAdd << win;
534         }
535         t.isVisible = canvasVisible;
536     }
537
538     if (isRunning()) {
539         if (!anyoneShowing) {
540             stopRendering();
541         } else {
542             lockInGui();
543             exhaustSyncEvent();
544             m_added_windows << toAdd;
545             m_removed_windows << toRemove;
546             while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
547                 if (isRenderBlocked)
548                     wake();
549                 wait();
550             }
551             unlockInGui();
552         }
553
554     } else if (anyoneShowing) {
555         Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
556         for (int i=0; i<toAdd.size(); ++i)
557             handleAddedWindow(toAdd.at(i));
558         startRendering();
559     }
560
561 }
562
563
564 void QQuickRenderThreadSingleContextWindowManager::run()
565 {
566 #ifdef THREAD_DEBUG
567     printf("QML Rendering Thread Started\n");
568 #endif
569
570     lock();
571     Q_ASSERT(!gl);
572     initialize();
573     // Wake GUI as it is waiting for the GL context to have appeared, as
574     // an indication that the render thread is now running.
575     wake();
576     unlock();
577
578     if (!gl)
579         return;
580
581     while (!shouldExit) {
582         lock();
583
584 #ifdef THREAD_DEBUG
585         printf("                RenderThread: *** NEW FRAME ***\n");
586 #endif
587
588         isExternalUpdatePending = false;
589         handleAddedWindows();
590
591         if (!isGuiLocked) {
592             isPostingSyncEvent = true;
593
594 #ifdef THREAD_DEBUG
595             printf("                RenderThread: aquired sync lock...\n");
596 #endif
597             allowMainThreadProcessingFlag = false;
598             QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
599
600 #ifdef THREAD_DEBUG
601             printf("                RenderThread: going to sleep...\n");
602 #endif
603             wake(); // In case the event got through all the way to wait() before this thread got to wait.
604             wait();
605
606
607             isPostingSyncEvent = false;
608         }
609
610 #ifdef THREAD_DEBUG
611         printf("                RenderThread: Doing locked sync\n");
612 #endif
613 #ifdef QQUICK_CANVAS_TIMING
614         if (qquick_canvas_timing)
615             threadTimer.start();
616 #endif
617         inSync = true;
618         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
619              it != m_rendered_windows.constEnd(); ++it) {
620             QQuickCanvas *canvas = it.key();
621
622 #ifdef THREAD_DEBUG
623             printf("                RenderThread: Syncing canvas: %p\n", canvas);
624 #endif
625
626             CanvasData *canvasData = it.value();
627             QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
628
629             Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
630
631             if (!canvasData->isVisible)
632                 gl->makeCurrent(masterCanvas());
633             else
634                 gl->makeCurrent(canvas);
635
636             if (canvasData->viewportSize != canvasData->windowSize) {
637 #ifdef THREAD_DEBUG
638                 printf("                RenderThread: --- window has changed size...\n");
639 #endif
640                 canvasData->viewportSize = canvasData->windowSize;
641                 canvasData->sizeWasChanged = true;
642                 glViewport(0, 0, canvasData->viewportSize.width(), canvasData->viewportSize.height());
643             }
644
645             canvasPrivate->syncSceneGraph();
646         }
647         inSync = false;
648
649         // Wake GUI after sync to let it continue animating and event processing.
650         allowMainThreadProcessingFlag = true;
651         wake();
652         unlock();
653 #ifdef THREAD_DEBUG
654         printf("                RenderThread: sync done\n");
655 #endif
656 #ifdef QQUICK_CANVAS_TIMING
657         if (qquick_canvas_timing)
658             syncTime = threadTimer.elapsed();
659 #endif
660
661         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
662              it != m_rendered_windows.constEnd(); ++it) {
663             QQuickCanvas *canvas = it.key();
664             CanvasData *canvasData = it.value();
665             QQuickCanvasPrivate *canvasPrivate = QQuickCanvasPrivate::get(canvas);
666
667 #ifdef THREAD_DEBUG
668             printf("                RenderThread: Rendering canvas %p\n", canvas);
669 #endif
670
671             Q_ASSERT(canvasData->windowSize.width() > 0 && canvasData->windowSize.height() > 0);
672
673 #ifdef THREAD_DEBUG
674             printf("                RenderThread: --- rendering at size %dx%d\n",
675                    canvasData->viewportSize.width(), canvasData->viewportSize.height()
676                    );
677 #endif
678
679             // We only need to re-makeCurrent when we have multiple surfaces.
680             if (m_rendered_windows.size() > 1)
681                 gl->makeCurrent(canvas);
682
683             canvasPrivate->renderSceneGraph(canvasData->viewportSize);
684 #ifdef QQUICK_CANVAS_TIMING
685             if (qquick_canvas_timing)
686                 renderTime = threadTimer.elapsed() - syncTime;
687 #endif
688
689             // The content of the target buffer is undefined after swap() so grab needs
690             // to happen before swap();
691             if (canvas == canvasToGrab) {
692 #ifdef THREAD_DEBUG
693                 printf("                RenderThread: --- grabbing...\n");
694 #endif
695                 grabContent = qt_gl_read_framebuffer(canvasData->windowSize, false, false);
696                 canvasToGrab = 0;
697             }
698
699 #ifdef THREAD_DEBUG
700             printf("                RenderThread: --- wait for swap...\n");
701 #endif
702
703             if (canvasData->isVisible)
704                 gl->swapBuffers(canvas);
705
706             canvasPrivate->fireFrameSwapped();
707 #ifdef THREAD_DEBUG
708             printf("                RenderThread: --- swap complete...\n");
709 #endif
710
711         }
712
713 #ifdef QQUICK_CANVAS_TIMING
714             if (qquick_canvas_timing) {
715                 swapTime = threadTimer.elapsed() - renderTime;
716                 qDebug() << "- Breakdown of frame time; sync:" << syncTime
717                          << "ms render:" << renderTime << "ms swap:" << swapTime
718                          << "ms total:" << swapTime + renderTime << "ms";
719             }
720 #endif
721
722         lock();
723
724         handleRemovedWindows();
725
726         isPaintCompleted = true;
727
728         // Update sizes...
729         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
730              it != m_rendered_windows.constEnd(); ++it) {
731             CanvasData *canvasData = it.value();
732             if (canvasData->sizeWasChanged) {
733                 canvasData->renderedSize = canvasData->viewportSize;
734                 canvasData->sizeWasChanged = false;
735             }
736         }
737
738
739         // Wake the GUI thread now that rendering is complete, to signal that painting
740         // is done, resizing is done or grabbing is completed. For grabbing, we're
741         // signalling this much later than needed (we could have done it before swap)
742         // but we don't want to lock an extra time.
743         wake();
744
745         if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
746 #ifdef THREAD_DEBUG
747             printf("                RenderThread: nothing to do, going to sleep...\n");
748 #endif
749             isRenderBlocked = true;
750             wait();
751             isRenderBlocked = false;
752         }
753
754         unlock();
755
756         QCoreApplication::processEvents();
757
758         // Process any "deleteLater" objects...
759         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
760     }
761
762 #ifdef THREAD_DEBUG
763     printf("                RenderThread: deleting all outstanding nodes\n");
764 #endif
765
766     m_removed_windows << m_rendered_windows.keys();
767     handleRemovedWindows();
768
769     sg->invalidate();
770
771     gl->doneCurrent();
772     delete gl;
773     gl = 0;
774
775 #ifdef THREAD_DEBUG
776     printf("                RenderThread: render loop exited... Good Night!\n");
777 #endif
778
779     lock();
780     hasExited = true;
781
782 #ifdef THREAD_DEBUG
783     printf("                RenderThread: waking GUI for final sleep..\n");
784 #endif
785     wake();
786     unlock();
787
788 #ifdef THREAD_DEBUG
789     printf("                RenderThread: All done...\n");
790 #endif
791 }
792
793 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
794 {
795     Q_ASSERT(QThread::currentThread() == qApp->thread());
796
797     if (e->type() == QEvent_Sync) {
798
799         // If all canvases have been hidden, ignore the event
800         if (!isRunning())
801             return true;
802
803         if (!syncAlreadyHappened)
804             sync(false);
805
806         syncAlreadyHappened = false;
807
808         if (animationRunning) {
809 #ifdef THREAD_DEBUG
810             printf("GUI: Advancing animations...\n");
811 #endif
812
813             animationDriver->advance();
814
815 #ifdef THREAD_DEBUG
816             printf("GUI: Animations advanced...\n");
817 #endif
818         }
819
820         return true;
821     } else if (e->type() == QEvent_DeferredUpdate) {
822         handleDeferredUpdate();
823
824     } else if (e->type() == QEvent::Timer) {
825 #ifdef THREAD_DEBUG
826             printf("GUI: Animations advanced via timer...\n");
827 #endif
828         animationDriver->advance();
829     }
830
831     return QThread::event(e);
832 }
833
834
835
836 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
837 {
838     if (isPostingSyncEvent) {
839         sync(true);
840         syncAlreadyHappened = true;
841     }
842 }
843
844
845
846 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
847 {
848 #ifdef THREAD_DEBUG
849     printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
850 #endif
851     if (!guiAlreadyLocked)
852         lockInGui();
853
854     for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
855          it != m_rendered_windows.constEnd(); ++it) {
856         QQuickCanvasPrivate::get(it.key())->polishItems();
857     }
858
859     wake();
860     wait();
861
862     if (!guiAlreadyLocked)
863         unlockInGui();
864 }
865
866
867
868
869 /*!
870     Acquires the mutex for the GUI thread. The function uses the isGuiLocked
871     variable to keep track of how many recursion levels the gui is locked with.
872     We only actually acquire the mutex for the first level to avoid deadlocking
873     ourselves.
874  */
875
876 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
877 {
878     if (++isGuiLocked == 1)
879         lock();
880
881 #ifdef THREAD_DEBUG
882     printf("GUI: aquired lock... level=%d\n", isGuiLocked);
883 #endif
884 }
885
886
887
888 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
889 {
890 #ifdef THREAD_DEBUG
891     printf("GUI: releasing lock... level=%d\n", isGuiLocked);
892 #endif
893
894     if (--isGuiLocked == 0)
895         unlock();
896 }
897
898
899
900
901 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
902 {
903 #ifdef THREAD_DEBUG
904     printf("GUI: animationStarted()\n");
905 #endif
906
907     if (!isRunning()) {
908         animationTimer = startTimer(1000/60);
909         return;
910     }
911
912     lockInGui();
913
914     animationRunning = true;
915
916     if (isRenderBlocked)
917         wake();
918
919     unlockInGui();
920 }
921
922
923
924 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
925 {
926 #ifdef THREAD_DEBUG
927     printf("GUI: animationStopped()...\n");
928 #endif
929
930     if (!isRunning()) {
931         killTimer(animationTimer);
932         animationTimer = -1;
933         return;
934     }
935
936     lockInGui();
937     animationRunning = false;
938     unlockInGui();
939 }
940
941
942 void QQuickRenderThreadSingleContextWindowManager::exposureChanged(QQuickCanvas *canvas)
943 {
944     Q_UNUSED(canvas);
945 #ifdef THREAD_DEBUG
946     printf("GUI: exposure changed: %p\n", canvas);
947 #endif
948
949 #ifdef THREAD_DEBUG
950     printf("GUI: exposure changed done: %p\n", canvas);
951 #endif
952 }
953
954
955
956 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
957 {
958 #ifdef THREAD_DEBUG
959     printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
960 #endif
961
962     // If the rendering thread is not running we do not need to do anything.
963     // Also if the canvas is being resized to an invalid size, it will be removed
964     // by the canvasVisibilityChanged slot as result of width/heightcChanged()
965     if (!isRunning() || size.width() <= 0 || size.height() <= 0)
966         return;
967
968     lockInGui();
969     exhaustSyncEvent();
970
971     CanvasData *canvasData = m_rendered_windows.value(canvas);
972     if (canvasData) {
973         canvasData->windowSize = size;
974         while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
975             if (isRenderBlocked)
976                 wake();
977             wait();
978         }
979     }
980     unlockInGui();
981
982 #ifdef THREAD_DEBUG
983     printf("GUI: Resize done: %p\n", canvas);
984 #endif
985 }
986
987
988
989 void QQuickRenderThreadSingleContextWindowManager::startRendering()
990 {
991 #ifdef THREAD_DEBUG
992     printf("GUI: Starting Render Thread\n");
993 #endif
994     hasExited = false;
995     shouldExit = false;
996     isGuiLocked = 0;
997     isPostingSyncEvent = false;
998     syncAlreadyHappened = false;
999     inSync = false;
1000
1001     lockInGui();
1002     animationRunning = animationDriver->isRunning();
1003     start(); // Start the render thread...
1004     wait();
1005     unlockInGui();
1006
1007     // Animations will now be driven from the rendering thread.
1008     if (animationTimer >= 0) {
1009         killTimer(animationTimer);
1010         animationTimer = -1;
1011     }
1012
1013
1014 }
1015
1016
1017
1018 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1019 {
1020 #ifdef THREAD_DEBUG
1021     printf("GUI: stopping render thread\n");
1022 #endif
1023
1024     lockInGui();
1025     exhaustSyncEvent();
1026     shouldExit = true;
1027
1028     if (isRenderBlocked) {
1029 #ifdef THREAD_DEBUG
1030         printf("GUI: waking up render thread\n");
1031 #endif
1032         wake();
1033     }
1034
1035     while (!hasExited) {
1036 #ifdef THREAD_DEBUG
1037         printf("GUI: waiting for render thread to have exited..\n");
1038 #endif
1039         wait();
1040     }
1041
1042     unlockInGui();
1043
1044 #ifdef THREAD_DEBUG
1045     printf("GUI: waiting for render thread to terminate..\n");
1046 #endif
1047     // Actually wait for the thread to terminate.  Otherwise we can delete it
1048     // too early and crash.
1049     QThread::wait();
1050
1051 #ifdef THREAD_DEBUG
1052     printf("GUI: thread has terminated and we're all good..\n");
1053 #endif
1054
1055     // Activate timer to keep animations running
1056     if (animationDriver->isRunning())
1057         animationTimer = startTimer(1000/60);
1058 }
1059
1060
1061
1062 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1063 {
1064     if (!isRunning())
1065         return QImage();
1066
1067     if (QThread::currentThread() != qApp->thread()) {
1068         qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1069         return QImage();
1070     }
1071
1072 #ifdef THREAD_DEBUG
1073     printf("GUI: doing a pixelwise grab..\n");
1074 #endif
1075
1076     lockInGui();
1077     exhaustSyncEvent();
1078
1079     canvasToGrab = canvas;
1080     isPaintCompleted = false;
1081     while (isRunning() && !isPaintCompleted) {
1082         if (isRenderBlocked)
1083             wake();
1084         wait();
1085     }
1086
1087     QImage grabbed = grabContent;
1088     grabContent = QImage();
1089
1090     unlockInGui();
1091
1092     return grabbed;
1093 }
1094
1095
1096 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1097 {
1098 #ifdef THREAD_DEBUG
1099     printf("GUI: handling update to ourselves...\n");
1100 #endif
1101
1102     isDeferredUpdatePosted = false;
1103
1104     lockInGui();
1105     isExternalUpdatePending = true;
1106     if (isRenderBlocked)
1107         wake();
1108     unlockInGui();
1109 }
1110
1111 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1112 {
1113     Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1114                "QQuickCanvas::update",
1115                "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1116
1117     if (inSync) {
1118         isExternalUpdatePending = true;
1119
1120     } else if (!isDeferredUpdatePosted) {
1121 #ifdef THREAD_DEBUG
1122         printf("GUI: posting update to ourselves...\n");
1123 #endif
1124         isDeferredUpdatePosted = true;
1125         QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1126     }
1127
1128 }
1129
1130 void QQuickRenderThreadSingleContextWindowManager::wakeup()
1131 {
1132     lockInGui();
1133     if (isRenderBlocked)
1134         wake();
1135     unlockInGui();
1136 }
1137
1138 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1139     : gl(0)
1140     , eventPending(false)
1141 {
1142     sg = QSGContext::createDefaultContext();
1143 }
1144
1145
1146 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1147 {
1148     CanvasData data;
1149     data.updatePending = false;
1150     data.grabOnly = false;
1151     m_windows[canvas] = data;
1152
1153     maybeUpdate(canvas);
1154 }
1155
1156 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1157 {
1158     if (!m_windows.contains(canvas))
1159         return;
1160
1161     m_windows.remove(canvas);
1162     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1163     cd->cleanupNodesOnShutdown();
1164
1165     if (m_windows.size() == 0) {
1166         sg->invalidate();
1167         delete gl;
1168         gl = 0;
1169     }
1170 }
1171
1172 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1173 {
1174     hide(canvas);
1175 }
1176
1177 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1178 {
1179     if (!m_windows.contains(canvas))
1180         return;
1181
1182     CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1183
1184     QQuickCanvas *masterCanvas = 0;
1185     if (!canvas->visible()) {
1186         // Find a "proper surface" to bind...
1187         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1188              it != m_windows.constEnd() && !masterCanvas; ++it) {
1189             if (it.key()->visible())
1190                 masterCanvas = it.key();
1191         }
1192     } else {
1193         masterCanvas = canvas;
1194     }
1195
1196     if (!masterCanvas)
1197         return;
1198
1199     if (!gl) {
1200         gl = new QOpenGLContext();
1201         gl->setFormat(masterCanvas->requestedFormat());
1202         gl->create();
1203         if (!gl->makeCurrent(masterCanvas))
1204             qWarning("QQuickCanvas: makeCurrent() failed...");
1205         sg->initialize(gl);
1206     } else {
1207         gl->makeCurrent(masterCanvas);
1208     }
1209
1210     bool alsoSwap = data.updatePending;
1211     data.updatePending = false;
1212
1213     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1214     cd->polishItems();
1215     cd->syncSceneGraph();
1216     cd->renderSceneGraph(canvas->size());
1217
1218     if (data.grabOnly) {
1219         grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1220         data.grabOnly = false;
1221     }
1222
1223     if (alsoSwap && canvas->visible()) {
1224         gl->swapBuffers(canvas);
1225         cd->fireFrameSwapped();
1226     }
1227
1228     // Might have been set during syncSceneGraph()
1229     if (data.updatePending)
1230         maybeUpdate(canvas);
1231 }
1232
1233 void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *canvas)
1234 {
1235 }
1236
1237 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1238 {
1239     if (!m_windows.contains(canvas))
1240         return QImage();
1241
1242     m_windows[canvas].grabOnly = true;
1243
1244     renderCanvas(canvas);
1245
1246     QImage grabbed = grabContent;
1247     grabContent = QImage();
1248     return grabbed;
1249 }
1250
1251
1252
1253 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1254 {
1255 }
1256
1257
1258
1259 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1260 {
1261     if (!m_windows.contains(canvas))
1262         return;
1263
1264     m_windows[canvas].updatePending = true;
1265
1266     if (!eventPending) {
1267         QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1268         eventPending = true;
1269     }
1270 }
1271
1272 void QQuickTrivialWindowManager::wakeup()
1273 {
1274 }
1275
1276 volatile bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1277 {
1278     return 0;
1279 }
1280
1281
1282
1283 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1284 {
1285     return sg;
1286 }
1287
1288
1289 bool QQuickTrivialWindowManager::event(QEvent *e)
1290 {
1291     if (e->type() == QEvent::User) {
1292         eventPending = false;
1293         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1294              it != m_windows.constEnd(); ++it) {
1295             const CanvasData &data = it.value();
1296             if (data.updatePending)
1297                 renderCanvas(it.key());
1298         }
1299         return true;
1300     }
1301     return QObject::event(e);
1302 }
1303
1304 #include "qquickwindowmanager.moc"
1305
1306 QT_END_NAMESPACE