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