diff --git a/libs/ui/input/kis_input_manager.cpp b/libs/ui/input/kis_input_manager.cpp --- a/libs/ui/input/kis_input_manager.cpp +++ b/libs/ui/input/kis_input_manager.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include @@ -451,6 +451,10 @@ QTabletEvent *tabletEvent = static_cast(event); retval = compressMoveEventCommon(tabletEvent); + if (d->tabletLatencyTracker) { + d->tabletLatencyTracker->push(tabletEvent->timestamp()); + } + /** * The flow of tablet events means the tablet is in the * proximity area, so activate it even when the diff --git a/libs/ui/input/kis_input_manager_p.h b/libs/ui/input/kis_input_manager_p.h --- a/libs/ui/input/kis_input_manager_p.h +++ b/libs/ui/input/kis_input_manager_p.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "kis_input_manager.h" #include "kis_shortcut_matcher.h" @@ -35,6 +37,7 @@ #include "kis_timed_signal_threshold.h" #include "kis_signal_auto_connection.h" +#include class KisToolInvocationAction; @@ -43,6 +46,7 @@ { public: Private(KisInputManager *qq); + ~Private(); bool tryHidePopupPalette(); void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons); void addKeyShortcut(KisAbstractInputAction* action, int index,const QList &keys); @@ -145,4 +149,46 @@ bool containsPointer = true; int accumulatedScrollDelta = 0; + + class LatencyTracker { + public: + LatencyTracker(int maximumAge = 2500); + virtual ~LatencyTracker(); + + virtual void push(qint64 timestamp); + int max() const; + + protected: + virtual qint64 currentTimestamp() const = 0; + + void gc(qint64 now); + + private: + struct Sample { + qint64 timestamp; + boost::heap::fibonacci_heap::handle_type handle; + }; + + QQueue samples; + boost::heap::fibonacci_heap values; + + const int maximumAge; + }; + + class EventLatencyTracker : public LatencyTracker { + protected: + qint64 currentTimestamp() const override; + }; + + class TabletLatencyTracker : public EventLatencyTracker { + public: + TabletLatencyTracker(); + + void push(qint64 timestamp) override; + + protected: + QElapsedTimer printTimer; + }; + + TabletLatencyTracker *tabletLatencyTracker; }; diff --git a/libs/ui/input/kis_input_manager_p.cpp b/libs/ui/input/kis_input_manager_p.cpp --- a/libs/ui/input/kis_input_manager_p.cpp +++ b/libs/ui/input/kis_input_manager_p.cpp @@ -145,6 +145,7 @@ , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE) , priorityEventFilterSeqNo(0) , canvasSwitcher(this, qq) + , tabletLatencyTracker(0) { KisConfig cfg; @@ -152,6 +153,15 @@ moveEventCompressor.setDelay(cfg.tabletEventsDelay()); testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents(); testingCompressBrushEvents = cfg.testingCompressBrushEvents(); + + if (cfg.trackTabletEventLatency()) { + tabletLatencyTracker = new TabletLatencyTracker(); + } +} + +KisInputManager::Private::~Private() +{ + delete tabletLatencyTracker; } static const int InputWidgetsThreshold = 2000; @@ -536,3 +546,72 @@ return retval; } + +KisInputManager::Private::LatencyTracker::LatencyTracker(int maximumAge) : + maximumAge(maximumAge) +{ +} + +KisInputManager::Private::LatencyTracker::~LatencyTracker() +{ +} + +void KisInputManager::Private::LatencyTracker::gc(qint64 now) +{ + while (!samples.empty()) { + if (now - samples.first().timestamp < maximumAge) { + break; + } + values.erase(samples.first().handle); + samples.dequeue(); + } +} + +void KisInputManager::Private::LatencyTracker::push(qint64 timestamp) +{ + const qint64 now = currentTimestamp(); + gc(now); + + const int latency = now - timestamp; + + Sample sample; + sample.timestamp = timestamp; + sample.handle = values.push(latency); + samples.enqueue(sample); + +} + +int KisInputManager::Private::LatencyTracker::max() const +{ + return values.empty() ? 0 : values.top(); +} + +qint64 KisInputManager::Private::EventLatencyTracker::currentTimestamp() const +{ + // this should return the current time as timestamp in the same + // type as QInputEvent::timestamp(). this is platform dependent + // and the QT docs only state that the timestamp "will normally + // be in milliseconds since some arbitrary point in time, such + // as the time when the system was started.". + + // this implementation works for OS X. other platforms untested. + QElapsedTimer elapsed; + elapsed.start(); + return elapsed.msecsSinceReference(); +} + +KisInputManager::Private::TabletLatencyTracker::TabletLatencyTracker() +{ + printTimer.start(); +} + +void KisInputManager::Private::TabletLatencyTracker::push(qint64 timestamp) +{ + EventLatencyTracker::push(timestamp); + + if (printTimer.elapsed() >= 1000) { + printTimer.restart(); + qInfo() << "tablet latency: " << max() << " ms"; + } +} + diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -447,6 +447,9 @@ int tabletEventsDelay(bool defaultValue = false) const; void setTabletEventsDelay(int value); + bool trackTabletEventLatency(bool defaultValue = false) const; + void setTrackTabletEventLatency(bool value); + bool testingAcceptCompressedTabletEvents(bool defaultValue = false) const; void setTestingAcceptCompressedTabletEvents(bool value); diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -1619,6 +1619,16 @@ m_cfg.writeEntry("tabletEventsDelay", value); } +bool KisConfig::trackTabletEventLatency(bool defaultValue) const +{ + return (defaultValue ? false : m_cfg.readEntry("trackTabletEventLatency", false)); +} + +void KisConfig::setTrackTabletEventLatency(bool value) +{ + m_cfg.writeEntry("trackTabletEventLatency", value); +} + bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false));