[krita/krita/4.2] libs: Fix hiccups when doing canvas actions

Dmitry Kazakov null at kde.org
Fri Jan 31 14:15:16 GMT 2020


Git commit 06cb53798c1c6ac69d28b3d280b86513424c6923 by Dmitry Kazakov.
Committed on 31/01/2020 at 14:14.
Pushed by dkazakov into branch 'krita/4.2'.

Fix hiccups when doing canvas actions

Fixed KisSignalCompressor became too precise and tries to keep
the interval too hard. Sometimes the signal handler is too slow, in
such cases we should still give some time for event loop to execute.

To achieve that, KisSignalCompressor now has an additional operational
mode: "additive interval mode". When this mode is active, then interval
time is counted from the moment, when the execution path is returned from
the signal handler. In "precise interval mode" (default), in reverse, the
interval is counted from the moment, when start() signal arrives.

BUG:414576,415773
CC:kimageshop at kde.org

M  +23   -3    libs/global/kis_signal_compressor.cpp
M  +7    -0    libs/global/kis_signal_compressor.h
M  +36   -4    libs/global/tests/KisSignalCompressorTest.cpp
M  +2    -0    libs/global/tests/KisSignalCompressorTest.h
M  +3    -1    libs/ui/input/kis_input_manager_p.cpp

https://invent.kde.org/kde/krita/commit/06cb53798c1c6ac69d28b3d280b86513424c6923

diff --git a/libs/global/kis_signal_compressor.cpp b/libs/global/kis_signal_compressor.cpp
index a87039b09b..581088672a 100644
--- a/libs/global/kis_signal_compressor.cpp
+++ b/libs/global/kis_signal_compressor.cpp
@@ -55,15 +55,22 @@ KisSignalCompressor::KisSignalCompressor()
 }
 
 KisSignalCompressor::KisSignalCompressor(int delay, Mode mode, QObject *parent)
+    : KisSignalCompressor(delay, mode, PRECISE_INTERVAL, parent)
+{
+}
+
+KisSignalCompressor::KisSignalCompressor(int delay, Mode mode, SlowHandlerMode slowHandlerMode, QObject *parent)
     : QObject(parent),
       m_timer(new QTimer(this)),
-      m_mode(mode)
+      m_mode(mode),
+      m_slowHandlerMode(slowHandlerMode)
 {
     m_timer->setSingleShot(false);
     m_timer->setInterval(delay);
     connect(m_timer, SIGNAL(timeout()), SLOT(slotTimerExpired()));
 }
 
+
 void KisSignalCompressor::setDelay(int delay)
 {
     const bool wasActive = m_timer->isActive();
@@ -99,11 +106,16 @@ void KisSignalCompressor::start()
     case FIRST_ACTIVE:
         if (isFirstStart) {
             m_timer->start();
-            m_lastEmittedTimer.restart();
+            if (m_slowHandlerMode == PRECISE_INTERVAL) {
+                m_lastEmittedTimer.restart();
+            }
             m_signalsPending = false;
             if (!tryEmitSignalSafely()) {
                 m_signalsPending = true;
             }
+            if (m_slowHandlerMode == ADDITIVE_INTERVAL) {
+                m_lastEmittedTimer.restart();
+            }
         } else {
             if (m_mode == FIRST_ACTIVE) {
                 m_signalsPending = true;
@@ -147,11 +159,19 @@ bool KisSignalCompressor::tryEmitOnTick(bool isFromTimer)
     if (m_signalsPending && m_lastEmittedTimer.elapsed() >= minInterval) {
         KIS_SAFE_ASSERT_RECOVER_NOOP(!isFromTimer || !m_isEmitting);
 
-        m_lastEmittedTimer.start();
+        if (m_slowHandlerMode == PRECISE_INTERVAL) {
+            m_lastEmittedTimer.start();
+        }
+
         m_signalsPending = false;
         if (!tryEmitSignalSafely()) {
             m_signalsPending = true;
         }
+
+        if (m_slowHandlerMode == ADDITIVE_INTERVAL) {
+            m_lastEmittedTimer.start();
+        }
+
         wasEmitted = true;
     } else if (!isFromTimer) {
         m_signalsPending = true;
diff --git a/libs/global/kis_signal_compressor.h b/libs/global/kis_signal_compressor.h
index 9252bb0b6d..9723bd0f16 100644
--- a/libs/global/kis_signal_compressor.h
+++ b/libs/global/kis_signal_compressor.h
@@ -69,9 +69,15 @@ public:
         UNDEFINED /* KisSignalCompressor is created without an explicit mode */
     };
 
+    enum SlowHandlerMode {
+        PRECISE_INTERVAL, /* Interval of timeout is forced to \p delay ms, whatever time the handler of timeout() takes */
+        ADDITIVE_INTERVAL /* When the handler of timeout() is slow, the timeout delay is increased to the (delay + handler_time) */
+    };
+
 public:
     KisSignalCompressor();
     KisSignalCompressor(int delay, Mode mode, QObject *parent = 0);
+    KisSignalCompressor(int delay, Mode mode, SlowHandlerMode slowHandlerMode, QObject *parent = 0);
     bool isActive() const;
     void setMode(Mode mode);
 
@@ -94,6 +100,7 @@ private:
 private:
     QTimer *m_timer = 0;
     Mode m_mode = UNDEFINED;
+    SlowHandlerMode m_slowHandlerMode = PRECISE_INTERVAL;
     bool m_signalsPending = false;
     QElapsedTimer m_lastEmittedTimer;
     int m_isEmitting = 0;
diff --git a/libs/global/tests/KisSignalCompressorTest.cpp b/libs/global/tests/KisSignalCompressorTest.cpp
index b25f8a7351..0b8b6b55cc 100644
--- a/libs/global/tests/KisSignalCompressorTest.cpp
+++ b/libs/global/tests/KisSignalCompressorTest.cpp
@@ -33,6 +33,13 @@
 struct CompressorTester : public QObject
 {
     Q_OBJECT
+
+public:
+    CompressorTester(int handlerDelay)
+        : m_handlerDelay(handlerDelay)
+    {
+    }
+
 public Q_SLOTS:
     void start() {
         if (!m_timer.isValid()) {
@@ -40,6 +47,10 @@ public Q_SLOTS:
         } else {
             m_acc(m_timer.restart());
         }
+
+        if (m_handlerDelay > 0) {
+            QTest::qSleep(m_handlerDelay);
+        }
     }
 public:
     void dump(const QString &testName) {
@@ -60,12 +71,17 @@ private:
 
     boost::accumulators::accumulator_set<qreal, stats> m_acc;
     QElapsedTimer m_timer;
+    int m_handlerDelay;
 };
 
-void testCompression(int timerInterval, int compressorInterval)
+void testCompression(int timerInterval, int compressorInterval,
+                     int handlerDelay = 0,
+                     KisSignalCompressor::SlowHandlerMode slowHandlerMode = KisSignalCompressor::PRECISE_INTERVAL)
 {
-    CompressorTester tester;
-    KisSignalCompressor compressor(compressorInterval, KisSignalCompressor::FIRST_ACTIVE);
+    CompressorTester tester(handlerDelay);
+    KisSignalCompressor compressor(compressorInterval,
+                                   KisSignalCompressor::FIRST_ACTIVE,
+                                   slowHandlerMode);
     QTimer timer;
     timer.setInterval(timerInterval);
     timer.setTimerType(Qt::PreciseTimer);
@@ -83,7 +99,8 @@ void testCompression(int timerInterval, int compressorInterval)
     QTest::qWait(compressorInterval * 2);
     compressor.stop();
 
-    tester.dump(QString("timer %1 compressor %2").arg(timerInterval).arg(compressorInterval));
+    tester.dump(QString("timer %1 compressor %2 handler delay %3")
+                .arg(timerInterval).arg(compressorInterval).arg(handlerDelay));
 
     QTest::qWait(compressorInterval * 10);
 }
@@ -96,6 +113,21 @@ void KisSignalCompressorTest::test()
     //testCompression(10, 25);
 }
 
+void KisSignalCompressorTest::testSlowHandlerPrecise()
+{
+    for (int i = 0; i < 31; i++) {
+        testCompression(6, 10, i, KisSignalCompressor::PRECISE_INTERVAL);
+    }
+}
+
+void KisSignalCompressorTest::testSlowHandlerAdditive()
+{
+    for (int i = 0; i < 31; i++) {
+        testCompression(6, 10, i, KisSignalCompressor::ADDITIVE_INTERVAL);
+    }
+}
+
+
 QTEST_MAIN(KisSignalCompressorTest)
 
 #include "KisSignalCompressorTest.moc"
diff --git a/libs/global/tests/KisSignalCompressorTest.h b/libs/global/tests/KisSignalCompressorTest.h
index 83c5faf9c2..5a85179705 100644
--- a/libs/global/tests/KisSignalCompressorTest.h
+++ b/libs/global/tests/KisSignalCompressorTest.h
@@ -28,6 +28,8 @@ class KisSignalCompressorTest : public QObject
 
 private Q_SLOTS:
     void test();
+    void testSlowHandlerPrecise();
+    void testSlowHandlerAdditive();
 };
 
 #endif // KISSIGNALCOMPRESSORTEST_H
diff --git a/libs/ui/input/kis_input_manager_p.cpp b/libs/ui/input/kis_input_manager_p.cpp
index 8711b9e103..000c34a070 100644
--- a/libs/ui/input/kis_input_manager_p.cpp
+++ b/libs/ui/input/kis_input_manager_p.cpp
@@ -176,7 +176,9 @@ void KisInputManager::Private::setMaskSyntheticEvents(bool value)
 
 KisInputManager::Private::Private(KisInputManager *qq)
     : q(qq)
-    , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE)
+    , moveEventCompressor(10 /* ms */,
+                          KisSignalCompressor::FIRST_ACTIVE,
+                          KisSignalCompressor::ADDITIVE_INTERVAL)
     , priorityEventFilterSeqNo(0)
     , canvasSwitcher(this, qq)
 {


More information about the kimageshop mailing list