7#include <QtCore/QMutex>
8#include <QtCore/QWaitCondition>
9#include <QtCore/QAnimationDriver>
10#include <QtCore/QQueue>
11#include <QtCore/QTimer>
13#include <QtGui/QGuiApplication>
14#include <QtGui/QScreen>
15#include <QtGui/QOffscreenSurface>
17#include <qpa/qwindowsysteminterface.h>
19#include <QtQuick/QQuickWindow>
20#include <private/qquickwindow_p.h>
21#include <private/qquickitem_p.h>
22#include <QtGui/qpa/qplatformwindow_p.h>
24#include <QtQuick/private/qsgrenderer_p.h>
28#include <private/qquickanimatorcontroller_p.h>
30#include <private/qquickprofiler_p.h>
31#include <private/qqmldebugserviceinterfaces_p.h>
32#include <private/qqmldebugconnector_p.h>
34#include <private/qsgrhishadereffectnode_p.h>
35#include <private/qsgdefaultrendercontext_p.h>
37#include <qtquick_tracepoints_p.h>
40#include <QtCore/private/qcore_mac_p.h>
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
86
90Q_TRACE_POINT(qtquick, QSG_polishAndSync_entry)
91Q_TRACE_POINT(qtquick, QSG_polishAndSync_exit)
92Q_TRACE_POINT(qtquick, QSG_wait_entry)
93Q_TRACE_POINT(qtquick, QSG_wait_exit)
94Q_TRACE_POINT(qtquick, QSG_syncAndRender_entry)
95Q_TRACE_POINT(qtquick, QSG_syncAndRender_exit)
96Q_TRACE_POINT(qtquick, QSG_animations_entry)
97Q_TRACE_POINT(qtquick, QSG_animations_exit)
99#define QSG_RT_PAD " (RT) %s"
101extern Q_GUI_EXPORT
QImage qt_gl_read_framebuffer(
const QSize &size,
bool alpha_format,
bool include_alpha);
109 for (
const auto &t : std::as_const(m_windows)) {
110 if (t.window == window)
111 return const_cast<Window *>(&t);
142 ,
dpr(
float(c->effectiveDevicePixelRatio()))
197 if (size() == 0 && wait) {
199 condition.wait(&mutex);
202 QEvent *e = dequeue();
209 bool has = !isEmpty();
216 QWaitCondition condition;
238#if defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY)
263 pendingUpdate |= RepaintRequest;
317 switch ((
int) e->type()) {
320 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Obscure");
326 QQuickWindowPrivate::get(window)->fireAboutToStop();
327 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window removed");
330 waitCondition.wakeOne();
337 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Exposed");
341 waitCondition.wakeOne();
347 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_RequestSync");
352 windowSize = se->size;
356 pendingUpdate |= SyncRequest;
358 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- triggered from expose");
359 pendingUpdate |= ExposeRequest;
362 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- repaint regardless");
363 pendingUpdate |= RepaintRequest;
368 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_TryRelease");
370 wm->m_lockedForSync =
true;
373 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- setting exit flag and invalidating");
376 Q_ASSERT_X(!wme
->inDestructor || !
active,
"QSGRenderThread::invalidateGraphics()",
"Thread's active state is not set to false when shutting down");
380 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- not releasing because window is still active");
382 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
383 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting external renderers such as Quick 3D to release cached resources");
384 emit d->context->releaseCachedResourcesRequested();
386 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting renderer to release cached resources");
387 d->renderer->releaseCachedResources();
389#if QT_CONFIG(quick_shadereffect)
390 QSGRhiShaderEffectNode::garbageCollectMaterialTypeCache(window);
394 waitCondition.wakeOne();
395 wm->m_lockedForSync =
false;
401 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Grab");
403 Q_ASSERT(ce->window);
408 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(ce->window);
412 cd->rhi->beginFrame(cd->swapchain);
413 cd->rhi->makeThreadLocalNativeContextCurrent();
414 cd->syncSceneGraph();
416 cd->renderSceneGraph();
417 *ce->image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer());
418 cd->rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
420 ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio());
422 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- waking gui to handle result");
423 waitCondition.wakeOne();
429 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_PostJob");
434 rhi->makeThreadLocalNativeContextCurrent();
438 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- job done");
444 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_ReleaseSwapchain");
451 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- swapchain released");
453 waitCondition.wakeOne();
461 return QThread::event(e);
466 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"invalidateGraphics()");
472 qCWarning(QSG_LOG_RENDERLOOP,
"QSGThreadedRenderLoop:QSGRenderThread: no window to make current...");
476 bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
477 bool wipeGraphics = inDestructor || (wipeSG && !window->isPersistentGraphics());
479 rhi->makeThreadLocalNativeContextCurrent();
481 QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
485 dd->cleanupNodesOnShutdown();
486#if QT_CONFIG(quick_shadereffect)
487 QSGRhiShaderEffectNode::resetMaterialTypeCache(window);
490 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent SG, avoiding cleanup");
495 QCoreApplication::processEvents();
496 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
498 dd->animationController.reset();
500 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- invalidating scene graph");
504 if (window->handle()) {
507 wm->releaseSwapchain(window);
509 qWarning(
"QSGThreadedRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
510 window, dd->swapchain);
514 QSGRhiSupport::instance()->destroyRhi(rhi, dd->graphicsConfig);
517 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- QRhi destroyed");
519 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent GL, avoiding cleanup");
524
525
526
529 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"sync()");
532 Q_ASSERT_X(
wm->m_lockedForSync,
"QSGRenderThread::sync()",
"sync triggered on bad terms as gui is not already locked...");
536 if (windowSize.width() > 0 && windowSize.height() > 0) {
542 rhi->makeThreadLocalNativeContextCurrent();
552 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
556 d->renderer->clearChangedFlag();
563 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
565 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window has bad size, sync aborted");
574 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- sync complete, waking Gui");
575 waitCondition.wakeOne();
582 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
583 wd->cleanupNodesOnShutdown();
587 QSGRhiSupport::instance()->destroyRhi(rhi, {});
594 if (!
rhi || !
rhi->isDeviceLost())
597 qWarning(
"Graphics device lost, cleaning up scenegraph and releasing RHI");
604 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
605 QElapsedTimer threadTimer;
606 qint64 syncTime = 0, renderTime = 0;
609 Q_TRACE_SCOPE(QSG_syncAndRender);
610 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
611 Q_TRACE(QSG_sync_entry);
613 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"syncAndRender()");
616 const qint64 elapsedSinceLastMs = m_threadTimeBetweenRenders.restart();
617 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: start, elapsed since last call: %d ms",
619 QThread::currentThread(),
620 int(elapsedSinceLastMs));
623 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
625 const bool syncRequested = (pendingUpdate & SyncRequest);
626 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
629 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
630 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
635 const bool hasValidSwapChain = (cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0);
636 if (hasValidSwapChain) {
639 const QSize effectiveOutputSize = cd->swapchain->surfacePixelSize();
643 if (effectiveOutputSize.isEmpty()) {
646 waitCondition.wakeOne();
652 const QSize previousOutputSize = cd->swapchain->currentPixelSize();
653 if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable ||
swapchainOutOfDate) {
654 if (cd->swapchainJustBecameRenderable)
655 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"just became exposed");
657 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"swapchain out of date");
659 cd->hasActiveSwapchain = cd->swapchain->createOrResize();
660 if (!cd->hasActiveSwapchain) {
661 bool bailOut =
false;
662 if (
rhi->isDeviceLost()) {
665 }
else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
666 qWarning(
"Failed to create swapchain."
667 " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
673 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
676 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed swapchain init, wake Gui");
678 waitCondition.wakeOne();
685 cd->swapchainJustBecameRenderable =
false;
687 cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
689 if (!cd->hasActiveSwapchain)
690 qWarning(
"Failed to build or resize swapchain");
692 qCDebug(QSG_LOG_RENDERLOOP) <<
"rhi swapchain size" << cd->swapchain->currentPixelSize();
695 emit window->beforeFrameBegin();
697 Q_ASSERT(
rhi == cd->rhi);
698 QRhi::FrameOpResult frameResult =
rhi->beginFrame(cd->swapchain);
699 if (frameResult != QRhi::FrameOpSuccess) {
700 if (frameResult == QRhi::FrameOpDeviceLost)
702 else if (frameResult == QRhi::FrameOpError)
703 qWarning(
"Failed to start frame");
704 else if (frameResult == QRhi::FrameOpSwapChainOutOfDate)
707 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
708 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
713 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed beginFrame, wake Gui");
716 waitCondition.wakeOne();
719 emit window->afterFrameEnd();
725 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- updatePending, doing sync");
728#ifndef QSG_NO_RENDER_TIMING
730 syncTime = threadTimer.nsecsElapsed();
732 Q_TRACE(QSG_sync_exit);
733 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
734 QQuickProfiler::SceneGraphRenderLoopSync);
746 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering started");
748 Q_TRACE(QSG_render_entry);
758 d->animationController->lock();
760 d->animationController->unlock();
765 const bool canRender = d->renderer && hasValidSwapChain;
766 double lastCompletedGpuTime = 0;
769 rhi->makeThreadLocalNativeContextCurrent();
771 d->renderSceneGraph();
774 renderTime = threadTimer.nsecsElapsed();
775 Q_TRACE(QSG_render_exit);
776 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
777 QQuickProfiler::SceneGraphRenderLoopRender);
778 Q_TRACE(QSG_swap_entry);
780 QRhi::FrameOpResult frameResult =
rhi->endFrame(cd->swapchain);
781 if (frameResult != QRhi::FrameOpSuccess) {
782 if (frameResult == QRhi::FrameOpDeviceLost)
784 else if (frameResult == QRhi::FrameOpError)
785 qWarning(
"Failed to end frame");
786 else if (frameResult == QRhi::FrameOpSwapChainOutOfDate)
788 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
789 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
791 lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
793 d->fireFrameSwapped();
795 Q_TRACE(QSG_render_exit);
796 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
797 QQuickProfiler::SceneGraphRenderLoopSync, 1);
798 Q_TRACE(QSG_swap_entry);
799 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window not ready, skipping render");
803 if (cd->swapchain &&
rhi->isRecordingFrame())
804 rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
807 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering done");
811 if (hasValidSwapChain)
812 emit window->afterFrameEnd();
819 if (exposeRequested) {
821 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- wake Gui after expose");
822 waitCondition.wakeOne();
834 qCDebug(QSG_LOG_TIME_RENDERLOOP,
835 "[window %p][render thread %p] syncAndRender: frame rendered in %dms, sync=%d, render=%d, swap=%d",
837 QThread::currentThread(),
838 int(threadTimer.elapsed()),
839 int((syncTime/1000000)),
840 int((renderTime - syncTime) / 1000000),
841 int((threadTimer.nsecsElapsed() - renderTime) / 1000000));
842 if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
843 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: last retrieved GPU frame time was %.4f ms",
845 QThread::currentThread(),
846 lastCompletedGpuTime * 1000.0);
850 Q_TRACE(QSG_swap_exit);
851 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
852 QQuickProfiler::SceneGraphRenderLoopSwap);
859 eventQueue.addEvent(e);
866 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEvents()");
867 while (eventQueue.hasMoreEvents()) {
868 QEvent *e = eventQueue.takeEvent(
false);
872 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEvents()");
877 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEventsAndWaitForMore()");
880 QEvent *e = eventQueue.takeEvent(
true);
884 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEventsAndWaitForMore()");
892 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
894 QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
899 rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
903 qWarning(
"Failed to create QRhi on the render thread; scenegraph is not functional");
909 if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
912 rhi->makeThreadLocalNativeContextCurrent();
913 QSGDefaultRenderContext::InitParams rcParams;
916 rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
917 rcParams.maybeSurface =
window;
918 sgrc->initialize(&rcParams);
920 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
921 if (
rhi && !cd->swapchain) {
923 QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource;
924 const QSurfaceFormat requestedFormat =
window->requestedFormat();
930 const bool alpha = requestedFormat.alphaBufferSize() > 0;
932 flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
937 if (requestedFormat.swapInterval() == 0) {
938 qCDebug(QSG_LOG_INFO,
"Swap interval is 0, attempting to disable vsync when presenting.");
939 flags |= QRhiSwapChain::NoVSync;
942 cd->swapchain =
rhi->newSwapChain();
943 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
944 if (depthBufferEnabled) {
945 cd->depthStencilForSwapchain =
rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
948 QRhiRenderBuffer::UsedWithSwapChainOnly
949 | QSGRhiSupport::depthStencilBufferFlags());
950 cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
952 cd->swapchain->setWindow(
window);
954 QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain, window);
955 qCDebug(QSG_LOG_INFO,
"MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
958 cd->swapchain->setFlags(flags);
959 cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
960 cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
966 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run()");
969 if (QQmlDebugConnector::service<QQmlProfilerService>())
970 QQuickProfiler::registerAnimationCallback();
972 m_threadTimeBetweenRenders.start();
976 QMacAutoReleasePool frameReleasePool;
993 QEvent *e =
new QEvent(QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure));
994 QCoreApplication::postEvent(window, e);
999 QCoreApplication::processEvents();
1001 if (active && (pendingUpdate == 0 || !window)) {
1002 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"done drawing, sleep...");
1009 Q_ASSERT_X(!
rhi,
"QSGRenderThread::run()",
"The graphics context should be cleaned up before exiting the render thread...");
1011 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run() completed");
1016 sgrc->moveToThread(
wm->thread());
1017 moveToThread(
wm->thread());
1021 : sg(QSGContext::createDefaultContext())
1022 , m_animation_timer(0)
1024 m_animation_driver = sg->createAnimationDriver(
this);
1026 connect(m_animation_driver, SIGNAL(started()),
this, SLOT(animationStarted()));
1029 m_animation_driver->install();
1034 qDeleteAll(pendingRenderContexts);
1040 auto context = sg->createRenderContext();
1041 pendingRenderContexts.insert(context);
1047 w->window->requestUpdate();
1052 return m_animation_driver;
1062 for (
int i=0; i<m_windows.size(); ++i) {
1063 QQuickWindow *c = m_windows.at(i).window;
1064 if (c->isVisible() && c->isExposed())
1072 return m_animation_driver->isRunning() && anyoneShowing();
1077 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStarted()");
1078 startOrStopAnimationTimer();
1080 for (
int i=0; i<m_windows.size(); ++i)
1081 postUpdateRequest(
const_cast<Window *>(&m_windows.at(i)));
1086 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStopped()");
1087 startOrStopAnimationTimer();
1093 if (!sg->isVSyncDependent(m_animation_driver))
1096 int exposedWindows = 0;
1097 int unthrottledWindows = 0;
1099 const Window *theOne =
nullptr;
1100 for (
int i=0; i<m_windows.size(); ++i) {
1101 const Window &w = m_windows.at(i);
1102 if (w.window->isVisible() && w.window->isExposed()) {
1105 if (w.actualWindowFormat.swapInterval() == 0)
1106 ++unthrottledWindows;
1132 const bool canUseVSyncBasedAnimation = exposedWindows == 1 && unthrottledWindows == 0 && badVSync == 0;
1134 if (m_animation_timer != 0 && (canUseVSyncBasedAnimation || !m_animation_driver->isRunning())) {
1135 qCDebug(QSG_LOG_RENDERLOOP,
"*** Stopping system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1136 exposedWindows, unthrottledWindows, badVSync);
1137 killTimer(m_animation_timer);
1138 m_animation_timer = 0;
1140 if (m_animation_driver->isRunning())
1141 postUpdateRequest(
const_cast<Window *>(theOne));
1142 }
else if (m_animation_timer == 0 && !canUseVSyncBasedAnimation && m_animation_driver->isRunning()) {
1143 qCDebug(QSG_LOG_RENDERLOOP,
"*** Starting system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1144 exposedWindows, unthrottledWindows, badVSync);
1145 m_animation_timer = startTimer(
int(sg->vsyncIntervalForAnimationDriver(m_animation_driver)));
1150
1151
1152
1153
1154
1155
1156
1157
1158
1162 qCDebug(QSG_LOG_RENDERLOOP) <<
"hide()" << window;
1164 if (window->isExposed())
1165 handleObscurity(windowFor(window));
1172 qCDebug(QSG_LOG_RENDERLOOP) <<
"resize()" << window;
1174 Window *w = windowFor(window);
1178 w->psTimeAccumulator = 0.0f;
1179 w->psTimeSampleCount = 0;
1183
1184
1185
1186
1189 qCDebug(QSG_LOG_RENDERLOOP) <<
"begin windowDestroyed()" << window;
1191 Window *w = windowFor(window);
1196 releaseResources(w,
true);
1199 while (thread->isRunning())
1200 QThread::yieldCurrentThread();
1201 Q_ASSERT(thread->thread() == QThread::currentThread());
1204 for (
int i=0; i<m_windows.size(); ++i) {
1205 if (m_windows.at(i).window == window) {
1206 m_windows.removeAt(i);
1214 startOrStopAnimationTimer();
1216 qCDebug(QSG_LOG_RENDERLOOP) <<
"done windowDestroyed()" << window;
1221 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1222 delete wd->rpDescForSwapchain;
1223 wd->rpDescForSwapchain =
nullptr;
1224 delete wd->swapchain;
1225 wd->swapchain =
nullptr;
1226 delete wd->depthStencilForSwapchain;
1227 wd->depthStencilForSwapchain =
nullptr;
1228 wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable =
false;
1233 qCDebug(QSG_LOG_RENDERLOOP) <<
"exposureChanged()" << window;
1241 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1242 if (!window->isExposed())
1243 wd->hasRenderableSwapchain =
false;
1245 bool skipThisExpose =
false;
1246 if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
1247 wd->hasRenderableSwapchain =
false;
1248 skipThisExpose =
true;
1251 if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
1252 && !wd->swapchain->surfacePixelSize().isEmpty())
1254 wd->hasRenderableSwapchain =
true;
1255 wd->swapchainJustBecameRenderable =
true;
1258 if (window->isExposed()) {
1259 if (!skipThisExpose)
1260 handleExposure(window);
1262 Window *w = windowFor(window);
1269
1270
1271
1274 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleExposure()" << window;
1276 Window *w = windowFor(window);
1278 qCDebug(QSG_LOG_RENDERLOOP,
"- adding window to list");
1280 win.window = window;
1281 win.actualWindowFormat = window->format();
1282 auto renderContext = QQuickWindowPrivate::get(window)->context;
1284 pendingRenderContexts.remove(renderContext);
1286 win.updateDuringSync =
false;
1287 win.forceRenderPass =
true;
1288 win.badVSync =
false;
1289 win.timeBetweenPolishAndSyncs.start();
1290 win.psTimeAccumulator = 0.0f;
1291 win.psTimeSampleCount = 0;
1293 w = &m_windows.last();
1295 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1296 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1302 if (w->window->width() <= 0 || w->window->height() <= 0
1303 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
1304 qWarning().noquote().nospace() <<
"QSGThreadedRenderLoop: expose event received for window "
1305 << w->window <<
" with invalid geometry: " << w->window->geometry()
1306 <<
" on " << w->window->screen();
1312 if (!w->window->handle())
1313 w->window->create();
1316 if (!w->thread->isRunning()) {
1317 qCDebug(QSG_LOG_RENDERLOOP,
"- starting render thread");
1323 if (!w->thread
->rhi) {
1324 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
1327 w->thread
->scProxyData = QRhi::updateSwapChainProxyData(rhiSupport->rhiBackend(), window);
1328 window->installEventFilter(
this);
1331 QQuickAnimatorController *controller
1332 = QQuickWindowPrivate::get(w->window)->animationController.get();
1333 if (controller->thread() != w->thread)
1334 controller->moveToThread(w->thread);
1337 if (w->thread->thread() == QThread::currentThread()) {
1338 w->thread
->sgrc->moveToThread(w->thread);
1339 w->thread->moveToThread(w->thread);
1342 if (!w->thread->isRunning())
1343 qFatal(
"Render thread failed to start, aborting application.");
1346 qCDebug(QSG_LOG_RENDERLOOP,
"- render thread already running");
1349 w->thread->mutex.lock();
1350 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
1351 w->thread->waitCondition.wait(&w->thread->mutex);
1352 w->thread->mutex.unlock();
1355 polishAndSync(w,
true);
1356 qCDebug(QSG_LOG_RENDERLOOP,
"- done with handleExposure()");
1358 startOrStopAnimationTimer();
1362
1363
1364
1365
1366
1367
1373 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleObscurity()" << w->window;
1374 if (w->thread->isRunning()) {
1375 if (!QQuickWindowPrivate::get(w->window)->updatesEnabled) {
1376 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1379 w->thread->mutex.lock();
1380 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Obscure)));
1381 w->thread->waitCondition.wait(&w->thread->mutex);
1382 w->thread->mutex.unlock();
1384 startOrStopAnimationTimer();
1389 switch (event->type()) {
1390 case QEvent::PlatformSurface:
1392 if (
static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1393 QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
1395 Window *w = windowFor(window);
1396 if (w && w->thread->isRunning()) {
1397 w->thread->mutex.lock();
1399 w->thread->waitCondition.wait(&w->thread->mutex);
1400 w->thread->mutex.unlock();
1410 return QObject::eventFilter(watched, event);
1415 qCDebug(QSG_LOG_RENDERLOOP) <<
"- update request" << window;
1416 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1417 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1420 Window *w = windowFor(window);
1427 Window *w = windowFor(window);
1433
1434
1435
1438 if (!QCoreApplication::instance())
1441 if (!w || !w->thread->isRunning())
1444 QThread *current = QThread::currentThread();
1445 if (current == w->thread && w->thread
->rhi && w->thread
->rhi->isDeviceLost())
1447 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
1448 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
1452 qCDebug(QSG_LOG_RENDERLOOP) <<
"update from item" << w->window;
1456 if (current == w->thread) {
1457 qCDebug(QSG_LOG_RENDERLOOP,
"- on render thread");
1458 w->updateDuringSync =
true;
1469 postUpdateRequest(w);
1473
1474
1475
1476
1479 Window *w = windowFor(window);
1483 const bool isRenderThread = QThread::currentThread() == w->thread;
1485 if (QPlatformWindow *platformWindow = window->handle()) {
1489 if (isRenderThread && !platformWindow->allowsIndependentThreadedRendering()) {
1493 qCDebug(QSG_LOG_RENDERLOOP) <<
"window is resizing. update on window" << w->window;
1494 QTimer::singleShot(0, window, &QWindow::requestUpdate);
1499 if (isRenderThread) {
1500 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window - on render thread" << w->window;
1505 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window" << w->window;
1508 w->forceRenderPass =
true;
1515 Window *w = windowFor(window);
1517 releaseResources(w,
false);
1521
1522
1523
1526 qCDebug(QSG_LOG_RENDERLOOP) <<
"releaseResources()" << (inDestructor ?
"in destructor" :
"in api-call") << w->window;
1528 w->thread->mutex.lock();
1529 if (w->thread->isRunning() && w->thread
->active) {
1530 QQuickWindow *window = w->window;
1537 qCDebug(QSG_LOG_RENDERLOOP,
"- posting release request to render thread");
1538 w->thread->postEvent(
new WMTryReleaseEvent(window, inDestructor, window->handle() ==
nullptr));
1539 w->thread->waitCondition.wait(&w->thread->mutex);
1548 qCDebug(QSG_LOG_RENDERLOOP) <<
" - waiting for render thread to exit" << w->window;
1550 qCDebug(QSG_LOG_RENDERLOOP) <<
" - render thread finished" << w->window;
1553 w->thread->mutex.unlock();
1558
1559
1562 qCDebug(QSG_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
1564 QQuickWindow *window = w->window;
1565 if (!w->thread || !w->thread
->window) {
1566 qCDebug(QSG_LOG_RENDERLOOP,
"- not exposed, abort");
1571 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
1573 w = windowFor(window);
1574 if (!w || !w->thread || !w->thread
->window) {
1575 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after event flushing, abort");
1579 Q_TRACE_SCOPE(QSG_polishAndSync);
1580 QElapsedTimer timer;
1581 qint64 polishTime = 0;
1582 qint64 waitTime = 0;
1583 qint64 syncTime = 0;
1585 const qint64 elapsedSinceLastMs = w->timeBetweenPolishAndSyncs.restart();
1587 if (w->actualWindowFormat.swapInterval() != 0 && sg->isVSyncDependent(m_animation_driver)) {
1588 w->psTimeAccumulator += elapsedSinceLastMs;
1589 w->psTimeSampleCount += 1;
1591 static const int PS_TIME_SAMPLE_LENGTH = 20;
1592 if (w->psTimeSampleCount > PS_TIME_SAMPLE_LENGTH) {
1593 const float t = w->psTimeAccumulator / w->psTimeSampleCount;
1594 const float vsyncRate = sg->vsyncIntervalForAnimationDriver(m_animation_driver);
1614 const float threshold = vsyncRate * 0.5f;
1615 const bool badVSync = t < threshold;
1616 if (badVSync && !w->badVSync) {
1627 qCDebug(QSG_LOG_INFO,
"Window %p is determined to have broken vsync throttling (%f < %f) "
1628 "switching to system timer to drive gui thread animations to remedy this "
1629 "(however, render thread animators will likely advance at an incorrect rate).",
1630 w->window, t, threshold);
1631 startOrStopAnimationTimer();
1634 w->psTimeAccumulator = 0.0f;
1635 w->psTimeSampleCount = 0;
1639 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
1640 if (profileFrames) {
1642 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] polishAndSync: start, elapsed since last call: %d ms",
1644 int(elapsedSinceLastMs));
1646 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
1647 Q_TRACE(QSG_polishItems_entry);
1649 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1655 polishTime = timer.nsecsElapsed();
1656 Q_TRACE(QSG_polishItems_exit);
1657 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1658 QQuickProfiler::SceneGraphPolishAndSyncPolish);
1660 w = windowFor(window);
1661 if (!w || !w->thread || !w->thread
->window) {
1662 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after polishing, abort");
1666 Q_TRACE(QSG_wait_entry);
1667 w->updateDuringSync =
false;
1669 emit window->afterAnimating();
1672 QRhi::updateSwapChainProxyData(QSGRhiSupport::instance()->rhiBackend(), window);
1674 qCDebug(QSG_LOG_RENDERLOOP,
"- lock for sync");
1675 w->thread->mutex.lock();
1676 m_lockedForSync =
true;
1677 w->thread->postEvent(
new WMSyncEvent(window, inExpose, w->forceRenderPass, scProxyData));
1678 w->forceRenderPass =
false;
1680 qCDebug(QSG_LOG_RENDERLOOP,
"- wait for sync");
1682 waitTime = timer.nsecsElapsed();
1683 Q_TRACE(QSG_wait_exit);
1684 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1685 QQuickProfiler::SceneGraphPolishAndSyncWait);
1686 Q_TRACE(QSG_sync_entry);
1688 w->thread->waitCondition.wait(&w->thread->mutex);
1689 m_lockedForSync =
false;
1690 w->thread->mutex.unlock();
1691 qCDebug(QSG_LOG_RENDERLOOP,
"- unlock after sync");
1694 syncTime = timer.nsecsElapsed();
1695 Q_TRACE(QSG_sync_exit);
1696 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1697 QQuickProfiler::SceneGraphPolishAndSyncSync);
1698 Q_TRACE(QSG_animations_entry);
1707 if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
1708 auto advanceAnimations = [
this, window=QPointer(window)] {
1709 qCDebug(QSG_LOG_RENDERLOOP,
"- advancing animations");
1710 m_animation_driver->advance();
1711 qCDebug(QSG_LOG_RENDERLOOP,
"- animations done..");
1726 window->requestUpdate();
1728 emit timeToIncubate();
1731#if defined(Q_OS_APPLE)
1742 QMetaObject::invokeMethod(
this, advanceAnimations, Qt::QueuedConnection);
1747 advanceAnimations();
1749 }
else if (w->updateDuringSync) {
1750 postUpdateRequest(w);
1753 if (profileFrames) {
1754 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] Frame prepared, polish=%d ms, lock=%d ms, blockedForSync=%d ms, animations=%d ms",
1756 int(polishTime / 1000000),
1757 int((waitTime - polishTime) / 1000000),
1758 int((syncTime - waitTime) / 1000000),
1759 int((timer.nsecsElapsed() - syncTime) / 1000000));
1762 Q_TRACE(QSG_animations_exit);
1763 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
1764 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
1769 switch ((
int) e->type()) {
1771 case QEvent::Timer: {
1772 Q_ASSERT(sg->isVSyncDependent(m_animation_driver));
1773 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
1774 if (te->timerId() == m_animation_timer) {
1775 qCDebug(QSG_LOG_RENDERLOOP,
"- ticking non-render thread timer");
1776 m_animation_driver->advance();
1777 emit timeToIncubate();
1787 return QObject::event(e);
1793
1794
1795
1796
1797
1798
1799
1800
1804 qCDebug(QSG_LOG_RENDERLOOP) <<
"grab()" << window;
1806 Window *w = windowFor(window);
1809 if (!w->thread->isRunning())
1812 if (!window->handle())
1815 qCDebug(QSG_LOG_RENDERLOOP,
"- polishing items");
1816 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1822 w->thread->mutex.lock();
1823 m_lockedForSync =
true;
1824 qCDebug(QSG_LOG_RENDERLOOP,
"- posting grab event");
1825 w->thread->postEvent(
new WMGrabEvent(window, &result));
1826 w->thread->waitCondition.wait(&w->thread->mutex);
1827 m_lockedForSync =
false;
1828 w->thread->mutex.unlock();
1830 qCDebug(QSG_LOG_RENDERLOOP,
"- grab complete");
1836
1837
1838
1841 Window *w = windowFor(window);
1842 if (w && w->thread && w->thread
->window)
1850#include "qsgthreadedrenderloop.moc"
1851#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