QmlProfiler: Remove template/virtual magic from SortedTimelineModel

We actually don't have to save the actual data in the
SortedTimelineModel if we keep the data synchronized to the ranges. This
is easy to do by just keeping track of the indices when new ranges are
inserted. Like that we can eliminate the virtual function calls from
AbstractTimelineModelPrivate and simplify the type hierarchy.

Change-Id: Ia7aa02df57380932b689ddfe9a50ff2031198a7d
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
This commit is contained in:
Ulf Hermann
2014-07-08 17:18:51 +02:00
parent e91c79cb11
commit 18bf7113e0
7 changed files with 142 additions and 194 deletions

View File

@@ -35,7 +35,7 @@ namespace QmlProfiler {
AbstractTimelineModel::AbstractTimelineModel(AbstractTimelineModelPrivate *dd, AbstractTimelineModel::AbstractTimelineModel(AbstractTimelineModelPrivate *dd,
const QString &displayName, QmlDebug::Message message, QmlDebug::RangeType rangeType, const QString &displayName, QmlDebug::Message message, QmlDebug::RangeType rangeType,
QObject *parent) : QObject *parent) :
QObject(parent), d_ptr(dd) SortedTimelineModel(parent), d_ptr(dd)
{ {
Q_D(AbstractTimelineModel); Q_D(AbstractTimelineModel);
d->q_ptr = this; d->q_ptr = this;
@@ -61,48 +61,6 @@ void AbstractTimelineModel::setModelManager(QmlProfilerModelManager *modelManage
d->modelId = d->modelManager->registerModelProxy(); d->modelId = d->modelManager->registerModelProxy();
} }
int AbstractTimelineModel::count() const
{
Q_D(const AbstractTimelineModel);
return d->count();
}
int AbstractTimelineModel::firstIndex(qint64 startTime) const
{
Q_D(const AbstractTimelineModel);
return d->firstIndex(startTime);
}
int AbstractTimelineModel::firstIndexNoParents(qint64 startTime) const
{
Q_D(const AbstractTimelineModel);
return d->firstIndexNoParents(startTime);
}
int AbstractTimelineModel::lastIndex(qint64 endTime) const
{
Q_D(const AbstractTimelineModel);
return d->lastIndex(endTime);
}
qint64 AbstractTimelineModel::duration(int index) const
{
Q_D(const AbstractTimelineModel);
return d->duration(index);
}
qint64 AbstractTimelineModel::startTime(int index) const
{
Q_D(const AbstractTimelineModel);
return d->startTime(index);
}
qint64 AbstractTimelineModel::endTime(int index) const
{
Q_D(const AbstractTimelineModel);
return d->startTime(index) + d->duration(index);
}
bool AbstractTimelineModel::isEmpty() const bool AbstractTimelineModel::isEmpty() const
{ {
return count() == 0; return count() == 0;
@@ -241,8 +199,6 @@ void AbstractTimelineModel::dataChanged()
default: default:
break; break;
} }
d->rowOffsets.clear();
} }
bool AbstractTimelineModel::accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const bool AbstractTimelineModel::accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const
@@ -272,4 +228,19 @@ QString AbstractTimelineModel::displayName() const
return d->displayName; return d->displayName;
} }
void AbstractTimelineModel::clear()
{
Q_D(AbstractTimelineModel);
bool wasExpanded = d->expanded;
bool hadRowHeights = !d->rowOffsets.empty();
d->rowOffsets.clear();
d->expanded = false;
SortedTimelineModel::clear();
if (hadRowHeights)
emit rowHeightChanged();
if (wasExpanded)
emit expandedChanged();
d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1);
}
} }

View File

@@ -34,13 +34,13 @@
#include "qmlprofiler_global.h" #include "qmlprofiler_global.h"
#include "qmlprofilermodelmanager.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilerdatamodel.h" #include "qmlprofilerdatamodel.h"
#include <QObject> #include "sortedtimelinemodel.h"
#include <QVariant> #include <QVariant>
#include <QColor> #include <QColor>
namespace QmlProfiler { namespace QmlProfiler {
class QMLPROFILER_EXPORT AbstractTimelineModel : public QObject class QMLPROFILER_EXPORT AbstractTimelineModel : public SortedTimelineModel
{ {
Q_OBJECT Q_OBJECT
@@ -61,13 +61,6 @@ public:
qint64 traceStartTime() const; qint64 traceStartTime() const;
qint64 traceEndTime() const; qint64 traceEndTime() const;
qint64 traceDuration() const; qint64 traceDuration() const;
qint64 duration(int index) const;
qint64 startTime(int index) const;
qint64 endTime(int index) const;
int firstIndex(qint64 startTime) const;
int firstIndexNoParents(qint64 startTime) const;
int lastIndex(qint64 endTime) const;
int count() const;
bool accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const; bool accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const;
bool expanded() const; bool expanded() const;
void setExpanded(bool expanded); void setExpanded(bool expanded);
@@ -81,7 +74,6 @@ public:
virtual QVariantMap details(int index) const = 0; virtual QVariantMap details(int index) const = 0;
virtual int row(int index) const = 0; virtual int row(int index) const = 0;
virtual void loadData() = 0; virtual void loadData() = 0;
virtual void clear() = 0;
// Methods which can optionally be implemented by child models. // Methods which can optionally be implemented by child models.
// returned map should contain "file", "line", "column" properties, or be empty // returned map should contain "file", "line", "column" properties, or be empty
@@ -92,6 +84,7 @@ public:
virtual float height(int index) const; virtual float height(int index) const;
virtual int rowMinValue(int rowNumber) const; virtual int rowMinValue(int rowNumber) const;
virtual int rowMaxValue(int rowNumber) const; virtual int rowMaxValue(int rowNumber) const;
virtual void clear();
signals: signals:
void expandedChanged(); void expandedChanged();

View File

@@ -36,17 +36,6 @@ namespace QmlProfiler {
class QMLPROFILER_EXPORT AbstractTimelineModel::AbstractTimelineModelPrivate { class QMLPROFILER_EXPORT AbstractTimelineModel::AbstractTimelineModelPrivate {
public: public:
virtual ~AbstractTimelineModelPrivate() {}
virtual int count() const = 0;
virtual qint64 duration(int index) const = 0;
virtual qint64 startTime(int index) const = 0;
virtual qint64 lastEndTime() const = 0;
virtual qint64 firstStartTime() const = 0;
virtual int firstIndex(qint64 startTime) const = 0;
virtual int firstIndexNoParents(qint64 startTime) const = 0;
virtual int lastIndex(qint64 endTime) const = 0;
QVector<int> rowOffsets; QVector<int> rowOffsets;
QmlProfilerModelManager *modelManager; QmlProfilerModelManager *modelManager;
int modelId; int modelId;

View File

@@ -30,7 +30,6 @@
#include "qmlprofilerpainteventsmodelproxy.h" #include "qmlprofilerpainteventsmodelproxy.h"
#include "qmlprofilermodelmanager.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilerdatamodel.h" #include "qmlprofilerdatamodel.h"
#include "sortedtimelinemodel.h"
#include "abstracttimelinemodel_p.h" #include "abstracttimelinemodel_p.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QCoreApplication> #include <QCoreApplication>
@@ -46,12 +45,10 @@
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
class PaintEventsModelProxy::PaintEventsModelProxyPrivate : class PaintEventsModelProxy::PaintEventsModelProxyPrivate : public AbstractTimelineModelPrivate
public SortedTimelineModel<QmlPaintEventData,
AbstractTimelineModel::AbstractTimelineModelPrivate>
{ {
public: public:
QVector<PaintEventsModelProxy::QmlPaintEventData> data;
int maxGuiThreadAnimations; int maxGuiThreadAnimations;
int maxRenderThreadAnimations; int maxRenderThreadAnimations;
int rowFromThreadId(QmlDebug::AnimationThread threadId) const; int rowFromThreadId(QmlDebug::AnimationThread threadId) const;
@@ -72,10 +69,9 @@ PaintEventsModelProxy::PaintEventsModelProxy(QObject *parent)
void PaintEventsModelProxy::clear() void PaintEventsModelProxy::clear()
{ {
Q_D(PaintEventsModelProxy); Q_D(PaintEventsModelProxy);
d->clear();
d->maxGuiThreadAnimations = d->maxRenderThreadAnimations = 0; d->maxGuiThreadAnimations = d->maxRenderThreadAnimations = 0;
d->expanded = false; d->data.clear();
d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1); AbstractTimelineModel::clear();
} }
bool PaintEventsModelProxy::accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const bool PaintEventsModelProxy::accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const
@@ -125,7 +121,7 @@ void PaintEventsModelProxy::loadData()
lastEvent.animationcount = (int)event.numericData2; lastEvent.animationcount = (int)event.numericData2;
QTC_ASSERT(lastEvent.animationcount > 0, continue); QTC_ASSERT(lastEvent.animationcount > 0, continue);
d->insert(realStartTime, realEndTime - realStartTime, lastEvent); d->data.insert(insert(realStartTime, realEndTime - realStartTime), lastEvent);
if (lastEvent.threadId == QmlDebug::GuiThread) if (lastEvent.threadId == QmlDebug::GuiThread)
d->maxGuiThreadAnimations = qMax(lastEvent.animationcount, d->maxGuiThreadAnimations); d->maxGuiThreadAnimations = qMax(lastEvent.animationcount, d->maxGuiThreadAnimations);
@@ -135,10 +131,10 @@ void PaintEventsModelProxy::loadData()
minNextStartTimes[lastEvent.threadId] = event.startTime + 1; minNextStartTimes[lastEvent.threadId] = event.startTime + 1;
d->modelManager->modelProxyCountUpdated(d->modelId, d->count(), referenceList.count()); d->modelManager->modelProxyCountUpdated(d->modelId, count(), referenceList.count());
} }
d->computeNesting(); computeNesting();
d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1); d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1);
} }
@@ -163,7 +159,7 @@ int PaintEventsModelProxy::PaintEventsModelProxyPrivate::rowFromThreadId(
int PaintEventsModelProxy::row(int index) const int PaintEventsModelProxy::row(int index) const
{ {
Q_D(const PaintEventsModelProxy); Q_D(const PaintEventsModelProxy);
return d->rowFromThreadId(d->range(index).threadId); return d->rowFromThreadId(d->data[index].threadId);
} }
int PaintEventsModelProxy::rowMaxValue(int rowNumber) const int PaintEventsModelProxy::rowMaxValue(int rowNumber) const
@@ -183,13 +179,13 @@ int PaintEventsModelProxy::rowMaxValue(int rowNumber) const
int PaintEventsModelProxy::eventId(int index) const int PaintEventsModelProxy::eventId(int index) const
{ {
Q_D(const PaintEventsModelProxy); Q_D(const PaintEventsModelProxy);
return d->range(index).threadId; return d->data[index].threadId;
} }
QColor PaintEventsModelProxy::color(int index) const QColor PaintEventsModelProxy::color(int index) const
{ {
Q_D(const PaintEventsModelProxy); Q_D(const PaintEventsModelProxy);
double fpsFraction = d->range(index).framerate / 60.0; double fpsFraction = d->data[index].framerate / 60.0;
if (fpsFraction > 1.0) if (fpsFraction > 1.0)
fpsFraction = 1.0; fpsFraction = 1.0;
if (fpsFraction < 0.0) if (fpsFraction < 0.0)
@@ -200,15 +196,15 @@ QColor PaintEventsModelProxy::color(int index) const
float PaintEventsModelProxy::height(int index) const float PaintEventsModelProxy::height(int index) const
{ {
Q_D(const PaintEventsModelProxy); Q_D(const PaintEventsModelProxy);
const PaintEventsModelProxyPrivate::Range &range = d->range(index); const QmlPaintEventData &data = d->data[index];
// Add some height to the events if we're far from the scale threshold of 2 * DefaultRowHeight. // Add some height to the events if we're far from the scale threshold of 2 * DefaultRowHeight.
// Like that you can see the smaller events more easily. // Like that you can see the smaller events more easily.
int scaleThreshold = 2 * DefaultRowHeight - rowHeight(d->rowFromThreadId(range.threadId)); int scaleThreshold = 2 * DefaultRowHeight - rowHeight(d->rowFromThreadId(data.threadId));
float boost = scaleThreshold > 0 ? (0.15 * scaleThreshold / DefaultRowHeight) : 0; float boost = scaleThreshold > 0 ? (0.15 * scaleThreshold / DefaultRowHeight) : 0;
return boost + (1.0 - boost) * (float)range.animationcount / return boost + (1.0 - boost) * (float)data.animationcount /
(float)(range.threadId == QmlDebug::GuiThread ? d->maxGuiThreadAnimations : (float)(data.threadId == QmlDebug::GuiThread ? d->maxGuiThreadAnimations :
d->maxRenderThreadAnimations); d->maxRenderThreadAnimations);
} }
@@ -242,10 +238,10 @@ QVariantMap PaintEventsModelProxy::details(int index) const
QVariantMap result; QVariantMap result;
result.insert(QStringLiteral("displayName"), displayName()); result.insert(QStringLiteral("displayName"), displayName());
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(d->range(index).duration)); result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration));
result.insert(tr("Framerate"), QString::fromLatin1("%1 FPS").arg(d->range(index).framerate)); result.insert(tr("Framerate"), QString::fromLatin1("%1 FPS").arg(d->data[index].framerate));
result.insert(tr("Animations"), QString::fromLatin1("%1").arg(d->range(index).animationcount)); result.insert(tr("Animations"), QString::fromLatin1("%1").arg(d->data[index].animationcount));
result.insert(tr("Context"), tr(d->range(index).threadId == QmlDebug::GuiThread ? result.insert(tr("Context"), tr(d->data[index].threadId == QmlDebug::GuiThread ?
"GUI Thread" : "Render Thread")); "GUI Thread" : "Render Thread"));
return result; return result;
} }

View File

@@ -30,7 +30,6 @@
#include "qmlprofilertimelinemodelproxy.h" #include "qmlprofilertimelinemodelproxy.h"
#include "qmlprofilermodelmanager.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilerdatamodel.h" #include "qmlprofilerdatamodel.h"
#include "sortedtimelinemodel.h"
#include "abstracttimelinemodel_p.h" #include "abstracttimelinemodel_p.h"
#include <QCoreApplication> #include <QCoreApplication>
@@ -45,9 +44,7 @@
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
class RangeTimelineModel::RangeTimelineModelPrivate : class RangeTimelineModel::RangeTimelineModelPrivate : public AbstractTimelineModelPrivate
public SortedTimelineModel<RangeTimelineModel::QmlRangeEventStartInstance,
AbstractTimelineModel::AbstractTimelineModelPrivate>
{ {
public: public:
// convenience functions // convenience functions
@@ -55,6 +52,7 @@ public:
void computeExpandedLevels(); void computeExpandedLevels();
void findBindingLoops(); void findBindingLoops();
QVector<QmlRangeEventStartInstance> data;
QVector<int> expandedRowTypes; QVector<int> expandedRowTypes;
int contractedRows; int contractedRows;
bool seenPaintEvent; bool seenPaintEvent;
@@ -68,7 +66,6 @@ RangeTimelineModel::RangeTimelineModel(QmlDebug::RangeType rangeType, QObject *p
{ {
Q_D(RangeTimelineModel); Q_D(RangeTimelineModel);
d->seenPaintEvent = false; d->seenPaintEvent = false;
d->expandedRowTypes.clear();
d->expandedRowTypes << -1; d->expandedRowTypes << -1;
d->contractedRows = 1; d->contractedRows = 1;
} }
@@ -76,14 +73,12 @@ RangeTimelineModel::RangeTimelineModel(QmlDebug::RangeType rangeType, QObject *p
void RangeTimelineModel::clear() void RangeTimelineModel::clear()
{ {
Q_D(RangeTimelineModel); Q_D(RangeTimelineModel);
d->clear();
d->expandedRowTypes.clear(); d->expandedRowTypes.clear();
d->expandedRowTypes << -1; d->expandedRowTypes << -1;
d->contractedRows = 1; d->contractedRows = 1;
d->seenPaintEvent = false; d->seenPaintEvent = false;
d->expanded = false; d->data.clear();
AbstractTimelineModel::clear();
d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1);
} }
void RangeTimelineModel::loadData() void RangeTimelineModel::loadData()
@@ -105,15 +100,15 @@ void RangeTimelineModel::loadData()
d->seenPaintEvent = true; d->seenPaintEvent = true;
// store starttime-based instance // store starttime-based instance
d->insert(event.startTime, event.duration, QmlRangeEventStartInstance(event.typeIndex)); d->data.insert(insert(event.startTime, event.duration),
QmlRangeEventStartInstance(event.typeIndex));
d->modelManager->modelProxyCountUpdated(d->modelId, d->count(), eventList.count() * 6); d->modelManager->modelProxyCountUpdated(d->modelId, count(), eventList.count() * 6);
} }
d->modelManager->modelProxyCountUpdated(d->modelId, 2, 6); d->modelManager->modelProxyCountUpdated(d->modelId, 2, 6);
// compute range nesting // compute range nesting
d->computeNesting(); computeNesting();
// compute nestingLevel - nonexpanded // compute nestingLevel - nonexpanded
d->computeNestingContracted(); d->computeNestingContracted();
@@ -134,8 +129,9 @@ void RangeTimelineModel::loadData()
void RangeTimelineModel::RangeTimelineModelPrivate::computeNestingContracted() void RangeTimelineModel::RangeTimelineModelPrivate::computeNestingContracted()
{ {
Q_Q(RangeTimelineModel);
int i; int i;
int eventCount = count(); int eventCount = q->count();
int nestingLevels = QmlDebug::Constants::QML_MIN_LEVEL; int nestingLevels = QmlDebug::Constants::QML_MIN_LEVEL;
contractedRows = nestingLevels + 1; contractedRows = nestingLevels + 1;
@@ -143,7 +139,7 @@ void RangeTimelineModel::RangeTimelineModelPrivate::computeNestingContracted()
nestingEndTimes.fill(0, nestingLevels + 1); nestingEndTimes.fill(0, nestingLevels + 1);
for (i = 0; i < eventCount; i++) { for (i = 0; i < eventCount; i++) {
qint64 st = ranges[i].start; qint64 st = q->ranges[i].start;
// per type // per type
if (nestingEndTimes[nestingLevels] > st) { if (nestingEndTimes[nestingLevels] > st) {
@@ -156,57 +152,57 @@ void RangeTimelineModel::RangeTimelineModelPrivate::computeNestingContracted()
nestingEndTimes[nestingLevels-1] <= st) nestingEndTimes[nestingLevels-1] <= st)
nestingLevels--; nestingLevels--;
} }
nestingEndTimes[nestingLevels] = st + ranges[i].duration; nestingEndTimes[nestingLevels] = st + q->ranges[i].duration;
ranges[i].displayRowCollapsed = nestingLevels; data[i].displayRowCollapsed = nestingLevels;
} }
} }
void RangeTimelineModel::RangeTimelineModelPrivate::computeExpandedLevels() void RangeTimelineModel::RangeTimelineModelPrivate::computeExpandedLevels()
{ {
Q_Q(RangeTimelineModel);
QHash<int, int> eventRow; QHash<int, int> eventRow;
int eventCount = count(); int eventCount = q->count();
for (int i = 0; i < eventCount; i++) { for (int i = 0; i < eventCount; i++) {
int eventId = ranges[i].eventId; int eventId = data[i].eventId;
if (!eventRow.contains(eventId)) { if (!eventRow.contains(eventId)) {
eventRow[eventId] = expandedRowTypes.size(); eventRow[eventId] = expandedRowTypes.size();
expandedRowTypes << eventId; expandedRowTypes << eventId;
} }
ranges[i].displayRowExpanded = eventRow[eventId]; data[i].displayRowExpanded = eventRow[eventId];
} }
} }
void RangeTimelineModel::RangeTimelineModelPrivate::findBindingLoops() void RangeTimelineModel::RangeTimelineModelPrivate::findBindingLoops()
{ {
Q_Q(RangeTimelineModel);
if (rangeType != QmlDebug::Binding && rangeType != QmlDebug::HandlingSignal) if (rangeType != QmlDebug::Binding && rangeType != QmlDebug::HandlingSignal)
return; return;
typedef QPair<int, int> CallStackEntry; typedef QPair<int, int> CallStackEntry;
QStack<CallStackEntry> callStack; QStack<CallStackEntry> callStack;
for (int i = 0; i < count(); ++i) { for (int i = 0; i < q->count(); ++i) {
Range *event = &ranges[i];
const Range *potentialParent = callStack.isEmpty() const Range *potentialParent = callStack.isEmpty()
? 0 : &ranges[callStack.top().second]; ? 0 : &q->ranges[callStack.top().second];
while (potentialParent while (potentialParent
&& !(potentialParent->start + potentialParent->duration > event->start)) { && !(potentialParent->start + potentialParent->duration > q->ranges[i].start)) {
callStack.pop(); callStack.pop();
potentialParent = callStack.isEmpty() ? 0 potentialParent = callStack.isEmpty() ? 0
: &ranges[callStack.top().second]; : &q->ranges[callStack.top().second];
} }
// check whether event is already in stack // check whether event is already in stack
for (int ii = 0; ii < callStack.size(); ++ii) { for (int ii = 0; ii < callStack.size(); ++ii) {
if (callStack.at(ii).first == event->eventId) { if (callStack.at(ii).first == data[i].eventId) {
event->bindingLoopHead = callStack.at(ii).second; data[i].bindingLoopHead = callStack.at(ii).second;
break; break;
} }
} }
CallStackEntry newEntry(event->eventId, i); CallStackEntry newEntry(data[i].eventId, i);
callStack.push(newEntry); callStack.push(newEntry);
} }
@@ -243,21 +239,21 @@ int RangeTimelineModel::row(int index) const
{ {
Q_D(const RangeTimelineModel); Q_D(const RangeTimelineModel);
if (d->expanded) if (d->expanded)
return d->range(index).displayRowExpanded; return d->data[index].displayRowExpanded;
else else
return d->range(index).displayRowCollapsed; return d->data[index].displayRowCollapsed;
} }
int RangeTimelineModel::eventId(int index) const int RangeTimelineModel::eventId(int index) const
{ {
Q_D(const RangeTimelineModel); Q_D(const RangeTimelineModel);
return d->range(index).eventId; return d->data[index].eventId;
} }
int RangeTimelineModel::bindingLoopDest(int index) const int RangeTimelineModel::bindingLoopDest(int index) const
{ {
Q_D(const RangeTimelineModel); Q_D(const RangeTimelineModel);
return d->range(index).bindingLoopHead; return d->data[index].bindingLoopHead;
} }
QColor RangeTimelineModel::color(int index) const QColor RangeTimelineModel::color(int index) const
@@ -296,7 +292,7 @@ QVariantMap RangeTimelineModel::details(int index) const
d->modelManager->qmlModel()->getEventTypes(); d->modelManager->qmlModel()->getEventTypes();
result.insert(QStringLiteral("displayName"), categoryLabel(d->rangeType)); result.insert(QStringLiteral("displayName"), categoryLabel(d->rangeType));
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(d->range(index).duration)); result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration));
QString detailsString = types[id].data; QString detailsString = types[id].data;
if (detailsString.length() > 40) if (detailsString.length() > 40)

View File

@@ -27,6 +27,9 @@
** **
****************************************************************************/ ****************************************************************************/
#include "sortedtimelinemodel.h"
namespace QmlProfiler {
/*! /*!
\class QmlProfiler::SortedTimelineModel \class QmlProfiler::SortedTimelineModel
\brief The SortedTimelineModel class provides a sorted model for timeline data. \brief The SortedTimelineModel class provides a sorted model for timeline data.
@@ -49,6 +52,11 @@
\fn SortedTimelineModel::clear() \fn SortedTimelineModel::clear()
Clears the ranges and their end times. Clears the ranges and their end times.
*/ */
void SortedTimelineModel::clear()
{
ranges.clear();
endTimes.clear();
}
/*! /*!
\fn int SortedTimelineModel::count() const \fn int SortedTimelineModel::count() const
@@ -66,18 +74,13 @@
*/ */
/*! /*!
\fn const SortedTimelineModel<Data>::Range &SortedTimelineModel::range(int index) const \fn const SortedTimelineModel::Range &SortedTimelineModel::range(int index) const
Returns the range data at the specified index. Returns the range data at the specified index.
*/ */
/*! /*!
\fn Data &SortedTimelineModel::data(int index) \fn int SortedTimelineModel::insert(qint64 startTime, qint64 duration)
Returns modifiable user data for the range at the specified index. Inserts a range at the given time position and returns its index.
*/
/*!
\fn int SortedTimelineModel::insert(qint64 startTime, qint64 duration, const Data &item)
Inserts the given data at the given time position and returns its index.
*/ */
/*! /*!
@@ -114,7 +117,48 @@
*/ */
/*! /*!
\fn void computeNesting() \fn void SortedTimelineModel::computeNesting()
Compute all ranges' parents. Compute all ranges' parents.
\sa findFirstIndex \sa findFirstIndex
*/ */
void SortedTimelineModel::computeNesting()
{
QLinkedList<int> parents;
for (int range = 0; range != count(); ++range) {
Range &current = ranges[range];
for (QLinkedList<int>::iterator parentIt = parents.begin();;) {
Range &parent = ranges[*parentIt];
qint64 parentEnd = parent.start + parent.duration;
if (parentEnd < current.start) {
if (parent.start == current.start) {
if (parent.parent == -1) {
parent.parent = range;
} else {
Range &ancestor = ranges[parent.parent];
if (ancestor.start == current.start &&
ancestor.duration < current.duration)
parent.parent = range;
}
// Just switch the old parent range for the new, larger one
*parentIt = range;
break;
} else {
parentIt = parents.erase(parentIt);
}
} else if (parentEnd >= current.start + current.duration) {
// no need to insert
current.parent = *parentIt;
break;
} else {
++parentIt;
}
if (parentIt == parents.end()) {
parents.append(range);
break;
}
}
}
}
}

View File

@@ -30,22 +30,21 @@
#ifndef SORTEDTIMELINEMODEL_H #ifndef SORTEDTIMELINEMODEL_H
#define SORTEDTIMELINEMODEL_H #define SORTEDTIMELINEMODEL_H
#include "abstracttimelinemodel_p.h" #include "qmlprofiler_global.h"
#include <QObject>
#include <QVector> #include <QVector>
#include <QLinkedList> #include <QLinkedList>
namespace QmlProfiler { namespace QmlProfiler {
// The template has to be inserted into the hierarchy of public/private classes when Data is known. class QMLPROFILER_EXPORT SortedTimelineModel : public QObject {
// Otherwise we'd have to add implementation details to the public headers. This is why the class to Q_OBJECT
// be derived from is given as template parameter.
template<class Data, class Base = AbstractTimelineModel::AbstractTimelineModelPrivate>
class SortedTimelineModel : public Base {
public: public:
struct Range : public Data { struct Range {
Range() : Data(), start(-1), duration(-1), parent(-1) {} Range() : start(-1), duration(-1), parent(-1) {}
Range(qint64 start, qint64 duration, const Data &item) : Range(qint64 start, qint64 duration) :
Data(item), start(start), duration(duration), parent(-1) {} start(start), duration(duration), parent(-1) {}
qint64 start; qint64 start;
qint64 duration; qint64 duration;
int parent; int parent;
@@ -61,37 +60,35 @@ public:
inline qint64 timestamp() const {return end;} inline qint64 timestamp() const {return end;}
}; };
void clear() SortedTimelineModel(QObject *parent = 0) : QObject(parent) {}
{
ranges.clear(); void clear();
endTimes.clear();
}
inline int count() const { return ranges.count(); } inline int count() const { return ranges.count(); }
qint64 duration(int index) const { return range(index).duration; } qint64 duration(int index) const { return ranges[index].duration; }
qint64 startTime(int index) const { return range(index).start; } qint64 startTime(int index) const { return ranges[index].start; }
qint64 endTime(int index) const { return ranges[index].start + ranges[index].duration; }
inline qint64 lastEndTime() const { return endTimes.last().end; } inline qint64 lastEndTime() const { return endTimes.last().end; }
inline qint64 firstStartTime() const { return ranges.first().start; } inline qint64 firstStartTime() const { return ranges.first().start; }
inline const Range &range(int index) const { return ranges[index]; } inline const Range &range(int index) const { return ranges[index]; }
inline Data &data(int index) { return ranges[index]; }
inline int insert(qint64 startTime, qint64 duration, const Data &item) inline int insert(qint64 startTime, qint64 duration)
{ {
/* Doing insert-sort here is preferable as most of the time the times will actually be /* Doing insert-sort here is preferable as most of the time the times will actually be
* presorted in the right way. So usually this will just result in appending. */ * presorted in the right way. So usually this will just result in appending. */
int index = insertSorted(ranges, Range(startTime, duration, item)); int index = insertSorted(ranges, Range(startTime, duration));
if (index < ranges.size() - 1) if (index < ranges.size() - 1)
incrementStartIndices(index); incrementStartIndices(index);
insertSorted(endTimes, RangeEnd(index, startTime + duration)); insertSorted(endTimes, RangeEnd(index, startTime + duration));
return index; return index;
} }
inline int insertStart(qint64 startTime, const Data &item) inline int insertStart(qint64 startTime)
{ {
int index = insertSorted(ranges, Range(startTime, 0, item)); int index = insertSorted(ranges, Range(startTime, 0));
if (index < ranges.size() - 1) if (index < ranges.size() - 1)
incrementStartIndices(index); incrementStartIndices(index);
return index; return index;
@@ -138,47 +135,9 @@ public:
return lowerBound(ranges, endTime); return lowerBound(ranges, endTime);
} }
inline void computeNesting()
{
QLinkedList<int> parents;
for (int range = 0; range != count(); ++range) {
Range &current = ranges[range];
for (QLinkedList<int>::iterator parentIt = parents.begin();;) {
Range &parent = ranges[*parentIt];
qint64 parentEnd = parent.start + parent.duration;
if (parentEnd < current.start) {
if (parent.start == current.start) {
if (parent.parent == -1) {
parent.parent = range;
} else {
Range &ancestor = ranges[parent.parent];
if (ancestor.start == current.start &&
ancestor.duration < current.duration)
parent.parent = range;
}
// Just switch the old parent range for the new, larger one
*parentIt = range;
break;
} else {
parentIt = parents.erase(parentIt);
}
} else if (parentEnd >= current.start + current.duration) {
// no need to insert
current.parent = *parentIt;
break;
} else {
++parentIt;
}
if (parentIt == parents.end()) {
parents.append(range);
break;
}
}
}
}
protected: protected:
void computeNesting();
void incrementStartIndices(int index) void incrementStartIndices(int index)
{ {
for (int i = 0; i < endTimes.size(); ++i) { for (int i = 0; i < endTimes.size(); ++i) {