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