Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
38
46
48{
49public:
50 QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
52 , size(c->size())
53 , dpr(c->effectiveDevicePixelRatio())
54 , syncInExpose(inExpose)
55 , forceRenderPass(force) { }
57 float dpr;
60};
61
69
78
79class QSGSoftwareEventQueue : public QQueue<QEvent *>
80{
81public:
82 void addEvent(QEvent *e) {
83 mutex.lock();
84 enqueue(e);
85 if (waiting)
86 condition.wakeOne();
87 mutex.unlock();
88 }
89
90 QEvent *takeEvent(bool wait) {
91 mutex.lock();
92 if (isEmpty() && wait) {
93 waiting = true;
94 condition.wait(&mutex);
95 waiting = false;
96 }
97 QEvent *e = dequeue();
98 mutex.unlock();
99 return e;
100 }
101
103 mutex.lock();
104 bool has = !isEmpty();
105 mutex.unlock();
106 return has;
107 }
108
109private:
110 QMutex mutex;
112 bool waiting = false;
113};
114
115static inline int qsgrl_animation_interval()
116{
118 return refreshRate < 1 ? 16 : int(1000 / refreshRate);
119}
120
122{
124public:
126 : renderLoop(rl)
127 {
128 rc = static_cast<QSGSoftwareRenderContext *>(renderContext);
130 }
131
133 {
134 delete rc;
135 }
136
137 bool event(QEvent *e) override;
138 void run() override;
139
140 void syncAndRender();
141 void sync(bool inExpose);
142
144 {
145 if (sleeping)
146 stopEventProcessing = true;
147 if (exposedWindow)
149 }
150
152 void processEvents();
153 void postEvent(QEvent *e);
154
160
164 volatile bool active = false;
166 bool sleeping = false;
179
180public slots:
183 }
184};
185
187{
188 switch ((int)e->type()) {
189
190 case WM_Obscure:
192 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow;
193 mutex.lock();
194 if (exposedWindow) {
195 QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
196 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed");
197 exposedWindow = nullptr;
198 delete backingStore;
199 backingStore = nullptr;
200 }
202 mutex.unlock();
203 return true;
204
205 case WM_RequestSync: {
206 QSGSoftwareSyncEvent *wme = static_cast<QSGSoftwareSyncEvent *>(e);
207 if (sleeping)
208 stopEventProcessing = true;
209 exposedWindow = wme->window;
210 if (backingStore == nullptr)
212 if (backingStore->size() != exposedWindow->size())
214 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow;
216 if (wme->syncInExpose) {
217 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose");
219 }
220 if (wme->forceRenderPass) {
221 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless");
223 }
224 return true;
225 }
226
227 case WM_TryRelease: {
228 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease");
229 mutex.lock();
230 renderLoop->lockedForSync = true;
232 // Only when no windows are exposed anymore or we are shutting down.
233 if (!exposedWindow || wme->destroying) {
234 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc");
235 if (wme->window) {
237 if (wme->destroying) {
238 // Bye bye nodes...
240 }
241 rc->invalidate();
244 if (wme->destroying)
246 }
247 if (wme->destroying)
248 active = false;
249 if (sleeping)
250 stopEventProcessing = true;
251 } else {
252 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active");
253 }
255 renderLoop->lockedForSync = false;
256 mutex.unlock();
257 return true;
258 }
259
260 case WM_Grab: {
261 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab");
262 QSGSoftwareGrabEvent *wme = static_cast<QSGSoftwareGrabEvent *>(e);
263 Q_ASSERT(wme->window);
264 Q_ASSERT(wme->window == exposedWindow || !exposedWindow);
265 mutex.lock();
266 if (wme->window) {
267 // Grabbing is generally done by rendering a frame and reading the
268 // color buffer contents back, without presenting, and then
269 // creating a QImage from the returned data. It is terribly
270 // inefficient since it involves a full blocking wait for the GPU.
271 // However, our hands are tied by the existing, synchronous APIs of
272 // QQuickWindow and such.
274 auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
275 if (softwareRenderer)
276 softwareRenderer->setBackingStore(backingStore);
277 rc->initialize(nullptr);
278 wd->syncSceneGraph();
279 rc->endSync();
280 wd->renderSceneGraph();
281 *wme->image = backingStore->handle()->toImage();
282 }
283 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result");
285 mutex.unlock();
286 return true;
287 }
288
289 case WM_PostJob: {
290 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob");
291 QSGSoftwareJobEvent *wme = static_cast<QSGSoftwareJobEvent *>(e);
292 Q_ASSERT(wme->window == exposedWindow);
293 if (exposedWindow) {
294 wme->job->run();
295 delete wme->job;
296 wme->job = nullptr;
297 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done");
298 }
299 return true;
300 }
301
302 default:
303 break;
304 }
305
306 return QThread::event(e);
307}
308
313
315{
316 while (eventQueue.hasMoreEvents()) {
317 QEvent *e = eventQueue.takeEvent(false);
318 event(e);
319 delete e;
320 }
321}
322
324{
325 stopEventProcessing = false;
326 while (!stopEventProcessing) {
327 QEvent *e = eventQueue.takeEvent(true);
328 event(e);
329 delete e;
330 }
331}
332
334{
335 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()");
336
338 rtAnim->install();
339
340 if (QQmlDebugConnector::service<QQmlProfilerService>())
342
344
345 while (active) {
346 if (exposedWindow)
348
351
352 if (pendingUpdate == 0 || !exposedWindow) {
353 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep");
354 sleeping = true;
356 sleeping = false;
357 }
358 }
359
360 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting");
361
362 delete rtAnim;
363 rtAnim = nullptr;
364
367}
368
370{
371 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync");
372
373 mutex.lock();
374 Q_ASSERT_X(renderLoop->lockedForSync, "QSGSoftwareRenderThread::sync()", "sync triggered with gui not locked");
375
376 if (exposedWindow) {
378 bool hadRenderer = wd->renderer != nullptr;
379 // If the scene graph was touched since the last sync() make sure it sends the
380 // changed signal.
381 if (wd->renderer)
383
384 rc->initialize(nullptr);
385 wd->syncSceneGraph();
386 rc->endSync();
387
388 if (!hadRenderer && wd->renderer) {
389 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer");
393 }
394
395 // Process deferred deletes now, directly after the sync as deleteLater
396 // on the GUI must now also have resulted in SG changes and the delete
397 // is a safe operation.
399 }
400
401 if (!inExpose) {
402 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui");
404 mutex.unlock();
405 }
406}
407
409{
410 Q_TRACE_SCOPE(QSG_syncAndRender);
411 Q_TRACE(QSG_sync_entry);
412 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
413
414 QElapsedTimer waitTimer;
415 waitTimer.start();
416
417 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()");
418
419 syncResultedInChanges = false;
421
422 const bool repaintRequested = pendingUpdate & RepaintRequest;
423 const bool syncRequested = pendingUpdate & SyncRequest;
424 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
425 pendingUpdate = 0;
426
427 emit exposedWindow->beforeFrameBegin();
428
429 if (syncRequested)
430 sync(exposeRequested);
431
432 Q_TRACE(QSG_sync_exit);
433 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
434 QQuickProfiler::SceneGraphRenderLoopSync);
435
436 if (!syncResultedInChanges && !repaintRequested) {
437 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted");
438 int waitTime = vsyncDelta - (int) waitTimer.elapsed();
439 if (waitTime > 0)
440 msleep(waitTime);
441 return;
442 }
443
444 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started");
445 Q_TRACE(QSG_render_entry);
446
447 if (rtAnim->isRunning()) {
449 rtAnim->advance();
451 }
452
453 bool canRender = wd->renderer != nullptr;
454
455 if (canRender) {
456 auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
457 if (softwareRenderer)
458 softwareRenderer->setBackingStore(backingStore);
459 wd->renderSceneGraph();
460
461 Q_TRACE(QSG_render_exit);
462 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
463 QQuickProfiler::SceneGraphRenderLoopRender);
464 Q_TRACE(QSG_swap_entry);
465
466 if (softwareRenderer)
467 backingStore->flush(softwareRenderer->flushRegion());
468
469 // Since there is no V-Sync with QBackingStore, throttle rendering the refresh
470 // rate of the current screen the window is on.
471 int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed();
472 if (blockTime > 0) {
473 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - blocking for %d ms", blockTime);
474 msleep(blockTime);
475 }
477
478 wd->fireFrameSwapped();
479 } else {
480 Q_TRACE(QSG_render_exit);
481 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
482 QQuickProfiler::SceneGraphRenderLoopSync, 1);
483 Q_TRACE(QSG_swap_entry);
484 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render");
485 }
486
487 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done");
488
489 emit exposedWindow->afterFrameEnd();
490
491 if (exposeRequested) {
492 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose");
494 mutex.unlock();
495 }
496
497 Q_TRACE(QSG_swap_exit);
498 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
499 QQuickProfiler::SceneGraphRenderLoopSwap);
500}
501
502QSGSoftwareThreadedRenderLoop::WindowData *QSGSoftwareThreadedRenderLoop::windowFor(QQuickWindow *window)
503{
504 for (const auto &t : std::as_const(m_windows)) {
505 if (t.window == window)
506 return const_cast<WindowData *>(&t);
507 }
508 return nullptr;
509}
510
511
513{
514 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor");
515 m_sg = new QSGSoftwareContext;
516 m_anim = m_sg->createAnimationDriver(this);
519 m_anim->install();
520}
521
523{
524 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor");
525 delete m_sg;
526}
527
529{
530 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window;
531}
532
534{
535 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window;
536
537 if (window->isExposed())
538 handleObscurity(windowFor(window));
539
541}
542
544{
545 if (!window->isExposed() || window->size().isEmpty())
546 return;
547
548 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size();
549}
550
552{
553 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window;
554
555 WindowData *w = windowFor(window);
556 if (!w)
557 return;
558
559 handleObscurity(w);
560 handleResourceRelease(w, true);
561
563 while (thread->isRunning())
565
567 delete thread;
568
569 for (int i = 0; i < m_windows.size(); ++i) {
570 if (m_windows.at(i).window == window) {
571 m_windows.removeAt(i);
572 break;
573 }
574 }
575
576 // Now that we altered the window list, we may need to stop the animation
577 // timer even if we didn't via handleObscurity. This covers the case where
578 // we destroy a visible & exposed QQuickWindow.
579 startOrStopAnimationTimer();
580}
581
583{
584 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window;
585
586 if (window->isExposed()) {
587 handleExposure(window);
588 } else {
589 WindowData *w = windowFor(window);
590 if (w)
591 handleObscurity(w);
592 }
593}
594
596{
597 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window;
598
599 WindowData *w = windowFor(window);
600 // Have to support invisible (but created()'ed) windows as well.
601 // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
602 const bool tempExpose = !w;
603 if (tempExpose) {
604 handleExposure(window);
605 w = windowFor(window);
606 Q_ASSERT(w);
607 }
608
609 if (!w->thread->isRunning())
610 return QImage();
611
612 if (!window->handle())
613 window->create();
614
616 wd->polishItems();
617
619 w->thread->mutex.lock();
620 lockedForSync = true;
621 w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result));
622 w->thread->waitCondition.wait(&w->thread->mutex);
623 lockedForSync = false;
624 w->thread->mutex.unlock();
625
626 result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
627
628 if (tempExpose)
629 handleObscurity(w);
630
631 return result;
632}
633
635{
636 WindowData *w = windowFor(window);
637 if (!w)
638 return;
639
640 if (w->thread == QThread::currentThread()) {
641 w->thread->requestRepaint();
642 return;
643 }
644
645 // We set forceRenderPass because we want to make sure the QQuickWindow
646 // actually does a full render pass after the next sync.
647 w->forceRenderPass = true;
648 scheduleUpdate(w);
649}
650
652{
653 WindowData *w = windowFor(window);
654 if (w)
655 scheduleUpdate(w);
656}
657
659{
660 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window;
661
662 WindowData *w = windowFor(window);
663 if (w)
664 polishAndSync(w, false);
665}
666
671
676
681
683{
684 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window;
685
686 WindowData *w = windowFor(window);
687 if (w)
688 handleResourceRelease(w, false);
689}
690
692{
693 WindowData *w = windowFor(window);
694 if (w && w->thread && w->thread->exposedWindow)
695 w->thread->postEvent(new QSGSoftwareJobEvent(window, job));
696 else
697 delete job;
698}
699
704
706{
707 bool somethingVisible = false;
708 for (const WindowData &w : m_windows) {
709 if (w.window->isVisible() && w.window->isExposed()) {
710 somethingVisible = true;
711 break;
712 }
713 }
714 return somethingVisible && m_anim->isRunning();
715}
716
721
723{
724 if (e->type() == QEvent::Timer) {
725 QTimerEvent *te = static_cast<QTimerEvent *>(e);
726 if (te->timerId() == animationTimer) {
727 m_anim->advance();
729 return true;
730 }
731 }
732
733 return QObject::event(e);
734}
735
737{
738 startOrStopAnimationTimer();
739
740 for (const WindowData &w : std::as_const(m_windows))
741 w.window->requestUpdate();
742}
743
745{
746 startOrStopAnimationTimer();
747}
748
749void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer()
750{
751 int exposedWindowCount = 0;
752 const WindowData *exposed = nullptr;
753
754 for (int i = 0; i < m_windows.size(); ++i) {
755 const WindowData &w(m_windows[i]);
756 if (w.window->isVisible() && w.window->isExposed()) {
757 ++exposedWindowCount;
758 exposed = &w;
759 }
760 }
761
762 if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
763 killTimer(animationTimer);
764 animationTimer = 0;
765 // If animations are running, make sure we keep on animating
766 if (m_anim->isRunning())
767 exposed->window->requestUpdate();
768 } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
769 animationTimer = startTimer(qsgrl_animation_interval());
770 }
771}
772
773void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
774{
775 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window;
776
777 WindowData *w = windowFor(window);
778 if (!w) {
779 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list");
780 WindowData win;
781 win.window = window;
782 QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership
783 win.thread = new QSGSoftwareRenderThread(this, rc);
784 win.updateDuringSync = false;
785 win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
786 m_windows.append(win);
787 w = &m_windows.last();
788 }
789
790 // set this early as we'll be rendering shortly anyway and this avoids
791 // special casing exposure in polishAndSync.
792 w->thread->exposedWindow = window;
793
794 if (w->window->size().isEmpty()
795 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
796#ifndef QT_NO_DEBUG
797 qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window "
798 << w->window << " with invalid geometry: " << w->window->geometry()
799 << " on " << w->window->screen();
800#endif
801 }
802
803 if (!w->window->handle())
804 w->window->create();
805
806 // Start render thread if it is not running
807 if (!w->thread->isRunning()) {
808 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread");
809 // Push a few things to the render thread.
810 QQuickAnimatorController *controller
811 = QQuickWindowPrivate::get(w->window)->animationController.get();
812 if (controller->thread() != w->thread)
813 controller->moveToThread(w->thread);
814 if (w->thread->thread() == QThread::currentThread()) {
815 w->thread->rc->moveToThread(w->thread);
816 w->thread->moveToThread(w->thread);
817 }
818
819 w->thread->active = true;
820 w->thread->start();
821
822 if (!w->thread->isRunning())
823 qFatal("Render thread failed to start, aborting application.");
824 }
825
826 polishAndSync(w, true);
827
828 startOrStopAnimationTimer();
829}
830
831void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w)
832{
833 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window;
834
835 if (w->thread->isRunning()) {
836 w->thread->mutex.lock();
837 w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, QEvent::Type(WM_Obscure)));
838 w->thread->waitCondition.wait(&w->thread->mutex);
839 w->thread->mutex.unlock();
840 }
841
842 startOrStopAnimationTimer();
843}
844
845void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w)
846{
848 return;
849
850 if (!w || !w->thread->isRunning())
851 return;
852
853 QThread *current = QThread::currentThread();
854 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
855 qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
856 return;
857 }
858
859 if (current == w->thread) {
860 w->updateDuringSync = true;
861 return;
862 }
863
864 w->window->requestUpdate();
865}
866
867void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying)
868{
869 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window;
870
871 w->thread->mutex.lock();
872 if (w->thread->isRunning() && w->thread->active) {
873 QQuickWindow *window = w->window;
874
875 // Note that window->handle() is typically null by this time because
876 // the platform window is already destroyed. This should not be a
877 // problem for the D3D cleanup.
878
879 w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying));
880 w->thread->waitCondition.wait(&w->thread->mutex);
881
882 // Avoid a shutdown race condition.
883 // If SG is invalidated and 'active' becomes false, the thread's run()
884 // method will exit. handleExposure() relies on QThread::isRunning() (because it
885 // potentially needs to start the thread again) and our mutex cannot be used to
886 // track the thread stopping, so we wait a few nanoseconds extra so the thread
887 // can exit properly.
888 if (!w->thread->active)
889 w->thread->wait();
890 }
891 w->thread->mutex.unlock();
892}
893
894void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose)
895{
896 qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
897
898 QQuickWindow *window = w->window;
899 if (!w->thread || !w->thread->exposedWindow) {
900 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort");
901 return;
902 }
903
904 // Flush pending touch events.
905 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
906 // The delivery of the event might have caused the window to stop rendering
907 w = windowFor(window);
908 if (!w || !w->thread || !w->thread->exposedWindow) {
909 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort");
910 return;
911 }
912
913 Q_TRACE_SCOPE(QSG_polishAndSync);
914
915 Q_TRACE(QSG_polishItems_entry);
916 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
917
919 wd->polishItems();
920
921 Q_TRACE(QSG_polishItems_exit);
922 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
923 QQuickProfiler::SceneGraphPolishAndSyncPolish);
924 Q_TRACE(QSG_sync_entry);
925
926 w->updateDuringSync = false;
927
928 emit window->afterAnimating();
929
930 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync");
931 w->thread->mutex.lock();
932 lockedForSync = true;
933 w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass));
934 w->forceRenderPass = false;
935
936 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync");
937
938 Q_TRACE(QSG_sync_exit);
939 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
940 QQuickProfiler::SceneGraphPolishAndSyncWait);
941 Q_TRACE(QSG_wait_entry);
942 w->thread->waitCondition.wait(&w->thread->mutex);
943 lockedForSync = false;
944 w->thread->mutex.unlock();
945 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync");
946
947 Q_TRACE(QSG_wait_exit);
948 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
949 QQuickProfiler::SceneGraphPolishAndSyncSync);
950 Q_TRACE(QSG_animations_entry);
951
952 if (!animationTimer && m_anim->isRunning()) {
953 qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations");
954 m_anim->advance();
955 // We need to trigger another sync to keep animations running...
956 w->window->requestUpdate();
958 } else if (w->updateDuringSync) {
959 w->window->requestUpdate();
960 }
961
962 Q_TRACE(QSG_animations_exit);
963 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
964 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
965}
966
968
969#include "qsgsoftwarethreadedrenderloop.moc"
970#include "moc_qsgsoftwarethreadedrenderloop_p.cpp"
\inmodule QtCore
void install()
Installs this animation driver.
void started()
This signal is emitted by the animation framework to notify the driver that continuous animation has ...
virtual void advance()
Advances the animation.
void stopped()
This signal is emitted by the animation framework to notify the driver that continuous animation has ...
The QBackingStore class provides a drawing area for QWindow.
QPlatformBackingStore * handle() const
Returns a pointer to the QPlatformBackingStore implementation.
void flush(const QRegion &region, QWindow *window=nullptr, const QPoint &offset=QPoint())
Flushes the given region from the specified window onto the screen.
void resize(const QSize &size)
Sets the size of the window surface to size.
QSize size() const
Returns the current size of the window surface.
static void processEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Processes some pending events for the calling thread according to the specified flags.
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
static void sendPostedEvents(QObject *receiver=nullptr, int event_type=0)
Immediately dispatches all events which have been previously queued with QCoreApplication::postEvent(...
\inmodule QtCore
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
\inmodule QtCore
Definition qcoreevent.h:45
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
@ DeferredDelete
Definition qcoreevent.h:100
Type type() const
Returns the event type.
Definition qcoreevent.h:304
QScreen * primaryScreen
the primary (or default) screen of the application.
\inmodule QtGui
Definition qimage.h:37
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
T & last()
Definition qlist.h:648
void removeAt(qsizetype i)
Definition qlist.h:590
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmutex.h:281
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:289
void lock() noexcept
Locks the mutex.
Definition qmutex.h:286
int startTimer(int interval, Qt::TimerType timerType=Qt::CoarseTimer)
This is an overloaded function that will start a timer of type timerType and a timeout of interval mi...
Definition qobject.cpp:1817
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1389
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
bool moveToThread(QThread *thread QT6_DECL_NEW_OVERLOAD_TAIL)
Changes the thread affinity for this object and its children and returns true on success.
Definition qobject.cpp:1643
void killTimer(int id)
Kills the timer with timer identifier, id.
Definition qobject.cpp:1912
virtual QImage toImage() const
Implemented in subclasses to return the content of the backingstore as a QImage.
\inmodule QtCore
Definition qqueue.h:14
void enqueue(const QEvent * &t)
Adds value t to the tail of the queue.
Definition qqueue.h:18
QEvent * dequeue()
Removes the head item in the queue and returns it.
Definition qqueue.h:19
static QQuickWindowPrivate * get(QQuickWindow *c)
QSGRenderer * renderer
QScopedPointer< QQuickAnimatorController > animationController
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore
Definition qrunnable.h:18
void sceneGraphChanged()
This signal is emitted on the first modification of a node in the tree after the last scene render.
The QSGContext holds the scene graph entry points for one QML engine.
virtual QAnimationDriver * createAnimationDriver(QObject *parent)
Creates a new animation driver.
virtual void initialize(const InitParams *params)
virtual void endSync()
QSGContext * sceneGraphContext() const
void timeToIncubate()
void clearChangedFlag()
QSGRenderContext * createRenderContext() override
QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
QSGSoftwareThreadedRenderLoop * renderLoop
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext)
void setBackingStore(QBackingStore *backingStore)
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
QSGRenderContext * createRenderContext(QSGContext *) const override
void postJob(QQuickWindow *window, QRunnable *job) override
void show(QQuickWindow *window) override
QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type)
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
qreal refreshRate
the approximate vertical refresh rate of the screen in Hz
Definition qscreen.h:64
\inmodule QtCore
Definition qsize.h:25
SurfaceType
The SurfaceType enum describes what type of surface this is.
Definition qsurface.h:30
@ RasterSurface
Definition qsurface.h:31
bool isRunning() const
Definition qthread.cpp:1064
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qthread.cpp:1029
static QThread * currentThread()
Definition qthread.cpp:1039
static void yieldCurrentThread()
Definition qthread.cpp:1054
static void msleep(unsigned long)
\inmodule QtCore
Definition qcoreevent.h:366
QWidget * window() const
Returns the window for this widget, i.e.
Definition qwidget.cpp:4313
Combined button and popup list for selecting options.
@ DirectConnection
Definition image.cpp:4
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
#define qCDebug(category,...)
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum condition
GLenum type
struct _cl_event * event
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
#define Q_QUICK_SG_PROFILE_END(Type, position)
#define Q_QUICK_SG_PROFILE_SKIP(Type, position, Skip)
#define Q_QUICK_SG_PROFILE_RECORD(Type, position)
#define Q_QUICK_SG_PROFILE_START(Type)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
@ WM_Obscure
@ WM_Grab
@ WM_PostJob
@ WM_TryRelease
@ WM_RequestSync
static int qsgrl_animation_interval()
#define Q_OBJECT
#define slots
#define emit
#define Q_TRACE_SCOPE(x,...)
Definition qtrace_p.h:146
#define Q_TRACE(x,...)
Definition qtrace_p.h:144
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QWidget * win
Definition settings.cpp:6
aWidget window() -> setWindowTitle("New Window Title")
[2]
static void registerAnimationCallback()
Definition moc.h:23