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