/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc ** All rights reserved. ** For any questions to Digia, please use contact form at http://qt.digia.com ** ** This file is part of the Qt Enterprise Qt Quick Profiler Add-on. ** ** Licensees holding valid Qt Enterprise licenses may use this file in ** accordance with the Qt Enterprise License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. ** ** If you have questions regarding the use of this file, please use ** contact form at http://qt.digia.com ** ****************************************************************************/ #include "pixmapcachemodel.h" #include "qmldebug/qmlprofilereventtypes.h" #include "qmlprofiler/qmlprofilermodelmanager.h" #include "qmlprofiler/sortedtimelinemodel.h" #include "qmlprofiler/singlecategorytimelinemodel_p.h" #include namespace QmlProfilerExtension { namespace Internal { using namespace QmlProfiler; class PixmapCacheModel::PixmapCacheModelPrivate : public SortedTimelineModel { public: void computeCacheSizes(); void resizeUnfinishedLoads(); void flattenLoads(); void computeRowCounts(); QVector < QString > pixmapUrls; QVector < QPair > pixmapSizes; int expandedRowCount; int collapsedRowCount; void addVP(QVariantList &l, QString label, qint64 time) const; qint64 minCacheSize; qint64 maxCacheSize; private: Q_DECLARE_PUBLIC(PixmapCacheModel) }; PixmapCacheModel::PixmapCacheModel(QObject *parent) : SingleCategoryTimelineModel(new PixmapCacheModelPrivate(), QLatin1String("PixmapCacheTimeLineModel"), QLatin1String("Pixmap Cache"), QmlDebug::PixmapCacheEvent, parent) { Q_D(PixmapCacheModel); d->collapsedRowCount = 1; d->expandedRowCount = 1; } int PixmapCacheModel::categoryDepth(int categoryIndex) const { Q_D(const PixmapCacheModel); Q_UNUSED(categoryIndex); if (isEmpty()) return 1; if (d->expanded) return d->expandedRowCount; return d->collapsedRowCount; } int PixmapCacheModel::getEventRow(int index) const { Q_D(const PixmapCacheModel); if (d->expanded) return d->range(index).rowNumberExpanded; return d->range(index).rowNumberCollapsed; } int PixmapCacheModel::getEventId(int index) const { Q_D(const PixmapCacheModel); return d->range(index).eventId; } QColor PixmapCacheModel::getColor(int index) const { Q_D(const PixmapCacheModel); if (d->range(index).pixmapEventType == PixmapCacheCountChanged) return QColor::fromHsl(240, 76, 166); int ndx = getEventId(index); return QColor::fromHsl((ndx*25)%360, 76, 166); } float PixmapCacheModel::getHeight(int index) const { Q_D(const PixmapCacheModel); if (d->range(index).pixmapEventType == PixmapCacheCountChanged) { float scale = d->maxCacheSize - d->minCacheSize; float fraction = 1.0f; if (scale > 1) fraction = (float)(d->range(index).cacheSize - d->minCacheSize) / scale; return fraction * 0.85f + 0.15f; } return 1.0f; } QString getFilenameOnly(QString absUrl) { int characterPos = absUrl.lastIndexOf(QLatin1Char('/'))+1; if (characterPos < absUrl.length()) absUrl = absUrl.mid(characterPos); return absUrl; } const QVariantList PixmapCacheModel::getLabelsForCategory(int category) const { Q_D(const PixmapCacheModel); Q_UNUSED(category); QVariantList result; if (d->expanded && !isEmpty()) { { // Cache Size QVariantMap element; element.insert(QLatin1String("displayName"), QVariant(QLatin1String("Cache Size"))); element.insert(QLatin1String("description"), QVariant(QLatin1String("Cache Size"))); element.insert(QLatin1String("id"), QVariant(0)); result << element; } for (int i=0; i < d->pixmapUrls.count(); i++) { // Loading QVariantMap element; element.insert(QLatin1String("displayName"), QVariant(getFilenameOnly(d->pixmapUrls[i]))); element.insert(QLatin1String("description"), QVariant(getFilenameOnly(d->pixmapUrls[i]))); element.insert(QLatin1String("id"), QVariant(i+1)); result << element; } } return result; } void PixmapCacheModel::PixmapCacheModelPrivate::addVP(QVariantList &l, QString label, qint64 time) const { if (time > 0) { QVariantMap res; res.insert(label, QVariant(QmlProfilerBaseModel::formatTime(time))); l << res; } } const QVariantList PixmapCacheModel::getEventDetails(int index) const { Q_D(const PixmapCacheModel); QVariantList result; const PixmapCacheModelPrivate::Range *ev = &d->range(index); { QVariantMap res; if (ev->pixmapEventType == PixmapCacheCountChanged) res.insert(QLatin1String("title"), QVariant(QLatin1String("Image Cached"))); else if (ev->pixmapEventType == PixmapLoadingStarted) res.insert(QLatin1String("title"), QVariant(QLatin1String("Image Loaded"))); result << res; } if (ev->pixmapEventType != PixmapCacheCountChanged) { d->addVP(result, tr("Duration"), ev->duration ); } { QVariantMap res; res.insert(tr("File"), QVariant(getFilenameOnly(d->pixmapUrls[ev->urlIndex]))); result << res; } { QVariantMap res; res.insert(tr("Width"), QVariant(QString::fromLatin1("%1 px").arg(d->pixmapSizes[ev->urlIndex].first))); result << res; res.clear(); res.insert(tr("Height"), QVariant(QString::fromLatin1("%1 px").arg(d->pixmapSizes[ev->urlIndex].second))); result << res; } if (ev->pixmapEventType == PixmapLoadingStarted && ev->cacheSize == -1) { QVariantMap res; res.insert(tr("Result"), QVariant(QLatin1String("Load Error"))); result << res; } return result; } void PixmapCacheModel::loadData() { Q_D(PixmapCacheModel); clear(); QmlProfilerDataModel *simpleModel = d->modelManager->qmlModel(); if (simpleModel->isEmpty()) return; int lastCacheSizeEvent = -1; int cumulatedCount = 0; QVector < int > pixmapStartPoints; QVector < int > pixmapCachePoints; foreach (const QmlProfilerDataModel::QmlEventData &event, simpleModel->getEvents()) { if (!eventAccepted(event)) continue; PixmapCacheEvent newEvent; newEvent.pixmapEventType = event.bindingType; qint64 startTime = event.startTime; bool isNewEntry = false; newEvent.urlIndex = d->pixmapUrls.indexOf(event.location.filename); if (newEvent.urlIndex == -1) { isNewEntry = true; newEvent.urlIndex = d->pixmapUrls.count(); d->pixmapUrls << event.location.filename; d->pixmapSizes << QPair(0,0); // default value pixmapStartPoints << -1; // dummy value to be filled by load event pixmapCachePoints << -1; // dummy value to be filled by cache event } newEvent.eventId = newEvent.urlIndex + 1; newEvent.rowNumberExpanded = newEvent.urlIndex + 2; switch (newEvent.pixmapEventType) { case PixmapSizeKnown: // pixmap size d->pixmapSizes[newEvent.urlIndex] = QPair((int)event.numericData1, (int)event.numericData2); if (pixmapCachePoints[newEvent.urlIndex] == -1) break; // else fall through and update cache size newEvent.pixmapEventType = PixmapCacheCountChanged; case PixmapCacheCountChanged: {// Cache Size Changed Event startTime = event.startTime + 1; // delay 1 ns for proper sorting newEvent.eventId = 0; newEvent.rowNumberExpanded = 1; newEvent.rowNumberCollapsed = 1; qint64 pixSize = d->pixmapSizes[newEvent.urlIndex].first * d->pixmapSizes[newEvent.urlIndex].second; qint64 prevSize = 0; if (lastCacheSizeEvent != -1) { prevSize = d->range(lastCacheSizeEvent).cacheSize; if (pixmapCachePoints[newEvent.urlIndex] == -1) { // else it's a synthesized update and doesn't have a valid cache count if (event.numericData3 < cumulatedCount) pixSize = -pixSize; cumulatedCount = event.numericData3; } d->insertEnd(lastCacheSizeEvent, startTime - d->range(lastCacheSizeEvent).start); } newEvent.cacheSize = prevSize + pixSize; lastCacheSizeEvent = d->insertStart(startTime, newEvent); pixmapCachePoints[newEvent.urlIndex] = lastCacheSizeEvent; break; } case PixmapLoadingStarted: // Load pixmapStartPoints[newEvent.urlIndex] = d->insertStart(startTime, newEvent); break; case PixmapLoadingFinished: case PixmapLoadingError: { int loadIndex = pixmapStartPoints[newEvent.urlIndex]; if (!isNewEntry && loadIndex != -1) { d->insertEnd(loadIndex, startTime - d->range(loadIndex).start); } else { // if it's a new entry it means that we don't have a corresponding start newEvent.pixmapEventType = PixmapLoadingStarted; newEvent.rowNumberExpanded = newEvent.urlIndex + 2; loadIndex = d->insert(traceStartTime(), startTime - traceStartTime(), newEvent); pixmapStartPoints[newEvent.urlIndex] = loadIndex; } if (event.bindingType == PixmapLoadingFinished) d->data(loadIndex).cacheSize = 1; // use count to mark success else d->data(loadIndex).cacheSize = -1; // ... or failure break; } default: break; } d->modelManager->modelProxyCountUpdated(d->modelId, d->count(), 2*simpleModel->getEvents().count()); } if (lastCacheSizeEvent != -1) { d->insertEnd(lastCacheSizeEvent, traceEndTime() - d->range(lastCacheSizeEvent).start); } d->resizeUnfinishedLoads(); d->computeCacheSizes(); d->flattenLoads(); d->computeRowCounts(); d->computeNesting(); d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1); } void PixmapCacheModel::clear() { Q_D(PixmapCacheModel); d->SortedTimelineModel::clear(); d->pixmapUrls.clear(); d->pixmapSizes.clear(); d->collapsedRowCount = 1; d->expandedRowCount = 1; d->expanded = false; d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1); } void PixmapCacheModel::PixmapCacheModelPrivate::computeCacheSizes() { minCacheSize = -1; maxCacheSize = -1; foreach (const PixmapCacheModel::PixmapCacheEvent &event, ranges) { if (event.pixmapEventType == PixmapCacheModel::PixmapCacheCountChanged) { if (minCacheSize == -1 || event.cacheSize < minCacheSize) minCacheSize = event.cacheSize; if (maxCacheSize == -1 || event.cacheSize > maxCacheSize) maxCacheSize = event.cacheSize; } } } void PixmapCacheModel::PixmapCacheModelPrivate::resizeUnfinishedLoads() { Q_Q(PixmapCacheModel); // all the "load start" events with duration 0 continue till the end of the trace for (int i = 0; i < count(); i++) { if (range(i).pixmapEventType == PixmapCacheModel::PixmapLoadingStarted && range(i).duration == 0) { insertEnd(i, q->traceEndTime() - range(i).start); } } } void PixmapCacheModel::PixmapCacheModelPrivate::flattenLoads() { // computes "compressed row" QVector eventEndTimes; for (int i = 0; i < count(); i++) { PixmapCacheModel::PixmapCacheEvent &event = data(i); const Range &start = range(i); if (event.pixmapEventType == PixmapCacheModel::PixmapLoadingStarted) { event.rowNumberCollapsed = 0; while (eventEndTimes.count() > event.rowNumberCollapsed && eventEndTimes[event.rowNumberCollapsed] > start.start) event.rowNumberCollapsed++; if (eventEndTimes.count() == event.rowNumberCollapsed) eventEndTimes << 0; // increase stack length, proper value added below eventEndTimes[event.rowNumberCollapsed] = start.start + start.duration; // readjust to account for category empty row and bargraph event.rowNumberCollapsed += 2; } } } void PixmapCacheModel::PixmapCacheModelPrivate::computeRowCounts() { expandedRowCount = 0; collapsedRowCount = 0; foreach (const PixmapCacheModel::PixmapCacheEvent &event, ranges) { if (event.rowNumberExpanded > expandedRowCount) expandedRowCount = event.rowNumberExpanded; if (event.rowNumberCollapsed > collapsedRowCount) collapsedRowCount = event.rowNumberCollapsed; } // Starting from 0, count is maxIndex+1 expandedRowCount++; collapsedRowCount++; } } // namespace Internal } // namespace QmlProfilerExtension