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