diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2023-01-25 19:47:17 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2023-02-08 20:14:43 +0000 |
commit | ac5bbbe4aca5e1abef4972aeeba22c8c2f923705 (patch) | |
tree | a6356c88de77576327f25d95d3df76bd44f95049 /src/quick/scenegraph/qsgcontext.cpp | |
parent | 403f4117e2ec25cc339876102dd9a25abcf0f442 (diff) |
Have a way to always use an elapsed time based animation driver
Set QSG_USE_SIMPLE_ANIMATION_DRIVER=1 to get an alternative animation
driver. (applies when using the threaded render loop) This behaves like
the fallback mode (TimerMode) of the other one: it advances based on
whatever delta the QElapsedTimer reports. Thus there is no dependency
on vsync-based throttling whatsoever, and a big chunk of the workaround
infrastructure (such as needing to fall back to QTimer with multiple
windows) is simply not needed.
It should be evaluated going forward if this should become the default.
For now, in order to avoid any surprises, it is only opt-in.
Pick-to: 6.5
Task-number: QTBUG-84328
Change-Id: I8e466caa08ba5f0471d767b1600d198ba787d8e5
Reviewed-by: Janne Koskinen <janne.p.koskinen@qt.io>
Reviewed-by: Robert Griebl <robert.griebl@qt.io>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/quick/scenegraph/qsgcontext.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgcontext.cpp | 125 |
1 files changed, 110 insertions, 15 deletions
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index f56e9898c6..be7c934fd7 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -63,6 +63,10 @@ Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph") // Timing inside the renderer base class Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer") +// Applicable for render loops that install their own animation driver, such as +// the 'threaded' loop. This env.var. is documented in the scenegraph docs. +DEFINE_BOOL_CONFIG_OPTION(useElapsedTimerBasedAnimationDriver, QSG_USE_SIMPLE_ANIMATION_DRIVER); + bool qsg_useConsistentTiming() { int use = -1; @@ -76,21 +80,9 @@ bool qsg_useConsistentTiming() class QSGAnimationDriver : public QAnimationDriver { - Q_OBJECT public: - enum Mode { - VSyncMode, - TimerMode - }; - QSGAnimationDriver(QObject *parent) : QAnimationDriver(parent) - , m_time(0) - , m_vsync(0) - , m_mode(VSyncMode) - , m_lag(0) - , m_bad(0) - , m_good(0) { QScreen *screen = QGuiApplication::primaryScreen(); if (screen) { @@ -103,6 +95,35 @@ public: } else { m_vsync = 16.67f; } + } + + float vsyncInterval() const { return m_vsync; } + + virtual bool isVSyncDependent() const = 0; + +protected: + float m_vsync = 0; +}; + +// default as in default for the threaded render loop +class QSGDefaultAnimationDriver : public QSGAnimationDriver +{ + Q_OBJECT +public: + enum Mode { + VSyncMode, + TimerMode + }; + + QSGDefaultAnimationDriver(QObject *parent) + : QSGAnimationDriver(parent) + , m_time(0) + , m_mode(VSyncMode) + , m_lag(0) + , m_bad(0) + , m_good(0) + { + QScreen *screen = QGuiApplication::primaryScreen(); if (screen && !qsg_useConsistentTiming()) { if (m_vsync <= 0) m_mode = TimerMode; @@ -192,10 +213,12 @@ public: advanceAnimation(); } - float vsyncInterval() const { return m_vsync; } // this should always return something sane, regardless of m_mode + bool isVSyncDependent() const override + { + return true; + } double m_time; - float m_vsync; Mode m_mode; QElapsedTimer m_timer; QElapsedTimer m_wallTime; @@ -204,6 +227,67 @@ public: int m_good; }; +// Advance based on QElapsedTimer. (so like the TimerMode of QSGDefaultAnimationDriver) +// Does not depend on vsync-based throttling. +// +// NB this is not the same as not installing a QAnimationDriver: the built-in +// approach in QtCore is to rely on 16 ms timer events which are potentially a +// lot less accurate. +// +// This has the benefits of: +// - not needing any of the infrastructure for falling back to a +// QTimer when there are multiple windows, +// - needing no heuristics trying determine if vsync-based throttling +// is missing or broken, +// - being compatible with any kind of temporal drifts in vsync throttling +// which is reportedly happening in various environments and platforms +// still, +// - not being tied to the primary screen's refresh rate, i.e. this is +// correct even if the window is on some secondary screen with a +// different refresh rate, +// - not having to worry about the potential effects of variable refresh +// rate solutions, +// - render thread animators work correctly regardless of vsync. +// +// On the downside, some animations might appear less smooth (compared to the +// ideal single window case of QSGDefaultAnimationDriver). +// +class QSGElapsedTimerAnimationDriver : public QSGAnimationDriver +{ +public: + QSGElapsedTimerAnimationDriver(QObject *parent) + : QSGAnimationDriver(parent) + { + qCDebug(QSG_LOG_INFO, "Animation Driver: using QElapsedTimer, thread %p %s", + QThread::currentThread(), + QThread::currentThread() == qGuiApp->thread() ? "(gui/main thread)" : "(render thread)"); + } + + void start() override + { + m_wallTime.restart(); + QAnimationDriver::start(); + } + + qint64 elapsed() const override + { + return m_wallTime.elapsed(); + } + + void advance() override + { + advanceAnimation(); + } + + bool isVSyncDependent() const override + { + return false; + } + +private: + QElapsedTimer m_wallTime; +}; + /*! \class QSGContext @@ -270,7 +354,10 @@ QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *) */ QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent) { - return new QSGAnimationDriver(parent); + if (useElapsedTimerBasedAnimationDriver()) + return new QSGElapsedTimerAnimationDriver(parent); + + return new QSGDefaultAnimationDriver(parent); } /*! @@ -282,6 +369,14 @@ float QSGContext::vsyncIntervalForAnimationDriver(QAnimationDriver *driver) return static_cast<QSGAnimationDriver *>(driver)->vsyncInterval(); } +/*! + \return true if \a driver relies on vsync-based throttling in some form. + */ +bool QSGContext::isVSyncDependent(QAnimationDriver *driver) +{ + return static_cast<QSGAnimationDriver *>(driver)->isVSyncDependent(); +} + QSize QSGContext::minimumFBOSize() const { return QSize(1, 1); |