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 cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
943 cd->swapchain->setWindow(
window);
945 QSGRhiSupport::instance()->applySwapChainFormat(cd->swapchain, window);
946 qCDebug(QSG_LOG_INFO,
"MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
949 cd->swapchain->setFlags(flags);
950 cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
951 cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
957 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run()");
960 if (QQmlDebugConnector::service<QQmlProfilerService>())
961 QQuickProfiler::registerAnimationCallback();
963 m_threadTimeBetweenRenders.start();
967 QMacAutoReleasePool frameReleasePool;
984 QEvent *e =
new QEvent(QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure));
985 QCoreApplication::postEvent(window, e);
990 QCoreApplication::processEvents();
992 if (active && (pendingUpdate == 0 || !window)) {
993 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"done drawing, sleep...");
1000 Q_ASSERT_X(!
rhi,
"QSGRenderThread::run()",
"The graphics context should be cleaned up before exiting the render thread...");
1002 qCDebug(QSG_LOG_RENDERLOOP,
QSG_RT_PAD,
"run() completed");
1007 sgrc->moveToThread(
wm->thread());
1008 moveToThread(
wm->thread());
1012 : sg(QSGContext::createDefaultContext())
1013 , m_animation_timer(0)
1015 m_animation_driver = sg->createAnimationDriver(
this);
1017 connect(m_animation_driver, SIGNAL(started()),
this, SLOT(animationStarted()));
1020 m_animation_driver->install();
1025 qDeleteAll(pendingRenderContexts);
1031 auto context = sg->createRenderContext();
1032 pendingRenderContexts.insert(context);
1038 w->window->requestUpdate();
1043 return m_animation_driver;
1053 for (
int i=0; i<m_windows.size(); ++i) {
1054 QQuickWindow *c = m_windows.at(i).window;
1055 if (c->isVisible() && c->isExposed())
1063 return m_animation_driver->isRunning() && anyoneShowing();
1068 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStarted()");
1069 startOrStopAnimationTimer();
1071 for (
int i=0; i<m_windows.size(); ++i)
1072 postUpdateRequest(
const_cast<Window *>(&m_windows.at(i)));
1077 qCDebug(QSG_LOG_RENDERLOOP,
"- animationStopped()");
1078 startOrStopAnimationTimer();
1084 if (!sg->isVSyncDependent(m_animation_driver))
1087 int exposedWindows = 0;
1088 int unthrottledWindows = 0;
1090 const Window *theOne =
nullptr;
1091 for (
int i=0; i<m_windows.size(); ++i) {
1092 const Window &w = m_windows.at(i);
1093 if (w.window->isVisible() && w.window->isExposed()) {
1096 if (w.actualWindowFormat.swapInterval() == 0)
1097 ++unthrottledWindows;
1123 const bool canUseVSyncBasedAnimation = exposedWindows == 1 && unthrottledWindows == 0 && badVSync == 0;
1125 if (m_animation_timer != 0 && (canUseVSyncBasedAnimation || !m_animation_driver->isRunning())) {
1126 qCDebug(QSG_LOG_RENDERLOOP,
"*** Stopping system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1127 exposedWindows, unthrottledWindows, badVSync);
1128 killTimer(m_animation_timer);
1129 m_animation_timer = 0;
1131 if (m_animation_driver->isRunning())
1132 postUpdateRequest(
const_cast<Window *>(theOne));
1133 }
else if (m_animation_timer == 0 && !canUseVSyncBasedAnimation && m_animation_driver->isRunning()) {
1134 qCDebug(QSG_LOG_RENDERLOOP,
"*** Starting system (not vsync-based) animation timer (exposedWindows=%d unthrottledWindows=%d badVSync=%d)",
1135 exposedWindows, unthrottledWindows, badVSync);
1136 m_animation_timer = startTimer(
int(sg->vsyncIntervalForAnimationDriver(m_animation_driver)));
1141
1142
1143
1144
1145
1146
1147
1148
1149
1153 qCDebug(QSG_LOG_RENDERLOOP) <<
"hide()" << window;
1155 if (window->isExposed())
1156 handleObscurity(windowFor(window));
1163 qCDebug(QSG_LOG_RENDERLOOP) <<
"resize()" << window;
1165 Window *w = windowFor(window);
1169 w->psTimeAccumulator = 0.0f;
1170 w->psTimeSampleCount = 0;
1174
1175
1176
1177
1180 qCDebug(QSG_LOG_RENDERLOOP) <<
"begin windowDestroyed()" << window;
1182 Window *w = windowFor(window);
1187 releaseResources(w,
true);
1190 while (thread->isRunning())
1191 QThread::yieldCurrentThread();
1192 Q_ASSERT(thread->thread() == QThread::currentThread());
1195 for (
int i=0; i<m_windows.size(); ++i) {
1196 if (m_windows.at(i).window == window) {
1197 m_windows.removeAt(i);
1205 startOrStopAnimationTimer();
1207 qCDebug(QSG_LOG_RENDERLOOP) <<
"done windowDestroyed()" << window;
1212 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1213 delete wd->rpDescForSwapchain;
1214 wd->rpDescForSwapchain =
nullptr;
1215 delete wd->swapchain;
1216 wd->swapchain =
nullptr;
1217 delete wd->depthStencilForSwapchain;
1218 wd->depthStencilForSwapchain =
nullptr;
1219 wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable =
false;
1224 qCDebug(QSG_LOG_RENDERLOOP) <<
"exposureChanged()" << window;
1232 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
1233 if (!window->isExposed())
1234 wd->hasRenderableSwapchain =
false;
1236 bool skipThisExpose =
false;
1237 if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
1238 wd->hasRenderableSwapchain =
false;
1239 skipThisExpose =
true;
1242 if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
1243 && !wd->swapchain->surfacePixelSize().isEmpty())
1245 wd->hasRenderableSwapchain =
true;
1246 wd->swapchainJustBecameRenderable =
true;
1249 if (window->isExposed()) {
1250 if (!skipThisExpose)
1251 handleExposure(window);
1253 Window *w = windowFor(window);
1260
1261
1262
1265 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleExposure()" << window;
1267 Window *w = windowFor(window);
1269 qCDebug(QSG_LOG_RENDERLOOP,
"- adding window to list");
1271 win.window = window;
1272 win.actualWindowFormat = window->format();
1273 auto renderContext = QQuickWindowPrivate::get(window)->context;
1275 pendingRenderContexts.remove(renderContext);
1277 win.updateDuringSync =
false;
1278 win.forceRenderPass =
true;
1279 win.badVSync =
false;
1280 win.timeBetweenPolishAndSyncs.start();
1281 win.psTimeAccumulator = 0.0f;
1282 win.psTimeSampleCount = 0;
1284 w = &m_windows.last();
1286 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1287 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1293 if (w->window->width() <= 0 || w->window->height() <= 0
1294 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
1295 qWarning().noquote().nospace() <<
"QSGThreadedRenderLoop: expose event received for window "
1296 << w->window <<
" with invalid geometry: " << w->window->geometry()
1297 <<
" on " << w->window->screen();
1303 if (!w->window->handle())
1304 w->window->create();
1307 if (!w->thread->isRunning()) {
1308 qCDebug(QSG_LOG_RENDERLOOP,
"- starting render thread");
1314 if (!w->thread
->rhi) {
1315 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
1316 if (!w->thread->offscreenSurface)
1317 w->thread->offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
1318 w->thread->scProxyData = QRhi::updateSwapChainProxyData(rhiSupport->rhiBackend(), window);
1319 window->installEventFilter(
this);
1322 QQuickAnimatorController *controller
1323 = QQuickWindowPrivate::get(w->window)->animationController.get();
1324 if (controller->thread() != w->thread)
1325 controller->moveToThread(w->thread);
1328 if (w->thread->thread() == QThread::currentThread()) {
1329 w->thread
->sgrc->moveToThread(w->thread);
1330 w->thread->moveToThread(w->thread);
1333 if (!w->thread->isRunning())
1334 qFatal(
"Render thread failed to start, aborting application.");
1337 qCDebug(QSG_LOG_RENDERLOOP,
"- render thread already running");
1340 w->thread->mutex.lock();
1341 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Exposed)));
1342 w->thread->waitCondition.wait(&w->thread->mutex);
1343 w->thread->mutex.unlock();
1346 polishAndSync(w,
true);
1347 qCDebug(QSG_LOG_RENDERLOOP,
"- done with handleExposure()");
1349 startOrStopAnimationTimer();
1353
1354
1355
1356
1357
1358
1364 qCDebug(QSG_LOG_RENDERLOOP) <<
"handleObscurity()" << w->window;
1365 if (w->thread->isRunning()) {
1366 if (!QQuickWindowPrivate::get(w->window)->updatesEnabled) {
1367 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1370 w->thread->mutex.lock();
1371 w->thread->postEvent(
new WMWindowEvent(w->window, QEvent::Type(WM_Obscure)));
1372 w->thread->waitCondition.wait(&w->thread->mutex);
1373 w->thread->mutex.unlock();
1375 startOrStopAnimationTimer();
1380 switch (event->type()) {
1381 case QEvent::PlatformSurface:
1383 if (
static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1384 QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
1386 Window *w = windowFor(window);
1387 if (w && w->thread->isRunning()) {
1388 w->thread->mutex.lock();
1390 w->thread->waitCondition.wait(&w->thread->mutex);
1391 w->thread->mutex.unlock();
1401 return QObject::eventFilter(watched, event);
1406 qCDebug(QSG_LOG_RENDERLOOP) <<
"- update request" << window;
1407 if (!QQuickWindowPrivate::get(window)->updatesEnabled) {
1408 qCDebug(QSG_LOG_RENDERLOOP,
"- updatesEnabled is false, abort");
1411 Window *w = windowFor(window);
1418 Window *w = windowFor(window);
1424
1425
1426
1429 if (!QCoreApplication::instance())
1432 if (!w || !w->thread->isRunning())
1435 QThread *current = QThread::currentThread();
1436 if (current == w->thread && w->thread
->rhi && w->thread
->rhi->isDeviceLost())
1438 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
1439 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
1443 qCDebug(QSG_LOG_RENDERLOOP) <<
"update from item" << w->window;
1447 if (current == w->thread) {
1448 qCDebug(QSG_LOG_RENDERLOOP,
"- on render thread");
1449 w->updateDuringSync =
true;
1460 postUpdateRequest(w);
1464
1465
1466
1467
1470 Window *w = windowFor(window);
1474 const bool isRenderThread = QThread::currentThread() == w->thread;
1476 if (QPlatformWindow *platformWindow = window->handle()) {
1480 if (isRenderThread && !platformWindow->allowsIndependentThreadedRendering()) {
1484 qCDebug(QSG_LOG_RENDERLOOP) <<
"window is resizing. update on window" << w->window;
1485 QTimer::singleShot(0, window, [=]{ window->requestUpdate(); });
1490 if (isRenderThread) {
1491 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window - on render thread" << w->window;
1496 qCDebug(QSG_LOG_RENDERLOOP) <<
"update on window" << w->window;
1499 w->forceRenderPass =
true;
1506 Window *w = windowFor(window);
1508 releaseResources(w,
false);
1512
1513
1514
1517 qCDebug(QSG_LOG_RENDERLOOP) <<
"releaseResources()" << (inDestructor ?
"in destructor" :
"in api-call") << w->window;
1519 w->thread->mutex.lock();
1520 if (w->thread->isRunning() && w->thread
->active) {
1521 QQuickWindow *window = w->window;
1528 qCDebug(QSG_LOG_RENDERLOOP,
"- posting release request to render thread");
1529 w->thread->postEvent(
new WMTryReleaseEvent(window, inDestructor, window->handle() ==
nullptr));
1530 w->thread->waitCondition.wait(&w->thread->mutex);
1539 qCDebug(QSG_LOG_RENDERLOOP) <<
" - waiting for render thread to exit" << w->window;
1541 qCDebug(QSG_LOG_RENDERLOOP) <<
" - render thread finished" << w->window;
1544 w->thread->mutex.unlock();
1549
1550
1553 qCDebug(QSG_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
1555 QQuickWindow *window = w->window;
1556 if (!w->thread || !w->thread
->window) {
1557 qCDebug(QSG_LOG_RENDERLOOP,
"- not exposed, abort");
1562 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
1564 w = windowFor(window);
1565 if (!w || !w->thread || !w->thread
->window) {
1566 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after event flushing, abort");
1570 Q_TRACE_SCOPE(QSG_polishAndSync);
1571 QElapsedTimer timer;
1572 qint64 polishTime = 0;
1573 qint64 waitTime = 0;
1574 qint64 syncTime = 0;
1576 const qint64 elapsedSinceLastMs = w->timeBetweenPolishAndSyncs.restart();
1578 if (w->actualWindowFormat.swapInterval() != 0 && sg->isVSyncDependent(m_animation_driver)) {
1579 w->psTimeAccumulator += elapsedSinceLastMs;
1580 w->psTimeSampleCount += 1;
1582 static const int PS_TIME_SAMPLE_LENGTH = 20;
1583 if (w->psTimeSampleCount > PS_TIME_SAMPLE_LENGTH) {
1584 const float t = w->psTimeAccumulator / w->psTimeSampleCount;
1585 const float vsyncRate = sg->vsyncIntervalForAnimationDriver(m_animation_driver);
1605 const float threshold = vsyncRate * 0.5f;
1606 const bool badVSync = t < threshold;
1607 if (badVSync && !w->badVSync) {
1618 qCDebug(QSG_LOG_INFO,
"Window %p is determined to have broken vsync throttling (%f < %f) "
1619 "switching to system timer to drive gui thread animations to remedy this "
1620 "(however, render thread animators will likely advance at an incorrect rate).",
1621 w->window, t, threshold);
1622 startOrStopAnimationTimer();
1625 w->psTimeAccumulator = 0.0f;
1626 w->psTimeSampleCount = 0;
1630 const bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
1631 if (profileFrames) {
1633 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] polishAndSync: start, elapsed since last call: %d ms",
1635 int(elapsedSinceLastMs));
1637 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
1638 Q_TRACE(QSG_polishItems_entry);
1640 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1646 polishTime = timer.nsecsElapsed();
1647 Q_TRACE(QSG_polishItems_exit);
1648 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1649 QQuickProfiler::SceneGraphPolishAndSyncPolish);
1651 w = windowFor(window);
1652 if (!w || !w->thread || !w->thread
->window) {
1653 qCDebug(QSG_LOG_RENDERLOOP,
"- removed after polishing, abort");
1657 Q_TRACE(QSG_wait_entry);
1658 w->updateDuringSync =
false;
1660 emit window->afterAnimating();
1663 QRhi::updateSwapChainProxyData(QSGRhiSupport::instance()->rhiBackend(), window);
1665 qCDebug(QSG_LOG_RENDERLOOP,
"- lock for sync");
1666 w->thread->mutex.lock();
1667 m_lockedForSync =
true;
1668 w->thread->postEvent(
new WMSyncEvent(window, inExpose, w->forceRenderPass, scProxyData));
1669 w->forceRenderPass =
false;
1671 qCDebug(QSG_LOG_RENDERLOOP,
"- wait for sync");
1673 waitTime = timer.nsecsElapsed();
1674 Q_TRACE(QSG_wait_exit);
1675 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1676 QQuickProfiler::SceneGraphPolishAndSyncWait);
1677 Q_TRACE(QSG_sync_entry);
1679 w->thread->waitCondition.wait(&w->thread->mutex);
1680 m_lockedForSync =
false;
1681 w->thread->mutex.unlock();
1682 qCDebug(QSG_LOG_RENDERLOOP,
"- unlock after sync");
1685 syncTime = timer.nsecsElapsed();
1686 Q_TRACE(QSG_sync_exit);
1687 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
1688 QQuickProfiler::SceneGraphPolishAndSyncSync);
1689 Q_TRACE(QSG_animations_entry);
1698 if (m_animation_timer == 0 && m_animation_driver->isRunning()) {
1699 auto advanceAnimations = [
this, window=QPointer(window)] {
1700 qCDebug(QSG_LOG_RENDERLOOP,
"- advancing animations");
1701 m_animation_driver->advance();
1702 qCDebug(QSG_LOG_RENDERLOOP,
"- animations done..");
1717 window->requestUpdate();
1719 emit timeToIncubate();
1722#if defined(Q_OS_APPLE)
1733 QMetaObject::invokeMethod(
this, advanceAnimations, Qt::QueuedConnection);
1738 advanceAnimations();
1740 }
else if (w->updateDuringSync) {
1741 postUpdateRequest(w);
1744 if (profileFrames) {
1745 qCDebug(QSG_LOG_TIME_RENDERLOOP,
"[window %p][gui thread] Frame prepared, polish=%d ms, lock=%d ms, blockedForSync=%d ms, animations=%d ms",
1747 int(polishTime / 1000000),
1748 int((waitTime - polishTime) / 1000000),
1749 int((syncTime - waitTime) / 1000000),
1750 int((timer.nsecsElapsed() - syncTime) / 1000000));
1753 Q_TRACE(QSG_animations_exit);
1754 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
1755 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
1760 switch ((
int) e->type()) {
1762 case QEvent::Timer: {
1763 Q_ASSERT(sg->isVSyncDependent(m_animation_driver));
1764 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
1765 if (te->timerId() == m_animation_timer) {
1766 qCDebug(QSG_LOG_RENDERLOOP,
"- ticking non-render thread timer");
1767 m_animation_driver->advance();
1768 emit timeToIncubate();
1778 return QObject::event(e);
1784
1785
1786
1787
1788
1789
1790
1791
1795 qCDebug(QSG_LOG_RENDERLOOP) <<
"grab()" << window;
1797 Window *w = windowFor(window);
1800 if (!w->thread->isRunning())
1803 if (!window->handle())
1806 qCDebug(QSG_LOG_RENDERLOOP,
"- polishing items");
1807 QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
1813 w->thread->mutex.lock();
1814 m_lockedForSync =
true;
1815 qCDebug(QSG_LOG_RENDERLOOP,
"- posting grab event");
1816 w->thread->postEvent(
new WMGrabEvent(window, &result));
1817 w->thread->waitCondition.wait(&w->thread->mutex);
1818 m_lockedForSync =
false;
1819 w->thread->mutex.unlock();
1821 qCDebug(QSG_LOG_RENDERLOOP,
"- grab complete");
1827
1828
1829
1832 Window *w = windowFor(window);
1833 if (w && w->thread && w->thread
->window)
1841#include "qsgthreadedrenderloop.moc"
1842#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