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;
316 switch ((
int) e->type()) {
319 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Obscure");
325 QQuickWindowPrivate::get(window)->fireAboutToStop();
326 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window removed");
329 waitCondition.wakeOne();
336 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Exposed");
340 waitCondition.wakeOne();
346 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_RequestSync");
351 windowSize = se->size;
355 pendingUpdate |= SyncRequest;
357 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- triggered from expose");
358 pendingUpdate |= ExposeRequest;
361 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- repaint regardless");
362 pendingUpdate |= RepaintRequest;
367 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_TryRelease");
369 wm->m_lockedForSync =
true;
372 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- setting exit flag and invalidating");
375 Q_ASSERT_X(!wme
->inDestructor || !
active,
"QSGRenderThread::invalidateGraphics()",
"Thread's active state is not set to false when shutting down");
379 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- not releasing because window is still active");
381 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
382 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting external renderers such as Quick 3D to release cached resources");
383 emit d->context->releaseCachedResourcesRequested();
385 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- requesting renderer to release cached resources");
386 d->renderer->releaseCachedResources();
388#if QT_CONFIG(quick_shadereffect)
389 QSGRhiShaderEffectNode::garbageCollectMaterialTypeCache(window);
393 waitCondition.wakeOne();
394 wm->m_lockedForSync =
false;
400 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_Grab");
402 Q_ASSERT(ce->window);
407 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(ce->window);
411 cd->rhi->beginFrame(cd->swapchain);
412 cd->rhi->makeThreadLocalNativeContextCurrent();
413 cd->syncSceneGraph();
415 cd->renderSceneGraph();
416 *ce->image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer());
417 cd->rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
419 ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio());
421 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- waking gui to handle result");
422 waitCondition.wakeOne();
428 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_PostJob");
433 rhi->makeThreadLocalNativeContextCurrent();
437 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- job done");
443 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"WM_ReleaseSwapchain");
450 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- swapchain released");
452 waitCondition.wakeOne();
460 return QThread::event(e);
465 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"invalidateGraphics()");
471 qCWarning(QSG_LOG_RENDERLOOP,
"QSGThreadedRenderLoop:QSGRenderThread: no window to make current...");
475 bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
476 bool wipeGraphics = inDestructor || (wipeSG && !window->isPersistentGraphics());
478 rhi->makeThreadLocalNativeContextCurrent();
480 QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
484 dd->cleanupNodesOnShutdown();
485#if QT_CONFIG(quick_shadereffect)
486 QSGRhiShaderEffectNode::resetMaterialTypeCache(window);
489 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent SG, avoiding cleanup");
494 QCoreApplication::processEvents();
495 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
497 dd->animationController.reset();
499 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- invalidating scene graph");
503 if (window->handle()) {
506 wm->releaseSwapchain(window);
508 qWarning(
"QSGThreadedRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
509 window, dd->swapchain);
513 QSGRhiSupport::instance()->destroyRhi(rhi, dd->graphicsConfig);
516 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- QRhi destroyed");
518 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- persistent GL, avoiding cleanup");
523
524
525
528 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"sync()");
531 Q_ASSERT_X(
wm->m_lockedForSync,
"QSGRenderThread::sync()",
"sync triggered on bad terms as gui is not already locked...");
535 if (windowSize.width() > 0 && windowSize.height() > 0) {
541 rhi->makeThreadLocalNativeContextCurrent();
551 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
555 d->renderer->clearChangedFlag();
562 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
564 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window has bad size, sync aborted");
573 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- sync complete, waking Gui");
574 waitCondition.wakeOne();
581 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
582 wd->cleanupNodesOnShutdown();
586 QSGRhiSupport::instance()->destroyRhi(rhi, {});
592 if (!
rhi || !
rhi->isDeviceLost())
595 qWarning(
"Graphics device lost, cleaning up scenegraph and releasing RHI");
602 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
603 QElapsedTimer threadTimer;
604 qint64 syncTime = 0, renderTime = 0;
607 Q_TRACE_SCOPE(QSG_syncAndRender);
608 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
609 Q_TRACE(QSG_sync_entry);
611 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"syncAndRender()");
614 const qint64 elapsedSinceLastMs = m_threadTimeBetweenRenders.restart();
615 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: start, elapsed since last call: %d ms",
617 QThread::currentThread(),
618 int(elapsedSinceLastMs));
621 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
623 const bool syncRequested = (pendingUpdate & SyncRequest);
624 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
627 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
628 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
633 const bool hasValidSwapChain = (cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0);
634 if (hasValidSwapChain) {
637 const QSize effectiveOutputSize = cd->swapchain->surfacePixelSize();
641 if (effectiveOutputSize.isEmpty()) {
644 waitCondition.wakeOne();
650 const QSize previousOutputSize = cd->swapchain->currentPixelSize();
651 if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
652 if (cd->swapchainJustBecameRenderable)
653 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"just became exposed");
655 cd->hasActiveSwapchain = cd->swapchain->createOrResize();
656 if (!cd->hasActiveSwapchain) {
657 bool bailOut =
false;
658 if (
rhi->isDeviceLost()) {
661 }
else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
662 qWarning(
"Failed to create swapchain."
663 " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
669 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
672 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed swapchain init, wake Gui");
674 waitCondition.wakeOne();
681 cd->swapchainJustBecameRenderable =
false;
682 cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
684 if (!cd->hasActiveSwapchain)
685 qWarning(
"Failed to build or resize swapchain");
687 qCDebug(QSG_LOG_RENDERLOOP) <<
"rhi swapchain size" << cd->swapchain->currentPixelSize();
690 emit window->beforeFrameBegin();
692 Q_ASSERT(
rhi == cd->rhi);
693 QRhi::FrameOpResult frameResult =
rhi->beginFrame(cd->swapchain);
694 if (frameResult != QRhi::FrameOpSuccess) {
695 if (frameResult == QRhi::FrameOpDeviceLost)
697 else if (frameResult == QRhi::FrameOpError)
698 qWarning(
"Failed to start frame");
700 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
701 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
706 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed beginFrame, wake Gui");
709 waitCondition.wakeOne();
712 emit window->afterFrameEnd();
718 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- updatePending, doing sync");
721#ifndef QSG_NO_RENDER_TIMING
723 syncTime = threadTimer.nsecsElapsed();
725 Q_TRACE(QSG_sync_exit);
726 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
727 QQuickProfiler::SceneGraphRenderLoopSync);
739 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering started");
741 Q_TRACE(QSG_render_entry);
751 d->animationController->lock();
753 d->animationController->unlock();
758 const bool canRender = d->renderer && hasValidSwapChain;
759 double lastCompletedGpuTime = 0;
762 rhi->makeThreadLocalNativeContextCurrent();
764 d->renderSceneGraph();
767 renderTime = threadTimer.nsecsElapsed();
768 Q_TRACE(QSG_render_exit);
769 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
770 QQuickProfiler::SceneGraphRenderLoopRender);
771 Q_TRACE(QSG_swap_entry);
773 QRhi::FrameOpResult frameResult =
rhi->endFrame(cd->swapchain);
774 if (frameResult != QRhi::FrameOpSuccess) {
775 if (frameResult == QRhi::FrameOpDeviceLost)
777 else if (frameResult == QRhi::FrameOpError)
778 qWarning(
"Failed to end frame");
779 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
780 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
782 lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
784 d->fireFrameSwapped();
786 Q_TRACE(QSG_render_exit);
787 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
788 QQuickProfiler::SceneGraphRenderLoopSync, 1);
789 Q_TRACE(QSG_swap_entry);
790 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window not ready, skipping render");
794 if (cd->swapchain &&
rhi->isRecordingFrame())
795 rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
798 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering done");
802 if (hasValidSwapChain)
803 emit window->afterFrameEnd();
810 if (exposeRequested) {
812 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- wake Gui after expose");
813 waitCondition.wakeOne();
825 qCDebug(QSG_LOG_TIME_RENDERLOOP,
826 "[window %p][render thread %p] syncAndRender: frame rendered in %dms, sync=%d, render=%d, swap=%d",
828 QThread::currentThread(),
829 int(threadTimer.elapsed()),
830 int((syncTime/1000000)),
831 int((renderTime - syncTime) / 1000000),
832 int((threadTimer.nsecsElapsed() - renderTime) / 1000000));
833 if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
834 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: last retrieved GPU frame time was %.4f ms",
836 QThread::currentThread(),
837 lastCompletedGpuTime * 1000.0);
841 Q_TRACE(QSG_swap_exit);
842 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
843 QQuickProfiler::SceneGraphRenderLoopSwap);
850 eventQueue.addEvent(e);
857 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEvents()");
858 while (eventQueue.hasMoreEvents()) {
859 QEvent *e = eventQueue.takeEvent(
false);
863 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEvents()");
868 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEventsAndWaitForMore()");
871 QEvent *e = eventQueue.takeEvent(
true);
875 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEventsAndWaitForMore()");
883 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
885 QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
890 rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
894 qWarning(
"Failed to create QRhi on the render thread; scenegraph is not functional");
900 if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
903 rhi->makeThreadLocalNativeContextCurrent();
904 QSGDefaultRenderContext::InitParams rcParams;
907 rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
908 rcParams.maybeSurface =
window;
909 sgrc->initialize(&rcParams);
911 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
912 if (
rhi && !cd->swapchain) {
914 QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource;
915 const QSurfaceFormat requestedFormat =
window->requestedFormat();
921 const bool alpha = requestedFormat.alphaBufferSize() > 0;
923 flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
928 if (requestedFormat.swapInterval() == 0) {
929 qCDebug(QSG_LOG_INFO,
"Swap interval is 0, attempting to disable vsync when presenting.");
930 flags |= QRhiSwapChain::NoVSync;
933 cd->swapchain =
rhi->newSwapChain();
934 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
935 if (depthBufferEnabled) {
936 cd->depthStencilForSwapchain =
rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
939 QRhiRenderBuffer::UsedWithSwapChainOnly);
940 cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
942 cd->swapchain->setWindow(
window);
944 QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain, window);
945 qCDebug(QSG_LOG_INFO,
"MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
948 cd->swapchain->setFlags(flags);
949 cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
950 cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
956 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run()");
959 if (QQmlDebugConnector::service<QQmlProfilerService>())
960 QQuickProfiler::registerAnimationCallback();
962 m_threadTimeBetweenRenders.start();
966 QMacAutoReleasePool frameReleasePool;
983 QEvent *e =
new QEvent(QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure));
984 QCoreApplication::postEvent(window, e);
989 QCoreApplication::processEvents();
991 if (active && (pendingUpdate == 0 || !window)) {
992 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"done drawing, sleep...");
999 Q_ASSERT_X(!
rhi,
"QSGRenderThread::run()",
"The graphics context should be cleaned up before exiting the render thread...");
1001 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run() completed");
1006 sgrc->moveToThread(
wm->thread());
1007 moveToThread(
wm->thread());
1011 : sg(QSGContext::createDefaultContext())
1012 , m_animation_timer(0)
1014 m_animation_driver = sg->createAnimationDriver(
this);
1016 connect(m_animation_driver, SIGNAL(started()),
this, SLOT(animationStarted()));
1019 m_animation_driver->install();
1024 qDeleteAll(pendingRenderContexts);
1030 auto context = sg->createRenderContext();
1031 pendingRenderContexts.insert(context);
1037 w->window->requestUpdate();
1042 return m_animation_driver;
1052 for (
int i=0; i<m_windows.size(); ++i) {
1053 QQuickWindow *c = m_windows.at(i).window;
1054 if (c->isVisible() && c->isExposed())
1062 return m_animation_driver->isRunning() && anyoneShowing();
1067 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStarted()");
1068 startOrStopAnimationTimer();
1070 for (
int i=0; i<m_windows.size(); ++i)
1071 postUpdateRequest(
const_cast<Window *>(&m_windows.at(i)));
1076 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStopped()");
1077 startOrStopAnimationTimer();
1083 if (!sg->isVSyncDependent(m_animation_driver))
1086 int exposedWindows = 0;
1087 int unthrottledWindows = 0;
1089 const Window *theOne =
nullptr;
1090 for (
int i=0; i<m_windows.size(); ++i) {
1091 const Window &w = m_windows.at(i);
1092 if (w.window->isVisible() && w.window->isExposed()) {
1095 if (w.actualWindowFormat.swapInterval() == 0)
1096 ++unthrottledWindows;
1122 const bool canUseVSyncBasedAnimation = exposedWindows == 1 && unthrottledWindows == 0 && badVSync == 0;
1124 if (m_animation_timer != 0 && (canUseVSyncBasedAnimation || !m_animation_driver->isRunning())) {
1125 qCDebug(QSG_LOG_RENDERLOOP,
"*** Stopping system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1126 exposedWindows, unthrottledWindows, badVSync);
1127 killTimer(m_animation_timer);
1128 m_animation_timer = 0;
1130 if (m_animation_driver->isRunning())
1131 postUpdateRequest(
const_cast<Window *>(theOne));
1132 }
else if (m_animation_timer == 0 && !canUseVSyncBasedAnimation && m_animation_driver->isRunning()) {
1133 qCDebug(QSG_LOG_RENDERLOOP,
"*** Starting system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1134 exposedWindows, unthrottledWindows, badVSync);
1135 m_animation_timer = startTimer(
int(sg->vsyncIntervalForAnimationDriver(m_animation_driver)));
1140
1141
1142
1143
1144
1145
1146
1147
1148
1152 qCDebug(QSG_LOG_RENDERLOOP) <<
"hide()" << window;
1154 if (window->isExposed())
1155 handleObscurity(windowFor(window));
1162 qCDebug(QSG_LOG_RENDERLOOP) <<
"resize()" << window;
1164 Window *w = windowFor(window);
1168 w->psTimeAccumulator = 0.0f;
1169 w->psTimeSampleCount = 0;
1173
1174
1175
1176
1179 qCDebug(QSG_LOG_RENDERLOOP) <<
"begin windowDestroyed()" << window;
1181 Window *w = windowFor(window);
1186 releaseResources(w,
true);
1189 while (thread->isRunning())
1190 QThread::yieldCurrentThread();
1191 Q_ASSERT(thread->thread() == QThread::currentThread());
1194 for (
int i=0; i<m_windows.size(); ++i) {
1195 if (m_windows.at(i).window == window) {
1196 m_windows.removeAt(i);
1204 startOrStopAnimationTimer();
1206 qCDebug(QSG_LOG_RENDERLOOP) <<
"done windowDestroyed()" << window;
1211 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1212 delete wd->rpDescForSwapchain;
1213 wd->rpDescForSwapchain =
nullptr;
1214 delete wd->swapchain;
1215 wd->swapchain =
nullptr;
1216 delete wd->depthStencilForSwapchain;
1217 wd->depthStencilForSwapchain =
nullptr;
1218 wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable =
false;
1223 qCDebug(QSG_LOG_RENDERLOOP) <<
"exposureChanged()" << window;
1231 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1232 if (!window->isExposed())
1233 wd->hasRenderableSwapchain =
false;
1235 bool skipThisExpose =
false;
1236 if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
1237 wd->hasRenderableSwapchain =
false;
1238 skipThisExpose =
true;
1241 if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
1242 && !wd->swapchain->surfacePixelSize().isEmpty())
1244 wd->hasRenderableSwapchain =
true;
1245 wd->swapchainJustBecameRenderable =
true;
1248 if (window->isExposed()) {
1249 if (!skipThisExpose)
1250 handleExposure(window);
1252 Window *w = windowFor(window);
1259
1260
1261
1264 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleExposure()" << window;
1266 Window *w = windowFor(window);
1268 qCDebug(QSG_LOG_RENDERLOOP,
"- adding window to list");
1270 win.window = window;
1271 win.actualWindowFormat = window->format();
1272 auto renderContext = QQuickWindowPrivate::get(window)->context;
1274 pendingRenderContexts.remove(renderContext);
1276 win.updateDuringSync =
false;
1277 win.forceRenderPass =
true;
1278 win.badVSync =
false;
1279 win.timeBetweenPolishAndSyncs.start();
1280 win.psTimeAccumulator = 0.0f;
1281 win.psTimeSampleCount = 0;
1283 w = &m_windows.last();
1285 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1286 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1292 if (w->window->width() <= 0 || w->window->height() <= 0
1293 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
1294 qWarning().noquote().nospace() <<
"QSGThreadedRenderLoop: expose event received for window "
1295 << w->window <<
" with invalid geometry: " << w->window->geometry()
1296 <<
" on " << w->window->screen();
1302 if (!w->window->handle())
1303 w->window->create();
1306 if (!w->thread->isRunning()) {
1307 qCDebug(QSG_LOG_RENDERLOOP,
"- starting render thread");
1313 if (!w->thread
->rhi) {
1314 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
1315 if (!w->thread->offscreenSurface)
1316 w->thread->offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
1317 w->thread->scProxyData = QRhi::updateSwapChainProxyData(rhiSupport->rhiBackend(), window);
1318 window->installEventFilter(
this);
1321 QQuickAnimatorController *controller
1322 = QQuickWindowPrivate::get(w->window)->animationController.get();
1323 if (controller->thread() != w->thread)
1324 controller->moveToThread(w->thread);
1327 if (w->thread->thread() == QThread::currentThread()) {
1328 w->thread
->sgrc->moveToThread(w->thread);
1329 w->thread->moveToThread(w->thread);
1332 if (!w->thread->isRunning())
1333 qFatal(
"Render thread failed to start, aborting application.");
1336 qCDebug(QSG_LOG_RENDERLOOP,
"- render thread already running");
1339 w->thread->mutex.lock();
1340 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
1341 w->thread->waitCondition.wait(&w->thread->mutex);
1342 w->thread->mutex.unlock();
1345 polishAndSync(w,
true);
1346 qCDebug(QSG_LOG_RENDERLOOP,
"- done with handleExposure()");
1348 startOrStopAnimationTimer();
1352
1353
1354
1355
1356
1357
1363 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleObscurity()" << w->window;
1364 if (w->thread->isRunning()) {
1365 if (!QQuickWindowPrivate::get(w->window)->updatesEnabled) {
1366 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1369 w->thread->mutex.lock();
1370 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Obscure)));
1371 w->thread->waitCondition.wait(&w->thread->mutex);
1372 w->thread->mutex.unlock();
1374 startOrStopAnimationTimer();
1379 switch (event->type()) {
1380 case QEvent::PlatformSurface:
1382 if (
static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1383 QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
1385 Window *w = windowFor(window);
1386 if (w && w->thread->isRunning()) {
1387 w->thread->mutex.lock();
1389 w->thread->waitCondition.wait(&w->thread->mutex);
1390 w->thread->mutex.unlock();
1400 return QObject::eventFilter(watched, event);
1405 qCDebug(QSG_LOG_RENDERLOOP) <<
"- update request" << window;
1406 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1407 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1410 Window *w = windowFor(window);
1417 Window *w = windowFor(window);
1423
1424
1425
1428 if (!QCoreApplication::instance())
1431 if (!w || !w->thread->isRunning())
1434 QThread *current = QThread::currentThread();
1435 if (current == w->thread && w->thread
->rhi && w->thread
->rhi->isDeviceLost())
1437 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
1438 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
1442 qCDebug(QSG_LOG_RENDERLOOP) <<
"update from item" << w->window;
1446 if (current == w->thread) {
1447 qCDebug(QSG_LOG_RENDERLOOP,
"- on render thread");
1448 w->updateDuringSync =
true;
1459 postUpdateRequest(w);
1463
1464
1465
1466
1469 Window *w = windowFor(window);
1473 const bool isRenderThread = QThread::currentThread() == w->thread;
1475 if (QPlatformWindow *platformWindow = window->handle()) {
1479 if (isRenderThread && !platformWindow->allowsIndependentThreadedRendering()) {
1483 qCDebug(QSG_LOG_RENDERLOOP) <<
"window is resizing. update on window" << w->window;
1484 QTimer::singleShot(0, window, [=]{ window->requestUpdate(); });
1489 if (isRenderThread) {
1490 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window - on render thread" << w->window;
1495 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window" << w->window;
1498 w->forceRenderPass =
true;
1505 Window *w = windowFor(window);
1507 releaseResources(w,
false);
1511
1512
1513
1516 qCDebug(QSG_LOG_RENDERLOOP) <<
"releaseResources()" << (inDestructor ?
"in destructor" :
"in api-call") << w->window;
1518 w->thread->mutex.lock();
1519 if (w->thread->isRunning() && w->thread
->active) {
1520 QQuickWindow *window = w->window;
1527 qCDebug(QSG_LOG_RENDERLOOP,
"- posting release request to render thread");
1528 w->thread->postEvent(
new WMTryReleaseEvent(window, inDestructor, window->handle() ==
nullptr));
1529 w->thread->waitCondition.wait(&w->thread->mutex);
1538 qCDebug(QSG_LOG_RENDERLOOP) <<
" - waiting for render thread to exit" << w->window;
1540 qCDebug(QSG_LOG_RENDERLOOP) <<
" - render thread finished" << w->window;
1543 w->thread->mutex.unlock();
1548
1549
1552 qCDebug(QSG_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
1554 QQuickWindow *window = w->window;
1555 if (!w->thread || !w->thread
->window) {
1556 qCDebug(QSG_LOG_RENDERLOOP,
"- not exposed, abort");
1561 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
1563 w = windowFor(window);
1564 if (!w || !w->thread || !w->thread
->window) {
1565 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after event flushing, abort");
1569 Q_TRACE_SCOPE(QSG_polishAndSync);
1570 QElapsedTimer timer;
1571 qint64 polishTime = 0;
1572 qint64 waitTime = 0;
1573 qint64 syncTime = 0;
1575 const qint64 elapsedSinceLastMs = w->timeBetweenPolishAndSyncs.restart();
1577 if (w->actualWindowFormat.swapInterval() != 0 && sg->isVSyncDependent(m_animation_driver)) {
1578 w->psTimeAccumulator += elapsedSinceLastMs;
1579 w->psTimeSampleCount += 1;
1581 static const int PS_TIME_SAMPLE_LENGTH = 20;
1582 if (w->psTimeSampleCount > PS_TIME_SAMPLE_LENGTH) {
1583 const float t = w->psTimeAccumulator / w->psTimeSampleCount;
1584 const float vsyncRate = sg->vsyncIntervalForAnimationDriver(m_animation_driver);
1604 const float threshold = vsyncRate * 0.5f;
1605 const bool badVSync = t < threshold;
1606 if (badVSync && !w->badVSync) {
1617 qCDebug(QSG_LOG_INFO,
"Window %p is determined to have broken vsync throttling (%f < %f) "
1618 "switching to system timer to drive gui thread animations to remedy this "
1619 "(however, render thread animators will likely advance at an incorrect rate).",
1620 w->window, t, threshold);
1621 startOrStopAnimationTimer();
1624 w->psTimeAccumulator = 0.0f;
1625 w->psTimeSampleCount = 0;
1629 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
1630 if (profileFrames) {
1632 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] polishAndSync: start, elapsed since last call: %d ms",
1634 int(elapsedSinceLastMs));
1636 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
1637 Q_TRACE(QSG_polishItems_entry);
1639 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1645 polishTime = timer.nsecsElapsed();
1646 Q_TRACE(QSG_polishItems_exit);
1647 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1648 QQuickProfiler::SceneGraphPolishAndSyncPolish);
1650 w = windowFor(window);
1651 if (!w || !w->thread || !w->thread
->window) {
1652 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after polishing, abort");
1656 Q_TRACE(QSG_wait_entry);
1657 w->updateDuringSync =
false;
1659 emit window->afterAnimating();
1662 QRhi::updateSwapChainProxyData(QSGRhiSupport::instance()->rhiBackend(), window);
1664 qCDebug(QSG_LOG_RENDERLOOP,
"- lock for sync");
1665 w->thread->mutex.lock();
1666 m_lockedForSync =
true;
1667 w->thread->postEvent(
new WMSyncEvent(window, inExpose, w->forceRenderPass, scProxyData));
1668 w->forceRenderPass =
false;
1670 qCDebug(QSG_LOG_RENDERLOOP,
"- wait for sync");
1672 waitTime = timer.nsecsElapsed();
1673 Q_TRACE(QSG_wait_exit);
1674 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1675 QQuickProfiler::SceneGraphPolishAndSyncWait);
1676 Q_TRACE(QSG_sync_entry);
1678 w->thread->waitCondition.wait(&w->thread->mutex);
1679 m_lockedForSync =
false;
1680 w->thread->mutex.unlock();
1681 qCDebug(QSG_LOG_RENDERLOOP,
"- unlock after sync");
1684 syncTime = timer.nsecsElapsed();
1685 Q_TRACE(QSG_sync_exit);
1686 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1687 QQuickProfiler::SceneGraphPolishAndSyncSync);
1688 Q_TRACE(QSG_animations_entry);
1697 if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
1698 auto advanceAnimations = [
this, window=QPointer(window)] {
1699 qCDebug(QSG_LOG_RENDERLOOP,
"- advancing animations");
1700 m_animation_driver->advance();
1701 qCDebug(QSG_LOG_RENDERLOOP,
"- animations done..");
1716 window->requestUpdate();
1718 emit timeToIncubate();
1721#if defined(Q_OS_APPLE)
1732 QMetaObject::invokeMethod(
this, advanceAnimations, Qt::QueuedConnection);
1737 advanceAnimations();
1739 }
else if (w->updateDuringSync) {
1740 postUpdateRequest(w);
1743 if (profileFrames) {
1744 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] Frame prepared, polish=%d ms, lock=%d ms, blockedForSync=%d ms, animations=%d ms",
1746 int(polishTime / 1000000),
1747 int((waitTime - polishTime) / 1000000),
1748 int((syncTime - waitTime) / 1000000),
1749 int((timer.nsecsElapsed() - syncTime) / 1000000));
1752 Q_TRACE(QSG_animations_exit);
1753 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
1754 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
1759 switch ((
int) e->type()) {
1761 case QEvent::Timer: {
1762 Q_ASSERT(sg->isVSyncDependent(m_animation_driver));
1763 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
1764 if (te->timerId() == m_animation_timer) {
1765 qCDebug(QSG_LOG_RENDERLOOP,
"- ticking non-render thread timer");
1766 m_animation_driver->advance();
1767 emit timeToIncubate();
1777 return QObject::event(e);
1783
1784
1785
1786
1787
1788
1789
1790
1794 qCDebug(QSG_LOG_RENDERLOOP) <<
"grab()" << window;
1796 Window *w = windowFor(window);
1799 if (!w->thread->isRunning())
1802 if (!window->handle())
1805 qCDebug(QSG_LOG_RENDERLOOP,
"- polishing items");
1806 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1812 w->thread->mutex.lock();
1813 m_lockedForSync =
true;
1814 qCDebug(QSG_LOG_RENDERLOOP,
"- posting grab event");
1815 w->thread->postEvent(
new WMGrabEvent(window, &result));
1816 w->thread->waitCondition.wait(&w->thread->mutex);
1817 m_lockedForSync =
false;
1818 w->thread->mutex.unlock();
1820 qCDebug(QSG_LOG_RENDERLOOP,
"- grab complete");
1826
1827
1828
1831 Window *w = windowFor(window);
1832 if (w && w->thread && w->thread
->window)
1840#include "qsgthreadedrenderloop.moc"
1841#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