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;
237#if defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY)
262 pendingUpdate |= RepaintRequest;
315 switch ((
int) e->type()) {
318 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Obscure");
324 QQuickWindowPrivate::get(window)->fireAboutToStop();
325 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window removed");
328 waitCondition.wakeOne();
335 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Exposed");
339 waitCondition.wakeOne();
345 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_RequestSync");
350 windowSize = se->size;
354 pendingUpdate |= SyncRequest;
356 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- triggered from expose");
357 pendingUpdate |= ExposeRequest;
360 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- repaint regardless");
361 pendingUpdate |= RepaintRequest;
366 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_TryRelease");
368 wm->m_lockedForSync =
true;
371 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- setting exit flag and invalidating");
374 Q_ASSERT_X(!wme
->inDestructor || !
active,
"QSGRenderThread::invalidateGraphics()",
"Thread's active state is not set to false when shutting down");
378 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- not releasing because window is still active");
380 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
381 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting external renderers such as Quick 3D to release cached resources");
382 emit d->context->releaseCachedResourcesRequested();
384 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting renderer to release cached resources");
385 d->renderer->releaseCachedResources();
387#if QT_CONFIG(quick_shadereffect)
388 QSGRhiShaderEffectNode::garbageCollectMaterialTypeCache(window);
392 waitCondition.wakeOne();
393 wm->m_lockedForSync =
false;
399 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Grab");
401 Q_ASSERT(ce->window);
406 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(ce->window);
410 cd->rhi->beginFrame(cd->swapchain);
411 cd->rhi->makeThreadLocalNativeContextCurrent();
412 cd->syncSceneGraph();
414 cd->renderSceneGraph();
415 *ce->image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer());
416 cd->rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
418 ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio());
420 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- waking gui to handle result");
421 waitCondition.wakeOne();
427 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_PostJob");
432 rhi->makeThreadLocalNativeContextCurrent();
436 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- job done");
442 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_ReleaseSwapchain");
449 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- swapchain released");
451 waitCondition.wakeOne();
459 return QThread::event(e);
464 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"invalidateGraphics()");
470 qCWarning(QSG_LOG_RENDERLOOP,
"QSGThreadedRenderLoop:QSGRenderThread: no window to make current...");
474 bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
475 bool wipeGraphics = inDestructor || (wipeSG && !window->isPersistentGraphics());
477 rhi->makeThreadLocalNativeContextCurrent();
479 QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
483 dd->cleanupNodesOnShutdown();
484#if QT_CONFIG(quick_shadereffect)
485 QSGRhiShaderEffectNode::resetMaterialTypeCache(window);
488 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent SG, avoiding cleanup");
493 QCoreApplication::processEvents();
494 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
496 dd->animationController.reset();
498 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- invalidating scene graph");
502 if (window->handle()) {
505 wm->releaseSwapchain(window);
507 qWarning(
"QSGThreadedRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
508 window, dd->swapchain);
512 QSGRhiSupport::instance()->destroyRhi(rhi, dd->graphicsConfig);
515 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- QRhi destroyed");
517 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent GL, avoiding cleanup");
522
523
524
527 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"sync()");
530 Q_ASSERT_X(
wm->m_lockedForSync,
"QSGRenderThread::sync()",
"sync triggered on bad terms as gui is not already locked...");
534 if (windowSize.width() > 0 && windowSize.height() > 0) {
540 rhi->makeThreadLocalNativeContextCurrent();
550 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
554 d->renderer->clearChangedFlag();
561 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
563 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window has bad size, sync aborted");
572 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- sync complete, waking Gui");
573 waitCondition.wakeOne();
580 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
581 wd->cleanupNodesOnShutdown();
585 QSGRhiSupport::instance()->destroyRhi(rhi, {});
591 if (!
rhi || !
rhi->isDeviceLost())
594 qWarning(
"Graphics device lost, cleaning up scenegraph and releasing RHI");
601 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
602 QElapsedTimer threadTimer;
603 qint64 syncTime = 0, renderTime = 0;
606 Q_TRACE_SCOPE(QSG_syncAndRender);
607 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
608 Q_TRACE(QSG_sync_entry);
610 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"syncAndRender()");
613 const qint64 elapsedSinceLastMs = m_threadTimeBetweenRenders.restart();
614 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: start, elapsed since last call: %d ms",
616 QThread::currentThread(),
617 int(elapsedSinceLastMs));
620 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
622 const bool syncRequested = (pendingUpdate & SyncRequest);
623 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
626 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
627 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
632 const bool hasValidSwapChain = (cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0);
633 if (hasValidSwapChain) {
636 const QSize effectiveOutputSize = cd->swapchain->surfacePixelSize();
640 if (effectiveOutputSize.isEmpty()) {
643 waitCondition.wakeOne();
649 const QSize previousOutputSize = cd->swapchain->currentPixelSize();
650 if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
651 if (cd->swapchainJustBecameRenderable)
652 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"just became exposed");
654 cd->hasActiveSwapchain = cd->swapchain->createOrResize();
655 if (!cd->hasActiveSwapchain) {
656 bool bailOut =
false;
657 if (
rhi->isDeviceLost()) {
660 }
else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
661 qWarning(
"Failed to create swapchain."
662 " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
668 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
671 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed swapchain init, wake Gui");
673 waitCondition.wakeOne();
680 cd->swapchainJustBecameRenderable =
false;
681 cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
683 if (!cd->hasActiveSwapchain)
684 qWarning(
"Failed to build or resize swapchain");
686 qCDebug(QSG_LOG_RENDERLOOP) <<
"rhi swapchain size" << cd->swapchain->currentPixelSize();
689 emit window->beforeFrameBegin();
691 Q_ASSERT(
rhi == cd->rhi);
692 QRhi::FrameOpResult frameResult =
rhi->beginFrame(cd->swapchain);
693 if (frameResult != QRhi::FrameOpSuccess) {
694 if (frameResult == QRhi::FrameOpDeviceLost)
696 else if (frameResult == QRhi::FrameOpError)
697 qWarning(
"Failed to start frame");
699 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
700 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
705 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed beginFrame, wake Gui");
708 waitCondition.wakeOne();
711 emit window->afterFrameEnd();
717 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- updatePending, doing sync");
720#ifndef QSG_NO_RENDER_TIMING
722 syncTime = threadTimer.nsecsElapsed();
724 Q_TRACE(QSG_sync_exit);
725 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
726 QQuickProfiler::SceneGraphRenderLoopSync);
738 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering started");
740 Q_TRACE(QSG_render_entry);
750 d->animationController->lock();
752 d->animationController->unlock();
757 const bool canRender = d->renderer && hasValidSwapChain;
758 double lastCompletedGpuTime = 0;
761 rhi->makeThreadLocalNativeContextCurrent();
763 d->renderSceneGraph();
766 renderTime = threadTimer.nsecsElapsed();
767 Q_TRACE(QSG_render_exit);
768 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
769 QQuickProfiler::SceneGraphRenderLoopRender);
770 Q_TRACE(QSG_swap_entry);
772 QRhi::FrameOpResult frameResult =
rhi->endFrame(cd->swapchain);
773 if (frameResult != QRhi::FrameOpSuccess) {
774 if (frameResult == QRhi::FrameOpDeviceLost)
776 else if (frameResult == QRhi::FrameOpError)
777 qWarning(
"Failed to end frame");
778 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
779 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
781 lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
783 d->fireFrameSwapped();
785 Q_TRACE(QSG_render_exit);
786 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
787 QQuickProfiler::SceneGraphRenderLoopSync, 1);
788 Q_TRACE(QSG_swap_entry);
789 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window not ready, skipping render");
793 if (cd->swapchain &&
rhi->isRecordingFrame())
794 rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
797 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering done");
801 if (hasValidSwapChain)
802 emit window->afterFrameEnd();
809 if (exposeRequested) {
811 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- wake Gui after expose");
812 waitCondition.wakeOne();
824 qCDebug(QSG_LOG_TIME_RENDERLOOP,
825 "[window %p][render thread %p] syncAndRender: frame rendered in %dms, sync=%d, render=%d, swap=%d",
827 QThread::currentThread(),
828 int(threadTimer.elapsed()),
829 int((syncTime/1000000)),
830 int((renderTime - syncTime) / 1000000),
831 int((threadTimer.nsecsElapsed() - renderTime) / 1000000));
832 if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
833 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: last retrieved GPU frame time was %.4f ms",
835 QThread::currentThread(),
836 lastCompletedGpuTime * 1000.0);
840 Q_TRACE(QSG_swap_exit);
841 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
842 QQuickProfiler::SceneGraphRenderLoopSwap);
849 eventQueue.addEvent(e);
856 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEvents()");
857 while (eventQueue.hasMoreEvents()) {
858 QEvent *e = eventQueue.takeEvent(
false);
862 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEvents()");
867 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEventsAndWaitForMore()");
870 QEvent *e = eventQueue.takeEvent(
true);
874 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEventsAndWaitForMore()");
882 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
884 QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
889 rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
893 qWarning(
"Failed to create QRhi on the render thread; scenegraph is not functional");
899 if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
902 rhi->makeThreadLocalNativeContextCurrent();
903 QSGDefaultRenderContext::InitParams rcParams;
906 rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
907 rcParams.maybeSurface =
window;
908 sgrc->initialize(&rcParams);
910 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
911 if (
rhi && !cd->swapchain) {
913 QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource;
914 const QSurfaceFormat requestedFormat =
window->requestedFormat();
920 const bool alpha = requestedFormat.alphaBufferSize() > 0;
922 flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
927 if (requestedFormat.swapInterval() == 0) {
928 qCDebug(QSG_LOG_INFO,
"Swap interval is 0, attempting to disable vsync when presenting.");
929 flags |= QRhiSwapChain::NoVSync;
932 cd->swapchain =
rhi->newSwapChain();
933 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
934 if (depthBufferEnabled) {
935 cd->depthStencilForSwapchain =
rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
938 QRhiRenderBuffer::UsedWithSwapChainOnly);
939 cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
941 cd->swapchain->setWindow(
window);
943 QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain, window);
944 qCDebug(QSG_LOG_INFO,
"MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
947 cd->swapchain->setFlags(flags);
948 cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
949 cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
955 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run()");
958 if (QQmlDebugConnector::service<QQmlProfilerService>())
959 QQuickProfiler::registerAnimationCallback();
961 m_threadTimeBetweenRenders.start();
965 QMacAutoReleasePool frameReleasePool;
982 QEvent *e =
new QEvent(QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure));
983 QCoreApplication::postEvent(window, e);
988 QCoreApplication::processEvents();
990 if (active && (pendingUpdate == 0 || !window)) {
991 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"done drawing, sleep...");
998 Q_ASSERT_X(!
rhi,
"QSGRenderThread::run()",
"The graphics context should be cleaned up before exiting the render thread...");
1000 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run() completed");
1005 sgrc->moveToThread(
wm->thread());
1006 moveToThread(
wm->thread());
1010 : sg(QSGContext::createDefaultContext())
1011 , m_animation_timer(0)
1013 m_animation_driver = sg->createAnimationDriver(
this);
1015 connect(m_animation_driver, SIGNAL(started()),
this, SLOT(animationStarted()));
1018 m_animation_driver->install();
1023 qDeleteAll(pendingRenderContexts);
1029 auto context = sg->createRenderContext();
1030 pendingRenderContexts.insert(context);
1036 w->window->requestUpdate();
1041 return m_animation_driver;
1051 for (
int i=0; i<m_windows.size(); ++i) {
1052 QQuickWindow *c = m_windows.at(i).window;
1053 if (c->isVisible() && c->isExposed())
1061 return m_animation_driver->isRunning() && anyoneShowing();
1066 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStarted()");
1067 startOrStopAnimationTimer();
1069 for (
int i=0; i<m_windows.size(); ++i)
1070 postUpdateRequest(
const_cast<Window *>(&m_windows.at(i)));
1075 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStopped()");
1076 startOrStopAnimationTimer();
1082 if (!sg->isVSyncDependent(m_animation_driver))
1085 int exposedWindows = 0;
1086 int unthrottledWindows = 0;
1088 const Window *theOne =
nullptr;
1089 for (
int i=0; i<m_windows.size(); ++i) {
1090 const Window &w = m_windows.at(i);
1091 if (w.window->isVisible() && w.window->isExposed()) {
1094 if (w.actualWindowFormat.swapInterval() == 0)
1095 ++unthrottledWindows;
1121 const bool canUseVSyncBasedAnimation = exposedWindows == 1 && unthrottledWindows == 0 && badVSync == 0;
1123 if (m_animation_timer != 0 && (canUseVSyncBasedAnimation || !m_animation_driver->isRunning())) {
1124 qCDebug(QSG_LOG_RENDERLOOP,
"*** Stopping system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1125 exposedWindows, unthrottledWindows, badVSync);
1126 killTimer(m_animation_timer);
1127 m_animation_timer = 0;
1129 if (m_animation_driver->isRunning())
1130 postUpdateRequest(
const_cast<Window *>(theOne));
1131 }
else if (m_animation_timer == 0 && !canUseVSyncBasedAnimation && m_animation_driver->isRunning()) {
1132 qCDebug(QSG_LOG_RENDERLOOP,
"*** Starting system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1133 exposedWindows, unthrottledWindows, badVSync);
1134 m_animation_timer = startTimer(
int(sg->vsyncIntervalForAnimationDriver(m_animation_driver)));
1139
1140
1141
1142
1143
1144
1145
1146
1147
1151 qCDebug(QSG_LOG_RENDERLOOP) <<
"hide()" << window;
1153 if (window->isExposed())
1154 handleObscurity(windowFor(window));
1161 qCDebug(QSG_LOG_RENDERLOOP) <<
"resize()" << window;
1163 Window *w = windowFor(window);
1167 w->psTimeAccumulator = 0.0f;
1168 w->psTimeSampleCount = 0;
1172
1173
1174
1175
1178 qCDebug(QSG_LOG_RENDERLOOP) <<
"begin windowDestroyed()" << window;
1180 Window *w = windowFor(window);
1185 releaseResources(w,
true);
1188 while (thread->isRunning())
1189 QThread::yieldCurrentThread();
1190 Q_ASSERT(thread->thread() == QThread::currentThread());
1193 for (
int i=0; i<m_windows.size(); ++i) {
1194 if (m_windows.at(i).window == window) {
1195 m_windows.removeAt(i);
1203 startOrStopAnimationTimer();
1205 qCDebug(QSG_LOG_RENDERLOOP) <<
"done windowDestroyed()" << window;
1210 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1211 delete wd->rpDescForSwapchain;
1212 wd->rpDescForSwapchain =
nullptr;
1213 delete wd->swapchain;
1214 wd->swapchain =
nullptr;
1215 delete wd->depthStencilForSwapchain;
1216 wd->depthStencilForSwapchain =
nullptr;
1217 wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable =
false;
1222 qCDebug(QSG_LOG_RENDERLOOP) <<
"exposureChanged()" << window;
1230 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1231 if (!window->isExposed())
1232 wd->hasRenderableSwapchain =
false;
1234 bool skipThisExpose =
false;
1235 if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
1236 wd->hasRenderableSwapchain =
false;
1237 skipThisExpose =
true;
1240 if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
1241 && !wd->swapchain->surfacePixelSize().isEmpty())
1243 wd->hasRenderableSwapchain =
true;
1244 wd->swapchainJustBecameRenderable =
true;
1247 if (window->isExposed()) {
1248 if (!skipThisExpose)
1249 handleExposure(window);
1251 Window *w = windowFor(window);
1258
1259
1260
1263 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleExposure()" << window;
1265 Window *w = windowFor(window);
1267 qCDebug(QSG_LOG_RENDERLOOP,
"- adding window to list");
1269 win.window = window;
1270 win.actualWindowFormat = window->format();
1271 auto renderContext = QQuickWindowPrivate::get(window)->context;
1273 pendingRenderContexts.remove(renderContext);
1275 win.updateDuringSync =
false;
1276 win.forceRenderPass =
true;
1277 win.badVSync =
false;
1278 win.timeBetweenPolishAndSyncs.start();
1279 win.psTimeAccumulator = 0.0f;
1280 win.psTimeSampleCount = 0;
1282 w = &m_windows.last();
1284 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1285 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1291 if (w->window->width() <= 0 || w->window->height() <= 0
1292 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
1293 qWarning().noquote().nospace() <<
"QSGThreadedRenderLoop: expose event received for window "
1294 << w->window <<
" with invalid geometry: " << w->window->geometry()
1295 <<
" on " << w->window->screen();
1301 if (!w->window->handle())
1302 w->window->create();
1305 if (!w->thread->isRunning()) {
1306 qCDebug(QSG_LOG_RENDERLOOP,
"- starting render thread");
1312 if (!w->thread
->rhi) {
1313 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
1314 if (!w->thread->offscreenSurface)
1315 w->thread->offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
1316 w->thread->scProxyData = QRhi::updateSwapChainProxyData(rhiSupport->rhiBackend(), window);
1317 window->installEventFilter(
this);
1320 QQuickAnimatorController *controller
1321 = QQuickWindowPrivate::get(w->window)->animationController.get();
1322 if (controller->thread() != w->thread)
1323 controller->moveToThread(w->thread);
1326 if (w->thread->thread() == QThread::currentThread()) {
1327 w->thread
->sgrc->moveToThread(w->thread);
1328 w->thread->moveToThread(w->thread);
1331 if (!w->thread->isRunning())
1332 qFatal(
"Render thread failed to start, aborting application.");
1335 qCDebug(QSG_LOG_RENDERLOOP,
"- render thread already running");
1338 w->thread->mutex.lock();
1339 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
1340 w->thread->waitCondition.wait(&w->thread->mutex);
1341 w->thread->mutex.unlock();
1344 polishAndSync(w,
true);
1345 qCDebug(QSG_LOG_RENDERLOOP,
"- done with handleExposure()");
1347 startOrStopAnimationTimer();
1351
1352
1353
1354
1355
1356
1362 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleObscurity()" << w->window;
1363 if (w->thread->isRunning()) {
1364 if (!QQuickWindowPrivate::get(w->window)->updatesEnabled) {
1365 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1368 w->thread->mutex.lock();
1369 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Obscure)));
1370 w->thread->waitCondition.wait(&w->thread->mutex);
1371 w->thread->mutex.unlock();
1373 startOrStopAnimationTimer();
1378 switch (event->type()) {
1379 case QEvent::PlatformSurface:
1381 if (
static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1382 QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
1384 Window *w = windowFor(window);
1385 if (w && w->thread->isRunning()) {
1386 w->thread->mutex.lock();
1388 w->thread->waitCondition.wait(&w->thread->mutex);
1389 w->thread->mutex.unlock();
1399 return QObject::eventFilter(watched, event);
1404 qCDebug(QSG_LOG_RENDERLOOP) <<
"- update request" << window;
1405 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1406 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1409 Window *w = windowFor(window);
1416 Window *w = windowFor(window);
1422
1423
1424
1427 if (!QCoreApplication::instance())
1430 if (!w || !w->thread->isRunning())
1433 QThread *current = QThread::currentThread();
1434 if (current == w->thread && w->thread
->rhi && w->thread
->rhi->isDeviceLost())
1436 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
1437 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
1441 qCDebug(QSG_LOG_RENDERLOOP) <<
"update from item" << w->window;
1445 if (current == w->thread) {
1446 qCDebug(QSG_LOG_RENDERLOOP,
"- on render thread");
1447 w->updateDuringSync =
true;
1458 postUpdateRequest(w);
1462
1463
1464
1465
1468 Window *w = windowFor(window);
1472 const bool isRenderThread = QThread::currentThread() == w->thread;
1474 if (QPlatformWindow *platformWindow = window->handle()) {
1478 if (isRenderThread && !platformWindow->allowsIndependentThreadedRendering()) {
1482 qCDebug(QSG_LOG_RENDERLOOP) <<
"window is resizing. update on window" << w->window;
1483 QTimer::singleShot(0, window, [=]{ window->requestUpdate(); });
1488 if (isRenderThread) {
1489 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window - on render thread" << w->window;
1494 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window" << w->window;
1497 w->forceRenderPass =
true;
1504 Window *w = windowFor(window);
1506 releaseResources(w,
false);
1510
1511
1512
1515 qCDebug(QSG_LOG_RENDERLOOP) <<
"releaseResources()" << (inDestructor ?
"in destructor" :
"in api-call") << w->window;
1517 w->thread->mutex.lock();
1518 if (w->thread->isRunning() && w->thread
->active) {
1519 QQuickWindow *window = w->window;
1526 qCDebug(QSG_LOG_RENDERLOOP,
"- posting release request to render thread");
1527 w->thread->postEvent(
new WMTryReleaseEvent(window, inDestructor, window->handle() ==
nullptr));
1528 w->thread->waitCondition.wait(&w->thread->mutex);
1537 qCDebug(QSG_LOG_RENDERLOOP) <<
" - waiting for render thread to exit" << w->window;
1539 qCDebug(QSG_LOG_RENDERLOOP) <<
" - render thread finished" << w->window;
1542 w->thread->mutex.unlock();
1547
1548
1551 qCDebug(QSG_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
1553 QQuickWindow *window = w->window;
1554 if (!w->thread || !w->thread
->window) {
1555 qCDebug(QSG_LOG_RENDERLOOP,
"- not exposed, abort");
1560 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
1562 w = windowFor(window);
1563 if (!w || !w->thread || !w->thread
->window) {
1564 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after event flushing, abort");
1568 Q_TRACE_SCOPE(QSG_polishAndSync);
1569 QElapsedTimer timer;
1570 qint64 polishTime = 0;
1571 qint64 waitTime = 0;
1572 qint64 syncTime = 0;
1574 const qint64 elapsedSinceLastMs = w->timeBetweenPolishAndSyncs.restart();
1576 if (w->actualWindowFormat.swapInterval() != 0 && sg->isVSyncDependent(m_animation_driver)) {
1577 w->psTimeAccumulator += elapsedSinceLastMs;
1578 w->psTimeSampleCount += 1;
1580 static const int PS_TIME_SAMPLE_LENGTH = 20;
1581 if (w->psTimeSampleCount > PS_TIME_SAMPLE_LENGTH) {
1582 const float t = w->psTimeAccumulator / w->psTimeSampleCount;
1583 const float vsyncRate = sg->vsyncIntervalForAnimationDriver(m_animation_driver);
1603 const float threshold = vsyncRate * 0.5f;
1604 const bool badVSync = t < threshold;
1605 if (badVSync && !w->badVSync) {
1616 qCDebug(QSG_LOG_INFO,
"Window %p is determined to have broken vsync throttling (%f < %f) "
1617 "switching to system timer to drive gui thread animations to remedy this "
1618 "(however, render thread animators will likely advance at an incorrect rate).",
1619 w->window, t, threshold);
1620 startOrStopAnimationTimer();
1623 w->psTimeAccumulator = 0.0f;
1624 w->psTimeSampleCount = 0;
1628 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
1629 if (profileFrames) {
1631 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] polishAndSync: start, elapsed since last call: %d ms",
1633 int(elapsedSinceLastMs));
1635 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
1636 Q_TRACE(QSG_polishItems_entry);
1638 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1644 polishTime = timer.nsecsElapsed();
1645 Q_TRACE(QSG_polishItems_exit);
1646 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1647 QQuickProfiler::SceneGraphPolishAndSyncPolish);
1649 w = windowFor(window);
1650 if (!w || !w->thread || !w->thread
->window) {
1651 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after polishing, abort");
1655 Q_TRACE(QSG_wait_entry);
1656 w->updateDuringSync =
false;
1658 emit window->afterAnimating();
1661 QRhi::updateSwapChainProxyData(QSGRhiSupport::instance()->rhiBackend(), window);
1663 qCDebug(QSG_LOG_RENDERLOOP,
"- lock for sync");
1664 w->thread->mutex.lock();
1665 m_lockedForSync =
true;
1666 w->thread->postEvent(
new WMSyncEvent(window, inExpose, w->forceRenderPass, scProxyData));
1667 w->forceRenderPass =
false;
1669 qCDebug(QSG_LOG_RENDERLOOP,
"- wait for sync");
1671 waitTime = timer.nsecsElapsed();
1672 Q_TRACE(QSG_wait_exit);
1673 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1674 QQuickProfiler::SceneGraphPolishAndSyncWait);
1675 Q_TRACE(QSG_sync_entry);
1677 w->thread->waitCondition.wait(&w->thread->mutex);
1678 m_lockedForSync =
false;
1679 w->thread->mutex.unlock();
1680 qCDebug(QSG_LOG_RENDERLOOP,
"- unlock after sync");
1683 syncTime = timer.nsecsElapsed();
1684 Q_TRACE(QSG_sync_exit);
1685 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1686 QQuickProfiler::SceneGraphPolishAndSyncSync);
1687 Q_TRACE(QSG_animations_entry);
1696 if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
1697 auto advanceAnimations = [
this, window=QPointer(window)] {
1698 qCDebug(QSG_LOG_RENDERLOOP,
"- advancing animations");
1699 m_animation_driver->advance();
1700 qCDebug(QSG_LOG_RENDERLOOP,
"- animations done..");
1715 window->requestUpdate();
1717 emit timeToIncubate();
1720#if defined(Q_OS_APPLE)
1731 QMetaObject::invokeMethod(
this, advanceAnimations, Qt::QueuedConnection);
1736 advanceAnimations();
1738 }
else if (w->updateDuringSync) {
1739 postUpdateRequest(w);
1742 if (profileFrames) {
1743 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] Frame prepared, polish=%d ms, lock=%d ms, blockedForSync=%d ms, animations=%d ms",
1745 int(polishTime / 1000000),
1746 int((waitTime - polishTime) / 1000000),
1747 int((syncTime - waitTime) / 1000000),
1748 int((timer.nsecsElapsed() - syncTime) / 1000000));
1751 Q_TRACE(QSG_animations_exit);
1752 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
1753 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
1758 switch ((
int) e->type()) {
1760 case QEvent::Timer: {
1761 Q_ASSERT(sg->isVSyncDependent(m_animation_driver));
1762 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
1763 if (te->timerId() == m_animation_timer) {
1764 qCDebug(QSG_LOG_RENDERLOOP,
"- ticking non-render thread timer");
1765 m_animation_driver->advance();
1766 emit timeToIncubate();
1776 return QObject::event(e);
1782
1783
1784
1785
1786
1787
1788
1789
1793 qCDebug(QSG_LOG_RENDERLOOP) <<
"grab()" << window;
1795 Window *w = windowFor(window);
1798 if (!w->thread->isRunning())
1801 if (!window->handle())
1804 qCDebug(QSG_LOG_RENDERLOOP,
"- polishing items");
1805 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1811 w->thread->mutex.lock();
1812 m_lockedForSync =
true;
1813 qCDebug(QSG_LOG_RENDERLOOP,
"- posting grab event");
1814 w->thread->postEvent(
new WMGrabEvent(window, &result));
1815 w->thread->waitCondition.wait(&w->thread->mutex);
1816 m_lockedForSync =
false;
1817 w->thread->mutex.unlock();
1819 qCDebug(QSG_LOG_RENDERLOOP,
"- grab complete");
1825
1826
1827
1830 Window *w = windowFor(window);
1831 if (w && w->thread && w->thread
->window)
1839#include "qsgthreadedrenderloop.moc"
1840#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()
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)
Combined button and popup list for selecting options.
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h