forked from qt-creator/qt-creator
QmlProfiler: Load the timeline model data event by event
All the models do the same thing when loading the data: They iterate the list of events, determine for each one if they accept it, and if so, they load it. After the list has been fully loaded, they do some finalization. This can be centralized, and ultimately we won't need to expose the central QVector<QmlEvent> for that anymore. Change-Id: Ia82facfdc3968200bbec323a02f2fcc02ac44e9e Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
@@ -28,11 +28,6 @@
|
|||||||
namespace QmlProfiler {
|
namespace QmlProfiler {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
bool DebugMessagesModel::accepted(const QmlEventType &event) const
|
|
||||||
{
|
|
||||||
return event.message == DebugMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
DebugMessagesModel::DebugMessagesModel(QmlProfilerModelManager *manager, QObject *parent) :
|
DebugMessagesModel::DebugMessagesModel(QmlProfilerModelManager *manager, QObject *parent) :
|
||||||
QmlProfilerTimelineModel(manager, DebugMessage, MaximumRangeType, ProfileDebugMessages, parent),
|
QmlProfilerTimelineModel(manager, DebugMessage, MaximumRangeType, ProfileDebugMessages, parent),
|
||||||
m_maximumMsgType(-1)
|
m_maximumMsgType(-1)
|
||||||
@@ -99,24 +94,16 @@ int DebugMessagesModel::collapsedRow(int index) const
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugMessagesModel::loadData()
|
void DebugMessagesModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
||||||
{
|
{
|
||||||
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
m_data.insert(insert(event.timestamp(), 0, type.detailType),
|
||||||
if (simpleModel->isEmpty())
|
MessageData(event.string(), event.typeIndex()));
|
||||||
return;
|
if (type.detailType > m_maximumMsgType)
|
||||||
|
m_maximumMsgType = event.typeIndex();
|
||||||
|
}
|
||||||
|
|
||||||
const QVector<QmlEventType> &types = simpleModel->eventTypes();
|
void DebugMessagesModel::finalize()
|
||||||
|
{
|
||||||
foreach (const QmlEvent &event, simpleModel->events()) {
|
|
||||||
const QmlEventType &type = types[event.typeIndex()];
|
|
||||||
if (!accepted(type) || event.timestamp() < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
m_data.insert(insert(event.timestamp(), 0, type.detailType),
|
|
||||||
MessageData(event.string(), event.typeIndex()));
|
|
||||||
if (type.detailType > m_maximumMsgType)
|
|
||||||
m_maximumMsgType = event.typeIndex();
|
|
||||||
}
|
|
||||||
setCollapsedRowCount(2);
|
setCollapsedRowCount(2);
|
||||||
setExpandedRowCount(m_maximumMsgType + 2);
|
setExpandedRowCount(m_maximumMsgType + 2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,6 @@ class DebugMessagesModel : public QmlProfilerTimelineModel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
protected:
|
|
||||||
bool accepted(const QmlEventType &event) const override;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DebugMessagesModel(QmlProfilerModelManager *manager, QObject *parent = 0);
|
DebugMessagesModel(QmlProfilerModelManager *manager, QObject *parent = 0);
|
||||||
|
|
||||||
@@ -46,7 +43,8 @@ public:
|
|||||||
QVariantMap details(int index) const override;
|
QVariantMap details(int index) const override;
|
||||||
int expandedRow(int index) const override;
|
int expandedRow(int index) const override;
|
||||||
int collapsedRow(int index) const override;
|
int collapsedRow(int index) const override;
|
||||||
void loadData() override;
|
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
|
||||||
|
void finalize() override;
|
||||||
void clear() override;
|
void clear() override;
|
||||||
QVariantMap location(int index) const override;
|
QVariantMap location(int index) const override;
|
||||||
|
|
||||||
|
|||||||
@@ -140,29 +140,22 @@ int InputEventsModel::collapsedRow(int index) const
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEventsModel::loadData()
|
void InputEventsModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
||||||
{
|
{
|
||||||
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
m_data.insert(insert(event.timestamp(), 0, type.detailType),
|
||||||
if (simpleModel->isEmpty())
|
InputEvent(static_cast<InputEventType>(event.number<qint32>(0)),
|
||||||
return;
|
event.number<qint32>(1), event.number<qint32>(2)));
|
||||||
|
|
||||||
const QVector<QmlEventType> &types = simpleModel->eventTypes();
|
if (type.detailType == Mouse) {
|
||||||
foreach (const QmlEvent &event, simpleModel->events()) {
|
if (m_mouseTypeId == -1)
|
||||||
const QmlEventType &type = types[event.typeIndex()];
|
m_mouseTypeId = event.typeIndex();
|
||||||
if (!accepted(type))
|
} else if (m_keyTypeId == -1) {
|
||||||
continue;
|
m_keyTypeId = event.typeIndex();
|
||||||
|
|
||||||
m_data.insert(insert(event.timestamp(), 0, type.detailType),
|
|
||||||
InputEvent(static_cast<InputEventType>(event.number<qint32>(0)),
|
|
||||||
event.number<qint32>(1), event.number<qint32>(2)));
|
|
||||||
|
|
||||||
if (type.detailType == Mouse) {
|
|
||||||
if (m_mouseTypeId == -1)
|
|
||||||
m_mouseTypeId = event.typeIndex();
|
|
||||||
} else if (m_keyTypeId == -1) {
|
|
||||||
m_keyTypeId = event.typeIndex();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputEventsModel::finalize()
|
||||||
|
{
|
||||||
setCollapsedRowCount(2);
|
setCollapsedRowCount(2);
|
||||||
setExpandedRowCount(3);
|
setExpandedRowCount(3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ class InputEventsModel : public QmlProfilerTimelineModel
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool accepted(const QmlEventType &event) const;
|
bool accepted(const QmlEventType &event) const;
|
||||||
|
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
|
||||||
|
void finalize() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct InputEvent {
|
struct InputEvent {
|
||||||
@@ -53,8 +56,6 @@ public:
|
|||||||
QVariantMap details(int index) const;
|
QVariantMap details(int index) const;
|
||||||
int expandedRow(int index) const;
|
int expandedRow(int index) const;
|
||||||
int collapsedRow(int index) const;
|
int collapsedRow(int index) const;
|
||||||
void loadData();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QMetaEnum metaEnum(const char *name);
|
static QMetaEnum metaEnum(const char *name);
|
||||||
|
|||||||
@@ -27,15 +27,12 @@
|
|||||||
#include "qmlprofilermodelmanager.h"
|
#include "qmlprofilermodelmanager.h"
|
||||||
#include "qmlprofilereventtypes.h"
|
#include "qmlprofilereventtypes.h"
|
||||||
|
|
||||||
#include <QStack>
|
|
||||||
|
|
||||||
namespace QmlProfiler {
|
namespace QmlProfiler {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
MemoryUsageModel::MemoryUsageModel(QmlProfilerModelManager *manager, QObject *parent) :
|
MemoryUsageModel::MemoryUsageModel(QmlProfilerModelManager *manager, QObject *parent) :
|
||||||
QmlProfilerTimelineModel(manager, MemoryAllocation, MaximumRangeType, ProfileMemory, parent)
|
QmlProfilerTimelineModel(manager, MemoryAllocation, MaximumRangeType, ProfileMemory, parent)
|
||||||
{
|
{
|
||||||
m_maxSize = 1;
|
|
||||||
announceFeatures((1ULL << mainFeature()) | Constants::QML_JS_RANGE_FEATURES);
|
announceFeatures((1ULL << mainFeature()) | Constants::QML_JS_RANGE_FEATURES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,94 +134,78 @@ QVariantMap MemoryUsageModel::details(int index) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RangeStackFrame {
|
bool MemoryUsageModel::accepted(const QmlEventType &type) const
|
||||||
RangeStackFrame() : originTypeIndex(-1), startTime(-1), endTime(-1) {}
|
|
||||||
RangeStackFrame(int originTypeIndex, qint64 startTime, qint64 endTime) :
|
|
||||||
originTypeIndex(originTypeIndex), startTime(startTime), endTime(endTime) {}
|
|
||||||
int originTypeIndex;
|
|
||||||
qint64 startTime;
|
|
||||||
qint64 endTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
void MemoryUsageModel::loadData()
|
|
||||||
{
|
{
|
||||||
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
return QmlProfilerTimelineModel::accepted(type) || type.rangeType != MaximumRangeType;
|
||||||
if (simpleModel->isEmpty())
|
}
|
||||||
|
|
||||||
|
void MemoryUsageModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
||||||
|
{
|
||||||
|
while (!m_rangeStack.empty() && m_rangeStack.top().endTime < event.timestamp())
|
||||||
|
m_rangeStack.pop();
|
||||||
|
if (type.message != MemoryAllocation) {
|
||||||
|
if (type.rangeType != MaximumRangeType) {
|
||||||
|
m_rangeStack.push(RangeStackFrame(event.typeIndex(), event.timestamp(),
|
||||||
|
event.timestamp() + event.duration()));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qint64 currentSize = 0;
|
if (type.detailType == SmallItem || type.detailType == LargeItem) {
|
||||||
qint64 currentUsage = 0;
|
if (!m_rangeStack.empty() && m_currentUsageIndex > -1 &&
|
||||||
int currentUsageIndex = -1;
|
type.detailType == selectionId(m_currentUsageIndex) &&
|
||||||
int currentJSHeapIndex = -1;
|
m_data[m_currentUsageIndex].originTypeIndex == m_rangeStack.top().originTypeIndex &&
|
||||||
|
m_rangeStack.top().startTime < startTime(m_currentUsageIndex)) {
|
||||||
|
m_data[m_currentUsageIndex].update(event.number<qint64>(0));
|
||||||
|
m_currentUsage = m_data[m_currentUsageIndex].size;
|
||||||
|
} else {
|
||||||
|
MemoryAllocationItem allocation(event.typeIndex(), m_currentUsage,
|
||||||
|
m_rangeStack.empty() ? -1 : m_rangeStack.top().originTypeIndex);
|
||||||
|
allocation.update(event.number<qint64>(0));
|
||||||
|
m_currentUsage = allocation.size;
|
||||||
|
|
||||||
QStack<RangeStackFrame> rangeStack;
|
if (m_currentUsageIndex != -1) {
|
||||||
|
insertEnd(m_currentUsageIndex,
|
||||||
const QVector<QmlEventType> &types = simpleModel->eventTypes();
|
event.timestamp() - startTime(m_currentUsageIndex) - 1);
|
||||||
foreach (const QmlEvent &event, simpleModel->events()) {
|
|
||||||
const QmlEventType &type = types[event.typeIndex()];
|
|
||||||
while (!rangeStack.empty() && rangeStack.top().endTime < event.timestamp())
|
|
||||||
rangeStack.pop();
|
|
||||||
if (!accepted(type)) {
|
|
||||||
if (type.rangeType != MaximumRangeType) {
|
|
||||||
rangeStack.push(RangeStackFrame(event.typeIndex(), event.timestamp(),
|
|
||||||
event.timestamp() + event.duration()));
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.detailType == SmallItem || type.detailType == LargeItem) {
|
|
||||||
if (!rangeStack.empty() && currentUsageIndex > -1 &&
|
|
||||||
type.detailType == selectionId(currentUsageIndex) &&
|
|
||||||
m_data[currentUsageIndex].originTypeIndex == rangeStack.top().originTypeIndex &&
|
|
||||||
rangeStack.top().startTime < startTime(currentUsageIndex)) {
|
|
||||||
m_data[currentUsageIndex].update(event.number<qint64>(0));
|
|
||||||
currentUsage = m_data[currentUsageIndex].size;
|
|
||||||
} else {
|
|
||||||
MemoryAllocationItem allocation(event.typeIndex(), currentUsage,
|
|
||||||
rangeStack.empty() ? -1 : rangeStack.top().originTypeIndex);
|
|
||||||
allocation.update(event.number<qint64>(0));
|
|
||||||
currentUsage = allocation.size;
|
|
||||||
|
|
||||||
if (currentUsageIndex != -1) {
|
|
||||||
insertEnd(currentUsageIndex,
|
|
||||||
event.timestamp() - startTime(currentUsageIndex) - 1);
|
|
||||||
}
|
|
||||||
currentUsageIndex = insertStart(event.timestamp(), SmallItem);
|
|
||||||
m_data.insert(currentUsageIndex, allocation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.detailType == HeapPage || type.detailType == LargeItem) {
|
|
||||||
if (!rangeStack.empty() && currentJSHeapIndex > -1 &&
|
|
||||||
type.detailType == selectionId(currentJSHeapIndex) &&
|
|
||||||
m_data[currentJSHeapIndex].originTypeIndex ==
|
|
||||||
rangeStack.top().originTypeIndex &&
|
|
||||||
rangeStack.top().startTime < startTime(currentJSHeapIndex)) {
|
|
||||||
m_data[currentJSHeapIndex].update(event.number<qint64>(0));
|
|
||||||
currentSize = m_data[currentJSHeapIndex].size;
|
|
||||||
} else {
|
|
||||||
MemoryAllocationItem allocation(event.typeIndex(), currentSize,
|
|
||||||
rangeStack.empty() ? -1 : rangeStack.top().originTypeIndex);
|
|
||||||
allocation.update(event.number<qint64>(0));
|
|
||||||
currentSize = allocation.size;
|
|
||||||
|
|
||||||
if (currentSize > m_maxSize)
|
|
||||||
m_maxSize = currentSize;
|
|
||||||
if (currentJSHeapIndex != -1)
|
|
||||||
insertEnd(currentJSHeapIndex,
|
|
||||||
event.timestamp() - startTime(currentJSHeapIndex) - 1);
|
|
||||||
currentJSHeapIndex = insertStart(event.timestamp(), type.detailType);
|
|
||||||
m_data.insert(currentJSHeapIndex, allocation);
|
|
||||||
}
|
}
|
||||||
|
m_currentUsageIndex = insertStart(event.timestamp(), SmallItem);
|
||||||
|
m_data.insert(m_currentUsageIndex, allocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentJSHeapIndex != -1)
|
if (type.detailType == HeapPage || type.detailType == LargeItem) {
|
||||||
insertEnd(currentJSHeapIndex, modelManager()->traceTime()->endTime() -
|
if (!m_rangeStack.empty() && m_currentJSHeapIndex > -1 &&
|
||||||
startTime(currentJSHeapIndex) - 1);
|
type.detailType == selectionId(m_currentJSHeapIndex) &&
|
||||||
if (currentUsageIndex != -1)
|
m_data[m_currentJSHeapIndex].originTypeIndex ==
|
||||||
insertEnd(currentUsageIndex, modelManager()->traceTime()->endTime() -
|
m_rangeStack.top().originTypeIndex &&
|
||||||
startTime(currentUsageIndex) - 1);
|
m_rangeStack.top().startTime < startTime(m_currentJSHeapIndex)) {
|
||||||
|
m_data[m_currentJSHeapIndex].update(event.number<qint64>(0));
|
||||||
|
m_currentSize = m_data[m_currentJSHeapIndex].size;
|
||||||
|
} else {
|
||||||
|
MemoryAllocationItem allocation(event.typeIndex(), m_currentSize,
|
||||||
|
m_rangeStack.empty() ? -1 : m_rangeStack.top().originTypeIndex);
|
||||||
|
allocation.update(event.number<qint64>(0));
|
||||||
|
m_currentSize = allocation.size;
|
||||||
|
|
||||||
|
if (m_currentSize > m_maxSize)
|
||||||
|
m_maxSize = m_currentSize;
|
||||||
|
if (m_currentJSHeapIndex != -1)
|
||||||
|
insertEnd(m_currentJSHeapIndex,
|
||||||
|
event.timestamp() - startTime(m_currentJSHeapIndex) - 1);
|
||||||
|
m_currentJSHeapIndex = insertStart(event.timestamp(), type.detailType);
|
||||||
|
m_data.insert(m_currentJSHeapIndex, allocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryUsageModel::finalize()
|
||||||
|
{
|
||||||
|
if (m_currentJSHeapIndex != -1)
|
||||||
|
insertEnd(m_currentJSHeapIndex, modelManager()->traceTime()->endTime() -
|
||||||
|
startTime(m_currentJSHeapIndex) - 1);
|
||||||
|
if (m_currentUsageIndex != -1)
|
||||||
|
insertEnd(m_currentUsageIndex, modelManager()->traceTime()->endTime() -
|
||||||
|
startTime(m_currentUsageIndex) - 1);
|
||||||
|
|
||||||
|
|
||||||
computeNesting();
|
computeNesting();
|
||||||
@@ -236,6 +217,11 @@ void MemoryUsageModel::clear()
|
|||||||
{
|
{
|
||||||
m_data.clear();
|
m_data.clear();
|
||||||
m_maxSize = 1;
|
m_maxSize = 1;
|
||||||
|
m_currentSize = 0;
|
||||||
|
m_currentUsage = 0;
|
||||||
|
m_currentUsageIndex = -1;
|
||||||
|
m_currentJSHeapIndex = -1;
|
||||||
|
m_rangeStack.clear();
|
||||||
QmlProfilerTimelineModel::clear();
|
QmlProfilerTimelineModel::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QStack>
|
||||||
|
|
||||||
namespace QmlProfiler {
|
namespace QmlProfiler {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -68,14 +69,30 @@ public:
|
|||||||
QVariantMap details(int index) const;
|
QVariantMap details(int index) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void loadData();
|
bool accepted(const QmlEventType &type) const override;
|
||||||
void clear();
|
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
|
||||||
|
void finalize() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct RangeStackFrame {
|
||||||
|
RangeStackFrame() : originTypeIndex(-1), startTime(-1), endTime(-1) {}
|
||||||
|
RangeStackFrame(int originTypeIndex, qint64 startTime, qint64 endTime) :
|
||||||
|
originTypeIndex(originTypeIndex), startTime(startTime), endTime(endTime) {}
|
||||||
|
int originTypeIndex;
|
||||||
|
qint64 startTime;
|
||||||
|
qint64 endTime;
|
||||||
|
};
|
||||||
|
|
||||||
static QString memoryTypeName(int type);
|
static QString memoryTypeName(int type);
|
||||||
|
|
||||||
QVector<MemoryAllocationItem> m_data;
|
QVector<MemoryAllocationItem> m_data;
|
||||||
qint64 m_maxSize;
|
QStack<RangeStackFrame> m_rangeStack;
|
||||||
|
qint64 m_maxSize = 1;
|
||||||
|
qint64 m_currentSize = 0;
|
||||||
|
qint64 m_currentUsage = 0;
|
||||||
|
int m_currentUsageIndex = -1;
|
||||||
|
int m_currentJSHeapIndex = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ PixmapCacheModel::PixmapCacheModel(QmlProfilerModelManager *manager, QObject *pa
|
|||||||
QmlProfilerTimelineModel(manager, PixmapCacheEvent, MaximumRangeType, ProfilePixmapCache,
|
QmlProfilerTimelineModel(manager, PixmapCacheEvent, MaximumRangeType, ProfilePixmapCache,
|
||||||
parent)
|
parent)
|
||||||
{
|
{
|
||||||
m_maxCacheSize = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PixmapCacheModel::rowMaxValue(int rowNumber) const
|
int PixmapCacheModel::rowMaxValue(int rowNumber) const
|
||||||
@@ -164,159 +163,159 @@ QVariantMap PixmapCacheModel::details(int index) const
|
|||||||
* necessarily the order the pixmaps are really loaded but it's the best we can do with the given
|
* necessarily the order the pixmaps are really loaded but it's the best we can do with the given
|
||||||
* information. If they're loaded sequentially the representation is correct.
|
* information. If they're loaded sequentially the representation is correct.
|
||||||
*/
|
*/
|
||||||
|
void PixmapCacheModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
||||||
void PixmapCacheModel::loadData()
|
|
||||||
{
|
{
|
||||||
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
PixmapCacheItem newEvent;
|
||||||
if (simpleModel->isEmpty())
|
newEvent.pixmapEventType = static_cast<PixmapEventType>(type.detailType);
|
||||||
return;
|
qint64 pixmapStartTime = event.timestamp();
|
||||||
|
|
||||||
int lastCacheSizeEvent = -1;
|
newEvent.urlIndex = -1;
|
||||||
int cumulatedCount = 0;
|
for (QVector<Pixmap>::const_iterator it(m_pixmaps.cend()); it != m_pixmaps.cbegin();) {
|
||||||
|
if ((--it)->url == type.location.filename) {
|
||||||
|
newEvent.urlIndex = it - m_pixmaps.cbegin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const QVector<QmlEventType> &types = simpleModel->eventTypes();
|
newEvent.sizeIndex = -1;
|
||||||
foreach (const QmlEvent &event, simpleModel->events()) {
|
if (newEvent.urlIndex == -1) {
|
||||||
const QmlEventType &type = types[event.typeIndex()];
|
newEvent.urlIndex = m_pixmaps.count();
|
||||||
if (!accepted(type))
|
m_pixmaps << Pixmap(type.location.filename);
|
||||||
continue;
|
}
|
||||||
|
|
||||||
PixmapCacheItem newEvent;
|
Pixmap &pixmap = m_pixmaps[newEvent.urlIndex];
|
||||||
newEvent.pixmapEventType = static_cast<PixmapEventType>(type.detailType);
|
switch (newEvent.pixmapEventType) {
|
||||||
qint64 pixmapStartTime = event.timestamp();
|
case PixmapSizeKnown: {// pixmap size
|
||||||
|
// Look for pixmaps for which we don't know the size, yet and which have actually been
|
||||||
|
// loaded.
|
||||||
|
for (QVector<PixmapState>::iterator i(pixmap.sizes.begin());
|
||||||
|
i != pixmap.sizes.end(); ++i) {
|
||||||
|
if (i->size.isValid() || i->cacheState == Uncacheable || i->cacheState == Corrupt)
|
||||||
|
continue;
|
||||||
|
|
||||||
newEvent.urlIndex = -1;
|
// We can't have cached it before we knew the size
|
||||||
for (QVector<Pixmap>::const_iterator it(m_pixmaps.cend()); it != m_pixmaps.cbegin();) {
|
Q_ASSERT(i->cacheState != Cached);
|
||||||
if ((--it)->url == type.location.filename) {
|
|
||||||
newEvent.urlIndex = it - m_pixmaps.cbegin();
|
i->size.setWidth(event.number<qint32>(0));
|
||||||
break;
|
i->size.setHeight(event.number<qint32>(1));
|
||||||
}
|
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
newEvent.sizeIndex = -1;
|
if (newEvent.sizeIndex == -1) {
|
||||||
if (newEvent.urlIndex == -1) {
|
newEvent.sizeIndex = pixmap.sizes.length();
|
||||||
newEvent.urlIndex = m_pixmaps.count();
|
pixmap.sizes << PixmapState(event.number<qint32>(0), event.number<qint32>(1));
|
||||||
m_pixmaps << Pixmap(type.location.filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pixmap &pixmap = m_pixmaps[newEvent.urlIndex];
|
PixmapState &state = pixmap.sizes[newEvent.sizeIndex];
|
||||||
switch (newEvent.pixmapEventType) {
|
if (state.cacheState == ToBeCached) {
|
||||||
case PixmapSizeKnown: {// pixmap size
|
m_lastCacheSizeEvent = updateCacheCount(m_lastCacheSizeEvent, pixmapStartTime,
|
||||||
// Look for pixmaps for which we don't know the size, yet and which have actually been
|
state.size.width() * state.size.height(), newEvent,
|
||||||
// loaded.
|
event.typeIndex());
|
||||||
for (QVector<PixmapState>::iterator i(pixmap.sizes.begin());
|
state.cacheState = Cached;
|
||||||
i != pixmap.sizes.end(); ++i) {
|
}
|
||||||
if (i->size.isValid() || i->cacheState == Uncacheable || i->cacheState == Corrupt)
|
break;
|
||||||
continue;
|
}
|
||||||
|
case PixmapCacheCountChanged: {// Cache Size Changed Event
|
||||||
|
pixmapStartTime = event.timestamp() + 1; // delay 1 ns for proper sorting
|
||||||
|
|
||||||
// We can't have cached it before we knew the size
|
bool uncache = m_cumulatedCount > event.number<qint32>(2);
|
||||||
Q_ASSERT(i->cacheState != Cached);
|
m_cumulatedCount = event.number<qint32>(2);
|
||||||
|
qint64 pixSize = 0;
|
||||||
|
|
||||||
i->size.setWidth(event.number<qint32>(0));
|
// First try to find a preferred pixmap, which either is Corrupt and will be uncached
|
||||||
i->size.setHeight(event.number<qint32>(1));
|
// or is uncached and will be cached.
|
||||||
|
for (QVector<PixmapState>::iterator i(pixmap.sizes.begin());
|
||||||
|
i != pixmap.sizes.end(); ++i) {
|
||||||
|
if (uncache && i->cacheState == Corrupt) {
|
||||||
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
||||||
|
i->cacheState = Uncacheable;
|
||||||
|
break;
|
||||||
|
} else if (!uncache && i->cacheState == Uncached) {
|
||||||
|
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
||||||
|
if (i->size.isValid()) {
|
||||||
|
pixSize = i->size.width() * i->size.height();
|
||||||
|
i->cacheState = Cached;
|
||||||
|
} else {
|
||||||
|
i->cacheState = ToBeCached;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newEvent.sizeIndex == -1) {
|
|
||||||
newEvent.sizeIndex = pixmap.sizes.length();
|
|
||||||
pixmap.sizes << PixmapState(event.number<qint32>(0), event.number<qint32>(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
PixmapState &state = pixmap.sizes[newEvent.sizeIndex];
|
|
||||||
if (state.cacheState == ToBeCached) {
|
|
||||||
lastCacheSizeEvent = updateCacheCount(lastCacheSizeEvent, pixmapStartTime,
|
|
||||||
state.size.width() * state.size.height(), newEvent,
|
|
||||||
event.typeIndex());
|
|
||||||
state.cacheState = Cached;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case PixmapCacheCountChanged: {// Cache Size Changed Event
|
|
||||||
pixmapStartTime = event.timestamp() + 1; // delay 1 ns for proper sorting
|
|
||||||
|
|
||||||
bool uncache = cumulatedCount > event.number<qint32>(2);
|
// If none found, check for cached or ToBeCached pixmaps that shall be uncached or
|
||||||
cumulatedCount = event.number<qint32>(2);
|
// Error pixmaps that become corrupt cache entries. We also accept Initial to be
|
||||||
qint64 pixSize = 0;
|
// uncached as we may have missed the matching PixmapCacheCountChanged that cached it.
|
||||||
|
if (newEvent.sizeIndex == -1) {
|
||||||
// First try to find a preferred pixmap, which either is Corrupt and will be uncached
|
|
||||||
// or is uncached and will be cached.
|
|
||||||
for (QVector<PixmapState>::iterator i(pixmap.sizes.begin());
|
for (QVector<PixmapState>::iterator i(pixmap.sizes.begin());
|
||||||
i != pixmap.sizes.end(); ++i) {
|
i != pixmap.sizes.end(); ++i) {
|
||||||
if (uncache && i->cacheState == Corrupt) {
|
if (uncache && (i->cacheState == Cached || i->cacheState == ToBeCached ||
|
||||||
|
i->cacheState == Uncached)) {
|
||||||
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
||||||
i->cacheState = Uncacheable;
|
if (i->size.isValid())
|
||||||
|
pixSize = -i->size.width() * i->size.height();
|
||||||
|
i->cacheState = Uncached;
|
||||||
break;
|
break;
|
||||||
} else if (!uncache && i->cacheState == Uncached) {
|
} else if (!uncache && i->cacheState == Uncacheable) {
|
||||||
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
||||||
if (i->size.isValid()) {
|
i->cacheState = Corrupt;
|
||||||
pixSize = i->size.width() * i->size.height();
|
|
||||||
i->cacheState = Cached;
|
|
||||||
} else {
|
|
||||||
i->cacheState = ToBeCached;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If none found, check for cached or ToBeCached pixmaps that shall be uncached or
|
// If that does't work, create a new entry.
|
||||||
// Error pixmaps that become corrupt cache entries. We also accept Initial to be
|
if (newEvent.sizeIndex == -1) {
|
||||||
// uncached as we may have missed the matching PixmapCacheCountChanged that cached it.
|
newEvent.sizeIndex = pixmap.sizes.length();
|
||||||
if (newEvent.sizeIndex == -1) {
|
pixmap.sizes << PixmapState(uncache ? Uncached : ToBeCached);
|
||||||
for (QVector<PixmapState>::iterator i(pixmap.sizes.begin());
|
}
|
||||||
i != pixmap.sizes.end(); ++i) {
|
|
||||||
if (uncache && (i->cacheState == Cached || i->cacheState == ToBeCached ||
|
m_lastCacheSizeEvent = updateCacheCount(m_lastCacheSizeEvent, pixmapStartTime, pixSize,
|
||||||
i->cacheState == Uncached)) {
|
newEvent, event.typeIndex());
|
||||||
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
break;
|
||||||
if (i->size.isValid())
|
}
|
||||||
pixSize = -i->size.width() * i->size.height();
|
case PixmapLoadingStarted: { // Load
|
||||||
i->cacheState = Uncached;
|
// Look for a pixmap that hasn't been started, yet. There may have been a refcount
|
||||||
break;
|
// event, which we ignore.
|
||||||
} else if (!uncache && i->cacheState == Uncacheable) {
|
for (QVector<PixmapState>::const_iterator i(pixmap.sizes.cbegin());
|
||||||
newEvent.sizeIndex = i - pixmap.sizes.begin();
|
i != pixmap.sizes.cend(); ++i) {
|
||||||
i->cacheState = Corrupt;
|
if (i->loadState == Initial) {
|
||||||
break;
|
newEvent.sizeIndex = i - pixmap.sizes.cbegin();
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (newEvent.sizeIndex == -1) {
|
||||||
|
newEvent.sizeIndex = pixmap.sizes.length();
|
||||||
|
pixmap.sizes << PixmapState();
|
||||||
|
}
|
||||||
|
|
||||||
// If that does't work, create a new entry.
|
PixmapState &state = pixmap.sizes[newEvent.sizeIndex];
|
||||||
if (newEvent.sizeIndex == -1) {
|
state.loadState = Loading;
|
||||||
newEvent.sizeIndex = pixmap.sizes.length();
|
newEvent.typeId = event.typeIndex();
|
||||||
pixmap.sizes << PixmapState(uncache ? Uncached : ToBeCached);
|
state.started = insertStart(pixmapStartTime, newEvent.urlIndex + 1);
|
||||||
}
|
m_data.insert(state.started, newEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PixmapLoadingFinished:
|
||||||
|
case PixmapLoadingError: {
|
||||||
|
// First try to find one that has already started
|
||||||
|
for (QVector<PixmapState>::const_iterator i(pixmap.sizes.cbegin());
|
||||||
|
i != pixmap.sizes.cend(); ++i) {
|
||||||
|
if (i->loadState != Loading)
|
||||||
|
continue;
|
||||||
|
// Pixmaps with known size cannot be errors and vice versa
|
||||||
|
if (newEvent.pixmapEventType == PixmapLoadingError && i->size.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
lastCacheSizeEvent = updateCacheCount(lastCacheSizeEvent, pixmapStartTime, pixSize,
|
newEvent.sizeIndex = i - pixmap.sizes.cbegin();
|
||||||
newEvent, event.typeIndex());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PixmapLoadingStarted: { // Load
|
|
||||||
// Look for a pixmap that hasn't been started, yet. There may have been a refcount
|
|
||||||
// event, which we ignore.
|
|
||||||
for (QVector<PixmapState>::const_iterator i(pixmap.sizes.cbegin());
|
|
||||||
i != pixmap.sizes.cend(); ++i) {
|
|
||||||
if (i->loadState == Initial) {
|
|
||||||
newEvent.sizeIndex = i - pixmap.sizes.cbegin();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newEvent.sizeIndex == -1) {
|
|
||||||
newEvent.sizeIndex = pixmap.sizes.length();
|
|
||||||
pixmap.sizes << PixmapState();
|
|
||||||
}
|
|
||||||
|
|
||||||
PixmapState &state = pixmap.sizes[newEvent.sizeIndex];
|
// If none was found use any other compatible one
|
||||||
state.loadState = Loading;
|
if (newEvent.sizeIndex == -1) {
|
||||||
newEvent.typeId = event.typeIndex();
|
|
||||||
state.started = insertStart(pixmapStartTime, newEvent.urlIndex + 1);
|
|
||||||
m_data.insert(state.started, newEvent);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PixmapLoadingFinished:
|
|
||||||
case PixmapLoadingError: {
|
|
||||||
// First try to find one that has already started
|
|
||||||
for (QVector<PixmapState>::const_iterator i(pixmap.sizes.cbegin());
|
for (QVector<PixmapState>::const_iterator i(pixmap.sizes.cbegin());
|
||||||
i != pixmap.sizes.cend(); ++i) {
|
i != pixmap.sizes.cend(); ++i) {
|
||||||
if (i->loadState != Loading)
|
if (i->loadState != Initial)
|
||||||
continue;
|
continue;
|
||||||
// Pixmaps with known size cannot be errors and vice versa
|
// Pixmaps with known size cannot be errors and vice versa
|
||||||
if (newEvent.pixmapEventType == PixmapLoadingError && i->size.isValid())
|
if (newEvent.pixmapEventType == PixmapLoadingError && i->size.isValid())
|
||||||
@@ -325,86 +324,74 @@ void PixmapCacheModel::loadData()
|
|||||||
newEvent.sizeIndex = i - pixmap.sizes.cbegin();
|
newEvent.sizeIndex = i - pixmap.sizes.cbegin();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If none was found use any other compatible one
|
// If again none was found, create one.
|
||||||
if (newEvent.sizeIndex == -1) {
|
if (newEvent.sizeIndex == -1) {
|
||||||
for (QVector<PixmapState>::const_iterator i(pixmap.sizes.cbegin());
|
newEvent.sizeIndex = pixmap.sizes.length();
|
||||||
i != pixmap.sizes.cend(); ++i) {
|
pixmap.sizes << PixmapState();
|
||||||
if (i->loadState != Initial)
|
}
|
||||||
continue;
|
|
||||||
// Pixmaps with known size cannot be errors and vice versa
|
|
||||||
if (newEvent.pixmapEventType == PixmapLoadingError && i->size.isValid())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
newEvent.sizeIndex = i - pixmap.sizes.cbegin();
|
PixmapState &state = pixmap.sizes[newEvent.sizeIndex];
|
||||||
break;
|
// If the pixmap loading wasn't started, start it at traceStartTime()
|
||||||
}
|
if (state.loadState == Initial) {
|
||||||
}
|
newEvent.pixmapEventType = PixmapLoadingStarted;
|
||||||
|
newEvent.typeId = event.typeIndex();
|
||||||
|
qint64 traceStart = modelManager()->traceTime()->startTime();
|
||||||
|
state.started = insert(traceStart, pixmapStartTime - traceStart,
|
||||||
|
newEvent.urlIndex + 1);
|
||||||
|
m_data.insert(state.started, newEvent);
|
||||||
|
|
||||||
// If again none was found, create one.
|
// All other indices are wrong now as we've prepended. Fix them ...
|
||||||
if (newEvent.sizeIndex == -1) {
|
if (m_lastCacheSizeEvent >= state.started)
|
||||||
newEvent.sizeIndex = pixmap.sizes.length();
|
++m_lastCacheSizeEvent;
|
||||||
pixmap.sizes << PixmapState();
|
|
||||||
}
|
|
||||||
|
|
||||||
PixmapState &state = pixmap.sizes[newEvent.sizeIndex];
|
for (int pixmapIndex = 0; pixmapIndex < m_pixmaps.count(); ++pixmapIndex) {
|
||||||
// If the pixmap loading wasn't started, start it at tracetimestamp()
|
Pixmap &brokenPixmap = m_pixmaps[pixmapIndex];
|
||||||
if (state.loadState == Initial) {
|
for (int sizeIndex = 0; sizeIndex < brokenPixmap.sizes.count(); ++sizeIndex) {
|
||||||
newEvent.pixmapEventType = PixmapLoadingStarted;
|
PixmapState &brokenSize = brokenPixmap.sizes[sizeIndex];
|
||||||
newEvent.typeId = event.typeIndex();
|
if ((pixmapIndex != newEvent.urlIndex || sizeIndex != newEvent.sizeIndex) &&
|
||||||
qint64 traceStart = modelManager()->traceTime()->startTime();
|
brokenSize.started >= state.started) {
|
||||||
state.started = insert(traceStart, pixmapStartTime - traceStart,
|
++brokenSize.started;
|
||||||
newEvent.urlIndex + 1);
|
|
||||||
m_data.insert(state.started, newEvent);
|
|
||||||
|
|
||||||
// All other indices are wrong now as we've prepended. Fix them ...
|
|
||||||
if (lastCacheSizeEvent >= state.started)
|
|
||||||
++lastCacheSizeEvent;
|
|
||||||
|
|
||||||
for (int pixmapIndex = 0; pixmapIndex < m_pixmaps.count(); ++pixmapIndex) {
|
|
||||||
Pixmap &brokenPixmap = m_pixmaps[pixmapIndex];
|
|
||||||
for (int sizeIndex = 0; sizeIndex < brokenPixmap.sizes.count(); ++sizeIndex) {
|
|
||||||
PixmapState &brokenSize = brokenPixmap.sizes[sizeIndex];
|
|
||||||
if ((pixmapIndex != newEvent.urlIndex || sizeIndex != newEvent.sizeIndex) &&
|
|
||||||
brokenSize.started >= state.started) {
|
|
||||||
++brokenSize.started;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
insertEnd(state.started, pixmapStartTime - startTime(state.started));
|
insertEnd(state.started, pixmapStartTime - startTime(state.started));
|
||||||
if (newEvent.pixmapEventType == PixmapLoadingError) {
|
if (newEvent.pixmapEventType == PixmapLoadingError) {
|
||||||
state.loadState = Error;
|
state.loadState = Error;
|
||||||
switch (state.cacheState) {
|
switch (state.cacheState) {
|
||||||
case Uncached:
|
case Uncached:
|
||||||
state.cacheState = Uncacheable;
|
state.cacheState = Uncacheable;
|
||||||
break;
|
break;
|
||||||
case ToBeCached:
|
case ToBeCached:
|
||||||
state.cacheState = Corrupt;
|
state.cacheState = Corrupt;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Cached cannot happen as size would have to be known and Corrupt or
|
// Cached cannot happen as size would have to be known and Corrupt or
|
||||||
// Uncacheable cannot happen as we only accept one finish or error event per
|
// Uncacheable cannot happen as we only accept one finish or error event per
|
||||||
// pixmap.
|
// pixmap.
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.loadState = Finished;
|
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
}
|
state.loadState = Finished;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PixmapCacheModel::finalize()
|
||||||
|
{
|
||||||
|
if (m_lastCacheSizeEvent != -1) {
|
||||||
|
insertEnd(m_lastCacheSizeEvent, modelManager()->traceTime()->endTime() -
|
||||||
|
startTime(m_lastCacheSizeEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastCacheSizeEvent != -1)
|
|
||||||
insertEnd(lastCacheSizeEvent, modelManager()->traceTime()->endTime() -
|
|
||||||
startTime(lastCacheSizeEvent));
|
|
||||||
|
|
||||||
resizeUnfinishedLoads();
|
resizeUnfinishedLoads();
|
||||||
|
|
||||||
computeMaxCacheSize();
|
computeMaxCacheSize();
|
||||||
flattenLoads();
|
flattenLoads();
|
||||||
computeNesting();
|
computeNesting();
|
||||||
@@ -413,14 +400,15 @@ void PixmapCacheModel::loadData()
|
|||||||
void PixmapCacheModel::clear()
|
void PixmapCacheModel::clear()
|
||||||
{
|
{
|
||||||
m_pixmaps.clear();
|
m_pixmaps.clear();
|
||||||
m_maxCacheSize = 1;
|
|
||||||
m_data.clear();
|
m_data.clear();
|
||||||
|
m_maxCacheSize = 1;
|
||||||
|
m_lastCacheSizeEvent = -1;
|
||||||
|
m_cumulatedCount = 0;
|
||||||
QmlProfilerTimelineModel::clear();
|
QmlProfilerTimelineModel::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixmapCacheModel::computeMaxCacheSize()
|
void PixmapCacheModel::computeMaxCacheSize()
|
||||||
{
|
{
|
||||||
m_maxCacheSize = 1;
|
|
||||||
foreach (const PixmapCacheModel::PixmapCacheItem &event, m_data) {
|
foreach (const PixmapCacheModel::PixmapCacheItem &event, m_data) {
|
||||||
if (event.pixmapEventType == PixmapCacheModel::PixmapCacheCountChanged) {
|
if (event.pixmapEventType == PixmapCacheModel::PixmapCacheCountChanged) {
|
||||||
if (event.cacheSize > m_maxCacheSize)
|
if (event.cacheSize > m_maxCacheSize)
|
||||||
|
|||||||
@@ -106,19 +106,23 @@ public:
|
|||||||
QVariantMap details(int index) const;
|
QVariantMap details(int index) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void loadData();
|
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
|
||||||
void clear();
|
void finalize() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void computeMaxCacheSize();
|
void computeMaxCacheSize();
|
||||||
void resizeUnfinishedLoads();
|
void resizeUnfinishedLoads();
|
||||||
void flattenLoads();
|
void flattenLoads();
|
||||||
int updateCacheCount(int lastCacheSizeEvent, qint64 startTime, qint64 pixSize,
|
int updateCacheCount(int m_lastCacheSizeEvent, qint64 startTime, qint64 pixSize,
|
||||||
PixmapCacheItem &newEvent, int typeId);
|
PixmapCacheItem &newEvent, int typeId);
|
||||||
|
|
||||||
QVector<PixmapCacheItem> m_data;
|
QVector<PixmapCacheItem> m_data;
|
||||||
QVector<Pixmap> m_pixmaps;
|
QVector<Pixmap> m_pixmaps;
|
||||||
qint64 m_maxCacheSize;
|
|
||||||
|
qint64 m_maxCacheSize = 1;
|
||||||
|
int m_lastCacheSizeEvent = -1;
|
||||||
|
int m_cumulatedCount = 0;
|
||||||
|
|
||||||
static const int s_pixmapCacheCountHue = 240;
|
static const int s_pixmapCacheCountHue = 240;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ QmlProfilerAnimationsModel::QmlProfilerAnimationsModel(QmlProfilerModelManager *
|
|||||||
QObject *parent) :
|
QObject *parent) :
|
||||||
QmlProfilerTimelineModel(manager, Event, MaximumRangeType, ProfileAnimations, parent)
|
QmlProfilerTimelineModel(manager, Event, MaximumRangeType, ProfileAnimations, parent)
|
||||||
{
|
{
|
||||||
m_maxGuiThreadAnimations = m_maxRenderThreadAnimations = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerAnimationsModel::clear()
|
void QmlProfilerAnimationsModel::clear()
|
||||||
{
|
{
|
||||||
|
m_minNextStartTimes[0] = m_minNextStartTimes[1] = 0;
|
||||||
m_maxGuiThreadAnimations = m_maxRenderThreadAnimations = 0;
|
m_maxGuiThreadAnimations = m_maxRenderThreadAnimations = 0;
|
||||||
m_data.clear();
|
m_data.clear();
|
||||||
QmlProfilerTimelineModel::clear();
|
QmlProfilerTimelineModel::clear();
|
||||||
@@ -59,59 +59,46 @@ bool QmlProfilerAnimationsModel::accepted(const QmlEventType &event) const
|
|||||||
return QmlProfilerTimelineModel::accepted(event) && event.detailType == AnimationFrame;
|
return QmlProfilerTimelineModel::accepted(event) && event.detailType == AnimationFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerAnimationsModel::loadData()
|
void QmlProfilerAnimationsModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
||||||
{
|
{
|
||||||
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
Q_UNUSED(type);
|
||||||
if (simpleModel->isEmpty())
|
AnimationThread lastThread = (AnimationThread)event.number<qint32>(2);
|
||||||
return;
|
|
||||||
|
|
||||||
// collect events
|
// initial estimation of the event duration: 1/framerate
|
||||||
const QVector<QmlEvent> &referenceList = simpleModel->events();
|
qint64 estimatedDuration = event.number<qint32>(0) > 0 ? 1e9 / event.number<qint32>(0) : 1;
|
||||||
const QVector<QmlEventType> &typeList = simpleModel->eventTypes();
|
|
||||||
|
|
||||||
AnimationThread lastThread;
|
// the profiler registers the animation events at the end of them
|
||||||
|
qint64 realEndTime = event.timestamp();
|
||||||
|
|
||||||
|
// ranges should not overlap. If they do, our estimate wasn't accurate enough
|
||||||
|
qint64 realStartTime = qMax(event.timestamp() - estimatedDuration,
|
||||||
|
m_minNextStartTimes[lastThread]);
|
||||||
|
|
||||||
|
// Sometimes our estimate is far off or the server has miscalculated the frame rate
|
||||||
|
if (realStartTime >= realEndTime)
|
||||||
|
realEndTime = realStartTime + 1;
|
||||||
|
|
||||||
|
// Don't "fix" the framerate even if we've fixed the duration.
|
||||||
|
// The server should know better after all and if it doesn't we want to see that.
|
||||||
QmlPaintEventData lastEvent;
|
QmlPaintEventData lastEvent;
|
||||||
qint64 minNextStartTimes[] = {0, 0};
|
lastEvent.typeId = event.typeIndex();
|
||||||
|
lastEvent.framerate = event.number<qint32>(0);
|
||||||
|
lastEvent.animationcount = event.number<qint32>(1);
|
||||||
|
QTC_ASSERT(lastEvent.animationcount > 0, return);
|
||||||
|
|
||||||
foreach (const QmlEvent &event, referenceList) {
|
m_data.insert(insert(realStartTime, realEndTime - realStartTime, lastThread), lastEvent);
|
||||||
const QmlEventType &type = typeList[event.typeIndex()];
|
|
||||||
if (!accepted(type))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
lastThread = (AnimationThread)event.number<qint32>(2);
|
if (lastThread == GuiThread)
|
||||||
|
m_maxGuiThreadAnimations = qMax(lastEvent.animationcount, m_maxGuiThreadAnimations);
|
||||||
|
else
|
||||||
|
m_maxRenderThreadAnimations = qMax(lastEvent.animationcount,
|
||||||
|
m_maxRenderThreadAnimations);
|
||||||
|
|
||||||
// initial estimation of the event duration: 1/framerate
|
m_minNextStartTimes[lastThread] = event.timestamp() + 1;
|
||||||
qint64 estimatedDuration = event.number<qint32>(0) > 0 ? 1e9 / event.number<qint32>(0) : 1;
|
}
|
||||||
|
|
||||||
// the profiler registers the animation events at the end of them
|
|
||||||
qint64 realEndTime = event.timestamp();
|
|
||||||
|
|
||||||
// ranges should not overlap. If they do, our estimate wasn't accurate enough
|
|
||||||
qint64 realStartTime = qMax(event.timestamp() - estimatedDuration,
|
|
||||||
minNextStartTimes[lastThread]);
|
|
||||||
|
|
||||||
// Sometimes our estimate is far off or the server has miscalculated the frame rate
|
|
||||||
if (realStartTime >= realEndTime)
|
|
||||||
realEndTime = realStartTime + 1;
|
|
||||||
|
|
||||||
// Don't "fix" the framerate even if we've fixed the duration.
|
|
||||||
// The server should know better after all and if it doesn't we want to see that.
|
|
||||||
lastEvent.typeId = event.typeIndex();
|
|
||||||
lastEvent.framerate = event.number<qint32>(0);
|
|
||||||
lastEvent.animationcount = event.number<qint32>(1);
|
|
||||||
QTC_ASSERT(lastEvent.animationcount > 0, continue);
|
|
||||||
|
|
||||||
m_data.insert(insert(realStartTime, realEndTime - realStartTime, lastThread), lastEvent);
|
|
||||||
|
|
||||||
if (lastThread == GuiThread)
|
|
||||||
m_maxGuiThreadAnimations = qMax(lastEvent.animationcount, m_maxGuiThreadAnimations);
|
|
||||||
else
|
|
||||||
m_maxRenderThreadAnimations = qMax(lastEvent.animationcount,
|
|
||||||
m_maxRenderThreadAnimations);
|
|
||||||
|
|
||||||
minNextStartTimes[lastThread] = event.timestamp() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void QmlProfilerAnimationsModel::finalize()
|
||||||
|
{
|
||||||
computeNesting();
|
computeNesting();
|
||||||
setExpandedRowCount((m_maxGuiThreadAnimations == 0 || m_maxRenderThreadAnimations == 0) ? 2 : 3);
|
setExpandedRowCount((m_maxGuiThreadAnimations == 0 || m_maxRenderThreadAnimations == 0) ? 2 : 3);
|
||||||
setCollapsedRowCount(expandedRowCount());
|
setCollapsedRowCount(expandedRowCount());
|
||||||
|
|||||||
@@ -64,16 +64,18 @@ public:
|
|||||||
QVariantList labels() const;
|
QVariantList labels() const;
|
||||||
QVariantMap details(int index) const;
|
QVariantMap details(int index) const;
|
||||||
|
|
||||||
bool accepted(const QmlEventType &event) const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void loadData();
|
bool accepted(const QmlEventType &event) const override;
|
||||||
void clear();
|
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
|
||||||
|
void finalize() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<QmlProfilerAnimationsModel::QmlPaintEventData> m_data;
|
QVector<QmlProfilerAnimationsModel::QmlPaintEventData> m_data;
|
||||||
int m_maxGuiThreadAnimations;
|
int m_maxGuiThreadAnimations = 0;
|
||||||
int m_maxRenderThreadAnimations;
|
int m_maxRenderThreadAnimations = 0;
|
||||||
|
qint64 m_minNextStartTimes[2] = {0, 0};
|
||||||
|
|
||||||
int rowFromThreadId(int threadId) const;
|
int rowFromThreadId(int threadId) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -62,25 +62,16 @@ bool QmlProfilerRangeModel::supportsBindingLoops() const
|
|||||||
return rangeType() == Binding || rangeType() == HandlingSignal;
|
return rangeType() == Binding || rangeType() == HandlingSignal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerRangeModel::loadData()
|
void QmlProfilerRangeModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
||||||
{
|
{
|
||||||
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
Q_UNUSED(type);
|
||||||
if (simpleModel->isEmpty())
|
// store starttime-based instance
|
||||||
return;
|
m_data.insert(insert(event.timestamp(), event.duration(), event.typeIndex()),
|
||||||
|
QmlRangeEventStartInstance());
|
||||||
// collect events
|
}
|
||||||
const QVector<QmlEvent> &eventList = simpleModel->events();
|
|
||||||
const QVector<QmlEventType> &typesList = simpleModel->eventTypes();
|
|
||||||
foreach (const QmlEvent &event, eventList) {
|
|
||||||
const QmlEventType &type = typesList[event.typeIndex()];
|
|
||||||
if (!accepted(type))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// store starttime-based instance
|
|
||||||
m_data.insert(insert(event.timestamp(), event.duration(), event.typeIndex()),
|
|
||||||
QmlRangeEventStartInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void QmlProfilerRangeModel::finalize()
|
||||||
|
{
|
||||||
// compute range nesting
|
// compute range nesting
|
||||||
computeNesting();
|
computeNesting();
|
||||||
|
|
||||||
|
|||||||
@@ -73,8 +73,9 @@ public:
|
|||||||
virtual QList<const Timeline::TimelineRenderPass *> supportedRenderPasses() const;
|
virtual QList<const Timeline::TimelineRenderPass *> supportedRenderPasses() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void loadData();
|
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
|
||||||
void clear();
|
void finalize() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -134,4 +134,21 @@ QVariantMap QmlProfilerTimelineModel::locationFromTypeId(int index) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QmlProfilerTimelineModel::loadData()
|
||||||
|
{
|
||||||
|
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
||||||
|
if (simpleModel->isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QVector<QmlEventType> &types = simpleModel->eventTypes();
|
||||||
|
|
||||||
|
foreach (const QmlEvent &event, simpleModel->events()) {
|
||||||
|
const QmlEventType &type = types[event.typeIndex()];
|
||||||
|
if (accepted(type)) {
|
||||||
|
loadEvent(event, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace QmlProfiler
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ public:
|
|||||||
Q_INVOKABLE virtual int bindingLoopDest(int index) const;
|
Q_INVOKABLE virtual int bindingLoopDest(int index) const;
|
||||||
QVariantMap locationFromTypeId(int index) const;
|
QVariantMap locationFromTypeId(int index) const;
|
||||||
|
|
||||||
virtual void loadData() = 0;
|
void loadData();
|
||||||
|
|
||||||
|
virtual void loadEvent(const QmlEvent &event, const QmlEventType &type) = 0;
|
||||||
|
virtual void finalize() = 0;
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@@ -68,6 +71,8 @@ private:
|
|||||||
const RangeType m_rangeType;
|
const RangeType m_rangeType;
|
||||||
const ProfileFeature m_mainFeature;
|
const ProfileFeature m_mainFeature;
|
||||||
QmlProfilerModelManager *const m_modelManager;
|
QmlProfilerModelManager *const m_modelManager;
|
||||||
|
|
||||||
|
void updateProgress(qint64 count, qint64 max) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace QmlProfiler
|
||||||
|
|||||||
@@ -131,104 +131,90 @@ QVariantMap SceneGraphTimelineModel::details(int index) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneGraphTimelineModel::loadData()
|
void SceneGraphTimelineModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
||||||
{
|
{
|
||||||
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
|
|
||||||
if (simpleModel->isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// combine the data of several eventtypes into two rows
|
// combine the data of several eventtypes into two rows
|
||||||
const QVector<QmlEventType> &types = simpleModel->eventTypes();
|
switch ((SceneGraphFrameType)type.detailType) {
|
||||||
foreach (const QmlEvent &event, simpleModel->events()) {
|
case SceneGraphRendererFrame: {
|
||||||
const QmlEventType &type = types[event.typeIndex()];
|
// Breakdown of render times. We repeat "render" here as "net" render time. It would
|
||||||
if (!accepted(type))
|
// look incomplete if that was left out as the printf profiler lists it, too, and people
|
||||||
continue;
|
// are apparently comparing that. Unfortunately it is somewhat redundant as the other
|
||||||
|
// parts of the breakdown are usually very short.
|
||||||
switch ((SceneGraphFrameType)type.detailType) {
|
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1) -
|
||||||
case SceneGraphRendererFrame: {
|
event.number<qint64>(2) - event.number<qint64>(3);
|
||||||
// Breakdown of render times. We repeat "render" here as "net" render time. It would
|
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(),
|
||||||
// look incomplete if that was left out as the printf profiler lists it, too, and people
|
RenderPreprocess);
|
||||||
// are apparently comparing that. Unfortunately it is somewhat redundant as the other
|
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(), RenderUpdate);
|
||||||
// parts of the breakdown are usually very short.
|
startTime += insert(startTime, event.number<qint64>(2), event.typeIndex(), RenderBind);
|
||||||
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1)
|
insert(startTime, event.number<qint64>(3), event.typeIndex(), RenderRender);
|
||||||
- event.number<qint64>(2) - event.number<qint64>(3);
|
break;
|
||||||
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(),
|
|
||||||
RenderPreprocess);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(),
|
|
||||||
RenderUpdate);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(2), event.typeIndex(), RenderBind);
|
|
||||||
insert(startTime, event.number<qint64>(3), event.typeIndex(), RenderRender);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphAdaptationLayerFrame: {
|
|
||||||
qint64 startTime = event.timestamp() - event.number<qint64>(1)
|
|
||||||
- event.number<qint64>(2);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(), GlyphRender,
|
|
||||||
event.number<qint64>(0));
|
|
||||||
insert(startTime, event.number<qint64>(2), event.typeIndex(), GlyphStore,
|
|
||||||
event.number<qint64>(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphContextFrame: {
|
|
||||||
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
|
||||||
event.typeIndex(), Material);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphRenderLoopFrame: {
|
|
||||||
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1)
|
|
||||||
- event.number<qint64>(2);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(),
|
|
||||||
RenderThreadSync);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(),
|
|
||||||
Render);
|
|
||||||
insert(startTime, event.number<qint64>(2), event.typeIndex(), Swap);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphTexturePrepare: {
|
|
||||||
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1)
|
|
||||||
- event.number<qint64>(2) - event.number<qint64>(3) - event.number<qint64>(4);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(), TextureBind);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(),
|
|
||||||
TextureConvert);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(2), event.typeIndex(),
|
|
||||||
TextureSwizzle);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(3), event.typeIndex(),
|
|
||||||
TextureUpload);
|
|
||||||
insert(startTime, event.number<qint64>(4), event.typeIndex(), TextureMipmap);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphTextureDeletion: {
|
|
||||||
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
|
||||||
event.typeIndex(), TextureDeletion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphPolishAndSync: {
|
|
||||||
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1)
|
|
||||||
- event.number<qint64>(2) - event.number<qint64>(3);
|
|
||||||
|
|
||||||
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(), Polish);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(), Wait);
|
|
||||||
startTime += insert(startTime, event.number<qint64>(2), event.typeIndex(),
|
|
||||||
GUIThreadSync);
|
|
||||||
insert(startTime, event.number<qint64>(3), event.typeIndex(), Animations);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphWindowsAnimations: {
|
|
||||||
// GUI thread, separate animations stage
|
|
||||||
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
|
||||||
event.typeIndex(), Animations);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SceneGraphPolishFrame: {
|
|
||||||
// GUI thread, separate polish stage
|
|
||||||
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
|
||||||
event.typeIndex(), Polish);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
case SceneGraphAdaptationLayerFrame: {
|
||||||
|
qint64 startTime = event.timestamp() - event.number<qint64>(1) - event.number<qint64>(2);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(), GlyphRender,
|
||||||
|
event.number<qint64>(0));
|
||||||
|
insert(startTime, event.number<qint64>(2), event.typeIndex(), GlyphStore,
|
||||||
|
event.number<qint64>(0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SceneGraphContextFrame: {
|
||||||
|
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
||||||
|
event.typeIndex(), Material);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SceneGraphRenderLoopFrame: {
|
||||||
|
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1) -
|
||||||
|
event.number<qint64>(2);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(),
|
||||||
|
RenderThreadSync);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(),
|
||||||
|
Render);
|
||||||
|
insert(startTime, event.number<qint64>(2), event.typeIndex(), Swap);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SceneGraphTexturePrepare: {
|
||||||
|
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1) -
|
||||||
|
event.number<qint64>(2) - event.number<qint64>(3) - event.number<qint64>(4);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(), TextureBind);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(), TextureConvert);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(2), event.typeIndex(), TextureSwizzle);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(3), event.typeIndex(), TextureUpload);
|
||||||
|
insert(startTime, event.number<qint64>(4), event.typeIndex(), TextureMipmap);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SceneGraphTextureDeletion: {
|
||||||
|
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
||||||
|
event.typeIndex(), TextureDeletion);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SceneGraphPolishAndSync: {
|
||||||
|
qint64 startTime = event.timestamp() - event.number<qint64>(0) - event.number<qint64>(1) -
|
||||||
|
event.number<qint64>(2) - event.number<qint64>(3);
|
||||||
|
|
||||||
|
startTime += insert(startTime, event.number<qint64>(0), event.typeIndex(), Polish);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(1), event.typeIndex(), Wait);
|
||||||
|
startTime += insert(startTime, event.number<qint64>(2), event.typeIndex(), GUIThreadSync);
|
||||||
|
insert(startTime, event.number<qint64>(3), event.typeIndex(), Animations);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SceneGraphWindowsAnimations: {
|
||||||
|
// GUI thread, separate animations stage
|
||||||
|
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
||||||
|
event.typeIndex(), Animations);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SceneGraphPolishFrame: {
|
||||||
|
// GUI thread, separate polish stage
|
||||||
|
insert(event.timestamp() - event.number<qint64>(0), event.number<qint64>(0),
|
||||||
|
event.typeIndex(), Polish);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGraphTimelineModel::finalize()
|
||||||
|
{
|
||||||
computeNesting();
|
computeNesting();
|
||||||
flattenLoads();
|
flattenLoads();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,8 +95,9 @@ public:
|
|||||||
QVariantMap details(int index) const;
|
QVariantMap details(int index) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void loadData();
|
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
|
||||||
void clear();
|
void finalize() override;
|
||||||
|
void clear() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void flattenLoads();
|
void flattenLoads();
|
||||||
|
|||||||
Reference in New Issue
Block a user