Fix grabbing to wait until the image has actually been grabbed.
[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();
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()
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         // 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();
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     if (isRenderBlocked)
1124         wake();
1125     unlockInGui();
1126 }
1127
1128 QQuickTrivialWindowManager::QQuickTrivialWindowManager()
1129     : gl(0)
1130     , eventPending(false)
1131 {
1132     sg = QSGContext::createDefaultContext();
1133 }
1134
1135
1136 void QQuickTrivialWindowManager::show(QQuickCanvas *canvas)
1137 {
1138     CanvasData data;
1139     data.updatePending = false;
1140     data.grabOnly = false;
1141     m_windows[canvas] = data;
1142
1143     maybeUpdate(canvas);
1144 }
1145
1146 void QQuickTrivialWindowManager::hide(QQuickCanvas *canvas)
1147 {
1148     if (!m_windows.contains(canvas))
1149         return;
1150
1151     m_windows.remove(canvas);
1152     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1153     cd->cleanupNodesOnShutdown();
1154
1155     if (m_windows.size() == 0) {
1156         sg->invalidate();
1157         delete gl;
1158         gl = 0;
1159     }
1160 }
1161
1162 void QQuickTrivialWindowManager::canvasDestroyed(QQuickCanvas *canvas)
1163 {
1164     hide(canvas);
1165 }
1166
1167 void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
1168 {
1169     if (!m_windows.contains(canvas))
1170         return;
1171
1172     CanvasData &data = const_cast<CanvasData &>(m_windows[canvas]);
1173
1174     QQuickCanvas *masterCanvas = 0;
1175     if (!canvas->visible()) {
1176         // Find a "proper surface" to bind...
1177         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1178              it != m_windows.constEnd() && !masterCanvas; ++it) {
1179             if (it.key()->visible())
1180                 masterCanvas = it.key();
1181         }
1182     } else {
1183         masterCanvas = canvas;
1184     }
1185
1186     if (!masterCanvas)
1187         return;
1188
1189     if (!gl) {
1190         gl = new QOpenGLContext();
1191         gl->setFormat(masterCanvas->requestedFormat());
1192         gl->create();
1193         if (!gl->makeCurrent(masterCanvas))
1194             qWarning("QQuickCanvas: makeCurrent() failed...");
1195         sg->initialize(gl);
1196     } else {
1197         gl->makeCurrent(masterCanvas);
1198     }
1199
1200     bool alsoSwap = data.updatePending;
1201     data.updatePending = false;
1202
1203     QQuickCanvasPrivate *cd = QQuickCanvasPrivate::get(canvas);
1204     cd->polishItems();
1205     cd->syncSceneGraph();
1206     cd->renderSceneGraph(canvas->size());
1207
1208     if (data.grabOnly) {
1209         grabContent = qt_gl_read_framebuffer(canvas->size(), false, false);
1210         data.grabOnly = false;
1211     }
1212
1213     if (alsoSwap && canvas->visible()) {
1214         gl->swapBuffers(canvas);
1215         cd->fireFrameSwapped();
1216     }
1217
1218     // Might have been set during syncSceneGraph()
1219     if (data.updatePending)
1220         maybeUpdate(canvas);
1221 }
1222
1223 void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *canvas)
1224 {
1225 }
1226
1227 QImage QQuickTrivialWindowManager::grab(QQuickCanvas *canvas)
1228 {
1229     if (!m_windows.contains(canvas))
1230         return QImage();
1231
1232     m_windows[canvas].grabOnly = true;
1233
1234     renderCanvas(canvas);
1235
1236     QImage grabbed = grabContent;
1237     grabContent = QImage();
1238     return grabbed;
1239 }
1240
1241
1242
1243 void QQuickTrivialWindowManager::resize(QQuickCanvas *, const QSize &)
1244 {
1245 }
1246
1247
1248
1249 void QQuickTrivialWindowManager::maybeUpdate(QQuickCanvas *canvas)
1250 {
1251     if (!m_windows.contains(canvas))
1252         return;
1253
1254     m_windows[canvas].updatePending = true;
1255
1256     if (!eventPending) {
1257         QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1258         eventPending = true;
1259     }
1260 }
1261
1262 void QQuickTrivialWindowManager::wakeup()
1263 {
1264 }
1265
1266 volatile bool *QQuickTrivialWindowManager::allowMainThreadProcessing()
1267 {
1268     return 0;
1269 }
1270
1271
1272
1273 QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const
1274 {
1275     return sg;
1276 }
1277
1278
1279 bool QQuickTrivialWindowManager::event(QEvent *e)
1280 {
1281     if (e->type() == QEvent::User) {
1282         eventPending = false;
1283         for (QHash<QQuickCanvas *, CanvasData>::const_iterator it = m_windows.constBegin();
1284              it != m_windows.constEnd(); ++it) {
1285             const CanvasData &data = it.value();
1286             if (data.updatePending)
1287                 renderCanvas(it.key());
1288         }
1289         return true;
1290     }
1291     return QObject::event(e);
1292 }
1293
1294 #include "qquickwindowmanager.moc"
1295
1296 QT_END_NAMESPACE