9#include <private/qsgrenderer_p.h>
10#include <private/qquickwindow_p.h>
11#include <private/qquickitem_p.h>
12#include <private/qquickprofiler_p.h>
13#include <private/qquickanimatorcontroller_p.h>
14#include <private/qquickprofiler_p.h>
15#include <private/qqmldebugserviceinterfaces_p.h>
16#include <private/qqmldebugconnector_p.h>
18#include <qpa/qplatformbackingstore.h>
20#include <QtCore/QQueue>
21#include <QtCore/QElapsedTimer>
22#include <QtCore/QThread>
23#include <QtCore/QMutex>
24#include <QtCore/QWaitCondition>
25#include <QtGui/QGuiApplication>
26#include <QtGui/QBackingStore>
27#include <QtQuick/QQuickWindow>
29#include <qtquick_tracepoints_p.h>
35Q_STATIC_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP,
"qt.scenegraph.renderloop")
37class QSGSoftwareWindowEvent :
public QEvent
40 QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
58 ,
dpr(c->effectiveDevicePixelRatio())
97 if (isEmpty() && wait) {
99 condition.wait(&mutex);
102 QEvent *e = dequeue();
109 bool has = !isEmpty();
116 QWaitCondition condition;
117 bool waiting =
false;
122 const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
123 return refreshRate < 1 ? 16 :
int(1000 / refreshRate);
153 pendingUpdate |= RepaintRequest;
193 switch ((
int)e->type()) {
197 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"RT - WM_Obscure" <<
exposedWindow;
200 QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
201 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Obscure - window removed");
206 waitCondition.wakeOne();
210 case WM_RequestSync: {
215 if (backingStore ==
nullptr)
216 backingStore =
new QBackingStore(exposedWindow);
219 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"RT - WM_RequestSync" <<
exposedWindow;
220 pendingUpdate |= SyncRequest;
222 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_RequestSync - triggered from expose");
223 pendingUpdate |= ExposeRequest;
226 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_RequestSync - repaint regardless");
227 pendingUpdate |= RepaintRequest;
232 case WM_TryRelease: {
233 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease");
239 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease - invalidating rc");
241 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
244 wd->cleanupNodesOnShutdown();
247 QCoreApplication::processEvents();
248 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
250 wd->animationController.reset();
257 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease - not releasing because window is still active");
259 waitCondition.wakeOne();
266 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Grab");
268 Q_ASSERT(wme->window);
278 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
279 auto softwareRenderer =
static_cast<QSGSoftwareRenderer*>(wd->renderer);
280 if (softwareRenderer)
282 rc->initialize(
nullptr);
283 wd->syncSceneGraph();
285 wd->renderSceneGraph();
288 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Grab - waking gui to handle result");
289 waitCondition.wakeOne();
295 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_PostJob");
302 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_PostJob - job done");
311 return QThread::event(e);
316 eventQueue.addEvent(e);
321 while (eventQueue.hasMoreEvents()) {
322 QEvent *e = eventQueue.takeEvent(
false);
332 QEvent *e = eventQueue.takeEvent(
true);
340 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - run()");
342 rtAnim = rc->sceneGraphContext()->createAnimationDriver(
nullptr);
345 if (QQmlDebugConnector::service<QQmlProfilerService>())
346 QQuickProfiler::registerAnimationCallback();
348 renderThrottleTimer.start();
355 QCoreApplication::processEvents();
357 if (pendingUpdate == 0 || !exposedWindow) {
358 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - done drawing, sleep");
365 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - run() exiting");
370 rc->moveToThread(renderLoop->thread());
376 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - sync");
379 Q_ASSERT_X(
renderLoop->lockedForSync,
"QSGSoftwareRenderThread::sync()",
"sync triggered with gui not locked");
382 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
383 bool hadRenderer = wd->renderer !=
nullptr;
387 wd->renderer->clearChangedFlag();
389 rc->initialize(
nullptr);
390 wd->syncSceneGraph();
393 if (!hadRenderer && wd->renderer) {
394 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - created renderer");
396 connect(wd->renderer, &QSGRenderer::sceneGraphChanged,
this,
397 &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection);
403 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
407 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - sync complete, waking gui");
408 waitCondition.wakeOne();
415 Q_TRACE_SCOPE(QSG_syncAndRender);
416 Q_TRACE(QSG_sync_entry);
417 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
419 QElapsedTimer waitTimer;
422 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - syncAndRender()");
425 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
427 const bool repaintRequested = pendingUpdate & RepaintRequest;
428 const bool syncRequested = pendingUpdate & SyncRequest;
429 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
432 emit exposedWindow->beforeFrameBegin();
437 Q_TRACE(QSG_sync_exit);
438 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
439 QQuickProfiler::SceneGraphRenderLoopSync);
442 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - no changes, render aborted");
443 int waitTime =
vsyncDelta - (
int) waitTimer.elapsed();
449 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - rendering started");
450 Q_TRACE(QSG_render_entry);
452 if (rtAnim->isRunning()) {
453 wd->animationController->lock();
455 wd->animationController->unlock();
458 bool canRender = wd->renderer !=
nullptr;
461 auto softwareRenderer =
static_cast<QSGSoftwareRenderer*>(wd->renderer);
462 if (softwareRenderer)
464 wd->renderSceneGraph();
466 Q_TRACE(QSG_render_exit);
467 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
468 QQuickProfiler::SceneGraphRenderLoopRender);
469 Q_TRACE(QSG_swap_entry);
471 if (softwareRenderer)
476 int blockTime = vsyncDelta - (
int) renderThrottleTimer.elapsed();
478 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - blocking for %d ms", blockTime);
481 renderThrottleTimer.start();
483 wd->fireFrameSwapped();
485 Q_TRACE(QSG_render_exit);
486 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
487 QQuickProfiler::SceneGraphRenderLoopSync, 1);
488 Q_TRACE(QSG_swap_entry);
489 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - window not ready, skipping render");
492 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - rendering done");
494 emit exposedWindow->afterFrameEnd();
496 if (exposeRequested) {
497 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - wake gui after initial expose");
498 waitCondition.wakeOne();
502 Q_TRACE(QSG_swap_exit);
503 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
504 QQuickProfiler::SceneGraphRenderLoopSwap);
509 for (
const auto &t : std::as_const(m_windows)) {
510 if (t.window == window)
511 return const_cast<WindowData *>(&t);
519 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"software threaded render loop constructor");
521 m_anim = m_sg->createAnimationDriver(
this);
522 connect(m_anim, &QAnimationDriver::started,
this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted);
529 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"software threaded render loop destructor");
535 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"show" << window;
540 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"hide" << window;
542 if (window->isExposed())
543 handleObscurity(windowFor(window));
545 releaseResources(window);
550 if (!window->isExposed() || window->size().isEmpty())
553 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"resize" << window << window->size();
558 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"window destroyed" << window;
560 WindowData *w = windowFor(window);
565 handleResourceRelease(w,
true);
568 while (thread->isRunning())
569 QThread::yieldCurrentThread();
571 Q_ASSERT(thread->thread() == QThread::currentThread());
574 for (
int i = 0; i < m_windows.size(); ++i) {
575 if (m_windows.at(i).window == window) {
576 m_windows.removeAt(i);
584 startOrStopAnimationTimer();
589 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"exposure changed" << window;
591 if (window->isExposed()) {
592 handleExposure(window);
594 WindowData *w = windowFor(window);
602 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"grab" << window;
604 WindowData *w = windowFor(window);
607 const bool tempExpose = !w;
609 handleExposure(window);
610 w = windowFor(window);
614 if (!w->thread->isRunning())
617 if (!window->handle())
620 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
624 w->thread->mutex.lock();
625 lockedForSync =
true;
627 w->thread->waitCondition.wait(&w->thread->mutex);
628 lockedForSync =
false;
629 w->thread->mutex.unlock();
631 result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
641 WindowData *w = windowFor(window);
645 if (w->thread == QThread::currentThread()) {
652 w->forceRenderPass =
true;
658 WindowData *w = windowFor(window);
665 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleUpdateRequest" << window;
667 WindowData *w = windowFor(window);
669 polishAndSync(w,
false);
684 return m_sg->createRenderContext();
689 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"releaseResources" << window;
691 WindowData *w = windowFor(window);
693 handleResourceRelease(w,
false);
698 WindowData *w = windowFor(window);
707 return QSurface::RasterSurface;
712 bool somethingVisible =
false;
713 for (
const WindowData &w : m_windows) {
714 if (w.window->isVisible() && w.window->isExposed()) {
715 somethingVisible =
true;
719 return somethingVisible && m_anim->isRunning();
724 return SupportsGrabWithoutExpose;
729 if (e->type() == QEvent::Timer) {
730 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
731 if (te->timerId() == animationTimer) {
733 emit timeToIncubate();
738 return QObject::event(e);
743 startOrStopAnimationTimer();
745 for (
const WindowData &w : std::as_const(m_windows))
746 w.window->requestUpdate();
751 startOrStopAnimationTimer();
756 int exposedWindowCount = 0;
757 const WindowData *exposed =
nullptr;
759 for (
int i = 0; i < m_windows.size(); ++i) {
760 const WindowData &w(m_windows[i]);
761 if (w.window->isVisible() && w.window->isExposed()) {
762 ++exposedWindowCount;
767 if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
768 killTimer(animationTimer);
771 if (m_anim->isRunning())
772 exposed->window->requestUpdate();
773 }
else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
780 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleExposure" << window;
782 WindowData *w = windowFor(window);
784 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"adding window to list");
787 QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context;
789 win.updateDuringSync =
false;
790 win.forceRenderPass =
true;
791 m_windows.append(win);
792 w = &m_windows.last();
799 if (w->window->size().isEmpty()
800 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
802 qWarning().noquote().nospace() <<
"QSGSotwareThreadedRenderLoop: expose event received for window "
803 << w->window <<
" with invalid geometry: " << w->window->geometry()
804 <<
" on " << w->window->screen();
808 if (!w->window->handle())
812 if (!w->thread->isRunning()) {
813 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"starting render thread");
815 QQuickAnimatorController *controller
816 = QQuickWindowPrivate::get(w->window)->animationController.get();
817 if (controller->thread() != w->thread)
818 controller->moveToThread(w->thread);
819 if (w->thread->thread() == QThread::currentThread()) {
820 w->thread->rc->moveToThread(w->thread);
821 w->thread->moveToThread(w->thread);
827 if (!w->thread->isRunning())
828 qFatal(
"Render thread failed to start, aborting application.");
831 polishAndSync(w,
true);
833 startOrStopAnimationTimer();
838 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleObscurity" << w->window;
840 if (w->thread->isRunning()) {
841 w->thread->mutex.lock();
842 w->thread->postEvent(
new QSGSoftwareWindowEvent(w->window, QEvent::Type(WM_Obscure)));
843 w->thread->waitCondition.wait(&w->thread->mutex);
844 w->thread->mutex.unlock();
847 startOrStopAnimationTimer();
852 if (!QCoreApplication::instance())
855 if (!w || !w->thread->isRunning())
858 QThread *current = QThread::currentThread();
859 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
860 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
864 if (current == w->thread) {
865 w->updateDuringSync =
true;
869 w->window->requestUpdate();
874 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleResourceRelease" << (destroying ?
"destroying" :
"hide/releaseResources") << w->window;
876 w->thread->mutex.lock();
877 if (w->thread->isRunning() && w->thread
->active) {
878 QQuickWindow *window = w->window;
885 w->thread->waitCondition.wait(&w->thread->mutex);
896 w->thread->mutex.unlock();
901 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
903 QQuickWindow *window = w->window;
905 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - not exposed, abort");
910 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
912 w = windowFor(window);
914 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - removed after touch event flushing, abort");
918 Q_TRACE_SCOPE(QSG_polishAndSync);
920 Q_TRACE(QSG_polishItems_entry);
921 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
923 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
926 Q_TRACE(QSG_polishItems_exit);
927 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
928 QQuickProfiler::SceneGraphPolishAndSyncPolish);
929 Q_TRACE(QSG_sync_entry);
931 w->updateDuringSync =
false;
933 emit window->afterAnimating();
935 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - lock for sync");
936 w->thread->mutex.lock();
937 lockedForSync =
true;
939 w->forceRenderPass =
false;
941 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - wait for sync");
943 Q_TRACE(QSG_sync_exit);
944 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
945 QQuickProfiler::SceneGraphPolishAndSyncWait);
946 Q_TRACE(QSG_wait_entry);
947 w->thread->waitCondition.wait(&w->thread->mutex);
948 lockedForSync =
false;
949 w->thread->mutex.unlock();
950 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - unlock after sync");
952 Q_TRACE(QSG_wait_exit);
953 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
954 QQuickProfiler::SceneGraphPolishAndSyncSync);
955 Q_TRACE(QSG_animations_entry);
957 if (!animationTimer && m_anim->isRunning()) {
958 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - advancing animations");
961 w->window->requestUpdate();
962 emit timeToIncubate();
963 }
else if (w->updateDuringSync) {
964 w->window->requestUpdate();
967 Q_TRACE(QSG_animations_exit);
968 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
969 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
974#include "qsgsoftwarethreadedrenderloop.moc"
975#include "moc_qsgsoftwarethreadedrenderloop_p.cpp"
QEvent * takeEvent(bool wait)
QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
QElapsedTimer renderThrottleTimer
bool syncResultedInChanges
QSGSoftwareRenderContext * rc
QAnimationDriver * rtAnim
QWaitCondition waitCondition
void processEventsAndWaitForMore()
QBackingStore * backingStore
QSGSoftwareThreadedRenderLoop * renderLoop
QSGSoftwareEventQueue eventQueue
void postEvent(QEvent *e)
QQuickWindow * exposedWindow
QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
void exposureChanged(QQuickWindow *window) override
QAnimationDriver * animationDriver() const override
void resize(QQuickWindow *window) override
void handleUpdateRequest(QQuickWindow *window) override
void update(QQuickWindow *window) override
void windowDestroyed(QQuickWindow *window) override
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
int flags() const override
~QSGSoftwareThreadedRenderLoop()
QImage grab(QQuickWindow *window) override
void maybeUpdate(QQuickWindow *window) override
void releaseResources(QQuickWindow *window) override
QSGContext * sceneGraphContext() const override
QSurface::SurfaceType windowSurfaceType() const override
void hide(QQuickWindow *window) override
void postJob(QQuickWindow *window, QRunnable *job) override
bool interleaveIncubation() const override
void onAnimationStopped()
void show(QQuickWindow *window) override
QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
Combined button and popup list for selecting options.
static int qsgrl_animation_interval()