diff options
Diffstat (limited to 'plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp')
-rw-r--r-- | plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp b/plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp new file mode 100644 index 00000000000..3bf3416c2bd --- /dev/null +++ b/plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp @@ -0,0 +1,475 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com <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 <http://qt.digia.com/> +** +****************************************************************************/ + +#include "scenegraphtimelinemodel.h" +#include "qmldebug/qmlprofilereventtypes.h" +#include "qmlprofiler/qmlprofilermodelmanager.h" + +#include <QCoreApplication> +#include <QDebug> + +namespace QmlProfilerExtension { +namespace Internal { + +using namespace QmlProfiler::Internal; + +enum SceneGraphEventType { + SceneGraphRendererFrame, + SceneGraphAdaptationLayerFrame, + SceneGraphContextFrame, + SceneGraphRenderLoopFrame, + SceneGraphTexturePrepare, + SceneGraphTextureDeletion, + SceneGraphPolishAndSync, + SceneGraphWindowsRenderShow, + SceneGraphWindowsAnimations, + SceneGraphWindowsPolishFrame, + + MaximumSceneGraphFrameType +}; + +enum SceneGraphCategoryType { + SceneGraphRenderThread, + SceneGraphGUIThread, + + MaximumSceneGraphCategoryType +}; + +class SceneGraphTimelineModel::SceneGraphTimelineModelPrivate { +public: + SceneGraphTimelineModelPrivate(SceneGraphTimelineModel *qq):q(qq) {} + ~SceneGraphTimelineModelPrivate(); + + SceneGraphTimelineModel *q; + + QVector < SceneGraphTimelineModel::SceneGraphEvent > eventList; + bool isExpanded; + QString displayTime(double time); + void addVP(QVariantList &l, QString label, qint64 time); +}; + +SceneGraphTimelineModel::SceneGraphTimelineModel(QObject *parent) + : AbstractTimelineModel(parent), d(new SceneGraphTimelineModelPrivate(this)) +{ +} + +SceneGraphTimelineModel::~SceneGraphTimelineModel() +{ +} + +int SceneGraphTimelineModel::categories() const +{ + return 1; +} + +QStringList SceneGraphTimelineModel::categoryTitles() const +{ + QStringList retString; + retString << categoryLabel(0); + return retString; +} + +QString SceneGraphTimelineModel::name() const +{ + return QLatin1String("SceneGraphTimeLineModel"); +} + +int SceneGraphTimelineModel::count() const +{ + return d->eventList.count(); +} + +bool SceneGraphTimelineModel::isEmpty() const +{ + return d->eventList.isEmpty(); +} + +bool SceneGraphTimelineModel::eventAccepted(const QmlProfiler::Internal::QmlProfilerSimpleModel::QmlEventData &event) const +{ + return (event.eventType == QmlDebug::SceneGraphFrameEvent); +} + +qint64 SceneGraphTimelineModel::lastTimeMark() const +{ + return d->eventList.last().startTime; +} + +void SceneGraphTimelineModel::setExpanded(int category, bool expanded) +{ + Q_UNUSED(category); + d->isExpanded = expanded; +} + +int SceneGraphTimelineModel::categoryDepth(int categoryIndex) const +{ + Q_UNUSED(categoryIndex); + if (isEmpty()) + return 1; + return 3; +} + +int SceneGraphTimelineModel::categoryCount() const +{ + return 1; +} + +const QString SceneGraphTimelineModel::categoryLabel(int categoryIndex) const +{ + Q_UNUSED(categoryIndex); + return tr("Scene Graph"); +} + +int SceneGraphTimelineModel::findFirstIndex(qint64 startTime) const +{ + int candidate = findFirstIndexNoParents(startTime); + // because there's two threads synchronized, the right index could be one off + if (candidate > 0 && d->eventList[candidate-1].startTime + d->eventList[candidate-1].duration >= startTime) + return candidate - 1; + + return candidate; +} + +int SceneGraphTimelineModel::findFirstIndexNoParents(qint64 startTime) const +{ + if (d->eventList.isEmpty()) + return -1; + if (d->eventList.count() == 1 || d->eventList.first().startTime+d->eventList.first().duration >= startTime) + return 0; + else + if (d->eventList.last().startTime+d->eventList.last().duration <= startTime) + return -1; + + int fromIndex = 0; + int toIndex = d->eventList.count()-1; + while (toIndex - fromIndex > 1) { + int midIndex = (fromIndex + toIndex)/2; + if (d->eventList[midIndex].startTime + d->eventList[midIndex].duration < startTime) + fromIndex = midIndex; + else + toIndex = midIndex; + } + return toIndex; +} + +int SceneGraphTimelineModel::findLastIndex(qint64 endTime) const +{ + if (d->eventList.isEmpty()) + return -1; + if (d->eventList.first().startTime >= endTime) + return -1; + if (d->eventList.count() == 1) + return 0; + if (d->eventList.last().startTime <= endTime) + return d->eventList.count()-1; + + int fromIndex = 0; + int toIndex = d->eventList.count()-1; + while (toIndex - fromIndex > 1) { + int midIndex = (fromIndex + toIndex)/2; + if (d->eventList[midIndex].startTime < endTime) + fromIndex = midIndex; + else + toIndex = midIndex; + } + + return fromIndex; +} + +int SceneGraphTimelineModel::getEventType(int index) const +{ + Q_UNUSED(index); + return QmlDebug::SceneGraphFrameEvent; +} + +int SceneGraphTimelineModel::getEventCategory(int index) const +{ + Q_UNUSED(index); + return 0; +} + +int SceneGraphTimelineModel::getEventRow(int index) const +{ + return d->eventList[index].sgEventType + 1; + if (d->isExpanded) + return d->eventList[index].sgEventType + 1; + else + return 0; +} + +qint64 SceneGraphTimelineModel::getDuration(int index) const +{ + return d->eventList[index].duration; +} + +qint64 SceneGraphTimelineModel::getStartTime(int index) const +{ + return d->eventList[index].startTime; +} + +qint64 SceneGraphTimelineModel::getEndTime(int index) const +{ + return getStartTime(index)+getDuration(index); +} + +int SceneGraphTimelineModel::getEventId(int index) const +{ + return d->eventList[index].sgEventType; +} + +QColor SceneGraphTimelineModel::getColor(int index) const +{ + // get duration in seconds + double eventDuration = getDuration(index) / 1e9; + + // supposedly never above 60 frames per second + // limit it in that case + if (eventDuration < 1/60.0) + eventDuration = 1/60.0; + + // generate hue based on fraction of the 60fps + double fpsFraction = 1 / (eventDuration * 60.0); + if (fpsFraction > 1.0) + fpsFraction = 1.0; + return QColor::fromHsl((fpsFraction*96)+10, 76, 166); +} + +float SceneGraphTimelineModel::getHeight(int index) const +{ + Q_UNUSED(index); + return 1.0f; +} + +QString labelForSGType(int t) +{ + switch ((SceneGraphCategoryType)t) { + case SceneGraphRenderThread: + return QCoreApplication::translate("SceneGraphTimelineModel", "Renderer Thread"); + case SceneGraphGUIThread: + return QCoreApplication::translate("SceneGraphTimelineModel", "GUI Thread"); + default: return QString(); + } +} + +const QVariantList SceneGraphTimelineModel::getLabelsForCategory(int category) const +{ + Q_UNUSED(category); + QVariantList result; + + if (d->isExpanded && !isEmpty()) { + for (int i = 0; i < MaximumSceneGraphCategoryType; i++) { + QVariantMap element; + + element.insert(QLatin1String("displayName"), QVariant(labelForSGType(i))); + element.insert(QLatin1String("description"), QVariant(labelForSGType(i))); + element.insert(QLatin1String("id"), QVariant(i)); + result << element; + } + } + + return result; +} + + + +QString SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::displayTime(double time) +{ + if (time < 1e6) + return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s"); + if (time < 1e9) + return QString::number(time/1e6,'f',3) + tr(" ms"); + + return QString::number(time/1e9,'f',3) + tr(" s"); +} + +void SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::addVP(QVariantList &l, QString label, qint64 time) +{ + if (time > 0) { + QVariantMap res; + res.insert(label, QVariant(displayTime(time))); + l << res; + } +} + + +const QVariantList SceneGraphTimelineModel::getEventDetails(int index) const +{ + QVariantList result; + SceneGraphEvent *ev = &d->eventList[index]; + + { + QVariantMap res; + res.insert(QLatin1String("title"), QVariant(labelForSGType(ev->sgEventType))); + result << res; + } + + d->addVP(result, tr("Duration"), ev->duration ); + + if (ev->sgEventType == SceneGraphRenderThread) { + d->addVP(result, tr("Polish"), ev->timing[14]); + d->addVP(result, tr("Sync"), ev->timing[0]); + d->addVP(result, tr("Preprocess"), ev->timing[1]); + d->addVP(result, tr("Upload"), ev->timing[2]); + d->addVP(result, tr("Swizzle"), ev->timing[3]); + d->addVP(result, tr("Convert"), ev->timing[4]); + d->addVP(result, tr("Mipmap"), ev->timing[5]); + d->addVP(result, tr("Bind"), ev->timing[6]); + d->addVP(result, tr("Material"), ev->timing[7]); + d->addVP(result, tr("Glyph Render"), ev->timing[8]); + d->addVP(result, tr("Glyph Store"), ev->timing[9]); + d->addVP(result, tr("Update"), ev->timing[10]); + d->addVP(result, tr("Binding"), ev->timing[11]); + d->addVP(result, tr("Render"), ev->timing[12]); + d->addVP(result, tr("Swap"), ev->timing[13]); + d->addVP(result, tr("Animations"), ev->timing[15]); + } + if (ev->sgEventType == SceneGraphGUIThread) { + d->addVP(result, tr("Polish"), ev->timing[0]); + d->addVP(result, tr("Wait"), ev->timing[1]); + d->addVP(result, tr("Sync"), ev->timing[2]); + d->addVP(result, tr("Animations"), ev->timing[3]); + } + + return result; +} + +const QVariantMap SceneGraphTimelineModel::getEventLocation(int /*index*/) const +{ + QVariantMap map; + return map; +} + +bool compareStartTimes(const SceneGraphTimelineModel::SceneGraphEvent&t1, const SceneGraphTimelineModel::SceneGraphEvent &t2) +{ + return t1.startTime < t2.startTime; +} + +void SceneGraphTimelineModel::loadData() +{ + clear(); + QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel(); + if (simpleModel->isEmpty()) + return; + + int lastRenderEvent = -1; + + // combine the data of several eventtypes into two rows + foreach (const QmlProfilerSimpleModel::QmlEventData &event, simpleModel->getEvents()) { + if (!eventAccepted(event)) + continue; + + if (event.bindingType == SceneGraphRenderLoopFrame) { + SceneGraphEvent newEvent; + newEvent.sgEventType = SceneGraphRenderThread; + newEvent.duration = event.numericData1 + event.numericData2 + event.numericData3; + newEvent.startTime = event.startTime - newEvent.duration; + for (int i=0; i < timingFieldCount; i++) + newEvent.timing[i] = 0; + + d->eventList << newEvent; + lastRenderEvent = d->eventList.count()-1; + } + + if (lastRenderEvent >= 0) { + switch ((SceneGraphEventType)event.bindingType) { + case SceneGraphRendererFrame: { + d->eventList[lastRenderEvent].timing[1] = event.numericData1; + d->eventList[lastRenderEvent].timing[10] = event.numericData2; + d->eventList[lastRenderEvent].timing[11] = event.numericData3; + d->eventList[lastRenderEvent].timing[12] = event.numericData4; + break; + } + case SceneGraphAdaptationLayerFrame: { + d->eventList[lastRenderEvent].timing[8] = event.numericData2; + d->eventList[lastRenderEvent].timing[9] = event.numericData3; + break; + } + case SceneGraphContextFrame: { + d->eventList[lastRenderEvent].timing[7] = event.numericData1; + break; + } + case SceneGraphRenderLoopFrame: { + d->eventList[lastRenderEvent].timing[0] = event.numericData1; + d->eventList[lastRenderEvent].timing[13] = event.numericData3; + + break; + } + case SceneGraphTexturePrepare: { + d->eventList[lastRenderEvent].timing[2] = event.numericData4; + d->eventList[lastRenderEvent].timing[3] = event.numericData3; + d->eventList[lastRenderEvent].timing[4] = event.numericData2; + d->eventList[lastRenderEvent].timing[5] = event.numericData5; + d->eventList[lastRenderEvent].timing[6] = event.numericData1; + break; + } + case SceneGraphPolishAndSync: { + // GUI thread + SceneGraphEvent newEvent; + newEvent.sgEventType = SceneGraphGUIThread; + newEvent.duration = event.numericData1 + event.numericData2 + event.numericData3 + event.numericData4; + newEvent.startTime = event.startTime - newEvent.duration; + for (int i=0; i < timingFieldCount; i++) + newEvent.timing[i] = 0; + + newEvent.timing[0] = event.numericData1; + newEvent.timing[1] = event.numericData2; + newEvent.timing[2] = event.numericData3; + newEvent.timing[3] = event.numericData4; + + d->eventList << newEvent; + break; + } + case SceneGraphWindowsAnimations: { + d->eventList[lastRenderEvent].timing[14] = event.numericData1; + break; + } + case SceneGraphWindowsPolishFrame: { + d->eventList[lastRenderEvent].timing[15] = event.numericData1; + break; + } + default: break; + } + } + } + + qSort(d->eventList.begin(), d->eventList.end(), compareStartTimes); +} + +void SceneGraphTimelineModel::clear() +{ + d->eventList.clear(); +} + +void SceneGraphTimelineModel::dataChanged() +{ + if (m_modelManager->state() == QmlProfilerDataState::Done) + loadData(); + + if (m_modelManager->state() == QmlProfilerDataState::Empty) + clear(); + + emit stateChanged(); + emit dataAvailable(); + emit emptyChanged(); + return; +} + + + + +} // namespace Internal +} // namespace QmlProfilerExtension |