d075d3b64b758d42c463fc448b9df754d45adf6f
[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::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.
117
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.
120
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.
125
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
128   swapping happens.
129
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()
133
134   RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
135   after shouldExit has been set to true.
136  */
137
138 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP);
139 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
140
141 //#define THREAD_DEBUG
142
143 QQuickWindowManager::~QQuickWindowManager()
144 {
145 }
146
147 class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager
148 {
149     Q_OBJECT
150 public:
151     QQuickRenderThreadSingleContextWindowManager()
152         : sg(QSGContext::createDefaultContext())
153         , gl(0)
154         , animationTimer(-1)
155         , allowMainThreadProcessingFlag(false)
156         , isGuiLocked(0)
157         , animationRunning(false)
158         , isPostingSyncEvent(false)
159         , isRenderBlocked(false)
160         , isExternalUpdatePending(false)
161         , syncAlreadyHappened(false)
162         , inSync(false)
163         , shouldExit(false)
164         , hasExited(false)
165         , isDeferredUpdatePosted(false)
166         , canvasToGrab(0)
167     {
168         sg->moveToThread(this);
169
170         animationDriver = sg->createAnimationDriver(this);
171         animationDriver->install();
172         connect(animationDriver, SIGNAL(started()), this, SLOT(animationStarted()));
173         connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
174     }
175
176     QSGContext *sceneGraphContext() const { return sg; }
177
178     void releaseResources() { }
179
180     void show(QQuickCanvas *canvas);
181     void hide(QQuickCanvas *canvas);
182
183     void canvasDestroyed(QQuickCanvas *canvas);
184
185     void exposureChanged(QQuickCanvas *canvas);
186     QImage grab(QQuickCanvas *canvas);
187     void resize(QQuickCanvas *canvas, const QSize &size);
188     void handleDeferredUpdate();
189     void maybeUpdate(QQuickCanvas *canvas);
190     void wakeup();
191
192     void startRendering();
193     void stopRendering();
194
195     void exhaustSyncEvent();
196     void sync(bool guiAlreadyLocked);
197
198     void initialize();
199
200     volatile bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; }
201
202     bool event(QEvent *);
203
204     inline void lock() { mutex.lock(); }
205     inline void unlock() { mutex.unlock(); }
206     inline void wait() { condition.wait(&mutex); }
207     inline void wake() { condition.wakeOne(); }
208     void lockInGui();
209     void unlockInGui();
210
211     void run();
212
213     QQuickCanvas *masterCanvas() {
214         QQuickCanvas *win = 0;
215         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
216             it != m_rendered_windows.constEnd() && !win; ++it) {
217             if (it.value()->isVisible)
218                 win = it.key();
219         }
220         return win;
221     }
222
223 public slots:
224     void animationStarted();
225     void animationStopped();
226     void canvasVisibilityChanged();
227
228 private:
229     void handleAddedWindows();
230     void handleAddedWindow(QQuickCanvas *canvas);
231     void handleRemovedWindows(bool clearGLContext = true);
232
233     QSGContext *sg;
234     QOpenGLContext *gl;
235     QAnimationDriver *animationDriver;
236     int animationTimer;
237
238     QMutex mutex;
239     QWaitCondition condition;
240
241     volatile bool allowMainThreadProcessingFlag;
242
243     int isGuiLocked;
244     uint animationRunning: 1;
245     uint isPostingSyncEvent : 1;
246     uint isRenderBlocked : 1;
247     uint isExternalUpdatePending : 1;
248     uint syncAlreadyHappened : 1;
249     uint inSync : 1;
250     uint shouldExit : 1;
251     uint hasExited : 1;
252     uint isDeferredUpdatePosted : 1;
253
254     QQuickCanvas *canvasToGrab;
255     QImage grabContent;
256
257     struct CanvasData {
258         QSize renderedSize;
259         QSize windowSize;
260         QSize viewportSize;
261
262         uint sizeWasChanged : 1;
263         uint isVisible : 1;
264     };
265
266     QHash<QQuickCanvas *, CanvasData *> m_rendered_windows;
267
268     struct CanvasTracker {
269         QQuickCanvas *canvas;
270         uint isVisible : 1;
271         uint toBeRemoved : 1;
272     };
273
274     QList<CanvasTracker> m_tracked_windows;
275
276     QList<QQuickCanvas *> m_removed_windows;
277     QList<QQuickCanvas *> m_added_windows;
278 };
279
280
281 class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager
282 {
283 public:
284     QQuickTrivialWindowManager();
285
286     void show(QQuickCanvas *canvas);
287     void hide(QQuickCanvas *canvas);
288
289     void canvasDestroyed(QQuickCanvas *canvas);
290
291     void initializeGL();
292     void renderCanvas(QQuickCanvas *canvas);
293     void exposureChanged(QQuickCanvas *canvas);
294     QImage grab(QQuickCanvas *canvas);
295     void resize(QQuickCanvas *canvas, const QSize &size);
296     void wakeup();
297
298     void maybeUpdate(QQuickCanvas *canvas);
299
300     void releaseResources() { }
301
302     volatile bool *allowMainThreadProcessing();
303
304     QSGContext *sceneGraphContext() const;
305
306     bool event(QEvent *);
307
308     struct CanvasData {
309         bool updatePending : 1;
310         bool grabOnly : 1;
311     };
312
313     QHash<QQuickCanvas *, CanvasData> m_windows;
314
315     QOpenGLContext *gl;
316     QSGContext *sg;
317
318     QImage grabContent;
319
320     bool eventPending;
321 };
322
323
324 QQuickWindowManager *QQuickWindowManager::instance()
325 {
326     static QQuickWindowManager *theInstance;
327
328     if (!theInstance) {
329
330         theInstance = QSGContext::createWindowManager();
331
332         bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL);
333         if (qmlNoThreadedRenderer())
334             fancy = false;
335
336         if (qmlFixedAnimationStep())
337             QUnifiedTimer::instance(true)->setConsistentTiming(true);
338
339         if (!theInstance) {
340             theInstance = fancy
341                     ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager
342                     : (QQuickWindowManager*) new QQuickTrivialWindowManager;
343         }
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(bool clearGLContext)
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 && clearGLContext)
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         // Update sizes...
720         for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
721              it != m_rendered_windows.constEnd(); ++it) {
722             CanvasData *canvasData = it.value();
723             if (canvasData->sizeWasChanged) {
724                 canvasData->renderedSize = canvasData->viewportSize;
725                 canvasData->sizeWasChanged = false;
726             }
727         }
728
729
730         // Wake the GUI thread now that rendering is complete, to signal that painting
731         // is done, resizing is done or grabbing is completed. For grabbing, we're
732         // signalling this much later than needed (we could have done it before swap)
733         // but we don't want to lock an extra time.
734         wake();
735
736         if (!animationRunning && !isExternalUpdatePending && !shouldExit && !canvasToGrab) {
737 #ifdef THREAD_DEBUG
738             printf("                RenderThread: nothing to do, going to sleep...\n");
739 #endif
740             isRenderBlocked = true;
741             wait();
742             isRenderBlocked = false;
743         }
744
745         unlock();
746
747         QCoreApplication::processEvents();
748
749         // Process any "deleteLater" objects...
750         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
751     }
752
753 #ifdef THREAD_DEBUG
754     printf("                RenderThread: deleting all outstanding nodes\n");
755 #endif
756
757     m_removed_windows << m_rendered_windows.keys();
758     handleRemovedWindows(false);
759
760     sg->invalidate();
761
762     gl->doneCurrent();
763     delete gl;
764     gl = 0;
765
766 #ifdef THREAD_DEBUG
767     printf("                RenderThread: render loop exited... Good Night!\n");
768 #endif
769
770     lock();
771     hasExited = true;
772
773 #ifdef THREAD_DEBUG
774     printf("                RenderThread: waking GUI for final sleep..\n");
775 #endif
776     wake();
777     unlock();
778
779 #ifdef THREAD_DEBUG
780     printf("                RenderThread: All done...\n");
781 #endif
782 }
783
784 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
785 {
786     Q_ASSERT(QThread::currentThread() == qApp->thread());
787
788     if (e->type() == QEvent_Sync) {
789
790         // If all canvases have been hidden, ignore the event
791         if (!isRunning())
792             return true;
793
794         if (!syncAlreadyHappened)
795             sync(false);
796
797         syncAlreadyHappened = false;
798
799         if (animationRunning) {
800 #ifdef THREAD_DEBUG
801             printf("GUI: Advancing animations...\n");
802 #endif
803
804             animationDriver->advance();
805
806 #ifdef THREAD_DEBUG
807             printf("GUI: Animations advanced...\n");
808 #endif
809         }
810
811         return true;
812     } else if (e->type() == QEvent_DeferredUpdate) {
813         handleDeferredUpdate();
814
815     } else if (e->type() == QEvent::Timer) {
816 #ifdef THREAD_DEBUG
817             printf("GUI: Animations advanced via timer...\n");
818 #endif
819         animationDriver->advance();
820     }
821
822     return QThread::event(e);
823 }
824
825
826
827 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
828 {
829     if (isPostingSyncEvent) {
830         sync(true);
831         syncAlreadyHappened = true;
832     }
833 }
834
835
836
837 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
838 {
839 #ifdef THREAD_DEBUG
840     printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
841 #endif
842     if (!guiAlreadyLocked)
843         lockInGui();
844
845     for (QHash<QQuickCanvas *, CanvasData *>::const_iterator it = m_rendered_windows.constBegin();
846          it != m_rendered_windows.constEnd(); ++it) {
847         QQuickCanvasPrivate::get(it.key())->polishItems();
848     }
849
850     wake();
851     wait();
852
853     if (!guiAlreadyLocked)
854         unlockInGui();
855 }
856
857
858
859
860 /*!
861     Acquires the mutex for the GUI thread. The function uses the isGuiLocked
862     variable to keep track of how many recursion levels the gui is locked with.
863     We only actually acquire the mutex for the first level to avoid deadlocking
864     ourselves.
865  */
866
867 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
868 {
869     if (++isGuiLocked == 1)
870         lock();
871
872 #ifdef THREAD_DEBUG
873     printf("GUI: aquired lock... level=%d\n", isGuiLocked);
874 #endif
875 }
876
877
878
879 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
880 {
881 #ifdef THREAD_DEBUG
882     printf("GUI: releasing lock... level=%d\n", isGuiLocked);
883 #endif
884
885     if (--isGuiLocked == 0)
886         unlock();
887 }
888
889
890
891
892 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
893 {
894 #ifdef THREAD_DEBUG
895     printf("GUI: animationStarted()\n");
896 #endif
897
898     if (!isRunning()) {
899         animationTimer = startTimer(1000/60);
900         return;
901     }
902
903     lockInGui();
904
905     animationRunning = true;
906
907     if (isRenderBlocked)
908         wake();
909
910     unlockInGui();
911 }
912
913
914
915 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
916 {
917 #ifdef THREAD_DEBUG
918     printf("GUI: animationStopped()...\n");
919 #endif
920
921     if (!isRunning()) {
922         killTimer(animationTimer);
923         animationTimer = -1;
924         return;
925     }
926
927     lockInGui();
928     animationRunning = false;
929     unlockInGui();
930 }
931
932
933 void QQuickRenderThreadSingleContextWindowManager::exposureChanged(QQuickCanvas *canvas)
934 {
935     Q_UNUSED(canvas);
936 #ifdef THREAD_DEBUG
937     printf("GUI: exposure changed: %p\n", canvas);
938 #endif
939
940 #ifdef THREAD_DEBUG
941     printf("GUI: exposure changed done: %p\n", canvas);
942 #endif
943 }
944
945
946
947 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickCanvas *canvas, const QSize &size)
948 {
949 #ifdef THREAD_DEBUG
950     printf("GUI: Resize Event: %p = %dx%d\n", canvas, size.width(), size.height());
951 #endif
952
953     // If the rendering thread is not running we do not need to do anything.
954     // Also if the canvas is being resized to an invalid size, it will be removed
955     // by the canvasVisibilityChanged slot as result of width/heightcChanged()
956     if (!isRunning() || size.width() <= 0 || size.height() <= 0)
957         return;
958
959     lockInGui();
960     exhaustSyncEvent();
961
962     CanvasData *canvasData = m_rendered_windows.value(canvas);
963     if (canvasData) {
964         canvasData->windowSize = size;
965         while (isRunning() && canvasData->renderedSize != size && size.width() > 0 && size.height() > 0) {
966             if (isRenderBlocked)
967                 wake();
968             wait();
969         }
970     }
971     unlockInGui();
972
973 #ifdef THREAD_DEBUG
974     printf("GUI: Resize done: %p\n", canvas);
975 #endif
976 }
977
978
979
980 void QQuickRenderThreadSingleContextWindowManager::startRendering()
981 {
982 #ifdef THREAD_DEBUG
983     printf("GUI: Starting Render Thread\n");
984 #endif
985     hasExited = false;
986     shouldExit = false;
987     isGuiLocked = 0;
988     isPostingSyncEvent = false;
989     syncAlreadyHappened = false;
990     inSync = false;
991
992     lockInGui();
993     animationRunning = animationDriver->isRunning();
994     start(); // Start the render thread...
995     wait();
996     unlockInGui();
997
998     // Animations will now be driven from the rendering thread.
999     if (animationTimer >= 0) {
1000         killTimer(animationTimer);
1001         animationTimer = -1;
1002     }
1003
1004
1005 }
1006
1007
1008
1009 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
1010 {
1011 #ifdef THREAD_DEBUG
1012     printf("GUI: stopping render thread\n");
1013 #endif
1014
1015     lockInGui();
1016     exhaustSyncEvent();
1017     shouldExit = true;
1018
1019     if (isRenderBlocked) {
1020 #ifdef THREAD_DEBUG
1021         printf("GUI: waking up render thread\n");
1022 #endif
1023         wake();
1024     }
1025
1026     while (!hasExited) {
1027 #ifdef THREAD_DEBUG
1028         printf("GUI: waiting for render thread to have exited..\n");
1029 #endif
1030         wait();
1031     }
1032
1033     unlockInGui();
1034
1035 #ifdef THREAD_DEBUG
1036     printf("GUI: waiting for render thread to terminate..\n");
1037 #endif
1038     // Actually wait for the thread to terminate.  Otherwise we can delete it
1039     // too early and crash.
1040     QThread::wait();
1041
1042 #ifdef THREAD_DEBUG
1043     printf("GUI: thread has terminated and we're all good..\n");
1044 #endif
1045
1046     // Activate timer to keep animations running
1047     if (animationDriver->isRunning())
1048         animationTimer = startTimer(1000/60);
1049 }
1050
1051
1052
1053 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickCanvas *canvas)
1054 {
1055     if (!isRunning())
1056         return QImage();
1057
1058     if (QThread::currentThread() != qApp->thread()) {
1059         qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
1060         return QImage();
1061     }
1062
1063 #ifdef THREAD_DEBUG
1064     printf("GUI: doing a pixelwise grab..\n");
1065 #endif
1066
1067     lockInGui();
1068     exhaustSyncEvent();
1069
1070     canvasToGrab = canvas;
1071     while (isRunning() && canvasToGrab) {
1072         if (isRenderBlocked)
1073             wake();
1074         wait();
1075     }
1076
1077     QImage grabbed = grabContent;
1078     grabContent = QImage();
1079
1080     unlockInGui();
1081
1082     return grabbed;
1083 }
1084
1085
1086 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
1087 {
1088 #ifdef THREAD_DEBUG
1089     printf("GUI: handling update to ourselves...\n");
1090 #endif
1091
1092     isDeferredUpdatePosted = false;
1093
1094     lockInGui();
1095     isExternalUpdatePending = true;
1096     if (isRenderBlocked)
1097         wake();
1098     unlockInGui();
1099 }
1100
1101 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickCanvas *)
1102 {
1103     Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
1104                "QQuickCanvas::update",
1105                "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
1106
1107     if (inSync) {
1108         isExternalUpdatePending = true;
1109
1110     } else if (!isDeferredUpdatePosted) {
1111 #ifdef THREAD_DEBUG
1112         printf("GUI: posting update to ourselves...\n");
1113 #endif
1114         isDeferredUpdatePosted = true;
1115         QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
1116     }
1117
1118 }
1119
1120 void QQuickRenderThreadSingleContextWindowManager::wakeup()
1121 {
1122     lockInGui();
1123     isExternalUpdatePending = true;
1124     if (isRenderBlocked || isPostingSyncEvent)
1125         wake();
1126     unlockInGui();
1127 }
1128
1129 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1130     : gl(0)
1131     , eventPending(false)
1132 {
1133     sg = QSGContext::createDefaultContext();
1134 }
1135
1136
1137 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1138 {
1139     CanvasData data;
1140     data.updatePending = false;
1141     data.grabOnly = false;
1142     m_windows[canvas] = data;
1143
1144     maybeUpdate(canvas);
1145 }
1146
1147 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1148 {
1149     if (!m_windows.contains(canvas))
1150         return;
1151
1152     m_windows.remove(canvas);
1153     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1154     cd->cleanupNodesOnShutdown();
1155
1156     if (m_windows.size() == 0) {
1157         sg->invalidate();
1158         delete gl;
1159         gl = 0;
1160     }
1161 }
1162
1163 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1164 {
1165     hide(canvas);
1166 }
1167
1168 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1169 {
1170     if (!m_windows.contains(canvas))
1171         return;
1172
1173     CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1174
1175     QQuickCanvas *masterCanvas = 0;
1176     if (!canvas->visible()) {
1177         // Find a "proper surface" to bind...
1178         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1179              it != m_windows.constEnd() && !masterCanvas; ++it) {
1180             if (it.key()->visible())
1181                 masterCanvas = it.key();
1182         }
1183     } else {
1184         masterCanvas = canvas;
1185     }
1186
1187     if (!masterCanvas)
1188         return;
1189
1190     if (!gl) {
1191         gl = new QOpenGLContext();
1192         gl->setFormat(masterCanvas->requestedFormat());
1193         gl->create();
1194         if (!gl->makeCurrent(masterCanvas))
1195             qWarning("QQuickCanvas: makeCurrent() failed...");
1196         sg->initialize(gl);
1197     } else {
1198         gl->makeCurrent(masterCanvas);
1199     }
1200
1201     bool alsoSwap = data.updatePending;
1202     data.updatePending = false;
1203
1204     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1205     cd->polishItems();
1206     cd->syncSceneGraph();
1207     cd->renderSceneGraph(canvas->size());
1208
1209     if (data.grabOnly) {
1210         grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1211         data.grabOnly = false;
1212     }
1213
1214     if (alsoSwap && canvas->visible()) {
1215         gl->swapBuffers(canvas);
1216         cd->fireFrameSwapped();
1217     }
1218
1219     // Might have been set during syncSceneGraph()
1220     if (data.updatePending)
1221         maybeUpdate(canvas);
1222 }
1223
1224 void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *)
1225 {
1226 }
1227
1228 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1229 {
1230     if (!m_windows.contains(canvas))
1231         return QImage();
1232
1233     m_windows[canvas].grabOnly = true;
1234
1235     renderCanvas(canvas);
1236
1237     QImage grabbed = grabContent;
1238     grabContent = QImage();
1239     return grabbed;
1240 }
1241
1242
1243
1244 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1245 {
1246 }
1247
1248
1249
1250 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1251 {
1252     if (!m_windows.contains(canvas))
1253         return;
1254
1255     m_windows[canvas].updatePending = true;
1256
1257     if (!eventPending) {
1258         QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1259         eventPending = true;
1260     }
1261 }
1262
1263 void QQuickTrivialWindowManager::wakeup()
1264 {
1265 }
1266
1267 volatile bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1268 {
1269     return 0;
1270 }
1271
1272
1273
1274 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1275 {
1276     return sg;
1277 }
1278
1279
1280 bool QQuickTrivialWindowManager::event(QEvent *e)
1281 {
1282     if (e->type() == QEvent::User) {
1283         eventPending = false;
1284         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1285              it != m_windows.constEnd(); ++it) {
1286             const CanvasData &data = it.value();
1287             if (data.updatePending)
1288                 renderCanvas(it.key());
1289         }
1290         return true;
1291     }
1292     return QObject::event(e);
1293 }
1294
1295 #include "qquickwindowmanager.moc"
1296
1297 QT_END_NAMESPACE