1 /*
2  *  Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "KisStrokeSpeedMonitor.h"
20 
21 #include <QGlobalStatic>
22 #include <QMutex>
23 #include <QMutexLocker>
24 
25 #include <KisRollingMeanAccumulatorWrapper.h>
26 #include "kis_paintop_preset.h"
27 #include "kis_paintop_settings.h"
28 
29 #include "kis_config.h"
30 #include "kis_config_notifier.h"
31 #include "KisImageConfigNotifier.h"
32 
33 
34 Q_GLOBAL_STATIC(KisStrokeSpeedMonitor, s_instance)
35 
36 
37 struct KisStrokeSpeedMonitor::Private
38 {
39     static const int averageWindow = 10;
40 
PrivateKisStrokeSpeedMonitor::Private41     Private()
42         : avgCursorSpeed(averageWindow),
43           avgRenderingSpeed(averageWindow),
44           avgFps(averageWindow)
45     {
46     }
47 
48     KisRollingMeanAccumulatorWrapper avgCursorSpeed;
49     KisRollingMeanAccumulatorWrapper avgRenderingSpeed;
50     KisRollingMeanAccumulatorWrapper avgFps;
51 
52     qreal cachedAvgCursorSpeed = 0;
53     qreal cachedAvgRenderingSpeed = 0;
54     qreal cachedAvgFps = 0;
55 
56     qreal lastCursorSpeed = 0;
57     qreal lastRenderingSpeed = 0;
58     qreal lastFps = 0;
59     bool lastStrokeSaturated = false;
60 
61     QByteArray lastPresetMd5;
62     QString lastPresetName;
63     qreal lastPresetSize = 0;
64 
65     bool haveStrokeSpeedMeasurement = true;
66 
67     QMutex mutex;
68 };
69 
KisStrokeSpeedMonitor()70 KisStrokeSpeedMonitor::KisStrokeSpeedMonitor()
71     : m_d(new Private())
72 {
73     connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetAccumulatedValues()));
74     connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SIGNAL(sigStatsUpdated()));
75     connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
76 
77     slotConfigChanged();
78 }
79 
~KisStrokeSpeedMonitor()80 KisStrokeSpeedMonitor::~KisStrokeSpeedMonitor()
81 {
82 }
83 
instance()84 KisStrokeSpeedMonitor *KisStrokeSpeedMonitor::instance()
85 {
86     return s_instance;
87 }
88 
haveStrokeSpeedMeasurement() const89 bool KisStrokeSpeedMonitor::haveStrokeSpeedMeasurement() const
90 {
91     return m_d->haveStrokeSpeedMeasurement;
92 }
93 
setHaveStrokeSpeedMeasurement(bool value)94 void KisStrokeSpeedMonitor::setHaveStrokeSpeedMeasurement(bool value)
95 {
96     m_d->haveStrokeSpeedMeasurement = value;
97 }
98 
resetAccumulatedValues()99 void KisStrokeSpeedMonitor::resetAccumulatedValues()
100 {
101     m_d->avgCursorSpeed.reset(m_d->averageWindow);
102     m_d->avgRenderingSpeed.reset(m_d->averageWindow);
103     m_d->avgFps.reset(m_d->averageWindow);
104 }
105 
slotConfigChanged()106 void KisStrokeSpeedMonitor::slotConfigChanged()
107 {
108     KisConfig cfg(true);
109     m_d->haveStrokeSpeedMeasurement = cfg.enableBrushSpeedLogging();
110     resetAccumulatedValues();
111     emit sigStatsUpdated();
112 }
113 
notifyStrokeFinished(qreal cursorSpeed,qreal renderingSpeed,qreal fps,KisPaintOpPresetSP preset)114 void KisStrokeSpeedMonitor::notifyStrokeFinished(qreal cursorSpeed, qreal renderingSpeed, qreal fps, KisPaintOpPresetSP preset)
115 {
116     if (qFuzzyCompare(cursorSpeed, 0.0) || qFuzzyCompare(renderingSpeed, 0.0)) return;
117 
118     QMutexLocker locker(&m_d->mutex);
119 
120     const bool isSamePreset =
121         m_d->lastPresetName == preset->name() &&
122         qFuzzyCompare(m_d->lastPresetSize, preset->settings()->paintOpSize());
123 
124     //ENTER_FUNCTION() << ppVar(isSamePreset);
125 
126     if (!isSamePreset) {
127         resetAccumulatedValues();
128         m_d->lastPresetName = preset->name();
129         m_d->lastPresetSize = preset->settings()->paintOpSize();
130     }
131 
132     m_d->lastCursorSpeed = cursorSpeed;
133     m_d->lastRenderingSpeed = renderingSpeed;
134     m_d->lastFps = fps;
135 
136 
137     static const qreal saturationSpeedThreshold = 0.30; // cursor speed should be at least 30% higher
138     m_d->lastStrokeSaturated = cursorSpeed / renderingSpeed > (1.0 + saturationSpeedThreshold);
139 
140 
141     if (m_d->lastStrokeSaturated) {
142         m_d->avgCursorSpeed(cursorSpeed);
143         m_d->avgRenderingSpeed(renderingSpeed);
144         m_d->avgFps(fps);
145 
146         m_d->cachedAvgCursorSpeed = m_d->avgCursorSpeed.rollingMean();
147         m_d->cachedAvgRenderingSpeed = m_d->avgRenderingSpeed.rollingMean();
148         m_d->cachedAvgFps = m_d->avgFps.rollingMean();
149     }
150 
151     emit sigStatsUpdated();
152 
153 
154     ENTER_FUNCTION() <<
155         QString(" CS: %1  RS: %2  FPS: %3 %4")
156             .arg(m_d->lastCursorSpeed, 5)
157             .arg(m_d->lastRenderingSpeed, 5)
158             .arg(m_d->lastFps, 5)
159             .arg(m_d->lastStrokeSaturated ? "(saturated)" : "");
160     ENTER_FUNCTION() <<
161         QString("ACS: %1 ARS: %2 AFPS: %3")
162             .arg(m_d->cachedAvgCursorSpeed, 5)
163             .arg(m_d->cachedAvgRenderingSpeed, 5)
164             .arg(m_d->cachedAvgFps, 5);
165 }
166 
lastPresetName() const167 QString KisStrokeSpeedMonitor::lastPresetName() const
168 {
169     return m_d->lastPresetName;
170 }
171 
lastPresetSize() const172 qreal KisStrokeSpeedMonitor::lastPresetSize() const
173 {
174     return m_d->lastPresetSize;
175 }
176 
lastCursorSpeed() const177 qreal KisStrokeSpeedMonitor::lastCursorSpeed() const
178 {
179     return m_d->lastCursorSpeed;
180 }
181 
lastRenderingSpeed() const182 qreal KisStrokeSpeedMonitor::lastRenderingSpeed() const
183 {
184     return m_d->lastRenderingSpeed;
185 }
186 
lastFps() const187 qreal KisStrokeSpeedMonitor::lastFps() const
188 {
189     return m_d->lastFps;
190 }
191 
lastStrokeSaturated() const192 bool KisStrokeSpeedMonitor::lastStrokeSaturated() const
193 {
194     return m_d->lastStrokeSaturated;
195 }
196 
avgCursorSpeed() const197 qreal KisStrokeSpeedMonitor::avgCursorSpeed() const
198 {
199     return m_d->cachedAvgCursorSpeed;
200 }
201 
avgRenderingSpeed() const202 qreal KisStrokeSpeedMonitor::avgRenderingSpeed() const
203 {
204     return m_d->cachedAvgRenderingSpeed;
205 }
206 
avgFps() const207 qreal KisStrokeSpeedMonitor::avgFps() const
208 {
209     return m_d->cachedAvgFps;
210 }
211