04c698c367744926bb8988f5f7fc91d7f015a7fb
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickthreadedwindowmanager.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 #include "qquickthreadedwindowmanager_p.h"
44
45 #include <QtCore/QTime>
46
47 #include <QtGui/QOpenGLContext>
48 #include <QtGui/private/qguiapplication_p.h>
49 #include <qpa/qplatformintegration.h>
50
51 #include <QtQml/private/qqmlglobal_p.h>
52
53 #include <QtQuick/QQuickWindow>
54 #include <QtQuick/private/qquickwindow_p.h>
55
56 QT_BEGIN_NAMESPACE
57
58 //#define THREAD_DEBUG
59 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
60
61 const QEvent::Type QEvent_Sync = QEvent::Type(QEvent::User);
62 const QEvent::Type QEvent_DeferredUpdate = QEvent::Type(QEvent::User + 1);
63
64 #define QQUICK_RENDER_TIMING
65 #ifdef QQUICK_RENDER_TIMING
66 DEFINE_BOOL_CONFIG_OPTION(qquick_render_timing, QML_RENDER_TIMING)
67 static QTime threadTimer;
68 static int syncTime;
69 static int renderTime;
70 static int swapTime;
71 #endif
72
73
74 /*
75   Threaded Rendering
76   ==================
77
78   The threaded rendering uses a number of different variables to track potential
79   states used to handle resizing, initial paint, grabbing and driving animations
80   while ALWAYS keeping the GL context in the rendering thread and keeping the
81   overhead of normal one-shot paints and vblank driven animations at a minimum.
82
83   Resize, initial show and grab suffer slightly in this model as they are locked
84   to the rendering in the rendering thread, but this is a necessary evil for
85   the system to work.
86
87   Variables that are used:
88
89   Private::animationRunning: This is true while the animations are running, and only
90   written to inside locks.
91
92   RenderThread::isGuiLocked: This is used to indicate that the GUI thread owns the
93   lock. This variable is an integer to allow for recursive calls to lockInGui()
94   without using a recursive mutex. See isPostingSyncEvent.
95
96   RenderThread::isPostingSyncEvent: This variable is set in the render thread just
97   before the sync event is sent to the GUI thread. It is used to avoid deadlocks
98   in the case where render thread waits while waiting for GUI to pick up the sync
99   event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
100   When this happens, we use the
101   exhaustSyncEvent() function to do the sync right there and mark the coming
102   sync event to be discarded. There can only ever be one sync incoming.
103
104   RenderThread::isRenderBlock: This variable is true when animations are not
105   running and the render thread has gone to sleep, waiting for more to do.
106
107   RenderThread::isExternalUpdatePending: This variable is set to false when
108   a new render pass is started and to true in maybeUpdate(). It is an
109   indication to the render thread that another render pass needs to take
110   place, rather than the render thread going to sleep after completing its swap.
111
112   RenderThread::doGrab: This variable is set by the grab() function and
113   tells the renderer to do a grab after rendering is complete and before
114   swapping happens.
115
116   RenderThread::shouldExit: This variable is used to determine if the render
117   thread should do a nother pass. It is typically set as a result of show()
118   and unset as a result of hide() or during shutdown()
119
120   RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
121   after shouldExit has been set to true.
122  */
123
124
125 void QQuickRenderThreadSingleContextWindowManager::initialize()
126 {
127     Q_ASSERT(m_rendered_windows.size());
128
129     QQuickWindow *win = masterWindow();
130     if (!win)
131         return;
132
133     gl = new QOpenGLContext();
134     // Pick up the surface format from one of them
135     gl->setFormat(win->requestedFormat());
136     gl->create();
137     if (!gl->makeCurrent(win))
138         qWarning("QQuickWindow: makeCurrent() failed...");
139
140     Q_ASSERT(!sg->isReady());
141     sg->initialize(gl);
142 }
143
144
145 /*!
146     This function is called when the window is created to register the window with
147     the window manager.
148
149     Called on GUI Thread.
150  */
151
152 void QQuickRenderThreadSingleContextWindowManager::show(QQuickWindow *window)
153 {
154 #ifdef THREAD_DEBUG
155     printf("GUI: Window added to windowing system, %p, %dx%d\n", window, window->width(), window->height());
156 #endif
157
158     WindowTracker tracker;
159     tracker.window = window;
160     tracker.isVisible = false;
161     tracker.toBeRemoved = false;
162     m_tracked_windows << tracker;
163
164     connect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection);
165     connect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection);
166
167     windowVisibilityChanged();
168 }
169
170
171 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickWindow *window)
172 {
173 #ifdef THREAD_DEBUG
174     printf("                RenderThread: adding window: %p\n", window);
175 #endif
176
177     WindowData *data = new WindowData;
178     data->sizeWasChanged = false;
179     data->windowSize = window->size();
180     data->isVisible = window->isVisible();
181     m_rendered_windows[window] = data;
182
183     isExternalUpdatePending = true;
184 }
185
186
187 /*!
188     Called on Render Thread
189  */
190 void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows()
191 {
192 #ifdef THREAD_DEBUG
193     printf("                RenderThread: about to add %d\n", m_added_windows.size());
194 #endif
195
196     while (m_added_windows.size()) {
197         QQuickWindow *window = m_added_windows.takeLast();
198         handleAddedWindow(window);
199     }
200 }
201
202
203 /*!
204     Called on the GUI Thread, from the window' destructor
205  */
206
207 void QQuickRenderThreadSingleContextWindowManager::windowDestroyed(QQuickWindow *window)
208 {
209 #ifdef THREAD_DEBUG
210     printf("GUI: Window destroyed: %p\n", window);
211 #endif
212
213     hide(window);
214 }
215
216
217 /*!
218     Called on GUI Thread
219  */
220
221 void QQuickRenderThreadSingleContextWindowManager::hide(QQuickWindow *window)
222 {
223 #ifdef THREAD_DEBUG
224     printf("GUI: Window hidden: %p\n", window);
225 #endif
226
227     int position = -1;
228     for (int i=0; i<m_tracked_windows.size(); ++i) {
229         if (m_tracked_windows.at(i).window == window) {
230             m_tracked_windows[i].toBeRemoved = true;
231             position = i;
232             break;
233         }
234     }
235
236     if (position >= 0) {
237         disconnect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged()));
238         disconnect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged()));
239         windowVisibilityChanged();
240         m_tracked_windows.removeAt(position);
241     }
242
243 #ifdef THREAD_DEBUG
244     printf("GUI: Window removal completed... %p\n", window);
245 #endif
246 }
247
248 /*!
249     Called on Render Thread
250  */
251 void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows(bool clearGLContext)
252 {
253 #ifdef THREAD_DEBUG
254     printf("                RenderThread: about to remove %d\n", m_removed_windows.size());
255 #endif
256
257     bool removedAnything = false;
258     while (m_removed_windows.size()) {
259         QQuickWindow *window = m_removed_windows.takeLast();
260 #ifdef THREAD_DEBUG
261     printf("                RenderThread: removing %p\n", window);
262 #endif
263
264         QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
265         delete m_rendered_windows.take(window);
266         removedAnything = true;
267     }
268
269     // If a window is removed because it has been hidden it will take with it
270     // the gl context (at least on Mac) if bound, so disconnect the gl context
271     // from anything
272     if (removedAnything && clearGLContext)
273         gl->doneCurrent();
274 }
275
276
277
278 /*!
279     Called on GUI Thread
280  */
281
282 void QQuickRenderThreadSingleContextWindowManager::windowVisibilityChanged()
283 {
284     bool anyoneShowing = false;
285     QList<QQuickWindow *> toAdd, toRemove;
286
287     // Not optimal, but also not frequently used...
288     for (int i=0; i<m_tracked_windows.size(); ++i) {
289         WindowTracker &t = const_cast<WindowTracker &>(m_tracked_windows.at(i));
290         QQuickWindow *win = t.window;
291
292         Q_ASSERT(win->isVisible() || QQuickWindowPrivate::get(win)->renderWithoutShowing || t.toBeRemoved);
293         bool windowVisible = win->width() > 0 && win->height() > 0;
294         anyoneShowing |= (windowVisible && !t.toBeRemoved);
295
296         if ((!windowVisible && t.isVisible) || t.toBeRemoved) {
297             toRemove << win;
298         } else if (windowVisible && !t.isVisible) {
299             toAdd << win;
300         }
301         t.isVisible = windowVisible;
302     }
303
304     if (isRunning()) {
305         if (!anyoneShowing) {
306             stopRendering();
307         } else {
308             lockInGui();
309             exhaustSyncEvent();
310             m_added_windows << toAdd;
311             m_removed_windows << toRemove;
312             while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) {
313                 if (isRenderBlocked)
314                     wake();
315                 wait();
316             }
317             unlockInGui();
318         }
319
320     } else if (anyoneShowing) {
321         Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now
322         for (int i=0; i<toAdd.size(); ++i)
323             handleAddedWindow(toAdd.at(i));
324         startRendering();
325     }
326
327 }
328
329
330 void QQuickRenderThreadSingleContextWindowManager::run()
331 {
332 #ifdef THREAD_DEBUG
333     printf("QML Rendering Thread Started\n");
334 #endif
335
336     lock();
337     Q_ASSERT(!gl);
338     initialize();
339     // Wake GUI as it is waiting for the GL context to have appeared, as
340     // an indication that the render thread is now running.
341     wake();
342     unlock();
343
344     if (!gl)
345         return;
346
347     while (!shouldExit) {
348         lock();
349
350 #ifdef THREAD_DEBUG
351         printf("                RenderThread: *** NEW FRAME ***\n");
352 #endif
353
354         isExternalUpdatePending = false;
355         handleAddedWindows();
356
357         if (!isGuiLocked) {
358             isPostingSyncEvent = true;
359
360 #ifdef THREAD_DEBUG
361             printf("                RenderThread: acquired sync lock...\n");
362 #endif
363             QCoreApplication::postEvent(this, new QEvent(QEvent_Sync));
364
365 #ifdef THREAD_DEBUG
366             printf("                RenderThread: going to sleep...\n");
367 #endif
368             wake(); // In case the event got through all the way to wait() before this thread got to wait.
369             wait();
370
371
372             isPostingSyncEvent = false;
373         }
374
375 #ifdef THREAD_DEBUG
376         printf("                RenderThread: Doing locked sync\n");
377 #endif
378 #ifdef QQUICK_RENDER_TIMING
379         if (qquick_render_timing())
380             threadTimer.start();
381 #endif
382         inSync = true;
383         for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
384              it != m_rendered_windows.constEnd(); ++it) {
385             QQuickWindow *window = it.key();
386
387 #ifdef THREAD_DEBUG
388             printf("                RenderThread: Syncing window: %p\n", window);
389 #endif
390
391             WindowData *windowData = it.value();
392             QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window);
393
394             Q_ASSERT(windowData->windowSize.width() > 0 && windowData->windowSize.height() > 0);
395
396             if (!windowData->isVisible)
397                 gl->makeCurrent(masterWindow());
398             else
399                 gl->makeCurrent(window);
400
401             if (windowData->viewportSize != windowData->windowSize) {
402 #ifdef THREAD_DEBUG
403                 printf("                RenderThread: --- window has changed size...\n");
404 #endif
405                 windowData->viewportSize = windowData->windowSize;
406                 windowData->sizeWasChanged = true;
407                 glViewport(0, 0, windowData->viewportSize.width(), windowData->viewportSize.height());
408             }
409
410             windowPrivate->syncSceneGraph();
411         }
412         inSync = false;
413
414         // Wake GUI after sync to let it continue animating and event processing.
415         wake();
416         unlock();
417 #ifdef THREAD_DEBUG
418         printf("                RenderThread: sync done\n");
419 #endif
420 #ifdef QQUICK_RENDER_TIMING
421         if (qquick_render_timing())
422             syncTime = threadTimer.elapsed();
423 #endif
424
425         for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
426              it != m_rendered_windows.constEnd(); ++it) {
427             QQuickWindow *window = it.key();
428             WindowData *windowData = it.value();
429             QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window);
430
431 #ifdef THREAD_DEBUG
432             printf("                RenderThread: Rendering window %p\n", window);
433 #endif
434
435             Q_ASSERT(windowData->windowSize.width() > 0 && windowData->windowSize.height() > 0);
436
437 #ifdef THREAD_DEBUG
438             printf("                RenderThread: --- rendering at size %dx%d\n",
439                    windowData->viewportSize.width(), windowData->viewportSize.height()
440                    );
441 #endif
442
443             // We only need to re-makeCurrent when we have multiple surfaces.
444             if (m_rendered_windows.size() > 1)
445                 gl->makeCurrent(window);
446
447             windowPrivate->renderSceneGraph(windowData->viewportSize);
448 #ifdef QQUICK_RENDER_TIMING
449             if (qquick_render_timing())
450                 renderTime = threadTimer.elapsed() - syncTime;
451 #endif
452
453             // The content of the target buffer is undefined after swap() so grab needs
454             // to happen before swap();
455             if (window == windowToGrab) {
456 #ifdef THREAD_DEBUG
457                 printf("                RenderThread: --- grabbing...\n");
458 #endif
459                 grabContent = qt_gl_read_framebuffer(windowData->windowSize, false, false);
460                 windowToGrab = 0;
461             }
462
463 #ifdef THREAD_DEBUG
464             printf("                RenderThread: --- wait for swap...\n");
465 #endif
466
467             if (windowData->isVisible && window->isExposed())
468                 gl->swapBuffers(window);
469
470             windowPrivate->fireFrameSwapped();
471 #ifdef THREAD_DEBUG
472             printf("                RenderThread: --- swap complete...\n");
473 #endif
474
475         }
476
477 #ifdef QQUICK_RENDER_TIMING
478             if (qquick_render_timing()) {
479                 static QTime lastFrameTime = QTime::currentTime();
480                 swapTime = threadTimer.elapsed() - renderTime - syncTime;
481                 qDebug() << "- Breakdown of frame time; sync:" << syncTime
482                          << "ms render:" << renderTime << "ms swap:" << swapTime
483                          << "ms total:" << swapTime + renderTime + syncTime
484                          << "ms time since last frame:" << (lastFrameTime.msecsTo(QTime::currentTime()))
485                          << "ms";
486                 lastFrameTime = QTime::currentTime();
487             }
488 #endif
489
490         lock();
491
492         handleRemovedWindows();
493
494         // Update sizes...
495         for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
496              it != m_rendered_windows.constEnd(); ++it) {
497             WindowData *windowData = it.value();
498             if (windowData->sizeWasChanged) {
499                 windowData->renderedSize = windowData->viewportSize;
500                 windowData->sizeWasChanged = false;
501             }
502         }
503
504
505         // Wake the GUI thread now that rendering is complete, to signal that painting
506         // is done, resizing is done or grabbing is completed. For grabbing, we're
507         // signalling this much later than needed (we could have done it before swap)
508         // but we don't want to lock an extra time.
509         wake();
510
511         if (!animationRunning && !isExternalUpdatePending && !shouldExit && !windowToGrab) {
512 #ifdef THREAD_DEBUG
513             printf("                RenderThread: nothing to do, going to sleep...\n");
514 #endif
515             isRenderBlocked = true;
516             wait();
517             isRenderBlocked = false;
518         }
519
520         unlock();
521
522         QCoreApplication::processEvents();
523
524         // Process any "deleteLater" objects...
525         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
526     }
527
528 #ifdef THREAD_DEBUG
529     printf("                RenderThread: deleting all outstanding nodes\n");
530 #endif
531
532     m_removed_windows << m_rendered_windows.keys();
533     handleRemovedWindows(false);
534
535     sg->invalidate();
536
537     gl->doneCurrent();
538     delete gl;
539     gl = 0;
540
541 #ifdef THREAD_DEBUG
542     printf("                RenderThread: render loop exited... Good Night!\n");
543 #endif
544
545     lock();
546     hasExited = true;
547
548 #ifdef THREAD_DEBUG
549     printf("                RenderThread: waking GUI for final sleep..\n");
550 #endif
551     wake();
552     unlock();
553
554 #ifdef THREAD_DEBUG
555     printf("                RenderThread: All done...\n");
556 #endif
557 }
558
559 bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e)
560 {
561     Q_ASSERT(QThread::currentThread() == qApp->thread());
562
563     if (e->type() == QEvent_Sync) {
564
565         // If all windowes have been hidden, ignore the event
566         if (!isRunning())
567             return true;
568
569         if (!syncAlreadyHappened)
570             sync(false);
571
572         syncAlreadyHappened = false;
573
574         if (animationRunning) {
575 #ifdef THREAD_DEBUG
576             printf("GUI: Advancing animations...\n");
577 #endif
578
579             animDriver->advance();
580
581 #ifdef THREAD_DEBUG
582             printf("GUI: Animations advanced...\n");
583 #endif
584         }
585
586         return true;
587     } else if (e->type() == QEvent_DeferredUpdate) {
588         handleDeferredUpdate();
589
590     } else if (e->type() == QEvent::Timer) {
591 #ifdef THREAD_DEBUG
592             printf("GUI: Animations advanced via timer...\n");
593 #endif
594         animDriver->advance();
595     }
596
597     return QThread::event(e);
598 }
599
600
601
602 void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent()
603 {
604     if (isPostingSyncEvent) {
605         sync(true);
606         syncAlreadyHappened = true;
607     }
608 }
609
610
611
612 void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked)
613 {
614 #ifdef THREAD_DEBUG
615     printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
616 #endif
617     if (!guiAlreadyLocked)
618         lockInGui();
619
620     for (QHash<QQuickWindow *, WindowData *>::const_iterator it = m_rendered_windows.constBegin();
621          it != m_rendered_windows.constEnd(); ++it) {
622         QQuickWindowPrivate::get(it.key())->polishItems();
623     }
624
625     wake();
626     wait();
627
628     if (!guiAlreadyLocked)
629         unlockInGui();
630 }
631
632
633
634
635 /*!
636     Acquires the mutex for the GUI thread. The function uses the isGuiLocked
637     variable to keep track of how many recursion levels the gui is locked with.
638     We only actually acquire the mutex for the first level to avoid deadlocking
639     ourselves.
640  */
641
642 void QQuickRenderThreadSingleContextWindowManager::lockInGui()
643 {
644     if (++isGuiLocked == 1)
645         lock();
646
647 #ifdef THREAD_DEBUG
648     printf("GUI: acquired lock... level=%d\n", isGuiLocked);
649 #endif
650 }
651
652
653
654 void QQuickRenderThreadSingleContextWindowManager::unlockInGui()
655 {
656 #ifdef THREAD_DEBUG
657     printf("GUI: releasing lock... level=%d\n", isGuiLocked);
658 #endif
659
660     if (--isGuiLocked == 0)
661         unlock();
662 }
663
664
665
666
667 void QQuickRenderThreadSingleContextWindowManager::animationStarted()
668 {
669 #ifdef THREAD_DEBUG
670     printf("GUI: animationStarted()\n");
671 #endif
672
673     if (!isRunning()) {
674         animationTimer = startTimer(1000/60);
675         return;
676     }
677
678     lockInGui();
679
680     animationRunning = true;
681
682     if (isRenderBlocked)
683         wake();
684
685     unlockInGui();
686 }
687
688
689
690 void QQuickRenderThreadSingleContextWindowManager::animationStopped()
691 {
692 #ifdef THREAD_DEBUG
693     printf("GUI: animationStopped()...\n");
694 #endif
695
696     if (!isRunning()) {
697         killTimer(animationTimer);
698         animationTimer = -1;
699         return;
700     }
701
702     lockInGui();
703     animationRunning = false;
704     unlockInGui();
705 }
706
707
708 void QQuickRenderThreadSingleContextWindowManager::exposureChanged(QQuickWindow *window)
709 {
710     Q_UNUSED(window);
711 #ifdef THREAD_DEBUG
712     printf("GUI: exposure changed: %p\n", window);
713 #endif
714
715     if (window->isExposed())
716         maybeUpdate(window);
717
718 #ifdef THREAD_DEBUG
719     printf("GUI: exposure changed done: %p\n", window);
720 #endif
721 }
722
723
724
725 void QQuickRenderThreadSingleContextWindowManager::resize(QQuickWindow *window, const QSize &size)
726 {
727 #ifdef THREAD_DEBUG
728     printf("GUI: Resize Event: %p = %dx%d\n", window, size.width(), size.height());
729 #endif
730
731     // If the rendering thread is not running we do not need to do anything.
732     // Also if the window is being resized to an invalid size, it will be removed
733     // by the windowVisibilityChanged slot as result of width/heightcChanged()
734     if (!isRunning() || size.width() <= 0 || size.height() <= 0)
735         return;
736
737     lockInGui();
738     exhaustSyncEvent();
739
740     WindowData *windowData = m_rendered_windows.value(window);
741     if (windowData) {
742         windowData->windowSize = size;
743         while (isRunning() && windowData->renderedSize != size && size.width() > 0 && size.height() > 0) {
744             if (isRenderBlocked)
745                 wake();
746             wait();
747         }
748     }
749     unlockInGui();
750
751 #ifdef THREAD_DEBUG
752     printf("GUI: Resize done: %p\n", window);
753 #endif
754 }
755
756
757
758 void QQuickRenderThreadSingleContextWindowManager::startRendering()
759 {
760 #ifdef THREAD_DEBUG
761     printf("GUI: Starting Render Thread\n");
762 #endif
763     hasExited = false;
764     shouldExit = false;
765     isGuiLocked = 0;
766     isPostingSyncEvent = false;
767     syncAlreadyHappened = false;
768     inSync = false;
769
770     lockInGui();
771     animationRunning = animDriver->isRunning();
772     start(); // Start the render thread...
773     wait();
774     unlockInGui();
775
776     // Animations will now be driven from the rendering thread.
777     if (animationTimer >= 0) {
778         killTimer(animationTimer);
779         animationTimer = -1;
780     }
781
782
783 }
784
785
786
787 void QQuickRenderThreadSingleContextWindowManager::stopRendering()
788 {
789 #ifdef THREAD_DEBUG
790     printf("GUI: stopping render thread\n");
791 #endif
792
793     lockInGui();
794     exhaustSyncEvent();
795     shouldExit = true;
796
797     if (isRenderBlocked) {
798 #ifdef THREAD_DEBUG
799         printf("GUI: waking up render thread\n");
800 #endif
801         wake();
802     }
803
804     while (!hasExited) {
805 #ifdef THREAD_DEBUG
806         printf("GUI: waiting for render thread to have exited..\n");
807 #endif
808         wait();
809     }
810
811     unlockInGui();
812
813 #ifdef THREAD_DEBUG
814     printf("GUI: waiting for render thread to terminate..\n");
815 #endif
816     // Actually wait for the thread to terminate.  Otherwise we can delete it
817     // too early and crash.
818     QThread::wait();
819
820 #ifdef THREAD_DEBUG
821     printf("GUI: thread has terminated and we're all good..\n");
822 #endif
823
824     // Activate timer to keep animations running
825     if (animDriver->isRunning())
826         animationTimer = startTimer(1000/60);
827 }
828
829
830
831 QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickWindow *window)
832 {
833     if (!isRunning())
834         return QImage();
835
836     if (QThread::currentThread() != qApp->thread()) {
837         qWarning("QQuickWindow::grabFrameBuffer: can only be called from the GUI thread");
838         return QImage();
839     }
840
841 #ifdef THREAD_DEBUG
842     printf("GUI: doing a pixelwise grab..\n");
843 #endif
844
845     lockInGui();
846     exhaustSyncEvent();
847
848     windowToGrab = window;
849     while (isRunning() && windowToGrab) {
850         if (isRenderBlocked)
851             wake();
852         wait();
853     }
854
855     QImage grabbed = grabContent;
856     grabContent = QImage();
857
858     unlockInGui();
859
860     return grabbed;
861 }
862
863
864 void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate()
865 {
866 #ifdef THREAD_DEBUG
867     printf("GUI: handling update to ourselves...\n");
868 #endif
869
870     isDeferredUpdatePosted = false;
871
872     lockInGui();
873     isExternalUpdatePending = true;
874     if (isRenderBlocked)
875         wake();
876     unlockInGui();
877 }
878
879 void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickWindow *)
880 {
881     Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
882                "QQuickWindow::update",
883                "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
884
885     if (inSync) {
886         isExternalUpdatePending = true;
887
888     } else if (!isDeferredUpdatePosted) {
889 #ifdef THREAD_DEBUG
890         printf("GUI: posting update to ourselves...\n");
891 #endif
892         isDeferredUpdatePosted = true;
893         QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate));
894     }
895
896 }
897
898 QT_END_NAMESPACE