forked from qt-creator/qt-creator
QmlProfiler: Merge SortedTimelineModel into AbstractTimelineModel
In order to provide a stable interface we need most of SortedTimelineModel to be private. There is no real benefit in keeping a distinction between AbstractTimelineModel and SortedTimelineModel as SortedTimelineModel isn't very useful on its own. Change-Id: Ibc6945e2740320f430f2634f95c7807d6b460123 Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
@@ -31,28 +31,124 @@
|
||||
#include "abstracttimelinemodel.h"
|
||||
#include "abstracttimelinemodel_p.h"
|
||||
|
||||
#include <QLinkedList>
|
||||
|
||||
namespace QmlProfiler {
|
||||
|
||||
/*!
|
||||
\class QmlProfiler::AbstractTimelineModel
|
||||
\brief The AbstractTimelineModel class provides a sorted model for timeline data.
|
||||
|
||||
The AbstractTimelineModel lets you keep range data sorted by both start and end times, so that
|
||||
visible ranges can easily be computed. The only precondition for that to work is that the ranges
|
||||
must be perfectly nested. A "parent" range of a range R is defined as a range for which the
|
||||
start time is smaller than R's start time and the end time is greater than R's end time. A set
|
||||
of ranges is perfectly nested if all parent ranges of any given range have a common parent
|
||||
range. Mind that you can always make that happen by defining a range that spans the whole
|
||||
available time span. That, however, will make any code that uses firstStartTime() and
|
||||
lastEndTime() for selecting subsets of the model always select all of it.
|
||||
|
||||
\note Indices returned from the various methods are only valid until a new range is inserted
|
||||
before them. Inserting a new range before a given index moves the range pointed to by the
|
||||
index by one. Incrementing the index by one will make it point to the item again.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn qint64 AbstractTimelineModelPrivate::firstStartTime() const
|
||||
Returns the begin of the first range in the model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn qint64 AbstractTimelineModelPrivate::lastEndTime() const
|
||||
Returns the end of the last range in the model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn const AbstractTimelineModelPrivate::Range &AbstractTimelineModelPrivate::range(int index) const
|
||||
Returns the range data at the specified index.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\fn void AbstractTimelineModel::computeNesting()
|
||||
Compute all ranges' parents.
|
||||
\sa findFirstIndex
|
||||
*/
|
||||
void AbstractTimelineModel::computeNesting()
|
||||
{
|
||||
Q_D(AbstractTimelineModel);
|
||||
QLinkedList<int> parents;
|
||||
for (int range = 0; range != count(); ++range) {
|
||||
AbstractTimelineModelPrivate::Range ¤t = d->ranges[range];
|
||||
for (QLinkedList<int>::iterator parentIt = parents.begin();;) {
|
||||
if (parentIt == parents.end()) {
|
||||
parents.append(range);
|
||||
break;
|
||||
}
|
||||
|
||||
AbstractTimelineModelPrivate::Range &parent = d->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 {
|
||||
AbstractTimelineModelPrivate::Range &ancestor = d->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractTimelineModel::AbstractTimelineModelPrivate::init(AbstractTimelineModel *q,
|
||||
const QString &newDisplayName,
|
||||
QmlDebug::Message newMessage,
|
||||
QmlDebug::RangeType newRangeType)
|
||||
{
|
||||
q_ptr = q;
|
||||
modelId = 0;
|
||||
modelManager = 0;
|
||||
expanded = false;
|
||||
hidden = false;
|
||||
displayName = newDisplayName;
|
||||
message = newMessage;
|
||||
rangeType = newRangeType;
|
||||
expandedRowCount = 1;
|
||||
collapsedRowCount = 1;
|
||||
connect(q,SIGNAL(rowHeightChanged()),q,SIGNAL(heightChanged()));
|
||||
connect(q,SIGNAL(expandedChanged()),q,SIGNAL(heightChanged()));
|
||||
connect(q,SIGNAL(hiddenChanged()),q,SIGNAL(heightChanged()));
|
||||
}
|
||||
|
||||
|
||||
AbstractTimelineModel::AbstractTimelineModel(AbstractTimelineModelPrivate *dd,
|
||||
const QString &displayName, QmlDebug::Message message, QmlDebug::RangeType rangeType,
|
||||
QObject *parent) :
|
||||
SortedTimelineModel(parent), d_ptr(dd)
|
||||
QObject(parent), d_ptr(dd)
|
||||
{
|
||||
Q_D(AbstractTimelineModel);
|
||||
connect(this,SIGNAL(rowHeightChanged()),this,SIGNAL(heightChanged()));
|
||||
connect(this,SIGNAL(expandedChanged()),this,SIGNAL(heightChanged()));
|
||||
connect(this,SIGNAL(hiddenChanged()),this,SIGNAL(heightChanged()));
|
||||
dd->init(this, displayName, message, rangeType);
|
||||
}
|
||||
|
||||
d->q_ptr = this;
|
||||
d->modelId = 0;
|
||||
d->modelManager = 0;
|
||||
d->expanded = false;
|
||||
d->hidden = false;
|
||||
d->displayName = displayName;
|
||||
d->message = message;
|
||||
d->rangeType = rangeType;
|
||||
d->expandedRowCount = 1;
|
||||
d->collapsedRowCount = 1;
|
||||
AbstractTimelineModel::AbstractTimelineModel(const QString &displayName, QmlDebug::Message message,
|
||||
QmlDebug::RangeType rangeType, QObject *parent) :
|
||||
QObject(parent), d_ptr(new AbstractTimelineModelPrivate)
|
||||
{
|
||||
d_ptr->init(this, displayName, message, rangeType);
|
||||
}
|
||||
|
||||
AbstractTimelineModel::~AbstractTimelineModel()
|
||||
@@ -138,6 +234,97 @@ int AbstractTimelineModel::height() const
|
||||
return d->rowOffsets.last() + (depth - d->rowOffsets.size()) * DefaultRowHeight;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int AbstractTimelineModel::count() const
|
||||
Returns the number of ranges in the model.
|
||||
*/
|
||||
int AbstractTimelineModel::count() const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
return d->ranges.count();
|
||||
}
|
||||
|
||||
qint64 AbstractTimelineModel::duration(int index) const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
return d->ranges[index].duration;
|
||||
}
|
||||
|
||||
qint64 AbstractTimelineModel::startTime(int index) const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
return d->ranges[index].start;
|
||||
}
|
||||
|
||||
qint64 AbstractTimelineModel::endTime(int index) const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
return d->ranges[index].start + d->ranges[index].duration;
|
||||
}
|
||||
|
||||
int AbstractTimelineModel::typeId(int index) const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
return d->ranges[index].typeId;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int AbstractTimelineModel::firstIndex(qint64 startTime) const
|
||||
Looks up the first range with an end time greater than the given time and
|
||||
returns its parent's index. If no such range is found, it returns -1. If there
|
||||
is no parent, it returns the found range's index. The parent of a range is the
|
||||
range with the lowest start time that completely covers the child range.
|
||||
"Completely covers" means:
|
||||
parent.startTime <= child.startTime && parent.endTime >= child.endTime
|
||||
*/
|
||||
int AbstractTimelineModel::firstIndex(qint64 startTime) const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
int index = firstIndexNoParents(startTime);
|
||||
if (index == -1)
|
||||
return -1;
|
||||
int parent = d->ranges[index].parent;
|
||||
return parent == -1 ? index : parent;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int AbstractTimelineModel::firstIndexNoParents(qint64 startTime) const
|
||||
Looks up the first range with an end time greater than the given time and
|
||||
returns its index. If no such range is found, it returns -1.
|
||||
*/
|
||||
int AbstractTimelineModel::firstIndexNoParents(qint64 startTime) const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
// in the "endtime" list, find the first event that ends after startTime
|
||||
if (d->endTimes.isEmpty())
|
||||
return -1;
|
||||
if (d->endTimes.count() == 1 || d->endTimes.first().end > startTime)
|
||||
return d->endTimes.first().startIndex;
|
||||
if (d->endTimes.last().end <= startTime)
|
||||
return -1;
|
||||
|
||||
return d->endTimes[d->lowerBound(d->endTimes, startTime) + 1].startIndex;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int AbstractTimelineModel::lastIndex(qint64 endTime) const
|
||||
Looks up the last range with a start time smaller than the given time and
|
||||
returns its index. If no such range is found, it returns -1.
|
||||
*/
|
||||
int AbstractTimelineModel::lastIndex(qint64 endTime) const
|
||||
{
|
||||
Q_D(const AbstractTimelineModel);
|
||||
// in the "starttime" list, find the last event that starts before endtime
|
||||
if (d->ranges.isEmpty() || d->ranges.first().start >= endTime)
|
||||
return -1;
|
||||
if (d->ranges.count() == 1)
|
||||
return 0;
|
||||
if (d->ranges.last().start < endTime)
|
||||
return d->ranges.count() - 1;
|
||||
|
||||
return d->lowerBound(d->ranges, endTime);
|
||||
}
|
||||
|
||||
QVariantMap AbstractTimelineModel::location(int index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
@@ -183,6 +370,51 @@ int AbstractTimelineModel::rowMaxValue(int rowNumber) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int AbstractTimelineModel::insert(qint64 startTime, qint64 duration)
|
||||
Inserts a range at the given time position and returns its index.
|
||||
*/
|
||||
int AbstractTimelineModel::insert(qint64 startTime, qint64 duration, int typeId)
|
||||
{
|
||||
Q_D(AbstractTimelineModel);
|
||||
/* 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. */
|
||||
int index = d->insertSorted(d->ranges,
|
||||
AbstractTimelineModelPrivate::Range(startTime, duration, typeId));
|
||||
if (index < d->ranges.size() - 1)
|
||||
d->incrementStartIndices(index);
|
||||
d->insertSorted(d->endTimes,
|
||||
AbstractTimelineModelPrivate::RangeEnd(index, startTime + duration));
|
||||
return index;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int AbstractTimelineModel::insertStart(qint64 startTime, int typeId)
|
||||
Inserts the given data as range start at the given time position and
|
||||
returns its index. The range end is not set.
|
||||
*/
|
||||
int AbstractTimelineModel::insertStart(qint64 startTime, int typeId)
|
||||
{
|
||||
Q_D(AbstractTimelineModel);
|
||||
int index = d->insertSorted(d->ranges,
|
||||
AbstractTimelineModelPrivate::Range(startTime, 0, typeId));
|
||||
if (index < d->ranges.size() - 1)
|
||||
d->incrementStartIndices(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int AbstractTimelineModel::insertEnd(int index, qint64 duration)
|
||||
Adds a range end for the given start index.
|
||||
*/
|
||||
void AbstractTimelineModel::insertEnd(int index, qint64 duration)
|
||||
{
|
||||
Q_D(AbstractTimelineModel);
|
||||
d->ranges[index].duration = duration;
|
||||
d->insertSorted(d->endTimes, AbstractTimelineModelPrivate::RangeEnd(index,
|
||||
d->ranges[index].start + duration));
|
||||
}
|
||||
|
||||
void AbstractTimelineModel::dataChanged()
|
||||
{
|
||||
Q_D(AbstractTimelineModel);
|
||||
@@ -255,7 +487,8 @@ int AbstractTimelineModel::rowCount() const
|
||||
|
||||
int AbstractTimelineModel::selectionId(int index) const
|
||||
{
|
||||
return range(index).typeId;
|
||||
Q_D(const AbstractTimelineModel);
|
||||
return d->ranges[index].typeId;
|
||||
}
|
||||
|
||||
void AbstractTimelineModel::clear()
|
||||
@@ -268,7 +501,8 @@ void AbstractTimelineModel::clear()
|
||||
d->rowOffsets.clear();
|
||||
d->expanded = false;
|
||||
d->hidden = false;
|
||||
SortedTimelineModel::clear();
|
||||
d->ranges.clear();
|
||||
d->endTimes.clear();
|
||||
if (hadRowHeights)
|
||||
emit rowHeightChanged();
|
||||
if (wasExpanded)
|
||||
|
||||
@@ -35,13 +35,12 @@
|
||||
#include "qmlprofiler_global.h"
|
||||
#include "qmlprofilermodelmanager.h"
|
||||
#include "qmlprofilerdatamodel.h"
|
||||
#include "sortedtimelinemodel.h"
|
||||
#include <QVariant>
|
||||
#include <QColor>
|
||||
|
||||
namespace QmlProfiler {
|
||||
|
||||
class QMLPROFILER_EXPORT AbstractTimelineModel : public SortedTimelineModel
|
||||
class QMLPROFILER_EXPORT AbstractTimelineModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString displayName READ displayName CONSTANT)
|
||||
@@ -51,6 +50,9 @@ class QMLPROFILER_EXPORT AbstractTimelineModel : public SortedTimelineModel
|
||||
|
||||
public:
|
||||
class AbstractTimelineModelPrivate;
|
||||
|
||||
AbstractTimelineModel(const QString &displayName, QmlDebug::Message message,
|
||||
QmlDebug::RangeType rangeType, QObject *parent);
|
||||
~AbstractTimelineModel();
|
||||
|
||||
// Trivial methods implemented by the abstract model itself
|
||||
@@ -63,6 +65,15 @@ public:
|
||||
int rowOffset(int rowNumber) const;
|
||||
void setRowHeight(int rowNumber, int height);
|
||||
int height() const;
|
||||
int count() const;
|
||||
qint64 duration(int index) const;
|
||||
qint64 startTime(int index) const;
|
||||
qint64 endTime(int index) const;
|
||||
int typeId(int index) const;
|
||||
|
||||
int firstIndex(qint64 startTime) const;
|
||||
int firstIndexNoParents(qint64 startTime) const;
|
||||
int lastIndex(qint64 endTime) const;
|
||||
|
||||
bool accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const;
|
||||
bool expanded() const;
|
||||
@@ -123,6 +134,11 @@ protected:
|
||||
return QColor::fromHsl(hue % 360, Saturation, Lightness);
|
||||
}
|
||||
|
||||
int insert(qint64 startTime, qint64 duration, int typeId);
|
||||
int insertStart(qint64 startTime, int typeId);
|
||||
void insertEnd(int index, qint64 duration);
|
||||
void computeNesting();
|
||||
|
||||
explicit AbstractTimelineModel(AbstractTimelineModelPrivate *dd, const QString &displayName,
|
||||
QmlDebug::Message message, QmlDebug::RangeType rangeType,
|
||||
QObject *parent);
|
||||
|
||||
@@ -37,6 +37,74 @@ namespace QmlProfiler {
|
||||
|
||||
class QMLPROFILER_EXPORT AbstractTimelineModel::AbstractTimelineModelPrivate {
|
||||
public:
|
||||
struct Range {
|
||||
Range() : start(-1), duration(-1), typeId(-1), parent(-1) {}
|
||||
Range(qint64 start, qint64 duration, int typeId) :
|
||||
start(start), duration(duration), typeId(typeId), parent(-1) {}
|
||||
qint64 start;
|
||||
qint64 duration;
|
||||
int typeId;
|
||||
int parent;
|
||||
inline qint64 timestamp() const {return start;}
|
||||
};
|
||||
|
||||
struct RangeEnd {
|
||||
RangeEnd() : startIndex(-1), end(-1) {}
|
||||
RangeEnd(int startIndex, qint64 end) :
|
||||
startIndex(startIndex), end(end) {}
|
||||
int startIndex;
|
||||
qint64 end;
|
||||
inline qint64 timestamp() const {return end;}
|
||||
};
|
||||
|
||||
void init(AbstractTimelineModel *q, const QString &displayName, QmlDebug::Message message,
|
||||
QmlDebug::RangeType rangeType);
|
||||
|
||||
inline qint64 lastEndTime() const { return endTimes.last().end; }
|
||||
inline qint64 firstStartTime() const { return ranges.first().start; }
|
||||
|
||||
void incrementStartIndices(int index)
|
||||
{
|
||||
for (int i = 0; i < endTimes.size(); ++i) {
|
||||
if (endTimes[i].startIndex >= index)
|
||||
endTimes[i].startIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RangeDelimiter>
|
||||
static inline int insertSorted(QVector<RangeDelimiter> &container, const RangeDelimiter &item)
|
||||
{
|
||||
for (int i = container.count();;) {
|
||||
if (i == 0) {
|
||||
container.prepend(item);
|
||||
return 0;
|
||||
}
|
||||
if (container[--i].timestamp() <= item.timestamp()) {
|
||||
container.insert(++i, item);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RangeDelimiter>
|
||||
static inline int lowerBound(const QVector<RangeDelimiter> &container, qint64 time)
|
||||
{
|
||||
int fromIndex = 0;
|
||||
int toIndex = container.count() - 1;
|
||||
while (toIndex - fromIndex > 1) {
|
||||
int midIndex = (fromIndex + toIndex)/2;
|
||||
if (container[midIndex].timestamp() < time)
|
||||
fromIndex = midIndex;
|
||||
else
|
||||
toIndex = midIndex;
|
||||
}
|
||||
|
||||
return fromIndex;
|
||||
}
|
||||
|
||||
QVector<Range> ranges;
|
||||
QVector<RangeEnd> endTimes;
|
||||
|
||||
QVector<int> rowOffsets;
|
||||
QmlProfilerModelManager *modelManager;
|
||||
int modelId;
|
||||
|
||||
@@ -30,7 +30,6 @@ SOURCES += \
|
||||
qmlprofilerviewmanager.cpp \
|
||||
qv8profilerdatamodel.cpp \
|
||||
qv8profilereventview.cpp \
|
||||
sortedtimelinemodel.cpp \
|
||||
timelinemodelaggregator.cpp \
|
||||
timelinerenderer.cpp \
|
||||
timelinezoomcontrol.cpp
|
||||
@@ -66,7 +65,6 @@ HEADERS += \
|
||||
qmlprofilerviewmanager.h \
|
||||
qv8profilerdatamodel.h \
|
||||
qv8profilereventview.h \
|
||||
sortedtimelinemodel.h \
|
||||
timelinemodelaggregator.h \
|
||||
timelinerenderer.h \
|
||||
timelinezoomcontrol.h
|
||||
|
||||
@@ -48,7 +48,6 @@ QtcPlugin {
|
||||
"qmlprofilerviewmanager.cpp", "qmlprofilerviewmanager.h",
|
||||
"qv8profilerdatamodel.cpp", "qv8profilerdatamodel.h",
|
||||
"qv8profilereventview.h", "qv8profilereventview.cpp",
|
||||
"sortedtimelinemodel.h", "sortedtimelinemodel.cpp",
|
||||
"timelinemodelaggregator.cpp", "timelinemodelaggregator.h",
|
||||
"timelinerenderer.cpp", "timelinerenderer.h",
|
||||
"timelinezoomcontrol.cpp", "timelinezoomcontrol.h"
|
||||
|
||||
@@ -237,7 +237,7 @@ QVariantMap QmlProfilerAnimationsModel::details(int index) const
|
||||
QVariantMap result;
|
||||
|
||||
result.insert(QStringLiteral("displayName"), displayName());
|
||||
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration));
|
||||
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(duration(index)));
|
||||
result.insert(tr("Framerate"), QString::fromLatin1("%1 FPS").arg(d->data[index].framerate));
|
||||
result.insert(tr("Animations"), QString::fromLatin1("%1").arg(d->data[index].animationcount));
|
||||
result.insert(tr("Context"), tr(d->data[index].threadId == QmlDebug::GuiThread ?
|
||||
|
||||
@@ -112,7 +112,7 @@ int QmlProfilerNotesModel::get(int timelineModel, int timelineIndex) const
|
||||
int QmlProfilerNotesModel::add(int timelineModel, int timelineIndex, const QString &text)
|
||||
{
|
||||
const AbstractTimelineModel *model = m_timelineModels[timelineModel];
|
||||
int typeId = model->range(timelineIndex).typeId;
|
||||
int typeId = model->typeId(timelineIndex);
|
||||
Note note = { text, timelineModel, timelineIndex };
|
||||
m_data << note;
|
||||
m_modified = true;
|
||||
@@ -167,9 +167,8 @@ int QmlProfilerNotesModel::add(int typeId, qint64 start, qint64 duration, const
|
||||
for (int i = model->firstIndex(start); i <= model->lastIndex(start + duration); ++i) {
|
||||
if (i < 0)
|
||||
continue;
|
||||
const SortedTimelineModel::Range &timelineRange = model->range(i);
|
||||
if (timelineRange.typeId == typeId && timelineRange.start == start &&
|
||||
timelineRange.duration == duration) {
|
||||
if (model->typeId(i) == typeId && model->startTime(i) == start &&
|
||||
model->duration(i) == duration) {
|
||||
timelineModel = model->modelId();
|
||||
timelineIndex = i;
|
||||
break;
|
||||
@@ -218,9 +217,10 @@ void QmlProfilerNotesModel::saveData()
|
||||
if (it == m_timelineModels.end())
|
||||
continue;
|
||||
|
||||
const SortedTimelineModel::Range ¬eRange = it.value()->range(note.timelineIndex);
|
||||
const AbstractTimelineModel *model = it.value();
|
||||
QmlProfilerDataModel::QmlEventNoteData save = {
|
||||
noteRange.typeId, noteRange.start, noteRange.duration, note.text
|
||||
model->typeId(note.timelineIndex), model->startTime(note.timelineIndex),
|
||||
model->duration(note.timelineIndex), note.text
|
||||
};
|
||||
notes.append(save);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::computeNestingContract
|
||||
nestingEndTimes.fill(0, nestingLevels + 1);
|
||||
|
||||
for (i = 0; i < eventCount; i++) {
|
||||
qint64 st = q->ranges[i].start;
|
||||
qint64 st = ranges[i].start;
|
||||
|
||||
// per type
|
||||
if (nestingEndTimes[nestingLevels] > st) {
|
||||
@@ -151,7 +151,7 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::computeNestingContract
|
||||
nestingEndTimes[nestingLevels-1] <= st)
|
||||
nestingLevels--;
|
||||
}
|
||||
nestingEndTimes[nestingLevels] = st + q->ranges[i].duration;
|
||||
nestingEndTimes[nestingLevels] = st + ranges[i].duration;
|
||||
|
||||
data[i].displayRowCollapsed = nestingLevels;
|
||||
}
|
||||
@@ -163,7 +163,7 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::computeExpandedLevels(
|
||||
QHash<int, int> eventRow;
|
||||
int eventCount = q->count();
|
||||
for (int i = 0; i < eventCount; i++) {
|
||||
int typeId = q->range(i).typeId;
|
||||
int typeId = ranges[i].typeId;
|
||||
if (!eventRow.contains(typeId)) {
|
||||
eventRow[typeId] = expandedRowTypes.size();
|
||||
expandedRowTypes << typeId;
|
||||
@@ -184,25 +184,23 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::findBindingLoops()
|
||||
|
||||
for (int i = 0; i < q->count(); ++i) {
|
||||
const Range *potentialParent = callStack.isEmpty()
|
||||
? 0 : &q->ranges[callStack.top().second];
|
||||
? 0 : &ranges[callStack.top().second];
|
||||
|
||||
while (potentialParent
|
||||
&& !(potentialParent->start + potentialParent->duration > q->ranges[i].start)) {
|
||||
&& !(potentialParent->start + potentialParent->duration > ranges[i].start)) {
|
||||
callStack.pop();
|
||||
potentialParent = callStack.isEmpty() ? 0
|
||||
: &q->ranges[callStack.top().second];
|
||||
potentialParent = callStack.isEmpty() ? 0 : &ranges[callStack.top().second];
|
||||
}
|
||||
|
||||
// check whether event is already in stack
|
||||
for (int ii = 0; ii < callStack.size(); ++ii) {
|
||||
if (callStack.at(ii).first == q->range(i).typeId) {
|
||||
if (callStack.at(ii).first == ranges[i].typeId) {
|
||||
data[i].bindingLoopHead = callStack.at(ii).second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CallStackEntry newEntry(q->range(i).typeId, i);
|
||||
CallStackEntry newEntry(ranges[i].typeId, i);
|
||||
callStack.push(newEntry);
|
||||
}
|
||||
|
||||
@@ -266,7 +264,7 @@ QVariantMap QmlProfilerRangeModel::details(int index) const
|
||||
d->modelManager->qmlModel()->getEventTypes();
|
||||
|
||||
result.insert(QStringLiteral("displayName"), categoryLabel(d->rangeType));
|
||||
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration));
|
||||
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(duration(index)));
|
||||
|
||||
result.insert(tr("Details"), types[id].data);
|
||||
result.insert(tr("Location"), types[id].displayName);
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://www.qt.io/licensing. For further information
|
||||
** use the contact form at http://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "sortedtimelinemodel.h"
|
||||
namespace QmlProfiler {
|
||||
|
||||
/*!
|
||||
\class QmlProfiler::SortedTimelineModel
|
||||
\brief The SortedTimelineModel class provides a sorted model for timeline data.
|
||||
|
||||
The SortedTimelineModel lets you keep range data sorted by both start and end times, so that
|
||||
visible ranges can easily be computed. The only precondition for that to work is that the ranges
|
||||
must be perfectly nested. A "parent" range of a range R is defined as a range for which the
|
||||
start time is smaller than R's start time and the end time is greater than R's end time. A set
|
||||
of ranges is perfectly nested if all parent ranges of any given range have a common parent
|
||||
range. Mind that you can always make that happen by defining a range that spans the whole
|
||||
available time span. That, however, will make any code that uses firstStartTime() and
|
||||
lastEndTime() for selecting subsets of the model always select all of it.
|
||||
|
||||
\note Indices returned from the various methods are only valid until a new range is inserted
|
||||
before them. Inserting a new range before a given index moves the range pointed to by the
|
||||
index by one. Incrementing the index by one will make it point to the item again.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn SortedTimelineModel::clear()
|
||||
Clears the ranges and their end times.
|
||||
*/
|
||||
void SortedTimelineModel::clear()
|
||||
{
|
||||
ranges.clear();
|
||||
endTimes.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn int SortedTimelineModel::count() const
|
||||
Returns the number of ranges in the model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn qint64 SortedTimelineModel::firstStartTime() const
|
||||
Returns the begin of the first range in the model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn qint64 SortedTimelineModel::lastEndTime() const
|
||||
Returns the end of the last range in the model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn const SortedTimelineModel::Range &SortedTimelineModel::range(int index) const
|
||||
Returns the range data at the specified index.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn int SortedTimelineModel::insert(qint64 startTime, qint64 duration)
|
||||
Inserts a range at the given time position and returns its index.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn int SortedTimelineModel::insertStart(qint64 startTime, const Data &item)
|
||||
Inserts the given data as range start at the given time position and
|
||||
returns its index. The range end is not set.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn int SortedTimelineModel::insertEnd(int index, qint64 duration)
|
||||
Adds a range end for the given start index.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn int SortedTimelineModel::firstIndexNoParents(qint64 startTime) const
|
||||
Looks up the first range with an end time greater than the given time and
|
||||
returns its index. If no such range is found, it returns -1.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn int SortedTimelineModel::firstIndex(qint64 startTime) const
|
||||
Looks up the first range with an end time greater than the given time and
|
||||
returns its parent's index. If no such range is found, it returns -1. If there
|
||||
is no parent, it returns the found range's index. The parent of a range is the
|
||||
range with the lowest start time that completely covers the child range.
|
||||
"Completely covers" means:
|
||||
parent.startTime <= child.startTime && parent.endTime >= child.endTime
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn int SortedTimelineModel::lastIndex(qint64 endTime) const
|
||||
Looks up the last range with a start time smaller than the given time and
|
||||
returns its index. If no such range is found, it returns -1.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void SortedTimelineModel::computeNesting()
|
||||
Compute all ranges' parents.
|
||||
\sa findFirstIndex
|
||||
*/
|
||||
void SortedTimelineModel::computeNesting()
|
||||
{
|
||||
QLinkedList<int> parents;
|
||||
for (int range = 0; range != count(); ++range) {
|
||||
Range ¤t = ranges[range];
|
||||
for (QLinkedList<int>::iterator parentIt = parents.begin();;) {
|
||||
if (parentIt == parents.end()) {
|
||||
parents.append(range);
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://www.qt.io/licensing. For further information
|
||||
** use the contact form at http://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef SORTEDTIMELINEMODEL_H
|
||||
#define SORTEDTIMELINEMODEL_H
|
||||
|
||||
#include "qmlprofiler_global.h"
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <QLinkedList>
|
||||
|
||||
namespace QmlProfiler {
|
||||
|
||||
class QMLPROFILER_EXPORT SortedTimelineModel : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Range {
|
||||
Range() : start(-1), duration(-1), typeId(-1), parent(-1) {}
|
||||
Range(qint64 start, qint64 duration, int typeId) :
|
||||
start(start), duration(duration), typeId(typeId), parent(-1) {}
|
||||
qint64 start;
|
||||
qint64 duration;
|
||||
int typeId;
|
||||
int parent;
|
||||
inline qint64 timestamp() const {return start;}
|
||||
};
|
||||
|
||||
struct RangeEnd {
|
||||
RangeEnd() : startIndex(-1), end(-1) {}
|
||||
RangeEnd(int startIndex, qint64 end) :
|
||||
startIndex(startIndex), end(end) {}
|
||||
int startIndex;
|
||||
qint64 end;
|
||||
inline qint64 timestamp() const {return end;}
|
||||
};
|
||||
|
||||
inline int count() const { return ranges.count(); }
|
||||
|
||||
qint64 duration(int index) const { return ranges[index].duration; }
|
||||
qint64 startTime(int index) const { return ranges[index].start; }
|
||||
qint64 endTime(int index) const { return ranges[index].start + ranges[index].duration; }
|
||||
int typeId(int index) const { return ranges[index].typeId; }
|
||||
|
||||
inline qint64 lastEndTime() const { return endTimes.last().end; }
|
||||
inline qint64 firstStartTime() const { return ranges.first().start; }
|
||||
|
||||
inline const Range &range(int index) const { return ranges[index]; }
|
||||
|
||||
inline int firstIndex(qint64 startTime) const
|
||||
{
|
||||
int index = firstIndexNoParents(startTime);
|
||||
if (index == -1)
|
||||
return -1;
|
||||
int parent = ranges[index].parent;
|
||||
return parent == -1 ? index : parent;
|
||||
}
|
||||
|
||||
inline int firstIndexNoParents(qint64 startTime) const
|
||||
{
|
||||
// in the "endtime" list, find the first event that ends after startTime
|
||||
if (endTimes.isEmpty())
|
||||
return -1;
|
||||
if (endTimes.count() == 1 || endTimes.first().end > startTime)
|
||||
return endTimes.first().startIndex;
|
||||
if (endTimes.last().end <= startTime)
|
||||
return -1;
|
||||
|
||||
return endTimes[lowerBound(endTimes, startTime) + 1].startIndex;
|
||||
}
|
||||
|
||||
inline int lastIndex(qint64 endTime) const
|
||||
{
|
||||
// in the "starttime" list, find the last event that starts before endtime
|
||||
if (ranges.isEmpty() || ranges.first().start >= endTime)
|
||||
return -1;
|
||||
if (ranges.count() == 1)
|
||||
return 0;
|
||||
if (ranges.last().start < endTime)
|
||||
return ranges.count() - 1;
|
||||
|
||||
return lowerBound(ranges, endTime);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
SortedTimelineModel(QObject *parent = 0) : QObject(parent) {}
|
||||
|
||||
void clear();
|
||||
|
||||
inline int insert(qint64 startTime, qint64 duration, int typeId)
|
||||
{
|
||||
/* 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. */
|
||||
int index = insertSorted(ranges, Range(startTime, duration, typeId));
|
||||
if (index < ranges.size() - 1)
|
||||
incrementStartIndices(index);
|
||||
insertSorted(endTimes, RangeEnd(index, startTime + duration));
|
||||
return index;
|
||||
}
|
||||
|
||||
inline int insertStart(qint64 startTime, int typeId)
|
||||
{
|
||||
int index = insertSorted(ranges, Range(startTime, 0, typeId));
|
||||
if (index < ranges.size() - 1)
|
||||
incrementStartIndices(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
inline void insertEnd(int index, qint64 duration)
|
||||
{
|
||||
ranges[index].duration = duration;
|
||||
insertSorted(endTimes, RangeEnd(index, ranges[index].start + duration));
|
||||
}
|
||||
|
||||
void computeNesting();
|
||||
|
||||
void incrementStartIndices(int index)
|
||||
{
|
||||
for (int i = 0; i < endTimes.size(); ++i) {
|
||||
if (endTimes[i].startIndex >= index)
|
||||
endTimes[i].startIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RangeDelimiter>
|
||||
static inline int insertSorted(QVector<RangeDelimiter> &container, const RangeDelimiter &item)
|
||||
{
|
||||
for (int i = container.count();;) {
|
||||
if (i == 0) {
|
||||
container.prepend(item);
|
||||
return 0;
|
||||
}
|
||||
if (container[--i].timestamp() <= item.timestamp()) {
|
||||
container.insert(++i, item);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RangeDelimiter>
|
||||
static inline int lowerBound(const QVector<RangeDelimiter> &container, qint64 time)
|
||||
{
|
||||
int fromIndex = 0;
|
||||
int toIndex = container.count() - 1;
|
||||
while (toIndex - fromIndex > 1) {
|
||||
int midIndex = (fromIndex + toIndex)/2;
|
||||
if (container[midIndex].timestamp() < time)
|
||||
fromIndex = midIndex;
|
||||
else
|
||||
toIndex = midIndex;
|
||||
}
|
||||
|
||||
return fromIndex;
|
||||
}
|
||||
|
||||
QVector<Range> ranges;
|
||||
QVector<RangeEnd> endTimes;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user