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, {});
593 if (!
rhi || !
rhi->isDeviceLost())
596 qWarning(
"Graphics device lost, cleaning up scenegraph and releasing RHI");
603 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
604 QElapsedTimer threadTimer;
605 qint64 syncTime = 0, renderTime = 0;
608 Q_TRACE_SCOPE(QSG_syncAndRender);
609 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
610 Q_TRACE(QSG_sync_entry);
612 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"syncAndRender()");
615 const qint64 elapsedSinceLastMs = m_threadTimeBetweenRenders.restart();
616 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: start, elapsed since last call: %d ms",
618 QThread::currentThread(),
619 int(elapsedSinceLastMs));
622 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
624 const bool syncRequested = (pendingUpdate & SyncRequest);
625 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
628 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
629 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
634 const bool hasValidSwapChain = (cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0);
635 if (hasValidSwapChain) {
638 const QSize effectiveOutputSize = cd->swapchain->surfacePixelSize();
642 if (effectiveOutputSize.isEmpty()) {
645 waitCondition.wakeOne();
651 const QSize previousOutputSize = cd->swapchain->currentPixelSize();
652 if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
653 if (cd->swapchainJustBecameRenderable)
654 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"just became exposed");
656 cd->hasActiveSwapchain = cd->swapchain->createOrResize();
657 if (!cd->hasActiveSwapchain) {
658 bool bailOut =
false;
659 if (
rhi->isDeviceLost()) {
662 }
else if (previousOutputSize.isEmpty() && !swRastFallbackDueToSwapchainFailure && rhiSupport->attemptReinitWithSwRastUponFail()) {
663 qWarning(
"Failed to create swapchain."
664 " Retrying by requesting a software rasterizer, if applicable for the 3D API implementation.");
670 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
673 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed swapchain init, wake Gui");
675 waitCondition.wakeOne();
682 cd->swapchainJustBecameRenderable =
false;
683 cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
685 if (!cd->hasActiveSwapchain)
686 qWarning(
"Failed to build or resize swapchain");
688 qCDebug(QSG_LOG_RENDERLOOP) <<
"rhi swapchain size" << cd->swapchain->currentPixelSize();
691 emit window->beforeFrameBegin();
693 Q_ASSERT(
rhi == cd->rhi);
694 QRhi::FrameOpResult frameResult =
rhi->beginFrame(cd->swapchain);
695 if (frameResult != QRhi::FrameOpSuccess) {
696 if (frameResult == QRhi::FrameOpDeviceLost)
698 else if (frameResult == QRhi::FrameOpError)
699 qWarning(
"Failed to start frame");
701 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
702 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
707 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- bailing out due to failed beginFrame, wake Gui");
710 waitCondition.wakeOne();
713 emit window->afterFrameEnd();
719 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- updatePending, doing sync");
722#ifndef QSG_NO_RENDER_TIMING
724 syncTime = threadTimer.nsecsElapsed();
726 Q_TRACE(QSG_sync_exit);
727 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
728 QQuickProfiler::SceneGraphRenderLoopSync);
740 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering started");
742 Q_TRACE(QSG_render_entry);
752 d->animationController->lock();
754 d->animationController->unlock();
759 const bool canRender = d->renderer && hasValidSwapChain;
760 double lastCompletedGpuTime = 0;
763 rhi->makeThreadLocalNativeContextCurrent();
765 d->renderSceneGraph();
768 renderTime = threadTimer.nsecsElapsed();
769 Q_TRACE(QSG_render_exit);
770 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
771 QQuickProfiler::SceneGraphRenderLoopRender);
772 Q_TRACE(QSG_swap_entry);
774 QRhi::FrameOpResult frameResult =
rhi->endFrame(cd->swapchain);
775 if (frameResult != QRhi::FrameOpSuccess) {
776 if (frameResult == QRhi::FrameOpDeviceLost)
778 else if (frameResult == QRhi::FrameOpError)
779 qWarning(
"Failed to end frame");
780 if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
781 QCoreApplication::postEvent(window,
new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
783 lastCompletedGpuTime = cd->swapchain->currentFrameCommandBuffer()->lastCompletedGpuTime();
785 d->fireFrameSwapped();
787 Q_TRACE(QSG_render_exit);
788 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
789 QQuickProfiler::SceneGraphRenderLoopSync, 1);
790 Q_TRACE(QSG_swap_entry);
791 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- window not ready, skipping render");
795 if (cd->swapchain &&
rhi->isRecordingFrame())
796 rhi->endFrame(cd->swapchain, QRhi::SkipPresent);
799 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- rendering done");
803 if (hasValidSwapChain)
804 emit window->afterFrameEnd();
811 if (exposeRequested) {
813 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"- wake Gui after expose");
814 waitCondition.wakeOne();
826 qCDebug(QSG_LOG_TIME_RENDERLOOP,
827 "[window %p][render thread %p] syncAndRender: frame rendered in %dms, sync=%d, render=%d, swap=%d",
829 QThread::currentThread(),
830 int(threadTimer.elapsed()),
831 int((syncTime/1000000)),
832 int((renderTime - syncTime) / 1000000),
833 int((threadTimer.nsecsElapsed() - renderTime) / 1000000));
834 if (!qFuzzyIsNull(lastCompletedGpuTime) && cd->graphicsConfig.timestampsEnabled()) {
835 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][render thread %p] syncAndRender: last retrieved GPU frame time was %.4f ms",
837 QThread::currentThread(),
838 lastCompletedGpuTime * 1000.0);
842 Q_TRACE(QSG_swap_exit);
843 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
844 QQuickProfiler::SceneGraphRenderLoopSwap);
851 eventQueue.addEvent(e);
858 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEvents()");
859 while (eventQueue.hasMoreEvents()) {
860 QEvent *e = eventQueue.takeEvent(
false);
864 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEvents()");
869 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- begin processEventsAndWaitForMore()");
872 QEvent *e = eventQueue.takeEvent(
true);
876 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"--- done processEventsAndWaitForMore()");
884 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
886 QSGRhiSupport::RhiCreateResult rhiResult = rhiSupport->createRhi(window, offscreenSurface, forcePreferSwRenderer);
891 rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
895 qWarning(
"Failed to create QRhi on the render thread; scenegraph is not functional");
901 if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
904 rhi->makeThreadLocalNativeContextCurrent();
905 QSGDefaultRenderContext::InitParams rcParams;
908 rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
909 rcParams.maybeSurface =
window;
910 sgrc->initialize(&rcParams);
912 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
913 if (
rhi && !cd->swapchain) {
915 QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource;
916 const QSurfaceFormat requestedFormat =
window->requestedFormat();
922 const bool alpha = requestedFormat.alphaBufferSize() > 0;
924 flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
929 if (requestedFormat.swapInterval() == 0) {
930 qCDebug(QSG_LOG_INFO,
"Swap interval is 0, attempting to disable vsync when presenting.");
931 flags |= QRhiSwapChain::NoVSync;
934 cd->swapchain =
rhi->newSwapChain();
935 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
936 if (depthBufferEnabled) {
937 cd->depthStencilForSwapchain =
rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
940 QRhiRenderBuffer::UsedWithSwapChainOnly
941 | QSGRhiSupport::depthStencilBufferFlags());
942 cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
944 cd->swapchain->setWindow(
window);
946 QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain, window);
947 qCDebug(QSG_LOG_INFO,
"MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
950 cd->swapchain->setFlags(flags);
951 cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
952 cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
958 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run()");
961 if (QQmlDebugConnector::service<QQmlProfilerService>())
962 QQuickProfiler::registerAnimationCallback();
964 m_threadTimeBetweenRenders.start();
968 QMacAutoReleasePool frameReleasePool;
985 QEvent *e =
new QEvent(QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure));
986 QCoreApplication::postEvent(window, e);
991 QCoreApplication::processEvents();
993 if (active && (pendingUpdate == 0 || !window)) {
994 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"done drawing, sleep...");
1001 Q_ASSERT_X(!
rhi,
"QSGRenderThread::run()",
"The graphics context should be cleaned up before exiting the render thread...");
1003 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run() completed");
1008 sgrc->moveToThread(
wm->thread());
1009 moveToThread(
wm->thread());
1013 : sg(QSGContext::createDefaultContext())
1014 , m_animation_timer(0)
1016 m_animation_driver = sg->createAnimationDriver(
this);
1018 connect(m_animation_driver, SIGNAL(started()),
this, SLOT(animationStarted()));
1021 m_animation_driver->install();
1026 qDeleteAll(pendingRenderContexts);
1032 auto context = sg->createRenderContext();
1033 pendingRenderContexts.insert(context);
1039 w->window->requestUpdate();
1044 return m_animation_driver;
1054 for (
int i=0; i<m_windows.size(); ++i) {
1055 QQuickWindow *c = m_windows.at(i).window;
1056 if (c->isVisible() && c->isExposed())
1064 return m_animation_driver->isRunning() && anyoneShowing();
1069 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStarted()");
1070 startOrStopAnimationTimer();
1072 for (
int i=0; i<m_windows.size(); ++i)
1073 postUpdateRequest(
const_cast<Window *>(&m_windows.at(i)));
1078 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStopped()");
1079 startOrStopAnimationTimer();
1085 if (!sg->isVSyncDependent(m_animation_driver))
1088 int exposedWindows = 0;
1089 int unthrottledWindows = 0;
1091 const Window *theOne =
nullptr;
1092 for (
int i=0; i<m_windows.size(); ++i) {
1093 const Window &w = m_windows.at(i);
1094 if (w.window->isVisible() && w.window->isExposed()) {
1097 if (w.actualWindowFormat.swapInterval() == 0)
1098 ++unthrottledWindows;
1124 const bool canUseVSyncBasedAnimation = exposedWindows == 1 && unthrottledWindows == 0 && badVSync == 0;
1126 if (m_animation_timer != 0 && (canUseVSyncBasedAnimation || !m_animation_driver->isRunning())) {
1127 qCDebug(QSG_LOG_RENDERLOOP,
"*** Stopping system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1128 exposedWindows, unthrottledWindows, badVSync);
1129 killTimer(m_animation_timer);
1130 m_animation_timer = 0;
1132 if (m_animation_driver->isRunning())
1133 postUpdateRequest(
const_cast<Window *>(theOne));
1134 }
else if (m_animation_timer == 0 && !canUseVSyncBasedAnimation && m_animation_driver->isRunning()) {
1135 qCDebug(QSG_LOG_RENDERLOOP,
"*** Starting system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1136 exposedWindows, unthrottledWindows, badVSync);
1137 m_animation_timer = startTimer(
int(sg->vsyncIntervalForAnimationDriver(m_animation_driver)));
1142
1143
1144
1145
1146
1147
1148
1149
1150
1154 qCDebug(QSG_LOG_RENDERLOOP) <<
"hide()" << window;
1156 if (window->isExposed())
1157 handleObscurity(windowFor(window));
1164 qCDebug(QSG_LOG_RENDERLOOP) <<
"resize()" << window;
1166 Window *w = windowFor(window);
1170 w->psTimeAccumulator = 0.0f;
1171 w->psTimeSampleCount = 0;
1175
1176
1177
1178
1181 qCDebug(QSG_LOG_RENDERLOOP) <<
"begin windowDestroyed()" << window;
1183 Window *w = windowFor(window);
1188 releaseResources(w,
true);
1191 while (thread->isRunning())
1192 QThread::yieldCurrentThread();
1193 Q_ASSERT(thread->thread() == QThread::currentThread());
1196 for (
int i=0; i<m_windows.size(); ++i) {
1197 if (m_windows.at(i).window == window) {
1198 m_windows.removeAt(i);
1206 startOrStopAnimationTimer();
1208 qCDebug(QSG_LOG_RENDERLOOP) <<
"done windowDestroyed()" << window;
1213 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1214 delete wd->rpDescForSwapchain;
1215 wd->rpDescForSwapchain =
nullptr;
1216 delete wd->swapchain;
1217 wd->swapchain =
nullptr;
1218 delete wd->depthStencilForSwapchain;
1219 wd->depthStencilForSwapchain =
nullptr;
1220 wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable =
false;
1225 qCDebug(QSG_LOG_RENDERLOOP) <<
"exposureChanged()" << window;
1233 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1234 if (!window->isExposed())
1235 wd->hasRenderableSwapchain =
false;
1237 bool skipThisExpose =
false;
1238 if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
1239 wd->hasRenderableSwapchain =
false;
1240 skipThisExpose =
true;
1243 if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
1244 && !wd->swapchain->surfacePixelSize().isEmpty())
1246 wd->hasRenderableSwapchain =
true;
1247 wd->swapchainJustBecameRenderable =
true;
1250 if (window->isExposed()) {
1251 if (!skipThisExpose)
1252 handleExposure(window);
1254 Window *w = windowFor(window);
1261
1262
1263
1266 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleExposure()" << window;
1268 Window *w = windowFor(window);
1270 qCDebug(QSG_LOG_RENDERLOOP,
"- adding window to list");
1272 win.window = window;
1273 win.actualWindowFormat = window->format();
1274 auto renderContext = QQuickWindowPrivate::get(window)->context;
1276 pendingRenderContexts.remove(renderContext);
1278 win.updateDuringSync =
false;
1279 win.forceRenderPass =
true;
1280 win.badVSync =
false;
1281 win.timeBetweenPolishAndSyncs.start();
1282 win.psTimeAccumulator = 0.0f;
1283 win.psTimeSampleCount = 0;
1285 w = &m_windows.last();
1287 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1288 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1294 if (w->window->width() <= 0 || w->window->height() <= 0
1295 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
1296 qWarning().noquote().nospace() <<
"QSGThreadedRenderLoop: expose event received for window "
1297 << w->window <<
" with invalid geometry: " << w->window->geometry()
1298 <<
" on " << w->window->screen();
1304 if (!w->window->handle())
1305 w->window->create();
1308 if (!w->thread->isRunning()) {
1309 qCDebug(QSG_LOG_RENDERLOOP,
"- starting render thread");
1315 if (!w->thread
->rhi) {
1316 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
1317 if (!w->thread->offscreenSurface)
1318 w->thread->offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
1319 w->thread->scProxyData = QRhi::updateSwapChainProxyData(rhiSupport->rhiBackend(), window);
1320 window->installEventFilter(
this);
1323 QQuickAnimatorController *controller
1324 = QQuickWindowPrivate::get(w->window)->animationController.get();
1325 if (controller->thread() != w->thread)
1326 controller->moveToThread(w->thread);
1329 if (w->thread->thread() == QThread::currentThread()) {
1330 w->thread
->sgrc->moveToThread(w->thread);
1331 w->thread->moveToThread(w->thread);
1334 if (!w->thread->isRunning())
1335 qFatal(
"Render thread failed to start, aborting application.");
1338 qCDebug(QSG_LOG_RENDERLOOP,
"- render thread already running");
1341 w->thread->mutex.lock();
1342 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
1343 w->thread->waitCondition.wait(&w->thread->mutex);
1344 w->thread->mutex.unlock();
1347 polishAndSync(w,
true);
1348 qCDebug(QSG_LOG_RENDERLOOP,
"- done with handleExposure()");
1350 startOrStopAnimationTimer();
1354
1355
1356
1357
1358
1359
1365 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleObscurity()" << w->window;
1366 if (w->thread->isRunning()) {
1367 if (!QQuickWindowPrivate::get(w->window)->updatesEnabled) {
1368 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1371 w->thread->mutex.lock();
1372 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Obscure)));
1373 w->thread->waitCondition.wait(&w->thread->mutex);
1374 w->thread->mutex.unlock();
1376 startOrStopAnimationTimer();
1381 switch (event->type()) {
1382 case QEvent::PlatformSurface:
1384 if (
static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1385 QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
1387 Window *w = windowFor(window);
1388 if (w && w->thread->isRunning()) {
1389 w->thread->mutex.lock();
1391 w->thread->waitCondition.wait(&w->thread->mutex);
1392 w->thread->mutex.unlock();
1402 return QObject::eventFilter(watched, event);
1407 qCDebug(QSG_LOG_RENDERLOOP) <<
"- update request" << window;
1408 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1409 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1412 Window *w = windowFor(window);
1419 Window *w = windowFor(window);
1425
1426
1427
1430 if (!QCoreApplication::instance())
1433 if (!w || !w->thread->isRunning())
1436 QThread *current = QThread::currentThread();
1437 if (current == w->thread && w->thread
->rhi && w->thread
->rhi->isDeviceLost())
1439 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
1440 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
1444 qCDebug(QSG_LOG_RENDERLOOP) <<
"update from item" << w->window;
1448 if (current == w->thread) {
1449 qCDebug(QSG_LOG_RENDERLOOP,
"- on render thread");
1450 w->updateDuringSync =
true;
1461 postUpdateRequest(w);
1465
1466
1467
1468
1471 Window *w = windowFor(window);
1475 const bool isRenderThread = QThread::currentThread() == w->thread;
1477 if (QPlatformWindow *platformWindow = window->handle()) {
1481 if (isRenderThread && !platformWindow->allowsIndependentThreadedRendering()) {
1485 qCDebug(QSG_LOG_RENDERLOOP) <<
"window is resizing. update on window" << w->window;
1486 QTimer::singleShot(0, window, &QWindow::requestUpdate);
1491 if (isRenderThread) {
1492 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window - on render thread" << w->window;
1497 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window" << w->window;
1500 w->forceRenderPass =
true;
1507 Window *w = windowFor(window);
1509 releaseResources(w,
false);
1513
1514
1515
1518 qCDebug(QSG_LOG_RENDERLOOP) <<
"releaseResources()" << (inDestructor ?
"in destructor" :
"in api-call") << w->window;
1520 w->thread->mutex.lock();
1521 if (w->thread->isRunning() && w->thread
->active) {
1522 QQuickWindow *window = w->window;
1529 qCDebug(QSG_LOG_RENDERLOOP,
"- posting release request to render thread");
1530 w->thread->postEvent(
new WMTryReleaseEvent(window, inDestructor, window->handle() ==
nullptr));
1531 w->thread->waitCondition.wait(&w->thread->mutex);
1540 qCDebug(QSG_LOG_RENDERLOOP) <<
" - waiting for render thread to exit" << w->window;
1542 qCDebug(QSG_LOG_RENDERLOOP) <<
" - render thread finished" << w->window;
1545 w->thread->mutex.unlock();
1550
1551
1554 qCDebug(QSG_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
1556 QQuickWindow *window = w->window;
1557 if (!w->thread || !w->thread
->window) {
1558 qCDebug(QSG_LOG_RENDERLOOP,
"- not exposed, abort");
1563 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
1565 w = windowFor(window);
1566 if (!w || !w->thread || !w->thread
->window) {
1567 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after event flushing, abort");
1571 Q_TRACE_SCOPE(QSG_polishAndSync);
1572 QElapsedTimer timer;
1573 qint64 polishTime = 0;
1574 qint64 waitTime = 0;
1575 qint64 syncTime = 0;
1577 const qint64 elapsedSinceLastMs = w->timeBetweenPolishAndSyncs.restart();
1579 if (w->actualWindowFormat.swapInterval() != 0 && sg->isVSyncDependent(m_animation_driver)) {
1580 w->psTimeAccumulator += elapsedSinceLastMs;
1581 w->psTimeSampleCount += 1;
1583 static const int PS_TIME_SAMPLE_LENGTH = 20;
1584 if (w->psTimeSampleCount > PS_TIME_SAMPLE_LENGTH) {
1585 const float t = w->psTimeAccumulator / w->psTimeSampleCount;
1586 const float vsyncRate = sg->vsyncIntervalForAnimationDriver(m_animation_driver);
1606 const float threshold = vsyncRate * 0.5f;
1607 const bool badVSync = t < threshold;
1608 if (badVSync && !w->badVSync) {
1619 qCDebug(QSG_LOG_INFO,
"Window %p is determined to have broken vsync throttling (%f < %f) "
1620 "switching to system timer to drive gui thread animations to remedy this "
1621 "(however, render thread animators will likely advance at an incorrect rate).",
1622 w->window, t, threshold);
1623 startOrStopAnimationTimer();
1626 w->psTimeAccumulator = 0.0f;
1627 w->psTimeSampleCount = 0;
1631 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
1632 if (profileFrames) {
1634 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] polishAndSync: start, elapsed since last call: %d ms",
1636 int(elapsedSinceLastMs));
1638 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
1639 Q_TRACE(QSG_polishItems_entry);
1641 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1647 polishTime = timer.nsecsElapsed();
1648 Q_TRACE(QSG_polishItems_exit);
1649 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1650 QQuickProfiler::SceneGraphPolishAndSyncPolish);
1652 w = windowFor(window);
1653 if (!w || !w->thread || !w->thread
->window) {
1654 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after polishing, abort");
1658 Q_TRACE(QSG_wait_entry);
1659 w->updateDuringSync =
false;
1661 emit window->afterAnimating();
1664 QRhi::updateSwapChainProxyData(QSGRhiSupport::instance()->rhiBackend(), window);
1666 qCDebug(QSG_LOG_RENDERLOOP,
"- lock for sync");
1667 w->thread->mutex.lock();
1668 m_lockedForSync =
true;
1669 w->thread->postEvent(
new WMSyncEvent(window, inExpose, w->forceRenderPass, scProxyData));
1670 w->forceRenderPass =
false;
1672 qCDebug(QSG_LOG_RENDERLOOP,
"- wait for sync");
1674 waitTime = timer.nsecsElapsed();
1675 Q_TRACE(QSG_wait_exit);
1676 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1677 QQuickProfiler::SceneGraphPolishAndSyncWait);
1678 Q_TRACE(QSG_sync_entry);
1680 w->thread->waitCondition.wait(&w->thread->mutex);
1681 m_lockedForSync =
false;
1682 w->thread->mutex.unlock();
1683 qCDebug(QSG_LOG_RENDERLOOP,
"- unlock after sync");
1686 syncTime = timer.nsecsElapsed();
1687 Q_TRACE(QSG_sync_exit);
1688 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1689 QQuickProfiler::SceneGraphPolishAndSyncSync);
1690 Q_TRACE(QSG_animations_entry);
1699 if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
1700 auto advanceAnimations = [
this, window=QPointer(window)] {
1701 qCDebug(QSG_LOG_RENDERLOOP,
"- advancing animations");
1702 m_animation_driver->advance();
1703 qCDebug(QSG_LOG_RENDERLOOP,
"- animations done..");
1718 window->requestUpdate();
1720 emit timeToIncubate();
1723#if defined(Q_OS_APPLE)
1734 QMetaObject::invokeMethod(
this, advanceAnimations, Qt::QueuedConnection);
1739 advanceAnimations();
1741 }
else if (w->updateDuringSync) {
1742 postUpdateRequest(w);
1745 if (profileFrames) {
1746 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] Frame prepared, polish=%d ms, lock=%d ms, blockedForSync=%d ms, animations=%d ms",
1748 int(polishTime / 1000000),
1749 int((waitTime - polishTime) / 1000000),
1750 int((syncTime - waitTime) / 1000000),
1751 int((timer.nsecsElapsed() - syncTime) / 1000000));
1754 Q_TRACE(QSG_animations_exit);
1755 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
1756 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
1761 switch ((
int) e->type()) {
1763 case QEvent::Timer: {
1764 Q_ASSERT(sg->isVSyncDependent(m_animation_driver));
1765 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
1766 if (te->timerId() == m_animation_timer) {
1767 qCDebug(QSG_LOG_RENDERLOOP,
"- ticking non-render thread timer");
1768 m_animation_driver->advance();
1769 emit timeToIncubate();
1779 return QObject::event(e);
1785
1786
1787
1788
1789
1790
1791
1792
1796 qCDebug(QSG_LOG_RENDERLOOP) <<
"grab()" << window;
1798 Window *w = windowFor(window);
1801 if (!w->thread->isRunning())
1804 if (!window->handle())
1807 qCDebug(QSG_LOG_RENDERLOOP,
"- polishing items");
1808 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1814 w->thread->mutex.lock();
1815 m_lockedForSync =
true;
1816 qCDebug(QSG_LOG_RENDERLOOP,
"- posting grab event");
1817 w->thread->postEvent(
new WMGrabEvent(window, &result));
1818 w->thread->waitCondition.wait(&w->thread->mutex);
1819 m_lockedForSync =
false;
1820 w->thread->mutex.unlock();
1822 qCDebug(QSG_LOG_RENDERLOOP,
"- grab complete");
1828
1829
1830
1833 Window *w = windowFor(window);
1834 if (w && w->thread && w->thread
->window)
1842#include "qsgthreadedrenderloop.moc"
1843#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