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