File indexing completed on 2024-05-12 15:58:47

0001 /*
0002  *  SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_update_time_monitor.h"
0008 
0009 #include <QGlobalStatic>
0010 #include <QHash>
0011 #include <QSet>
0012 #include <QMutex>
0013 #include <QMutexLocker>
0014 #include <QPointF>
0015 #include <QRect>
0016 #include <QRegion>
0017 #include <QFile>
0018 #include <QDir>
0019 
0020 #include <QElapsedTimer>
0021 
0022 #include <QFileInfo>
0023 
0024 #include "kis_debug.h"
0025 #include "kis_global.h"
0026 #include "kis_image_config.h"
0027 
0028 
0029 #include <brushengine/kis_paintop_preset.h>
0030 
0031 Q_GLOBAL_STATIC(KisUpdateTimeMonitor, s_instance)
0032 
0033 
0034 struct StrokeTicket
0035 {
0036     StrokeTicket()
0037         : m_jobTime(0)
0038         , m_updateTime(0) {}
0039 
0040     QRegion dirtyRegion;
0041 
0042     void start() {
0043         m_timer.start();
0044     }
0045 
0046     void jobCompleted() {
0047         m_jobTime = m_timer.restart();
0048     }
0049 
0050     void updateCompleted() {
0051         m_updateTime = m_timer.restart();
0052     }
0053 
0054     qint64 jobTime() const {
0055         return m_jobTime;
0056     }
0057 
0058     qint64 updateTime() const {
0059         return m_updateTime;
0060     }
0061 
0062 private:
0063     QElapsedTimer m_timer;
0064     qint64 m_jobTime;
0065     qint64 m_updateTime;
0066 };
0067 
0068 struct Q_DECL_HIDDEN KisUpdateTimeMonitor::Private
0069 {
0070     Private()
0071         : jobsTime(0),
0072           responseTime(0),
0073           numTickets(0),
0074           numUpdates(0),
0075           mousePath(0.0),
0076           loggingEnabled(false)
0077     {
0078         loggingEnabled = KisImageConfig(true).enablePerfLog();
0079     }
0080 
0081     QHash<void*, StrokeTicket*> preliminaryTickets;
0082     QSet<StrokeTicket*> finishedTickets;
0083 
0084     qint64 jobsTime;
0085     qint64 responseTime;
0086     qint32 numTickets;
0087     qint32 numUpdates;
0088     QMutex mutex;
0089 
0090     qreal mousePath;
0091     QPointF lastMousePos;
0092     QElapsedTimer strokeTime;
0093     KisPaintOpPresetSP preset;
0094 
0095     bool loggingEnabled;
0096 };
0097 
0098 KisUpdateTimeMonitor::KisUpdateTimeMonitor()
0099     : m_d(new Private)
0100 {
0101     if (m_d->loggingEnabled) {
0102         QDir dir;
0103         if (dir.exists("log")) {
0104             dir.remove("log");
0105         }
0106         dir.mkdir("log");
0107     }
0108 }
0109 
0110 KisUpdateTimeMonitor::~KisUpdateTimeMonitor()
0111 {
0112     delete m_d;
0113 }
0114 
0115 KisUpdateTimeMonitor* KisUpdateTimeMonitor::instance()
0116 {
0117     return s_instance;
0118 }
0119 
0120 void KisUpdateTimeMonitor::startStrokeMeasure()
0121 {
0122     if (!m_d->loggingEnabled) return;
0123 
0124     QMutexLocker locker(&m_d->mutex);
0125 
0126     m_d->jobsTime = 0;
0127     m_d->responseTime = 0;
0128     m_d->numTickets = 0;
0129     m_d->numUpdates = 0;
0130     m_d->mousePath = 0;
0131 
0132     m_d->lastMousePos = QPointF();
0133     m_d->preset = 0;
0134     m_d->strokeTime.start();
0135 }
0136 
0137 void KisUpdateTimeMonitor::endStrokeMeasure()
0138 {
0139     if (!m_d->loggingEnabled) return;
0140 
0141     QMutexLocker locker(&m_d->mutex);
0142 
0143     if (m_d->numTickets) {
0144         printValues();
0145     }
0146 }
0147 
0148 void KisUpdateTimeMonitor::reportPaintOpPreset(KisPaintOpPresetSP preset)
0149 {
0150     if (!m_d->loggingEnabled) return;
0151 
0152     m_d->preset = preset;
0153 }
0154 
0155 void KisUpdateTimeMonitor::reportMouseMove(const QPointF &pos)
0156 {
0157     if (!m_d->loggingEnabled) return;
0158 
0159     QMutexLocker locker(&m_d->mutex);
0160 
0161     if (!m_d->lastMousePos.isNull()) {
0162         qreal distance = kisDistance(m_d->lastMousePos, pos);
0163         m_d->mousePath += distance;
0164     }
0165 
0166     m_d->lastMousePos = pos;
0167 }
0168 
0169 void KisUpdateTimeMonitor::printValues()
0170 {
0171     qint64 strokeTime = m_d->strokeTime.elapsed();
0172     qreal responseTime = qreal(m_d->responseTime) / m_d->numTickets;
0173     qreal nonUpdateTime = qreal(m_d->jobsTime) / m_d->numTickets;
0174     qreal jobsPerUpdate = qreal(m_d->numTickets) / m_d->numUpdates;
0175     qreal mouseSpeed = qreal(m_d->mousePath) / strokeTime;
0176 
0177     QString prefix;
0178 
0179     if (m_d->preset) {
0180         KoResourceSP preset = m_d->preset->clone();
0181         prefix = QString("%1.").arg(preset->name());
0182         preset->setFilename(QString("log/%1.kpp").arg(preset->name()));
0183         preset->save();
0184     }
0185 
0186     QFile logFile(QString("log/%1stroke.rdata").arg(prefix));
0187     logFile.open(QIODevice::Append);
0188     QTextStream stream(&logFile);
0189     stream.setCodec("UTF-8");
0190 
0191     stream << i18n("Stroke Time:") << strokeTime << "\t"
0192            << i18n("Mouse Speed:") << QString::number( mouseSpeed, 'f', 3 ) << "\t"
0193            << i18n("Jobs/Update:") << QString::number( jobsPerUpdate, 'f', 3 ) << "\t"
0194            << i18n("Non Update Time:") << QString::number( nonUpdateTime, 'f', 3 ) << "\t"
0195            << i18n("Response Time:") << responseTime << endl; // 'endl' will use the correct OS line ending
0196     logFile.close();
0197 }
0198 
0199 void KisUpdateTimeMonitor::reportJobStarted(void *key)
0200 {
0201     if (!m_d->loggingEnabled) return;
0202 
0203     QMutexLocker locker(&m_d->mutex);
0204 
0205     StrokeTicket *ticket = new StrokeTicket();
0206     ticket->start();
0207 
0208     m_d->preliminaryTickets.insert(key, ticket);
0209 }
0210 
0211 void KisUpdateTimeMonitor::reportJobFinished(void *key, const QVector<QRect> &rects)
0212 {
0213     if (!m_d->loggingEnabled) return;
0214 
0215     QMutexLocker locker(&m_d->mutex);
0216 
0217     StrokeTicket *ticket = m_d->preliminaryTickets.take(key);
0218     if( ticket ){
0219         ticket->jobCompleted();
0220 
0221         Q_FOREACH (const QRect &rect, rects) {
0222             ticket->dirtyRegion += rect;
0223         }
0224         m_d->finishedTickets.insert(ticket);
0225     }
0226 }
0227 
0228 void KisUpdateTimeMonitor::reportUpdateFinished(const QRect &rect)
0229 {
0230     if (!m_d->loggingEnabled) return;
0231 
0232     QMutexLocker locker(&m_d->mutex);
0233 
0234     Q_FOREACH (StrokeTicket *ticket, m_d->finishedTickets) {
0235         ticket->dirtyRegion -= rect;
0236         if(ticket->dirtyRegion.isEmpty()) {
0237             ticket->updateCompleted();
0238             m_d->jobsTime += ticket->jobTime();
0239             m_d->responseTime += ticket->jobTime() + ticket->updateTime();
0240             m_d->numTickets++;
0241 
0242             m_d->finishedTickets.remove(ticket);
0243             delete ticket;
0244         }
0245     }
0246     m_d->numUpdates++;
0247 }