6#include <QtCore/QMutex>
7#include <QtCore/QWaitCondition>
8#include <QtCore/QAnimationDriver>
9#include <QtCore/QQueue>
10#include <QtCore/QTimer>
12#include <QtGui/QGuiApplication>
13#include <QtGui/QScreen>
14#include <QtGui/QOffscreenSurface>
16#include <qpa/qwindowsysteminterface.h>
18#include <QtQuick/QQuickWindow>
19#include <private/qquickwindow_p.h>
20#include <private/qquickitem_p.h>
21#include <QtGui/qpa/qplatformwindow_p.h>
23#include <QtQuick/private/qsgrenderer_p.h>
27#include <private/qquickanimatorcontroller_p.h>
29#include <private/qquickprofiler_p.h>
30#include <private/qqmldebugserviceinterfaces_p.h>
31#include <private/qqmldebugconnector_p.h>
33#include <private/qsgrhishadereffectnode_p.h>
34#include <private/qsgdefaultrendercontext_p.h>
36#include <qtquick_tracepoints_p.h>
39#include <QtCore/private/qcore_mac_p.h>
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
89Q_TRACE_POINT(qtquick, QSG_polishAndSync_entry)
90Q_TRACE_POINT(qtquick, QSG_polishAndSync_exit)
91Q_TRACE_POINT(qtquick, QSG_wait_entry)
92Q_TRACE_POINT(qtquick, QSG_wait_exit)
93Q_TRACE_POINT(qtquick, QSG_syncAndRender_entry)
94Q_TRACE_POINT(qtquick, QSG_syncAndRender_exit)
95Q_TRACE_POINT(qtquick, QSG_animations_entry)
96Q_TRACE_POINT(qtquick, QSG_animations_exit)
98#define QSG_RT_PAD " (RT) %s"
100extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(
const QSize &size,
bool alpha_format,
bool include_alpha);
108 for (
const auto &t : std::as_const(m_windows)) {
109 if (t.window == window)
110 return const_cast<Window *>(&t);
141 ,
dpr(
float(c->effectiveDevicePixelRatio()))
196 if (size() == 0 && wait) {
198 condition.wait(&mutex);
201 QEvent *e = dequeue();
208 bool has = !isEmpty();
215 QWaitCondition condition;
238#if defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY)
263 pendingUpdate |= RepaintRequest;
323 switch ((
int) e->type()) {
326 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Obscure");
332 QQuickWindowPrivate::get(window)->fireAboutToStop();
333 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window removed");
336 waitCondition.wakeOne();
343 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Exposed");
347 waitCondition.wakeOne();
353 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_RequestSync");
358 windowSize = se->size;
362 pendingUpdate |= SyncRequest;
364 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- triggered from expose");
365 pendingUpdate |= ExposeRequest;
368 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- repaint regardless");
369 pendingUpdate |= RepaintRequest;
374 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_TryRelease");
376 wm->m_lockedForSync =
true;
379 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- setting exit flag and invalidating");
382 Q_ASSERT_X(!wme
->inDestructor || !
active,
"QSGRenderThread::invalidateGraphics()",
"Thread's active state is not set to false when shutting down");
386 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- not releasing because window is still active");
388 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
389 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting external renderers such as Quick 3D to release cached resources");
390 emit d->context->releaseCachedResourcesRequested();
392 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting renderer to release cached resources");
393 d->renderer->releaseCachedResources();
395#if QT_CONFIG(quick_shadereffect)
396 QSGRhiShaderEffectNode::garbageCollectMaterialTypeCache(window);
400 waitCondition.wakeOne();
401 wm->m_lockedForSync =
false;
407 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Grab");
414 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(ce->window);
418 cd->rhi->beginFrame(cd->swapchain);
419 cd->rhi->makeThreadLocalNativeContextCurrent();
420 cd->syncSceneGraph();
422 cd->renderSceneGraph();
423 *ce->image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer());
424 cd->rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
426 ce->image->setDevicePixelRatio(ce
->window->effectiveDevicePixelRatio());
428 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- waking gui to handle result");
429 waitCondition.wakeOne();
435 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_PostJob");
440 rhi->makeThreadLocalNativeContextCurrent();
444 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- job done");
450 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_ReleaseSwapchain");
457 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- swapchain released");
459 waitCondition.wakeOne();
467 return QThread::event(e);
472 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"invalidateGraphics()");
478 qCWarning(QSG_LOG_RENDERLOOP,
"QSGThreadedRenderLoop:QSGRenderThread: no window to make current...");
482 bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
483 bool wipeGraphics = inDestructor || (wipeSG && !window->isPersistentGraphics());
485 rhi->makeThreadLocalNativeContextCurrent();
487 QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
491 dd->cleanupNodesOnShutdown();
492#if QT_CONFIG(quick_shadereffect)
493 QSGRhiShaderEffectNode::resetMaterialTypeCache(window);
496 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent SG, avoiding cleanup");
501 QCoreApplication::processEvents();
502 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
504 dd->animationController.reset();
506 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- invalidating scene graph");
510 if (window->handle()) {
513 wm->releaseSwapchain(window);
515 qWarning(
"QSGThreadedRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
516 window, dd->swapchain);
520 QSGRhiSupport::instance()->destroyRhi(rhi, dd->graphicsConfig);
523 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- QRhi destroyed");
525 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent GL, avoiding cleanup");
530
531
532
535 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"sync()");
538 Q_ASSERT_X(
wm->m_lockedForSync,
"QSGRenderThread::sync()",
"sync triggered on bad terms as gui is not already locked...");
542 if (windowSize.width() > 0 && windowSize.height() > 0) {
548 rhi->makeThreadLocalNativeContextCurrent();
558 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
559 bool hadRenderer = d->renderer !=
nullptr;
563 d->renderer->clearChangedFlag();
566 if (!hadRenderer && d->renderer) {
567 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- renderer was created");
569 connect(d->renderer, SIGNAL(sceneGraphChanged()),
this, SLOT(sceneGraphChanged()), Qt::DirectConnection);
575 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
577 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window has bad size, sync aborted");
586 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- sync complete, waking Gui");
587 waitCondition.wakeOne();
594 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
595 wd->cleanupNodesOnShutdown();
599 QSGRhiSupport::instance()->destroyRhi(rhi, {});
605 if (!
rhi || !
rhi->isDeviceLost())
608 qWarning(
"Graphics device lost, cleaning up scenegraph and releasing RHI");
615 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
616 QElapsedTimer threadTimer;
617 qint64 syncTime = 0, renderTime = 0;
620 Q_TRACE_SCOPE(QSG_syncAndRender);
621 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
622 Q_TRACE(QSG_sync_entry);
624 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"syncAndRender()");
627 const qint64 elapsedSinceLastMs = m_threadTimeBetweenRenders.restart();
628 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: start, elapsed since last call: %d ms",
630 QThread::currentThread(),
631 int(elapsedSinceLastMs));
635 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
637 const bool syncRequested = (pendingUpdate & SyncRequest);
638 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
641 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
642 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
647 const bool hasValidSwapChain = (cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0);
648 if (hasValidSwapChain) {
651 const QSize effectiveOutputSize = cd->swapchain->surfacePixelSize();
655 if (effectiveOutputSize.isEmpty()) {
658 waitCondition.wakeOne();
664 const QSize previousOutputSize = cd->swapchain->currentPixelSize();
665 if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
666 if (cd->swapchainJustBecameRenderable)
667 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"just became exposed");
669 cd->hasActiveSwapchain = cd->swapchain->createOrResize();
670 if (!cd->hasActiveSwapchain) {
671 bool bailOut =
false;
672 if (
rhi->isDeviceLost()) {
675 }
else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
676 qWarning(
"Failed to create swapchain."
677 " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
683 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
686 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed swapchain init, wake Gui");
688 waitCondition.wakeOne();
695 cd->swapchainJustBecameRenderable =
false;
696 cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
698 if (!cd->hasActiveSwapchain)
699 qWarning(
"Failed to build or resize swapchain");
701 qCDebug(QSG_LOG_RENDERLOOP) <<
"rhi swapchain size" << cd->swapchain->currentPixelSize();
704 emit window->beforeFrameBegin();
706 Q_ASSERT(
rhi == cd->rhi);
707 QRhi::FrameOpResult frameResult =
rhi->beginFrame(cd->swapchain);
708 if (frameResult != QRhi::FrameOpSuccess) {
709 if (frameResult == QRhi::FrameOpDeviceLost)
711 else if (frameResult == QRhi::FrameOpError)
712 qWarning(
"Failed to start frame");
714 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
715 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
720 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed beginFrame, wake Gui");
723 waitCondition.wakeOne();
726 emit window->afterFrameEnd();
732 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- updatePending, doing sync");
735#ifndef QSG_NO_RENDER_TIMING
737 syncTime = threadTimer.nsecsElapsed();
739 Q_TRACE(QSG_sync_exit);
740 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
741 QQuickProfiler::SceneGraphRenderLoopSync);
754 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering started");
756 Q_TRACE(QSG_render_entry);
766 d->animationController->lock();
768 d->animationController->unlock();
773 const bool canRender = d->renderer && hasValidSwapChain;
774 double lastCompletedGpuTime = 0;
777 rhi->makeThreadLocalNativeContextCurrent();
779 d->renderSceneGraph();
782 renderTime = threadTimer.nsecsElapsed();
783 Q_TRACE(QSG_render_exit);
784 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
785 QQuickProfiler::SceneGraphRenderLoopRender);
786 Q_TRACE(QSG_swap_entry);
788 QRhi::FrameOpResult frameResult =
rhi->endFrame(cd->swapchain);
789 if (frameResult != QRhi::FrameOpSuccess) {
790 if (frameResult == QRhi::FrameOpDeviceLost)
792 else if (frameResult == QRhi::FrameOpError)
793 qWarning(
"Failed to end frame");
794 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
795 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
797 lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
799 d->fireFrameSwapped();
801 Q_TRACE(QSG_render_exit);
802 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
803 QQuickProfiler::SceneGraphRenderLoopSync, 1);
804 Q_TRACE(QSG_swap_entry);
805 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window not ready, skipping render");
809 if (cd->swapchain && rhi->isRecordingFrame())
810 rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
813 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering done");
817 if (hasValidSwapChain)
818 emit window->afterFrameEnd();
825 if (exposeRequested) {
827 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- wake Gui after expose");
828 waitCondition.wakeOne();
840 qCDebug(QSG_LOG_TIME_RENDERLOOP,
841 "[window %p][render thread %p] syncAndRender: frame rendered in %dms, sync=%d, render=%d, swap=%d",
843 QThread::currentThread(),
844 int(threadTimer.elapsed()),
845 int((syncTime/1000000)),
846 int((renderTime - syncTime) / 1000000),
847 int((threadTimer.nsecsElapsed() - renderTime) / 1000000));
848 if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
849 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: last retrieved GPU frame time was %.4f ms",
851 QThread::currentThread(),
852 lastCompletedGpuTime * 1000.0);
856 Q_TRACE(QSG_swap_exit);
857 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
858 QQuickProfiler::SceneGraphRenderLoopSwap);
865 eventQueue.addEvent(e);
872 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEvents()");
873 while (eventQueue.hasMoreEvents()) {
874 QEvent *e = eventQueue.takeEvent(
false);
878 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEvents()");
883 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEventsAndWaitForMore()");
886 QEvent *e = eventQueue.takeEvent(
true);
890 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEventsAndWaitForMore()");
898 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
900 QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
905 rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
909 qWarning(
"Failed to create QRhi on the render thread; scenegraph is not functional");
915 if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
918 rhi->makeThreadLocalNativeContextCurrent();
919 QSGDefaultRenderContext::InitParams rcParams;
922 rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
923 rcParams.maybeSurface =
window;
924 sgrc->initialize(&rcParams);
926 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
927 if (
rhi && !cd->swapchain) {
929 QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource;
930 const QSurfaceFormat requestedFormat =
window->requestedFormat();
936 const bool alpha = requestedFormat.alphaBufferSize() > 0;
938 flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
943 if (requestedFormat.swapInterval() == 0) {
944 qCDebug(QSG_LOG_INFO,
"Swap interval is 0, attempting to disable vsync when presenting.");
945 flags |= QRhiSwapChain::NoVSync;
948 cd->swapchain =
rhi->newSwapChain();
949 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
950 if (depthBufferEnabled) {
951 cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
954 QRhiRenderBuffer::UsedWithSwapChainOnly);
955 cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
957 cd->swapchain->setWindow(
window);
959 QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain, window);
960 qCDebug(QSG_LOG_INFO,
"MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
961 rhiSampleCount, alpha ?
"yes" :
"no");
963 cd->swapchain->setFlags(flags);
964 cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
965 cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
971 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run()");
974 if (QQmlDebugConnector::service<QQmlProfilerService>())
975 QQuickProfiler::registerAnimationCallback();
977 m_threadTimeBetweenRenders.start();
981 QMacAutoReleasePool frameReleasePool;
998 QEvent *e =
new QEvent(QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure));
999 QCoreApplication::postEvent(window, e);
1004 QCoreApplication::processEvents();
1006 if (active && (pendingUpdate == 0 || !window)) {
1007 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"done drawing, sleep...");
1014 Q_ASSERT_X(!
rhi,
"QSGRenderThread::run()",
"The graphics context should be cleaned up before exiting the render thread...");
1016 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run() completed");
1021 sgrc->moveToThread(
wm->thread());
1022 moveToThread(
wm->thread());
1026 : sg(QSGContext::createDefaultContext())
1027 , m_animation_timer(0)
1029 m_animation_driver = sg->createAnimationDriver(
this);
1031 connect(m_animation_driver, SIGNAL(started()),
this, SLOT(animationStarted()));
1034 m_animation_driver->install();
1039 qDeleteAll(pendingRenderContexts);
1045 auto context = sg->createRenderContext();
1046 pendingRenderContexts.insert(context);
1052 w->window->requestUpdate();
1057 return m_animation_driver;
1067 for (
int i=0; i<m_windows.size(); ++i) {
1068 QQuickWindow *c = m_windows.at(i).window;
1069 if (c->isVisible() && c->isExposed())
1077 return m_animation_driver->isRunning() && anyoneShowing();
1082 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStarted()");
1083 startOrStopAnimationTimer();
1085 for (
int i=0; i<m_windows.size(); ++i)
1086 postUpdateRequest(
const_cast<Window *>(&m_windows.at(i)));
1091 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStopped()");
1092 startOrStopAnimationTimer();
1098 if (!sg->isVSyncDependent(m_animation_driver))
1101 int exposedWindows = 0;
1102 int unthrottledWindows = 0;
1104 const Window *theOne =
nullptr;
1105 for (
int i=0; i<m_windows.size(); ++i) {
1106 const Window &w = m_windows.at(i);
1107 if (w.window->isVisible() && w.window->isExposed()) {
1110 if (w.actualWindowFormat.swapInterval() == 0)
1111 ++unthrottledWindows;
1137 const bool canUseVSyncBasedAnimation = exposedWindows == 1 && unthrottledWindows == 0 && badVSync == 0;
1139 if (m_animation_timer != 0 && (canUseVSyncBasedAnimation || !m_animation_driver->isRunning())) {
1140 qCDebug(QSG_LOG_RENDERLOOP,
"*** Stopping system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1141 exposedWindows, unthrottledWindows, badVSync);
1142 killTimer(m_animation_timer);
1143 m_animation_timer = 0;
1145 if (m_animation_driver->isRunning())
1146 postUpdateRequest(
const_cast<Window *>(theOne));
1147 }
else if (m_animation_timer == 0 && !canUseVSyncBasedAnimation && m_animation_driver->isRunning()) {
1148 qCDebug(QSG_LOG_RENDERLOOP,
"*** Starting system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1149 exposedWindows, unthrottledWindows, badVSync);
1150 m_animation_timer = startTimer(
int(sg->vsyncIntervalForAnimationDriver(m_animation_driver)));
1155
1156
1157
1158
1159
1160
1161
1162
1163
1167 qCDebug(QSG_LOG_RENDERLOOP) <<
"hide()" << window;
1169 if (window->isExposed())
1170 handleObscurity(windowFor(window));
1177 qCDebug(QSG_LOG_RENDERLOOP) <<
"reisze()" << window;
1179 Window *w = windowFor(window);
1183 w->psTimeAccumulator = 0.0f;
1184 w->psTimeSampleCount = 0;
1188
1189
1190
1191
1194 qCDebug(QSG_LOG_RENDERLOOP) <<
"begin windowDestroyed()" << window;
1196 Window *w = windowFor(window);
1201 releaseResources(w,
true);
1204 while (thread->isRunning())
1205 QThread::yieldCurrentThread();
1206 Q_ASSERT(thread->thread() == QThread::currentThread());
1209 for (
int i=0; i<m_windows.size(); ++i) {
1210 if (m_windows.at(i).window == window) {
1211 m_windows.removeAt(i);
1219 startOrStopAnimationTimer();
1221 qCDebug(QSG_LOG_RENDERLOOP) <<
"done windowDestroyed()" << window;
1226 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1227 delete wd->rpDescForSwapchain;
1228 wd->rpDescForSwapchain =
nullptr;
1229 delete wd->swapchain;
1230 wd->swapchain =
nullptr;
1231 delete wd->depthStencilForSwapchain;
1232 wd->depthStencilForSwapchain =
nullptr;
1233 wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable =
false;
1238 qCDebug(QSG_LOG_RENDERLOOP) <<
"exposureChanged()" << window;
1246 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1247 if (!window->isExposed())
1248 wd->hasRenderableSwapchain =
false;
1250 bool skipThisExpose =
false;
1251 if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
1252 wd->hasRenderableSwapchain =
false;
1253 skipThisExpose =
true;
1256 if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
1257 && !wd->swapchain->surfacePixelSize().isEmpty())
1259 wd->hasRenderableSwapchain =
true;
1260 wd->swapchainJustBecameRenderable =
true;
1263 if (window->isExposed()) {
1264 if (!skipThisExpose)
1265 handleExposure(window);
1267 Window *w = windowFor(window);
1274
1275
1276
1279 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleExposure()" << window;
1281 Window *w = windowFor(window);
1283 qCDebug(QSG_LOG_RENDERLOOP,
"- adding window to list");
1285 win.window = window;
1286 win.actualWindowFormat = window->format();
1287 auto renderContext = QQuickWindowPrivate::get(window)->context;
1289 pendingRenderContexts.remove(renderContext);
1291 win.updateDuringSync =
false;
1292 win.forceRenderPass =
true;
1293 win.badVSync =
false;
1294 win.timeBetweenPolishAndSyncs.start();
1295 win.psTimeAccumulator = 0.0f;
1296 win.psTimeSampleCount = 0;
1298 w = &m_windows.last();
1300 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1301 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1307 if (w->window->width() <= 0 || w->window->height() <= 0
1308 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
1309 qWarning().noquote().nospace() <<
"QSGThreadedRenderLoop: expose event received for window "
1310 << w->window <<
" with invalid geometry: " << w->window->geometry()
1311 <<
" on " << w->window->screen();
1317 if (!w->window->handle())
1318 w->window->create();
1321 if (!w->thread->isRunning()) {
1322 qCDebug(QSG_LOG_RENDERLOOP,
"- starting render thread");
1328 if (!w->thread
->rhi) {
1329 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
1330 if (!w->thread->offscreenSurface)
1331 w->thread->offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
1332 w->thread->scProxyData = QRhi::updateSwapChainProxyData(rhiSupport->rhiBackend(), window);
1333 window->installEventFilter(
this);
1336 QQuickAnimatorController *controller
1337 = QQuickWindowPrivate::get(w->window)->animationController.get();
1338 if (controller->thread() != w->thread)
1339 controller->moveToThread(w->thread);
1342 if (w->thread->thread() == QThread::currentThread()) {
1343 w->thread
->sgrc->moveToThread(w->thread);
1344 w->thread->moveToThread(w->thread);
1347 if (!w->thread->isRunning())
1348 qFatal(
"Render thread failed to start, aborting application.");
1351 qCDebug(QSG_LOG_RENDERLOOP,
"- render thread already running");
1354 w->thread->mutex.lock();
1355 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
1356 w->thread->waitCondition.wait(&w->thread->mutex);
1357 w->thread->mutex.unlock();
1360 polishAndSync(w,
true);
1361 qCDebug(QSG_LOG_RENDERLOOP,
"- done with handleExposure()");
1363 startOrStopAnimationTimer();
1367
1368
1369
1370
1371
1372
1378 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleObscurity()" << w->window;
1379 if (w->thread->isRunning()) {
1380 if (!QQuickWindowPrivate::get(w->window)->updatesEnabled) {
1381 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1384 w->thread->mutex.lock();
1385 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Obscure)));
1386 w->thread->waitCondition.wait(&w->thread->mutex);
1387 w->thread->mutex.unlock();
1389 startOrStopAnimationTimer();
1394 switch (event->type()) {
1395 case QEvent::PlatformSurface:
1397 if (
static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1398 QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
1400 Window *w = windowFor(window);
1401 if (w && w->thread->isRunning()) {
1402 w->thread->mutex.lock();
1404 w->thread->waitCondition.wait(&w->thread->mutex);
1405 w->thread->mutex.unlock();
1415 return QObject::eventFilter(watched, event);
1420 qCDebug(QSG_LOG_RENDERLOOP) <<
"- update request" << window;
1421 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1422 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1425 Window *w = windowFor(window);
1432 Window *w = windowFor(window);
1438
1439
1440
1443 if (!QCoreApplication::instance())
1446 if (!w || !w->thread->isRunning())
1449 QThread *current = QThread::currentThread();
1450 if (current == w->thread && w->thread
->rhi && w->thread
->rhi->isDeviceLost())
1452 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
1453 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
1457 qCDebug(QSG_LOG_RENDERLOOP) <<
"update from item" << w->window;
1461 if (current == w->thread) {
1462 qCDebug(QSG_LOG_RENDERLOOP,
"- on render thread");
1463 w->updateDuringSync =
true;
1474 postUpdateRequest(w);
1478
1479
1480
1481
1484 Window *w = windowFor(window);
1488 const bool isRenderThread = QThread::currentThread() == w->thread;
1490 if (QPlatformWindow *platformWindow = window->handle()) {
1494 if (isRenderThread && !platformWindow->allowsIndependentThreadedRendering()) {
1498 qCDebug(QSG_LOG_RENDERLOOP) <<
"window is resizing. update on window" << w->window;
1499 QTimer::singleShot(0, window, [=]{ window->requestUpdate(); });
1504 if (isRenderThread) {
1505 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window - on render thread" << w->window;
1510 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window" << w->window;
1513 w->forceRenderPass =
true;
1520 Window *w = windowFor(window);
1522 releaseResources(w,
false);
1526
1527
1528
1531 qCDebug(QSG_LOG_RENDERLOOP) <<
"releaseResources()" << (inDestructor ?
"in destructor" :
"in api-call") << w->window;
1533 w->thread->mutex.lock();
1534 if (w->thread->isRunning() && w->thread
->active) {
1535 QQuickWindow *window = w->window;
1542 qCDebug(QSG_LOG_RENDERLOOP,
"- posting release request to render thread");
1543 w->thread->postEvent(
new WMTryReleaseEvent(window, inDestructor, window->handle() ==
nullptr));
1544 w->thread->waitCondition.wait(&w->thread->mutex);
1553 qCDebug(QSG_LOG_RENDERLOOP) <<
" - waiting for render thread to exit" << w->window;
1555 qCDebug(QSG_LOG_RENDERLOOP) <<
" - render thread finished" << w->window;
1558 w->thread->mutex.unlock();
1563
1564
1567 qCDebug(QSG_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
1569 QQuickWindow *window = w->window;
1570 if (!w->thread || !w->thread
->window) {
1571 qCDebug(QSG_LOG_RENDERLOOP,
"- not exposed, abort");
1576 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
1578 w = windowFor(window);
1579 if (!w || !w->thread || !w->thread
->window) {
1580 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after event flushing, abort");
1584 Q_TRACE_SCOPE(QSG_polishAndSync);
1585 QElapsedTimer timer;
1586 qint64 polishTime = 0;
1587 qint64 waitTime = 0;
1588 qint64 syncTime = 0;
1590 const qint64 elapsedSinceLastMs = w->timeBetweenPolishAndSyncs.restart();
1592 if (w->actualWindowFormat.swapInterval() != 0 && sg->isVSyncDependent(m_animation_driver)) {
1593 w->psTimeAccumulator += elapsedSinceLastMs;
1594 w->psTimeSampleCount += 1;
1596 static const int PS_TIME_SAMPLE_LENGTH = 20;
1597 if (w->psTimeSampleCount > PS_TIME_SAMPLE_LENGTH) {
1598 const float t = w->psTimeAccumulator / w->psTimeSampleCount;
1599 const float vsyncRate = sg->vsyncIntervalForAnimationDriver(m_animation_driver);
1619 const float threshold = vsyncRate * 0.5f;
1620 const bool badVSync = t < threshold;
1621 if (badVSync && !w->badVSync) {
1632 qCDebug(QSG_LOG_INFO,
"Window %p is determined to have broken vsync throttling (%f < %f) "
1633 "switching to system timer to drive gui thread animations to remedy this "
1634 "(however, render thread animators will likely advance at an incorrect rate).",
1635 w->window, t, threshold);
1636 startOrStopAnimationTimer();
1639 w->psTimeAccumulator = 0.0f;
1640 w->psTimeSampleCount = 0;
1644 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
1645 if (profileFrames) {
1647 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] polishAndSync: start, elapsed since last call: %d ms",
1649 int(elapsedSinceLastMs));
1651 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
1652 Q_TRACE(QSG_polishItems_entry);
1654 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1660 polishTime = timer.nsecsElapsed();
1661 Q_TRACE(QSG_polishItems_exit);
1662 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1663 QQuickProfiler::SceneGraphPolishAndSyncPolish);
1665 w = windowFor(window);
1666 if (!w || !w->thread || !w->thread
->window) {
1667 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after polishing, abort");
1671 Q_TRACE(QSG_wait_entry);
1672 w->updateDuringSync =
false;
1674 emit window->afterAnimating();
1677 QRhi::updateSwapChainProxyData(QSGRhiSupport::instance()->rhiBackend(), window);
1679 qCDebug(QSG_LOG_RENDERLOOP,
"- lock for sync");
1680 w->thread->mutex.lock();
1681 m_lockedForSync =
true;
1682 w->thread->postEvent(
new WMSyncEvent(window, inExpose, w->forceRenderPass, scProxyData));
1683 w->forceRenderPass =
false;
1685 qCDebug(QSG_LOG_RENDERLOOP,
"- wait for sync");
1687 waitTime = timer.nsecsElapsed();
1688 Q_TRACE(QSG_wait_exit);
1689 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1690 QQuickProfiler::SceneGraphPolishAndSyncWait);
1691 Q_TRACE(QSG_sync_entry);
1693 w->thread->waitCondition.wait(&w->thread->mutex);
1694 m_lockedForSync =
false;
1695 w->thread->mutex.unlock();
1696 qCDebug(QSG_LOG_RENDERLOOP,
"- unlock after sync");
1699 syncTime = timer.nsecsElapsed();
1700 Q_TRACE(QSG_sync_exit);
1701 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1702 QQuickProfiler::SceneGraphPolishAndSyncSync);
1703 Q_TRACE(QSG_animations_entry);
1712 if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
1713 auto advanceAnimations = [
this, window=QPointer(window)] {
1714 qCDebug(QSG_LOG_RENDERLOOP,
"- advancing animations");
1715 m_animation_driver->advance();
1716 qCDebug(QSG_LOG_RENDERLOOP,
"- animations done..");
1731 window->requestUpdate();
1733 emit timeToIncubate();
1736#if defined(Q_OS_APPLE)
1747 QMetaObject::invokeMethod(
this, advanceAnimations, Qt::QueuedConnection);
1752 advanceAnimations();
1754 }
else if (w->updateDuringSync) {
1755 postUpdateRequest(w);
1758 if (profileFrames) {
1759 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] Frame prepared, polish=%d ms, lock=%d ms, blockedForSync=%d ms, animations=%d ms",
1761 int(polishTime / 1000000),
1762 int((waitTime - polishTime) / 1000000),
1763 int((syncTime - waitTime) / 1000000),
1764 int((timer.nsecsElapsed() - syncTime) / 1000000));
1767 Q_TRACE(QSG_animations_exit);
1768 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
1769 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
1774 switch ((
int) e->type()) {
1776 case QEvent::Timer: {
1777 Q_ASSERT(sg->isVSyncDependent(m_animation_driver));
1778 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
1779 if (te->timerId() == m_animation_timer) {
1780 qCDebug(QSG_LOG_RENDERLOOP,
"- ticking non-render thread timer");
1781 m_animation_driver->advance();
1782 emit timeToIncubate();
1792 return QObject::event(e);
1798
1799
1800
1801
1802
1803
1804
1805
1809 qCDebug(QSG_LOG_RENDERLOOP) <<
"grab()" << window;
1811 Window *w = windowFor(window);
1814 if (!w->thread->isRunning())
1817 if (!window->handle())
1820 qCDebug(QSG_LOG_RENDERLOOP,
"- polishing items");
1821 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1827 w->thread->mutex.lock();
1828 m_lockedForSync =
true;
1829 qCDebug(QSG_LOG_RENDERLOOP,
"- posting grab event");
1830 w->thread->postEvent(
new WMGrabEvent(window, &result));
1831 w->thread->waitCondition.wait(&w->thread->mutex);
1832 m_lockedForSync =
false;
1833 w->thread->mutex.unlock();
1835 qCDebug(QSG_LOG_RENDERLOOP,
"- grab complete");
1841
1842
1843
1846 Window *w = windowFor(window);
1847 if (w && w->thread && w->thread
->window)
1855#include "qsgthreadedrenderloop.moc"
1856#include "moc_qsgthreadedrenderloop_p.cpp"
QSGRenderThreadEventQueue()
QEvent * takeEvent(bool wait)
QElapsedTimer m_threadTimeBetweenRenders
QOffscreenSurface * offscreenSurface
QSGRenderThreadEventQueue eventQueue
bool event(QEvent *) override
This virtual function receives events to an object and should return true if the event e was recogniz...
void postEvent(QEvent *e)
QSGDefaultRenderContext * sgrc
void processEventsAndWaitForMore()
bool syncResultedInChanges
QRhiSwapChainProxyData scProxyData
bool guiNotifiedAboutRhiFailure
QSGThreadedRenderLoop * wm
QAnimationDriver * animatorDriver
bool swRastFallbackDueToSwapchainFailure
QWaitCondition waitCondition
bool interleaveIncubation() const override
bool event(QEvent *) override
This virtual function receives events to an object and should return true if the event e was recogniz...
QImage grab(QQuickWindow *) override
QSGRenderContext * createRenderContext(QSGContext *) const override
bool eventFilter(QObject *watched, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
void postJob(QQuickWindow *window, QRunnable *job) override
QSGContext * sceneGraphContext() const override
void resize(QQuickWindow *window) override
void update(QQuickWindow *window) override
void handleUpdateRequest(QQuickWindow *window) override
QAnimationDriver * animationDriver() const override
void maybeUpdate(QQuickWindow *window) override
void exposureChanged(QQuickWindow *window) override
void releaseResources(QQuickWindow *window) override
void hide(QQuickWindow *) override
void windowDestroyed(QQuickWindow *window) override
WMGrabEvent(QQuickWindow *c, QImage *result)
WMJobEvent(QQuickWindow *c, QRunnable *postedJob)
WMReleaseSwapchainEvent(QQuickWindow *c)
WMSyncEvent(QQuickWindow *c, bool inExpose, bool force, const QRhiSwapChainProxyData &scProxyData)
QRhiSwapChainProxyData scProxyData
WMTryReleaseEvent(QQuickWindow *win, bool destroy, bool needsFallbackSurface)
WMWindowEvent(QQuickWindow *c, QEvent::Type type)
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h