Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsgsoftwarethreadedrenderloop.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7
8#include <private/qsgrenderer_p.h>
9#include <private/qquickwindow_p.h>
10#include <private/qquickitem_p.h>
11#include <private/qquickprofiler_p.h>
12#include <private/qquickanimatorcontroller_p.h>
13#include <private/qquickprofiler_p.h>
14#include <private/qqmldebugserviceinterfaces_p.h>
15#include <private/qqmldebugconnector_p.h>
16
17#include <qpa/qplatformbackingstore.h>
18
19#include <QtCore/QQueue>
20#include <QtCore/QElapsedTimer>
21#include <QtCore/QThread>
22#include <QtCore/QMutex>
23#include <QtCore/QWaitCondition>
24#include <QtGui/QGuiApplication>
25#include <QtGui/QBackingStore>
26#include <QtQuick/QQuickWindow>
27
28#include <qtquick_tracepoints_p.h>
29
31
32// Used to debug the renderloop logic. Primarily useful for platform integrators
33// and when investigating the render loop logic.
34Q_STATIC_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP, "qt.scenegraph.renderloop")
35
36class QSGSoftwareWindowEvent : public QEvent
37{
38public:
39 QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
40 QQuickWindow *window;
41};
42
44{
45public:
46 QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
49};
50
52{
53public:
54 QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
56 , size(c->size())
57 , dpr(c->effectiveDevicePixelRatio())
58 , syncInExpose(inExpose)
59 , forceRenderPass(force) { }
61 float dpr;
64};
65
67{
68public:
69 QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
72};
73
75{
76public:
77 QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
79 ~QSGSoftwareJobEvent() { delete job; }
81};
82
84{
85public:
86 void addEvent(QEvent *e) {
87 mutex.lock();
88 enqueue(e);
89 if (waiting)
90 condition.wakeOne();
91 mutex.unlock();
92 }
93
94 QEvent *takeEvent(bool wait) {
95 mutex.lock();
96 if (isEmpty() && wait) {
97 waiting = true;
98 condition.wait(&mutex);
99 waiting = false;
100 }
101 QEvent *e = dequeue();
102 mutex.unlock();
103 return e;
104 }
105
107 mutex.lock();
108 bool has = !isEmpty();
109 mutex.unlock();
110 return has;
111 }
112
113private:
114 QMutex mutex;
115 QWaitCondition condition;
116 bool waiting = false;
117};
118
119static inline int qsgrl_animation_interval()
120{
121 const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
122 return refreshRate < 1 ? 16 : int(1000 / refreshRate);
123}
124
126{
128public:
135
137 {
138 delete rc;
139 }
140
142 void run() override;
143
145 void sync(bool inExpose);
146
148 {
149 if (sleeping)
150 stopEventProcessing = true;
151 if (exposedWindow)
152 pendingUpdate |= RepaintRequest;
153 }
154
157 void postEvent(QEvent *e);
158
164
168 volatile bool active = false;
170 bool sleeping = false;
175 QQuickWindow *exposedWindow = nullptr;
176 QBackingStore *backingStore = nullptr;
183
184public slots:
187 }
188};
189
190bool QSGSoftwareRenderThread::event(QEvent *e)
191{
192 switch ((int)e->type()) {
193
194 case WM_Obscure:
195 Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGSoftwareWindowEvent *>(e)->window);
196 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow;
197 mutex.lock();
198 if (exposedWindow) {
199 QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
200 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed");
201 exposedWindow = nullptr;
202 delete backingStore;
203 backingStore = nullptr;
204 }
205 waitCondition.wakeOne();
206 mutex.unlock();
207 return true;
208
209 case WM_RequestSync: {
210 QSGSoftwareSyncEvent *wme = static_cast<QSGSoftwareSyncEvent *>(e);
211 if (sleeping)
212 stopEventProcessing = true;
213 exposedWindow = wme->window;
214 if (backingStore == nullptr)
215 backingStore = new QBackingStore(exposedWindow);
216 if (backingStore->size() != exposedWindow->size())
217 backingStore->resize(exposedWindow->size());
218 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow;
219 pendingUpdate |= SyncRequest;
220 if (wme->syncInExpose) {
221 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose");
222 pendingUpdate |= ExposeRequest;
223 }
224 if (wme->forceRenderPass) {
225 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless");
226 pendingUpdate |= RepaintRequest;
227 }
228 return true;
229 }
230
231 case WM_TryRelease: {
232 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease");
233 mutex.lock();
234 renderLoop->lockedForSync = true;
236 // Only when no windows are exposed anymore or we are shutting down.
237 if (!exposedWindow || wme->destroying) {
238 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc");
239 if (wme->window) {
240 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
241 if (wme->destroying) {
242 // Bye bye nodes...
243 wd->cleanupNodesOnShutdown();
244 }
245 rc->invalidate();
246 QCoreApplication::processEvents();
247 QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
248 if (wme->destroying)
249 wd->animationController.reset();
250 }
251 if (wme->destroying)
252 active = false;
253 if (sleeping)
254 stopEventProcessing = true;
255 } else {
256 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active");
257 }
258 waitCondition.wakeOne();
259 renderLoop->lockedForSync = false;
260 mutex.unlock();
261 return true;
262 }
263
264 case WM_Grab: {
265 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab");
266 QSGSoftwareGrabEvent *wme = static_cast<QSGSoftwareGrabEvent *>(e);
267 Q_ASSERT(wme->window);
268 Q_ASSERT(wme->window == exposedWindow || !exposedWindow);
269 mutex.lock();
270 if (wme->window) {
271 // Grabbing is generally done by rendering a frame and reading the
272 // color buffer contents back, without presenting, and then
273 // creating a QImage from the returned data. It is terribly
274 // inefficient since it involves a full blocking wait for the GPU.
275 // However, our hands are tied by the existing, synchronous APIs of
276 // QQuickWindow and such.
277 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
278 auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
279 if (softwareRenderer)
280 softwareRenderer->setBackingStore(backingStore);
281 rc->initialize(nullptr);
282 wd->syncSceneGraph();
283 rc->endSync();
284 wd->renderSceneGraph();
285 *wme->image = backingStore->handle()->toImage();
286 }
287 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result");
288 waitCondition.wakeOne();
289 mutex.unlock();
290 return true;
291 }
292
293 case WM_PostJob: {
294 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob");
295 QSGSoftwareJobEvent *wme = static_cast<QSGSoftwareJobEvent *>(e);
296 Q_ASSERT(wme->window == exposedWindow);
297 if (exposedWindow) {
298 wme->job->run();
299 delete wme->job;
300 wme->job = nullptr;
301 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done");
302 }
303 return true;
304 }
305
306 default:
307 break;
308 }
309
310 return QThread::event(e);
311}
312
314{
315 eventQueue.addEvent(e);
316}
317
319{
320 while (eventQueue.hasMoreEvents()) {
321 QEvent *e = eventQueue.takeEvent(false);
322 event(e);
323 delete e;
324 }
325}
326
328{
329 stopEventProcessing = false;
330 while (!stopEventProcessing) {
331 QEvent *e = eventQueue.takeEvent(true);
332 event(e);
333 delete e;
334 }
335}
336
338{
339 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()");
340
341 rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr);
342 rtAnim->install();
343
344 if (QQmlDebugConnector::service<QQmlProfilerService>())
345 QQuickProfiler::registerAnimationCallback();
346
347 renderThrottleTimer.start();
348
349 while (active) {
350 if (exposedWindow)
352
354 QCoreApplication::processEvents();
355
356 if (pendingUpdate == 0 || !exposedWindow) {
357 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep");
358 sleeping = true;
360 sleeping = false;
361 }
362 }
363
364 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting");
365
366 delete rtAnim;
367 rtAnim = nullptr;
368
369 rc->moveToThread(renderLoop->thread());
370 moveToThread(renderLoop->thread());
371}
372
373void QSGSoftwareRenderThread::sync(bool inExpose)
374{
375 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync");
376
377 mutex.lock();
378 Q_ASSERT_X(renderLoop->lockedForSync, "QSGSoftwareRenderThread::sync()", "sync triggered with gui not locked");
379
380 if (exposedWindow) {
381 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
382 bool hadRenderer = wd->renderer != nullptr;
383 // If the scene graph was touched since the last sync() make sure it sends the
384 // changed signal.
385 if (wd->renderer)
386 wd->renderer->clearChangedFlag();
387
388 rc->initialize(nullptr);
389 wd->syncSceneGraph();
390 rc->endSync();
391
392 if (!hadRenderer && wd->renderer) {
393 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer");
395 connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this,
396 &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection);
397 }
398
399 // Process deferred deletes now, directly after the sync as deleteLater
400 // on the GUI must now also have resulted in SG changes and the delete
401 // is a safe operation.
402 QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
403 }
404
405 if (!inExpose) {
406 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui");
407 waitCondition.wakeOne();
408 mutex.unlock();
409 }
410}
411
413{
414 Q_TRACE_SCOPE(QSG_syncAndRender);
415 Q_TRACE(QSG_sync_entry);
416 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
417
418 QElapsedTimer waitTimer;
419 waitTimer.start();
420
421 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()");
422
423 syncResultedInChanges = false;
424 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
425
426 const bool repaintRequested = pendingUpdate & RepaintRequest;
427 const bool syncRequested = pendingUpdate & SyncRequest;
428 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
429 pendingUpdate = 0;
430
431 emit exposedWindow->beforeFrameBegin();
432
433 if (syncRequested)
434 sync(exposeRequested);
435
436 Q_TRACE(QSG_sync_exit);
437 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
438 QQuickProfiler::SceneGraphRenderLoopSync);
439
440 if (!syncResultedInChanges && !repaintRequested) {
441 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted");
442 int waitTime = vsyncDelta - (int) waitTimer.elapsed();
443 if (waitTime > 0)
444 msleep(waitTime);
445 return;
446 }
447
448 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started");
449 Q_TRACE(QSG_render_entry);
450
451 if (rtAnim->isRunning()) {
452 wd->animationController->lock();
453 rtAnim->advance();
454 wd->animationController->unlock();
455 }
456
457 bool canRender = wd->renderer != nullptr;
458
459 if (canRender) {
460 auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
461 if (softwareRenderer)
462 softwareRenderer->setBackingStore(backingStore);
463 wd->renderSceneGraph();
464
465 Q_TRACE(QSG_render_exit);
466 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
467 QQuickProfiler::SceneGraphRenderLoopRender);
468 Q_TRACE(QSG_swap_entry);
469
470 if (softwareRenderer)
471 backingStore->flush(softwareRenderer->flushRegion());
472
473 // Since there is no V-Sync with QBackingStore, throttle rendering the refresh
474 // rate of the current screen the window is on.
475 int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed();
476 if (blockTime > 0) {
477 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - blocking for %d ms", blockTime);
478 msleep(blockTime);
479 }
480 renderThrottleTimer.start();
481
482 wd->fireFrameSwapped();
483 } else {
484 Q_TRACE(QSG_render_exit);
485 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
486 QQuickProfiler::SceneGraphRenderLoopSync, 1);
487 Q_TRACE(QSG_swap_entry);
488 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render");
489 }
490
491 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done");
492
493 emit exposedWindow->afterFrameEnd();
494
495 if (exposeRequested) {
496 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose");
497 waitCondition.wakeOne();
498 mutex.unlock();
499 }
500
501 Q_TRACE(QSG_swap_exit);
502 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
503 QQuickProfiler::SceneGraphRenderLoopSwap);
504}
505
506QSGSoftwareThreadedRenderLoop::WindowData *QSGSoftwareThreadedRenderLoop::windowFor(QQuickWindow *window)
507{
508 for (const auto &t : std::as_const(m_windows)) {
509 if (t.window == window)
510 return const_cast<WindowData *>(&t);
511 }
512 return nullptr;
513}
514
515
516QSGSoftwareThreadedRenderLoop::QSGSoftwareThreadedRenderLoop()
517{
518 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor");
519 m_sg = new QSGSoftwareContext;
520 m_anim = m_sg->createAnimationDriver(this);
521 connect(m_anim, &QAnimationDriver::started, this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted);
522 connect(m_anim, &QAnimationDriver::stopped, this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped);
523 m_anim->install();
524}
525
527{
528 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor");
529 delete m_sg;
530}
531
532void QSGSoftwareThreadedRenderLoop::show(QQuickWindow *window)
533{
534 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window;
535}
536
537void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window)
538{
539 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window;
540
541 if (window->isExposed())
542 handleObscurity(windowFor(window));
543
544 releaseResources(window);
545}
546
547void QSGSoftwareThreadedRenderLoop::resize(QQuickWindow *window)
548{
549 if (!window->isExposed() || window->size().isEmpty())
550 return;
551
552 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size();
553}
554
556{
557 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window;
558
559 WindowData *w = windowFor(window);
560 if (!w)
561 return;
562
563 handleObscurity(w);
564 handleResourceRelease(w, true);
565
566 QSGSoftwareRenderThread *thread = w->thread;
567 while (thread->isRunning())
568 QThread::yieldCurrentThread();
569
570 Q_ASSERT(thread->thread() == QThread::currentThread());
571 delete thread;
572
573 for (int i = 0; i < m_windows.size(); ++i) {
574 if (m_windows.at(i).window == window) {
575 m_windows.removeAt(i);
576 break;
577 }
578 }
579
580 // Now that we altered the window list, we may need to stop the animation
581 // timer even if we didn't via handleObscurity. This covers the case where
582 // we destroy a visible & exposed QQuickWindow.
583 startOrStopAnimationTimer();
584}
585
587{
588 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window;
589
590 if (window->isExposed()) {
591 handleExposure(window);
592 } else {
593 WindowData *w = windowFor(window);
594 if (w)
595 handleObscurity(w);
596 }
597}
598
600{
601 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window;
602
603 WindowData *w = windowFor(window);
604 // Have to support invisible (but created()'ed) windows as well.
605 // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
606 const bool tempExpose = !w;
607 if (tempExpose) {
608 handleExposure(window);
609 w = windowFor(window);
610 Q_ASSERT(w);
611 }
612
613 if (!w->thread->isRunning())
614 return QImage();
615
616 if (!window->handle())
617 window->create();
618
619 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
620 wd->polishItems();
621
622 QImage result;
623 w->thread->mutex.lock();
624 lockedForSync = true;
625 w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result));
626 w->thread->waitCondition.wait(&w->thread->mutex);
627 lockedForSync = false;
628 w->thread->mutex.unlock();
629
630 result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
631
632 if (tempExpose)
633 handleObscurity(w);
634
635 return result;
636}
637
638void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window)
639{
640 WindowData *w = windowFor(window);
641 if (!w)
642 return;
643
644 if (w->thread == QThread::currentThread()) {
645 w->thread->requestRepaint();
646 return;
647 }
648
649 // We set forceRenderPass because we want to make sure the QQuickWindow
650 // actually does a full render pass after the next sync.
651 w->forceRenderPass = true;
652 scheduleUpdate(w);
653}
654
656{
657 WindowData *w = windowFor(window);
658 if (w)
659 scheduleUpdate(w);
660}
661
663{
664 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window;
665
666 WindowData *w = windowFor(window);
667 if (w)
668 polishAndSync(w, false);
669}
670
675
677{
678 return m_sg;
679}
680
681QSGRenderContext *QSGSoftwareThreadedRenderLoop::createRenderContext(QSGContext *) const
682{
683 return m_sg->createRenderContext();
684}
685
687{
688 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window;
689
690 WindowData *w = windowFor(window);
691 if (w)
692 handleResourceRelease(w, false);
693}
694
695void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
696{
697 WindowData *w = windowFor(window);
698 if (w && w->thread && w->thread->exposedWindow)
699 w->thread->postEvent(new QSGSoftwareJobEvent(window, job));
700 else
701 delete job;
702}
703
705{
706 return QSurface::RasterSurface;
707}
708
710{
711 bool somethingVisible = false;
712 for (const WindowData &w : m_windows) {
713 if (w.window->isVisible() && w.window->isExposed()) {
714 somethingVisible = true;
715 break;
716 }
717 }
718 return somethingVisible && m_anim->isRunning();
719}
720
722{
723 return SupportsGrabWithoutExpose;
724}
725
727{
728 if (e->type() == QEvent::Timer) {
729 QTimerEvent *te = static_cast<QTimerEvent *>(e);
730 if (te->timerId() == animationTimer) {
731 m_anim->advance();
732 emit timeToIncubate();
733 return true;
734 }
735 }
736
737 return QObject::event(e);
738}
739
740void QSGSoftwareThreadedRenderLoop::onAnimationStarted()
741{
742 startOrStopAnimationTimer();
743
744 for (const WindowData &w : std::as_const(m_windows))
745 w.window->requestUpdate();
746}
747
749{
750 startOrStopAnimationTimer();
751}
752
753void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer()
754{
755 int exposedWindowCount = 0;
756 const WindowData *exposed = nullptr;
757
758 for (int i = 0; i < m_windows.size(); ++i) {
759 const WindowData &w(m_windows[i]);
760 if (w.window->isVisible() && w.window->isExposed()) {
761 ++exposedWindowCount;
762 exposed = &w;
763 }
764 }
765
766 if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
767 killTimer(animationTimer);
768 animationTimer = 0;
769 // If animations are running, make sure we keep on animating
770 if (m_anim->isRunning())
771 exposed->window->requestUpdate();
772 } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
773 animationTimer = startTimer(qsgrl_animation_interval());
774 }
775}
776
777void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
778{
779 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window;
780
781 WindowData *w = windowFor(window);
782 if (!w) {
783 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list");
784 WindowData win;
785 win.window = window;
786 QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership
787 win.thread = new QSGSoftwareRenderThread(this, rc);
788 win.updateDuringSync = false;
789 win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
790 m_windows.append(win);
791 w = &m_windows.last();
792 }
793
794 // set this early as we'll be rendering shortly anyway and this avoids
795 // special casing exposure in polishAndSync.
796 w->thread->exposedWindow = window;
797
798 if (w->window->size().isEmpty()
799 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
800#ifndef QT_NO_DEBUG
801 qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window "
802 << w->window << " with invalid geometry: " << w->window->geometry()
803 << " on " << w->window->screen();
804#endif
805 }
806
807 if (!w->window->handle())
808 w->window->create();
809
810 // Start render thread if it is not running
811 if (!w->thread->isRunning()) {
812 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread");
813 // Push a few things to the render thread.
814 QQuickAnimatorController *controller
815 = QQuickWindowPrivate::get(w->window)->animationController.get();
816 if (controller->thread() != w->thread)
817 controller->moveToThread(w->thread);
818 if (w->thread->thread() == QThread::currentThread()) {
819 w->thread->rc->moveToThread(w->thread);
820 w->thread->moveToThread(w->thread);
821 }
822
823 w->thread->active = true;
824 w->thread->start();
825
826 if (!w->thread->isRunning())
827 qFatal("Render thread failed to start, aborting application.");
828 }
829
830 polishAndSync(w, true);
831
832 startOrStopAnimationTimer();
833}
834
835void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w)
836{
837 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window;
838
839 if (w->thread->isRunning()) {
840 w->thread->mutex.lock();
841 w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, QEvent::Type(WM_Obscure)));
842 w->thread->waitCondition.wait(&w->thread->mutex);
843 w->thread->mutex.unlock();
844 }
845
846 startOrStopAnimationTimer();
847}
848
849void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w)
850{
851 if (!QCoreApplication::instance())
852 return;
853
854 if (!w || !w->thread->isRunning())
855 return;
856
857 QThread *current = QThread::currentThread();
858 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
859 qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
860 return;
861 }
862
863 if (current == w->thread) {
864 w->updateDuringSync = true;
865 return;
866 }
867
868 w->window->requestUpdate();
869}
870
871void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying)
872{
873 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window;
874
875 w->thread->mutex.lock();
876 if (w->thread->isRunning() && w->thread->active) {
877 QQuickWindow *window = w->window;
878
879 // Note that window->handle() is typically null by this time because
880 // the platform window is already destroyed. This should not be a
881 // problem for the D3D cleanup.
882
883 w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying));
884 w->thread->waitCondition.wait(&w->thread->mutex);
885
886 // Avoid a shutdown race condition.
887 // If SG is invalidated and 'active' becomes false, the thread's run()
888 // method will exit. handleExposure() relies on QThread::isRunning() (because it
889 // potentially needs to start the thread again) and our mutex cannot be used to
890 // track the thread stopping, so we wait a few nanoseconds extra so the thread
891 // can exit properly.
892 if (!w->thread->active)
893 w->thread->wait();
894 }
895 w->thread->mutex.unlock();
896}
897
898void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose)
899{
900 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
901
902 QQuickWindow *window = w->window;
903 if (!w->thread || !w->thread->exposedWindow) {
904 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort");
905 return;
906 }
907
908 // Flush pending touch events.
909 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
910 // The delivery of the event might have caused the window to stop rendering
911 w = windowFor(window);
912 if (!w || !w->thread || !w->thread->exposedWindow) {
913 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort");
914 return;
915 }
916
917 Q_TRACE_SCOPE(QSG_polishAndSync);
918
919 Q_TRACE(QSG_polishItems_entry);
920 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
921
922 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
923 wd->polishItems();
924
925 Q_TRACE(QSG_polishItems_exit);
926 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
927 QQuickProfiler::SceneGraphPolishAndSyncPolish);
928 Q_TRACE(QSG_sync_entry);
929
930 w->updateDuringSync = false;
931
932 emit window->afterAnimating();
933
934 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync");
935 w->thread->mutex.lock();
936 lockedForSync = true;
937 w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass));
938 w->forceRenderPass = false;
939
940 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync");
941
942 Q_TRACE(QSG_sync_exit);
943 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
944 QQuickProfiler::SceneGraphPolishAndSyncWait);
945 Q_TRACE(QSG_wait_entry);
946 w->thread->waitCondition.wait(&w->thread->mutex);
947 lockedForSync = false;
948 w->thread->mutex.unlock();
949 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync");
950
951 Q_TRACE(QSG_wait_exit);
952 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
953 QQuickProfiler::SceneGraphPolishAndSyncSync);
954 Q_TRACE(QSG_animations_entry);
955
956 if (!animationTimer && m_anim->isRunning()) {
957 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations");
958 m_anim->advance();
959 // We need to trigger another sync to keep animations running...
960 w->window->requestUpdate();
961 emit timeToIncubate();
962 } else if (w->updateDuringSync) {
963 w->window->requestUpdate();
964 }
965
966 Q_TRACE(QSG_animations_exit);
967 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
968 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
969}
970
971QT_END_NAMESPACE
972
973#include "qsgsoftwarethreadedrenderloop.moc"
974#include "moc_qsgsoftwarethreadedrenderloop_p.cpp"
QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
QSGSoftwareThreadedRenderLoop * renderLoop
QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
void exposureChanged(QQuickWindow *window) override
QAnimationDriver * animationDriver() const override
void resize(QQuickWindow *window) override
void handleUpdateRequest(QQuickWindow *window) override
void update(QQuickWindow *window) override
void windowDestroyed(QQuickWindow *window) override
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
QImage grab(QQuickWindow *window) override
void maybeUpdate(QQuickWindow *window) override
void releaseResources(QQuickWindow *window) override
QSGContext * sceneGraphContext() const override
QSurface::SurfaceType windowSurfaceType() const override
void hide(QQuickWindow *window) override
void postJob(QQuickWindow *window, QRunnable *job) override
void show(QQuickWindow *window) override
QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
static int qsgrl_animation_interval()