8#include <private/qsgrenderer_p.h>
9#include <private/qquickwindow_p.h>
10#include <private/qquickitem_p.h>
11#include <private/qquickprofiler_p.h>
12#include <private/qquickanimatorcontroller_p.h>
13#include <private/qquickprofiler_p.h>
14#include <private/qqmldebugserviceinterfaces_p.h>
15#include <private/qqmldebugconnector_p.h>
17#include <qpa/qplatformbackingstore.h>
19#include <QtCore/QQueue>
20#include <QtCore/QElapsedTimer>
21#include <QtCore/QThread>
22#include <QtCore/QMutex>
23#include <QtCore/QWaitCondition>
24#include <QtGui/QGuiApplication>
25#include <QtGui/QBackingStore>
26#include <QtQuick/QQuickWindow>
28#include <qtquick_tracepoints_p.h>
34Q_STATIC_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP,
"qt.scenegraph.renderloop")
36class QSGSoftwareWindowEvent :
public QEvent
39 QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
57 ,
dpr(c->effectiveDevicePixelRatio())
96 if (isEmpty() && wait) {
98 condition.wait(&mutex);
101 QEvent *e = dequeue();
108 bool has = !isEmpty();
115 QWaitCondition condition;
116 bool waiting =
false;
121 const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
122 return refreshRate < 1 ? 16 :
int(1000 / refreshRate);
152 pendingUpdate |= RepaintRequest;
192 switch ((
int)e->type()) {
196 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"RT - WM_Obscure" << exposedWindow;
199 QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
200 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Obscure - window removed");
205 waitCondition.wakeOne();
209 case WM_RequestSync: {
214 if (backingStore ==
nullptr)
215 backingStore =
new QBackingStore(exposedWindow);
218 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"RT - WM_RequestSync" << exposedWindow;
219 pendingUpdate |= SyncRequest;
221 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_RequestSync - triggered from expose");
222 pendingUpdate |= ExposeRequest;
225 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_RequestSync - repaint regardless");
226 pendingUpdate |= RepaintRequest;
231 case WM_TryRelease: {
232 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease");
238 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease - invalidating rc");
240 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
243 wd->cleanupNodesOnShutdown();
246 QCoreApplication::processEvents();
247 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
249 wd->animationController.reset();
256 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_TryRelease - not releasing because window is still active");
258 waitCondition.wakeOne();
265 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Grab");
267 Q_ASSERT(wme->window);
277 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
278 auto softwareRenderer =
static_cast<QSGSoftwareRenderer*>(wd->renderer);
279 if (softwareRenderer)
281 rc->initialize(
nullptr);
282 wd->syncSceneGraph();
284 wd->renderSceneGraph();
287 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_Grab - waking gui to handle result");
288 waitCondition.wakeOne();
294 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_PostJob");
301 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - WM_PostJob - job done");
310 return QThread::event(e);
315 eventQueue.addEvent(e);
320 while (eventQueue.hasMoreEvents()) {
321 QEvent *e = eventQueue.takeEvent(
false);
331 QEvent *e = eventQueue.takeEvent(
true);
339 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - run()");
341 rtAnim = rc->sceneGraphContext()->createAnimationDriver(
nullptr);
344 if (QQmlDebugConnector::service<QQmlProfilerService>())
345 QQuickProfiler::registerAnimationCallback();
347 renderThrottleTimer.start();
354 QCoreApplication::processEvents();
356 if (pendingUpdate == 0 || !exposedWindow) {
357 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - done drawing, sleep");
364 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - run() exiting");
369 rc->moveToThread(renderLoop->thread());
375 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - sync");
378 Q_ASSERT_X(
renderLoop->lockedForSync,
"QSGSoftwareRenderThread::sync()",
"sync triggered with gui not locked");
381 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
382 bool hadRenderer = wd->renderer !=
nullptr;
386 wd->renderer->clearChangedFlag();
388 rc->initialize(
nullptr);
389 wd->syncSceneGraph();
392 if (!hadRenderer && wd->renderer) {
393 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - created renderer");
395 connect(wd->renderer, &QSGRenderer::sceneGraphChanged,
this,
396 &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection);
402 QCoreApplication::sendPostedEvents(
nullptr, QEvent::DeferredDelete);
406 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - sync complete, waking gui");
407 waitCondition.wakeOne();
414 Q_TRACE_SCOPE(QSG_syncAndRender);
415 Q_TRACE(QSG_sync_entry);
416 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
418 QElapsedTimer waitTimer;
421 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - syncAndRender()");
424 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
426 const bool repaintRequested = pendingUpdate & RepaintRequest;
427 const bool syncRequested = pendingUpdate & SyncRequest;
428 const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
431 emit exposedWindow->beforeFrameBegin();
436 Q_TRACE(QSG_sync_exit);
437 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
438 QQuickProfiler::SceneGraphRenderLoopSync);
441 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - no changes, render aborted");
442 int waitTime =
vsyncDelta - (
int) waitTimer.elapsed();
448 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - rendering started");
449 Q_TRACE(QSG_render_entry);
451 if (rtAnim->isRunning()) {
452 wd->animationController->lock();
454 wd->animationController->unlock();
457 bool canRender = wd->renderer !=
nullptr;
460 auto softwareRenderer =
static_cast<QSGSoftwareRenderer*>(wd->renderer);
461 if (softwareRenderer)
463 wd->renderSceneGraph();
465 Q_TRACE(QSG_render_exit);
466 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
467 QQuickProfiler::SceneGraphRenderLoopRender);
468 Q_TRACE(QSG_swap_entry);
470 if (softwareRenderer)
475 int blockTime = vsyncDelta - (
int) renderThrottleTimer.elapsed();
477 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - blocking for %d ms", blockTime);
480 renderThrottleTimer.start();
482 wd->fireFrameSwapped();
484 Q_TRACE(QSG_render_exit);
485 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
486 QQuickProfiler::SceneGraphRenderLoopSync, 1);
487 Q_TRACE(QSG_swap_entry);
488 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - window not ready, skipping render");
491 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - rendering done");
493 emit exposedWindow->afterFrameEnd();
495 if (exposeRequested) {
496 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"RT - wake gui after initial expose");
497 waitCondition.wakeOne();
501 Q_TRACE(QSG_swap_exit);
502 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
503 QQuickProfiler::SceneGraphRenderLoopSwap);
508 for (
const auto &t : std::as_const(m_windows)) {
509 if (t.window == window)
510 return const_cast<WindowData *>(&t);
518 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"software threaded render loop constructor");
520 m_anim = m_sg->createAnimationDriver(
this);
521 connect(m_anim, &QAnimationDriver::started,
this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted);
522 connect(m_anim, &QAnimationDriver::stopped,
this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped);
528 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"software threaded render loop destructor");
534 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"show" << window;
539 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"hide" << window;
541 if (window->isExposed())
542 handleObscurity(windowFor(window));
544 releaseResources(window);
549 if (!window->isExposed() || window->size().isEmpty())
552 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"resize" << window << window->size();
557 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"window destroyed" << window;
559 WindowData *w = windowFor(window);
564 handleResourceRelease(w,
true);
567 while (thread->isRunning())
568 QThread::yieldCurrentThread();
570 Q_ASSERT(thread->thread() == QThread::currentThread());
573 for (
int i = 0; i < m_windows.size(); ++i) {
574 if (m_windows.at(i).window == window) {
575 m_windows.removeAt(i);
583 startOrStopAnimationTimer();
588 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"exposure changed" << window;
590 if (window->isExposed()) {
591 handleExposure(window);
593 WindowData *w = windowFor(window);
601 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"grab" << window;
603 WindowData *w = windowFor(window);
606 const bool tempExpose = !w;
608 handleExposure(window);
609 w = windowFor(window);
613 if (!w->thread->isRunning())
616 if (!window->handle())
619 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
623 w->thread->mutex.lock();
624 lockedForSync =
true;
626 w->thread->waitCondition.wait(&w->thread->mutex);
627 lockedForSync =
false;
628 w->thread->mutex.unlock();
630 result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
640 WindowData *w = windowFor(window);
644 if (w->thread == QThread::currentThread()) {
651 w->forceRenderPass =
true;
657 WindowData *w = windowFor(window);
664 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleUpdateRequest" << window;
666 WindowData *w = windowFor(window);
668 polishAndSync(w,
false);
683 return m_sg->createRenderContext();
688 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"releaseResources" << window;
690 WindowData *w = windowFor(window);
692 handleResourceRelease(w,
false);
697 WindowData *w = windowFor(window);
706 return QSurface::RasterSurface;
711 bool somethingVisible =
false;
712 for (
const WindowData &w : m_windows) {
713 if (w.window->isVisible() && w.window->isExposed()) {
714 somethingVisible =
true;
718 return somethingVisible && m_anim->isRunning();
723 return SupportsGrabWithoutExpose;
728 if (e->type() == QEvent::Timer) {
729 QTimerEvent *te =
static_cast<QTimerEvent *>(e);
730 if (te->timerId() == animationTimer) {
732 emit timeToIncubate();
737 return QObject::event(e);
742 startOrStopAnimationTimer();
744 for (
const WindowData &w : std::as_const(m_windows))
745 w.window->requestUpdate();
750 startOrStopAnimationTimer();
755 int exposedWindowCount = 0;
756 const WindowData *exposed =
nullptr;
758 for (
int i = 0; i < m_windows.size(); ++i) {
759 const WindowData &w(m_windows[i]);
760 if (w.window->isVisible() && w.window->isExposed()) {
761 ++exposedWindowCount;
766 if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
767 killTimer(animationTimer);
770 if (m_anim->isRunning())
771 exposed->window->requestUpdate();
772 }
else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
779 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleExposure" << window;
781 WindowData *w = windowFor(window);
783 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"adding window to list");
786 QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context;
788 win.updateDuringSync =
false;
789 win.forceRenderPass =
true;
790 m_windows.append(win);
791 w = &m_windows.last();
798 if (w->window->size().isEmpty()
799 || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
801 qWarning().noquote().nospace() <<
"QSGSotwareThreadedRenderLoop: expose event received for window "
802 << w->window <<
" with invalid geometry: " << w->window->geometry()
803 <<
" on " << w->window->screen();
807 if (!w->window->handle())
811 if (!w->thread->isRunning()) {
812 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"starting render thread");
814 QQuickAnimatorController *controller
815 = QQuickWindowPrivate::get(w->window)->animationController.get();
816 if (controller->thread() != w->thread)
817 controller->moveToThread(w->thread);
818 if (w->thread->thread() == QThread::currentThread()) {
819 w->thread->rc->moveToThread(w->thread);
820 w->thread->moveToThread(w->thread);
826 if (!w->thread->isRunning())
827 qFatal(
"Render thread failed to start, aborting application.");
830 polishAndSync(w,
true);
832 startOrStopAnimationTimer();
837 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleObscurity" << w->window;
839 if (w->thread->isRunning()) {
840 w->thread->mutex.lock();
841 w->thread->postEvent(
new QSGSoftwareWindowEvent(w->window, QEvent::Type(WM_Obscure)));
842 w->thread->waitCondition.wait(&w->thread->mutex);
843 w->thread->mutex.unlock();
846 startOrStopAnimationTimer();
851 if (!QCoreApplication::instance())
854 if (!w || !w->thread->isRunning())
857 QThread *current = QThread::currentThread();
858 if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
859 qWarning() <<
"Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
863 if (current == w->thread) {
864 w->updateDuringSync =
true;
868 w->window->requestUpdate();
873 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"handleResourceRelease" << (destroying ?
"destroying" :
"hide/releaseResources") << w->window;
875 w->thread->mutex.lock();
876 if (w->thread->isRunning() && w->thread
->active) {
877 QQuickWindow *window = w->window;
884 w->thread->waitCondition.wait(&w->thread->mutex);
895 w->thread->mutex.unlock();
900 qCDebug(QSG_RASTER_LOG_RENDERLOOP) <<
"polishAndSync" << (inExpose ?
"(in expose)" :
"(normal)") << w->window;
902 QQuickWindow *window = w->window;
904 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - not exposed, abort");
909 QQuickWindowPrivate::get(window)->deliveryAgentPrivate()->flushFrameSynchronousEvents(window);
911 w = windowFor(window);
913 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - removed after touch event flushing, abort");
917 Q_TRACE_SCOPE(QSG_polishAndSync);
919 Q_TRACE(QSG_polishItems_entry);
920 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
922 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
925 Q_TRACE(QSG_polishItems_exit);
926 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
927 QQuickProfiler::SceneGraphPolishAndSyncPolish);
928 Q_TRACE(QSG_sync_entry);
930 w->updateDuringSync =
false;
932 emit window->afterAnimating();
934 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - lock for sync");
935 w->thread->mutex.lock();
936 lockedForSync =
true;
938 w->forceRenderPass =
false;
940 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - wait for sync");
942 Q_TRACE(QSG_sync_exit);
943 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
944 QQuickProfiler::SceneGraphPolishAndSyncWait);
945 Q_TRACE(QSG_wait_entry);
946 w->thread->waitCondition.wait(&w->thread->mutex);
947 lockedForSync =
false;
948 w->thread->mutex.unlock();
949 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - unlock after sync");
951 Q_TRACE(QSG_wait_exit);
952 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
953 QQuickProfiler::SceneGraphPolishAndSyncSync);
954 Q_TRACE(QSG_animations_entry);
956 if (!animationTimer && m_anim->isRunning()) {
957 qCDebug(QSG_RASTER_LOG_RENDERLOOP,
"polishAndSync - advancing animations");
960 w->window->requestUpdate();
961 emit timeToIncubate();
962 }
else if (w->updateDuringSync) {
963 w->window->requestUpdate();
966 Q_TRACE(QSG_animations_exit);
967 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync,
968 QQuickProfiler::SceneGraphPolishAndSyncAnimations);
973#include "qsgsoftwarethreadedrenderloop.moc"
974#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)
static int qsgrl_animation_interval()