Commit c6294f47 authored by Bernhard Liebl's avatar Bernhard Liebl

Adds debug functions for stylus latency tracking

Using the hidden configuration "trackTabletEventLatency", Krita can now
output some stats on how long a stylus event remains in the event queue
before it is getting processed by Krita.

Differential Revision: https://phabricator.kde.org/D8201
parent bbd2129a
......@@ -19,6 +19,7 @@ set(kritaglobal_LIB_SRCS
kis_signal_compressor.cpp
kis_signal_compressor_with_param.cpp
kis_acyclic_signal_connector.cpp
kis_latency_tracker.cpp
KisQPainterStateSaver.cpp
KisLoggingManager.cpp
)
......
/*
* Copyright (C) 2017 Bernhard Liebl <poke1024@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_latency_tracker.h"
KisLatencyTracker::KisLatencyTracker(int windowSize) :
KisScalarTracker<qint64>("event latency", windowSize)
{
}
void KisLatencyTracker::push(qint64 timestamp)
{
const qint64 latency = currentTimestamp() - timestamp;
KisScalarTracker<qint64>::push(latency);
}
/*
* Copyright (C) 2017 Bernhard Liebl <poke1024@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KRITA_KIS_SCALAR_TRACKER_H
#define KRITA_KIS_SCALAR_TRACKER_H
#include "kis_shared.h"
#include <kritaglobal_export.h>
#include <QQueue>
#include <QElapsedTimer>
#include <QDebug>
#include <boost/heap/fibonacci_heap.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/rolling_mean.hpp>
#include <boost/accumulators/statistics/rolling_variance.hpp>
template<typename T>
class KisRollingMax {
public:
KisRollingMax(int windowSize) : m_windowSize(windowSize) {
}
void push(T value) {
while (m_samples.size() > m_windowSize) {
m_values.erase(m_samples.dequeue());
}
m_samples.enqueue(m_values.push(value));
}
T max() const {
if (m_values.empty()) {
throw std::runtime_error("no values to get max of");
} else {
return m_values.top();
}
}
private:
const int m_windowSize;
typedef boost::heap::fibonacci_heap<T> heap_type;
QQueue<typename heap_type::handle_type> m_samples;
heap_type m_values;
};
template<typename T>
class KisScalarTracker : public KisShared {
public:
/**
* Create a tracker with the given window size.
* @param window The maximum number of elements to take into account for calculation
* of max, mean and variance values.
*/
KisScalarTracker(const QString &name, int windowSize = 500) :
m_name(name),
m_windowSize(windowSize),
m_addCount(0),
m_max(windowSize),
m_acc(boost::accumulators::tag::rolling_window::window_size = windowSize)
{
m_printTimer.start();
}
virtual ~KisScalarTracker() {
}
/**
* Add a scalar.
* @param value the scalar to be added.
*/
virtual void push(T value) {
m_max.push(value);
m_acc(value);
m_addCount++;
if (m_addCount >= m_windowSize || m_printTimer.elapsed() >= 1000) {
m_printTimer.restart();
QString s = format(boost::accumulators::rolling_mean(m_acc),
boost::accumulators::rolling_variance(m_acc),
m_max.max());
print(s);
m_addCount = 0;
}
}
protected:
/**
* Print out a message.
* @param message the message to print
*/
virtual void print(const QString &message) {
qInfo() << qUtf8Printable(message);
}
/**
* Formats a message for printing.
* @param mean the mean scalar in the window
* @param variance the variance of the scalar in the window
* @param max the max scalar in the window
*/
virtual QString format(qint64 mean, qint64 variance, qint64 max) {
return QString("%1: mean %2 ms, var %3, max %4 ms").arg(m_name).arg(mean).arg(variance).arg(max);
}
private:
const QString m_name;
const int m_windowSize;
int m_addCount;
QElapsedTimer m_printTimer;
KisRollingMax<T> m_max;
// see https://svn.boost.org/trac10/ticket/11437
typedef boost::accumulators::stats<
boost::accumulators::tag::lazy_rolling_mean,
boost::accumulators::tag::rolling_variance> stats;
boost::accumulators::accumulator_set<T, stats> m_acc;
};
/**
* KisLatencyTracker tracks the time it takes events to reach a certain point in the program.
*/
class KRITAGLOBAL_EXPORT KisLatencyTracker : public KisScalarTracker<qint64> {
public:
/**
* Create a tracker with the given window size.
* @param window The maximum number of elements to take into account for calculation
* of max, mean and variance values.
*/
KisLatencyTracker(int windowSize = 500);
/**
* Register that an event with the given timestamp has arrived just now.
* @param timestamp Timestamp of the event that just arrived (the difference to the
* current time is the latency).
*/
virtual void push(qint64 timestamp);
protected:
/**
* @return The timestamp of "right now" in a frame that is comparable to those
* timestamps given to push().
*/
virtual qint64 currentTimestamp() const = 0;
};
#endif //KRITA_KIS_SCALAR_TRACKER_H
......@@ -25,7 +25,7 @@
#include <klocalizedstring.h>
#include <QApplication>
#include <QTouchEvent>
#include <QTouchEvent>
#include <QElapsedTimer>
#include <KoToolManager.h>
......@@ -466,6 +466,10 @@ bool KisInputManager::eventFilterImpl(QEvent * event)
QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(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
......
......@@ -153,6 +153,10 @@ KisInputManager::Private::Private(KisInputManager *qq)
moveEventCompressor.setDelay(cfg.tabletEventsDelay());
testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents();
testingCompressBrushEvents = cfg.testingCompressBrushEvents();
if (cfg.trackTabletEventLatency()) {
tabletLatencyTracker = new TabletLatencyTracker();
}
}
static const int InputWidgetsThreshold = 2000;
......@@ -568,3 +572,19 @@ bool KisInputManager::Private::handleCompressedTabletEvent(QEvent *event)
return retval;
}
qint64 KisInputManager::Private::TabletLatencyTracker::currentTimestamp() const
{
// on OS X, we need to compute the timestamp that compares correctly against the native event timestamp,
// which seems to be the msecs since system startup. On Linux with WinTab, we produce the timestamp that
// we compare against ourselves in QWindowSystemInterface.
QElapsedTimer elapsed;
elapsed.start();
return elapsed.msecsSinceReference();
}
void KisInputManager::Private::TabletLatencyTracker::print(const QString &message)
{
dbgTablet << qUtf8Printable(message);
}
......@@ -24,6 +24,8 @@
#include <QTouchEvent>
#include <QScopedPointer>
#include <QPointer>
#include <QQueue>
#include <QElapsedTimer>
#include "kis_input_manager.h"
#include "kis_shortcut_matcher.h"
......@@ -34,7 +36,7 @@
#include "input/kis_tablet_debugger.h"
#include "kis_timed_signal_threshold.h"
#include "kis_signal_auto_connection.h"
#include "kis_latency_tracker.h"
class KisToolInvocationAction;
......@@ -146,4 +148,12 @@ public:
bool containsPointer = true;
int accumulatedScrollDelta = 0;
class TabletLatencyTracker : public KisLatencyTracker {
protected:
virtual qint64 currentTimestamp() const;
virtual void print(const QString &message);
};
KisSharedPtr<TabletLatencyTracker> tabletLatencyTracker;
};
......@@ -582,7 +582,7 @@ void QWindowSystemInterface::handleTabletEvent(QWindow *w, const QPointF &local,
qreal tangentialPressure, qreal rotation, int z, qint64 uid,
Qt::KeyboardModifiers modifiers)
{
qint64 timestamp = g_eventTimer.elapsed();
qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed();
QWindowSystemInterfacePrivate::TabletEvent *e =
new QWindowSystemInterfacePrivate::TabletEvent(w, timestamp, local, global, device, pointerType, buttons, pressure,
......@@ -682,7 +682,7 @@ void processTabletEvent(QWindowSystemInterfacePrivate::TabletEvent *e)
void QWindowSystemInterface::handleTabletEnterProximityEvent(int device, int pointerType, qint64 uid)
{
qint64 timestamp = g_eventTimer.elapsed();
qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed();
QTabletEvent ev(QEvent::TabletEnterProximity, QPointF(), QPointF(),
device, pointerType, 0, 0, 0,
......@@ -694,7 +694,7 @@ void QWindowSystemInterface::handleTabletEnterProximityEvent(int device, int poi
void QWindowSystemInterface::handleTabletLeaveProximityEvent(int device, int pointerType, qint64 uid)
{
qint64 timestamp = g_eventTimer.elapsed();
qint64 timestamp = g_eventTimer.msecsSinceReference() + g_eventTimer.elapsed();
QTabletEvent ev(QEvent::TabletLeaveProximity, QPointF(), QPointF(),
device, pointerType, 0, 0, 0,
......
......@@ -1642,6 +1642,16 @@ void KisConfig::setTabletEventsDelay(int value)
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));
......
......@@ -453,6 +453,9 @@ public:
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment