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