diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp index 56dbd4e5641..6a436ad0439 100644 --- a/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp +++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.cpp @@ -180,6 +180,9 @@ struct QmlEventStartTimeData { qint64 nestingDepth; QmlEventData *description; + // animation-related data + int frameRate; + int animationCount; }; struct QmlEventTypeCount { @@ -281,6 +284,10 @@ public: qint64 m_qmlMeasuredTime; qint64 m_v8MeasuredTime; + QmlEventStartTimeData *m_lastFrameEvent; + qint64 m_maximumAnimationCount; + qint64 m_minimumAnimationCount; + // file to load QString m_filename; }; @@ -302,7 +309,9 @@ QmlProfilerEventList::QmlProfilerEventList(QObject *parent) : d->m_rootEventDesc = tr("Main Program"); d->clearQmlRootEvent(); d->clearV8RootEvent(); - + d->m_lastFrameEvent = 0; + d->m_maximumAnimationCount = 0; + d->m_minimumAnimationCount = 0; } QmlProfilerEventList::~QmlProfilerEventList() @@ -335,6 +344,10 @@ void QmlProfilerEventList::clear() d->m_qmlMeasuredTime = 0; d->m_v8MeasuredTime = 0; + d->m_lastFrameEvent = 0; + d->m_maximumAnimationCount = 0; + d->m_minimumAnimationCount = 0; + emit countChanged(); emit dataClear(); } @@ -423,6 +436,8 @@ void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 len startTimeData.length = length; startTimeData.description = newEvent; startTimeData.endTimeIndex = d->m_endTimeSortedList.count(); + startTimeData.animationCount = -1; + startTimeData.frameRate = 1e9/length; d->m_endTimeSortedList << endTimeData; d->m_startTimeSortedList << startTimeData; @@ -494,6 +509,58 @@ void QmlProfilerEventList::addV8Event(int depth, const QString &function, const } } +void QmlProfilerEventList::addFrameEvent(qint64 time, int framerate, int animationcount) +{ + QString displayName, eventHashStr, details; + + emit processingData(); + + details = tr("Animation Timer Update"); + displayName = tr(""); + eventHashStr = displayName; + + QmlEventData *newEvent; + if (d->m_eventDescriptions.contains(eventHashStr)) { + newEvent = d->m_eventDescriptions[eventHashStr]; + } else { + newEvent = new QmlEventData; + newEvent->displayname = displayName; + newEvent->filename = QString(); + newEvent->eventHashStr = eventHashStr; + newEvent->line = -1; + newEvent->eventType = QmlJsDebugClient::Painting; + newEvent->details = details; + d->m_eventDescriptions.insert(eventHashStr, newEvent); + } + + qint64 length = 1e9/framerate; + // avoid overlap + if (d->m_lastFrameEvent && d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length >= time) { + d->m_lastFrameEvent->length = time - 1 - d->m_lastFrameEvent->startTime; + d->m_endTimeSortedList[d->m_lastFrameEvent->endTimeIndex].endTime = d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length; + } + + QmlEventEndTimeData endTimeData; + endTimeData.endTime = time + length; + endTimeData.description = newEvent; + endTimeData.startTimeIndex = d->m_startTimeSortedList.count(); + + QmlEventStartTimeData startTimeData; + startTimeData.startTime = time; + startTimeData.length = length; + startTimeData.description = newEvent; + startTimeData.endTimeIndex = d->m_endTimeSortedList.count(); + startTimeData.animationCount = animationcount; + startTimeData.frameRate = framerate; + + d->m_endTimeSortedList << endTimeData; + d->m_startTimeSortedList << startTimeData; + + d->m_lastFrameEvent = &d->m_startTimeSortedList.last(); + + emit countChanged(); +} + void QmlProfilerEventList::QmlProfilerEventListPrivate::collectV8Statistics() { if (!m_v8EventList.isEmpty()) { @@ -634,6 +701,11 @@ void QmlProfilerEventList::compileStatistics(qint64 startTime, qint64 endTime) continue; } + if (eventDescription->eventType == QmlJsDebugClient::Painting && d->m_startTimeSortedList[index].animationCount >=0) { + // skip animation events + continue; + } + eventDescription->calls++; qint64 duration = d->m_startTimeSortedList[index].length; eventDescription->duration += duration; @@ -831,6 +903,30 @@ void QmlProfilerEventList::sortEndTimes() d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].endTimeIndex = i; } +void QmlProfilerEventList::findAnimationLimits() +{ + d->m_maximumAnimationCount = 0; + d->m_minimumAnimationCount = 0; + d->m_lastFrameEvent = 0; + + for (int i = 0; i < d->m_startTimeSortedList.count(); i++) { + if (d->m_startTimeSortedList[i].description->eventType == QmlJsDebugClient::Painting && + d->m_startTimeSortedList[i].animationCount >= 0) { + int animationcount = d->m_startTimeSortedList[i].animationCount; + if (d->m_lastFrameEvent) { + if (animationcount > d->m_maximumAnimationCount) + d->m_maximumAnimationCount = animationcount; + if (animationcount < d->m_minimumAnimationCount) + d->m_minimumAnimationCount = animationcount; + } else { + d->m_maximumAnimationCount = animationcount; + d->m_minimumAnimationCount = animationcount; + } + d->m_lastFrameEvent = &d->m_startTimeSortedList[i]; + } + } +} + void QmlProfilerEventList::computeNestingLevels() { // compute levels @@ -903,10 +999,11 @@ void QmlProfilerEventList::postProcess() if (count() != 0) { sortStartTimes(); sortEndTimes(); + findAnimationLimits(); computeLevels(); linkEndsToStarts(); - prepareForDisplay(); compileStatistics(traceStartTime(), traceEndTime()); + prepareForDisplay(); } // data is ready even when there's no data emit dataReady(); @@ -1116,6 +1213,11 @@ bool QmlProfilerEventList::save(const QString &filename) stream.writeAttribute("startTime", QString::number(rangedEvent.startTime)); stream.writeAttribute("duration", QString::number(rangedEvent.length)); stream.writeAttribute("eventIndex", QString::number(d->m_eventDescriptions.keys().indexOf(rangedEvent.description->eventHashStr))); + if (rangedEvent.description->eventType == QmlJsDebugClient::Painting && rangedEvent.animationCount >= 0) { + // animation frame + stream.writeAttribute("framerate", QString::number(rangedEvent.frameRate)); + stream.writeAttribute("animationcount", QString::number(rangedEvent.animationCount)); + } stream.writeEndElement(); } stream.writeEndElement(); // eventList @@ -1254,6 +1356,10 @@ void QmlProfilerEventList::load() rangedEvent.startTime = attributes.value("startTime").toString().toLongLong(); if (attributes.hasAttribute("duration")) rangedEvent.length = attributes.value("duration").toString().toLongLong(); + if (attributes.hasAttribute("framerate")) + rangedEvent.frameRate = attributes.value("framerate").toString().toInt(); + if (attributes.hasAttribute("animationcount")) + rangedEvent.animationCount = attributes.value("animationcount").toString().toInt(); if (attributes.hasAttribute("eventIndex")) { int ndx = attributes.value("eventIndex").toString().toInt(); if (!descriptionBuffer.value(ndx)) @@ -1513,6 +1619,12 @@ int QmlProfilerEventList::getLine(int index) const { } QString QmlProfilerEventList::getDetails(int index) const { + // special: animations + if (d->m_startTimeSortedList[index].description->eventType == QmlJsDebugClient::Painting && + d->m_startTimeSortedList[index].animationCount >= 0) + return tr("%1 animations at %2 FPS").arg( + QString::number(d->m_startTimeSortedList[index].animationCount), + QString::number(d->m_startTimeSortedList[index].frameRate)); return d->m_startTimeSortedList[index].description->details; } @@ -1520,6 +1632,26 @@ int QmlProfilerEventList::getEventId(int index) const { return d->m_startTimeSortedList[index].description->eventId; } +int QmlProfilerEventList::getFramerate(int index) const +{ + return d->m_startTimeSortedList[index].frameRate; +} + +int QmlProfilerEventList::getAnimationCount(int index) const +{ + return d->m_startTimeSortedList[index].animationCount; +} + +int QmlProfilerEventList::getMaximumAnimationCount() const +{ + return d->m_maximumAnimationCount; +} + +int QmlProfilerEventList::getMinimumAnimationCount() const +{ + return d->m_minimumAnimationCount; +} + int QmlProfilerEventList::uniqueEventsOfType(int type) const { if (!d->m_typeCounts.contains(type)) return 0; diff --git a/src/libs/qmljsdebugclient/qmlprofilereventlist.h b/src/libs/qmljsdebugclient/qmlprofilereventlist.h index 6c61c811395..ff52c87b170 100644 --- a/src/libs/qmljsdebugclient/qmlprofilereventlist.h +++ b/src/libs/qmljsdebugclient/qmlprofilereventlist.h @@ -141,6 +141,10 @@ public: Q_INVOKABLE int getLine(int index) const; Q_INVOKABLE QString getDetails(int index) const; Q_INVOKABLE int getEventId(int index) const; + Q_INVOKABLE int getFramerate(int index) const; + Q_INVOKABLE int getAnimationCount(int index) const; + Q_INVOKABLE int getMaximumAnimationCount() const; + Q_INVOKABLE int getMinimumAnimationCount() const; // per-type data Q_INVOKABLE int uniqueEventsOfType(int type) const; @@ -172,6 +176,7 @@ public slots: void complete(); void addV8Event(int depth,const QString &function,const QString &filename, int lineNumber, double totalTime, double selfTime); + void addFrameEvent(qint64 time, int framerate, int animationcount); bool save(const QString &filename); void load(const QString &filename); void setFilename(const QString &filename); @@ -183,6 +188,7 @@ public slots: private: void postProcess(); void sortEndTimes(); + void findAnimationLimits(); void sortStartTimes(); void computeLevels(); void computeNestingLevels(); diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp index adc591e0d98..675a14ab64f 100644 --- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp +++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp @@ -151,6 +151,12 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data) if (event == EndTrace) { emit this->traceFinished(time); d->maximumTime = time; + d->maximumTime = qMax(time, d->maximumTime); + } else if (event == AnimationFrame) { + int frameRate, animationCount; + stream >> frameRate >> animationCount; + emit this->frame(time, frameRate, animationCount); + d->maximumTime = qMax(time, d->maximumTime); } else if (event == StartTrace) { emit this->traceStarted(time); d->maximumTime = time; diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h index aec23d47193..56b80606b0d 100644 --- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h +++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h @@ -101,6 +101,7 @@ signals: void traceStarted( qint64 time ); void range(int type, qint64 startTime, qint64 length, const QStringList &data, const QString &fileName, int line); + void frame(qint64 time, int frameRate, int animationCount); void recordingChanged(bool arg); diff --git a/src/plugins/qmlprofiler/qml/Overview.js b/src/plugins/qmlprofiler/qml/Overview.js index 317b22f9e76..cacb7afdbf1 100644 --- a/src/plugins/qmlprofiler/qml/Overview.js +++ b/src/plugins/qmlprofiler/qml/Overview.js @@ -73,9 +73,25 @@ function drawData(canvas, ctxt, region) xx = Math.round(xx); var ty = qmlEventList.getType(ii); if (xx + eventWidth > highest[ty]) { - var hue = ( qmlEventList.getEventId(ii) * 25 ) % 360; - ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)"; - ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight); + // special: animations + if (ty === 0 && qmlEventList.getAnimationCount(ii) >= 0) { + var vertScale = qmlEventList.getMaximumAnimationCount() - qmlEventList.getMinimumAnimationCount(); + if (vertScale < 1) + vertScale = 1; + var fraction = (qmlEventList.getAnimationCount(ii) - qmlEventList.getMinimumAnimationCount()) / vertScale; + var eventHeight = blockHeight * (fraction * 0.85 + 0.15); + var yy = bump + ty*blockHeight + blockHeight - eventHeight; + + var fpsFraction = qmlEventList.getFramerate(ii) / 60.0; + if (fpsFraction > 1.0) + fpsFraction = 1.0; + ctxt.fillStyle = "hsl("+(fpsFraction*0.27+0.028)+",0.3,0.65)"; + ctxt.fillRect(xx, yy, eventWidth, eventHeight); + } else { + var hue = ( qmlEventList.getEventId(ii) * 25 ) % 360; + ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)"; + ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight); + } highest[ty] = xx+eventWidth; } } diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelineview.cpp index 0e6a30839bc..325dd0d591c 100644 --- a/src/plugins/qmlprofiler/timelineview.cpp +++ b/src/plugins/qmlprofiler/timelineview.cpp @@ -123,9 +123,10 @@ QColor TimelineView::colorForItem(int itemIndex) void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex) { - int x,y,width,rowNumber, eventType; + int x, y, width, height, rowNumber, eventType; for (int i = fromIndex; i <= toIndex; i++) { x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing; + eventType = m_eventList->getType(i); if (m_rowsExpanded[eventType]) y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->eventPosInType(i) + 1); @@ -141,8 +142,25 @@ void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex) continue; m_rowLastX[rowNumber] = x+width; - p->setBrush(colorForItem(i)); - p->drawRect(x,y,width,DefaultRowHeight); + // special: animations + if (eventType == 0 && m_eventList->getAnimationCount(i) >= 0) { + double scale = m_eventList->getMaximumAnimationCount() - m_eventList->getMinimumAnimationCount(); + if (scale < 1) + scale = 1; + double fraction = (double)(m_eventList->getAnimationCount(i) - m_eventList->getMinimumAnimationCount()) / scale; + height = DefaultRowHeight * (fraction * 0.85 + 0.15); + y += DefaultRowHeight - height; + + double fpsFraction = m_eventList->getFramerate(i) / 60.0; + if (fpsFraction > 1.0) + fpsFraction = 1.0; + p->setBrush(QColor::fromHsl((fpsFraction*96)+10, 76, 166)); + p->drawRect(x, y, width, height); + } else { + // normal events + p->setBrush(colorForItem(i)); + p->drawRect(x, y, width, DefaultRowHeight); + } } } diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp index d021e42628f..15ebad97cb2 100644 --- a/src/plugins/qmlprofiler/tracewindow.cpp +++ b/src/plugins/qmlprofiler/tracewindow.cpp @@ -137,6 +137,7 @@ TraceWindow::TraceWindow(QWidget *parent) connect(this,SIGNAL(range(int,qint64,qint64,QStringList,QString,int)), m_eventList, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QString,int))); connect(this, SIGNAL(traceFinished(qint64)), m_eventList, SLOT(setTraceEndTime(qint64))); connect(this, SIGNAL(traceStarted(qint64)), m_eventList, SLOT(setTraceStartTime(qint64))); + connect(this, SIGNAL(frameEvent(qint64,int,int)), m_eventList, SLOT(addFrameEvent(qint64,int,int))); connect(this,SIGNAL(viewUpdated()), m_eventList, SLOT(complete())); m_mainView->rootContext()->setContextProperty("qmlEventList", m_eventList); m_overview->rootContext()->setContextProperty("qmlEventList", m_eventList); @@ -311,6 +312,7 @@ void TraceWindow::connectClientSignals() this, SIGNAL(range(int,qint64,qint64,QStringList,QString,int))); connect(m_plugin.data(), SIGNAL(traceFinished(qint64)), this, SIGNAL(traceFinished(qint64))); connect(m_plugin.data(), SIGNAL(traceStarted(qint64)), this, SIGNAL(traceStarted(qint64))); + connect(m_plugin.data(), SIGNAL(frame(qint64,int,int)), this, SIGNAL(frameEvent(qint64,int,int))); connect(m_plugin.data(), SIGNAL(enabledChanged()), this, SLOT(updateProfilerState())); connect(m_plugin.data(), SIGNAL(enabledChanged()), m_plugin.data(), SLOT(sendRecordingStatus())); } diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/tracewindow.h index fb98fc46487..9e72faf5c5d 100644 --- a/src/plugins/qmlprofiler/tracewindow.h +++ b/src/plugins/qmlprofiler/tracewindow.h @@ -136,6 +136,7 @@ signals: int lineNumber, double totalTime, double selfTime); void traceFinished(qint64); void traceStarted(qint64); + void frameEvent(qint64, int, int); void internalClearDisplay(); void jumpToPrev();