aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgcontext.cpp
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@digia.com>2014-10-09 18:13:54 +0200
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-10-09 18:14:56 +0200
commite1dfb78667bd7e8dc418e12a9669404adea7e2cb (patch)
tree16f40a9852281a63c1f98af21c680bddedaa5258 /src/quick/scenegraph/qsgcontext.cpp
parent28949c9699014b4f7abba396c28dea207b29821e (diff)
parent26bbd784d67d151eee531e5ff57977a5353549f5 (diff)
Merge remote-tracking branch 'origin/5.4' into dev
Conflicts: src/quick/items/context2d/qquickcanvasitem.cpp src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp src/quick/scenegraph/coreapi/qsgrenderer.cpp src/quick/scenegraph/qsgadaptationlayer.cpp src/quick/scenegraph/qsgrenderloop.cpp src/quick/scenegraph/qsgthreadedrenderloop.cpp src/quick/scenegraph/qsgwindowsrenderloop.cpp src/quick/scenegraph/util/qsgatlastexture.cpp src/quick/scenegraph/util/qsgtexture.cpp src/quick/util/qquickprofiler_p.h Change-Id: Ie274c3baf72a8a0711c87d67238d68e2b2887429
Diffstat (limited to 'src/quick/scenegraph/qsgcontext.cpp')
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp135
1 files changed, 134 insertions, 1 deletions
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index e7b37e50d2..b87ab3686f 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -50,6 +50,7 @@
#include <QtQuick/private/qquickpixmapcache_p.h>
#include <QGuiApplication>
+#include <QScreen>
#include <QOpenGLContext>
#include <QQuickWindow>
#include <QtGui/qopenglframebufferobject.h>
@@ -58,6 +59,7 @@
#include <QtQuick/private/qsgtexture_p.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtCore/private/qabstractanimation_p.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformsharedgraphicscache.h>
@@ -134,6 +136,135 @@ public:
bool distanceFieldAntialiasingDecided;
};
+static bool qsg_useConsistentTiming()
+{
+ static int use = -1;
+ if (use < 0) {
+ QByteArray fixed = qgetenv("QSG_FIXED_ANIMATION_STEP");
+ use = !(fixed.isEmpty() || fixed == "no");
+ qCDebug(QSG_LOG_INFO, "Using %s", bool(use) ? "fixed animation steps" : "sg animation driver");
+ }
+ return bool(use);
+}
+
+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_bad(0)
+ , m_reallyBad(0)
+ , m_good(0)
+ {
+ QScreen *screen = QGuiApplication::primaryScreen();
+ if (screen && !qsg_useConsistentTiming()) {
+ m_vsync = 1000.0 / screen->refreshRate();
+ if (m_vsync <= 0)
+ m_mode = TimerMode;
+ } else {
+ m_mode = TimerMode;
+ if (qsg_useConsistentTiming())
+ QUnifiedTimer::instance(true)->setConsistentTiming(true);
+ }
+ if (m_mode == VSyncMode)
+ qCDebug(QSG_LOG_INFO, "Animation Driver: using vsync: %.2f ms", m_vsync);
+ else
+ qCDebug(QSG_LOG_INFO, "Animation Driver: using walltime");
+ }
+
+ void start() Q_DECL_OVERRIDE
+ {
+ m_time = 0;
+ m_timer.start();
+ QAnimationDriver::start();
+ }
+
+ qint64 elapsed() const Q_DECL_OVERRIDE
+ {
+ return m_mode == VSyncMode
+ ? qint64(m_time)
+ : QAnimationDriver::elapsed();
+ }
+
+ void advance() Q_DECL_OVERRIDE
+ {
+ qint64 delta = m_timer.restart();
+
+ if (m_mode == VSyncMode) {
+ // If a frame is skipped, either because rendering was slow or because
+ // the QML was slow, we accept it and continue advancing with a single
+ // vsync tick. The reason for this is that by the time we notice this
+ // on the GUI thread, the temporal distortion has already gone to screen
+ // and by catching up, we will introduce a second distortion which will
+ // worse. We accept that the animation time falls behind wall time because
+ // it comes out looking better.
+ // Only when multiple bad frames are hit in a row, do we consider
+ // switching. A few really bad frames and we switch right away. For frames
+ // just above the vsync delta, we tolerate a bit more since a buffered
+ // driver can have vsync deltas on the form: 4, 21, 21, 2, 23, 16, and
+ // still manage to put the frames to screen at 16 ms intervals. In addition
+ // to that, we tolerate a 25% margin of error on the value of m_vsync
+ // reported from the system as this value is often not precise.
+
+ m_time += m_vsync;
+
+ if (delta > m_vsync * 5) {
+ ++m_reallyBad;
+ ++m_bad;
+ } else if (delta > m_vsync * 1.25) {
+ ++m_bad;
+ } else {
+ // reset counters on a good frame.
+ m_reallyBad = 0;
+ m_bad = 0;
+ }
+
+ // rational for the 3 and 50. If we have several really bad frames
+ // in a row, that would indicate a huge performance problem and we should
+ // switch right away. For the case of m_bad, we're a bit more tolerant.
+ if (m_reallyBad > 3 || m_bad > 50) {
+ m_mode = TimerMode;
+ qCDebug(QSG_LOG_INFO, "animation driver switched to timer mode");
+ }
+
+ } else {
+ if (delta < 1.25 * m_vsync) {
+ ++m_good;
+ } else {
+ m_good = 0;
+ }
+
+ // We've been solid for a while, switch back to vsync mode. Tolerance
+ // for switching back is lower than switching to timer mode, as we
+ // want to stay in vsync mode as much as possible.
+ if (m_good > 10 && !qsg_useConsistentTiming()) {
+ m_time = elapsed();
+ m_mode = VSyncMode;
+ qCDebug(QSG_LOG_INFO, "animation driver switched to vsync mode");
+ }
+ }
+
+ advanceAnimation();
+ }
+
+ float m_time;
+ float m_vsync;
+ Mode m_mode;
+ QElapsedTimer m_timer;
+ int m_bad;
+ int m_reallyBad;
+ int m_good;
+};
+
class QSGTextureCleanupEvent : public QEvent
{
public:
@@ -389,7 +520,7 @@ bool QSGContext::isDistanceFieldEnabled() const
QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
{
- return new QAnimationDriver(parent);
+ return new QSGAnimationDriver(parent);
}
QSGRenderContext::QSGRenderContext(QSGContext *context)
@@ -730,4 +861,6 @@ void QSGRenderContext::initialize(QSGMaterialShader *shader)
shader->initialize();
}
+#include "qsgcontext.moc"
+
QT_END_NAMESPACE