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 }