Files
qt-creator/plugins/qmlprofilerextended/scenegraphtimelinemodel.cpp

474 lines
14 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2013 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://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
****************************************************************************/
#include "scenegraphtimelinemodel.h"
#include "qmldebug/qmlprofilereventtypes.h"
#include "qmlprofiler/qmlprofilermodelmanager.h"
#include <QDebug>
namespace QmlProfilerExtended {
namespace Internal {
using namespace QmlProfiler::Internal;
enum SceneGraphEventType {
SceneGraphRendererFrame,
SceneGraphAdaptationLayerFrame,
SceneGraphContextFrame,
SceneGraphRenderLoopFrame,
SceneGraphTexturePrepare,
SceneGraphTextureDeletion,
SceneGraphPolishAndSync,
SceneGraphWindowsRenderShow,
SceneGraphWindowsAnimations,
SceneGraphWindowsPolishFrame,
MaximumSceneGraphFrameType
};
enum SceneGraphCategoryType {
SceneGraphRenderThread,
SceneGraphGUIThread,
MaximumSceneGraphCategoryType
};
class SceneGraphTimelineModel::SceneGraphTimelineModelPrivate {
public:
SceneGraphTimelineModelPrivate(SceneGraphTimelineModel *qq):q(qq) {}
~SceneGraphTimelineModelPrivate();
SceneGraphTimelineModel *q;
QVector < SceneGraphTimelineModel::SceneGraphEvent > eventList;
bool isExpanded;
QString displayTime(double time);
void addVP(QVariantList &l, QString label, qint64 time);
};
SceneGraphTimelineModel::SceneGraphTimelineModel(QObject *parent)
: AbstractTimelineModel(parent), d(new SceneGraphTimelineModelPrivate(this))
{
}
SceneGraphTimelineModel::~SceneGraphTimelineModel()
{
}
int SceneGraphTimelineModel::categories() const
{
return 1;
}
QStringList SceneGraphTimelineModel::categoryTitles() const
{
QStringList retString;
retString << categoryLabel(0);
return retString;
}
QString SceneGraphTimelineModel::name() const
{
return QLatin1String("SceneGraphTimeLineModel");
}
int SceneGraphTimelineModel::count() const
{
return d->eventList.count();
}
bool SceneGraphTimelineModel::isEmpty() const
{
return d->eventList.isEmpty();
}
bool SceneGraphTimelineModel::eventAccepted(const QmlProfiler::Internal::QmlProfilerSimpleModel::QmlEventData &event) const
{
return (event.eventType == QmlDebug::SceneGraphFrameEvent);
}
qint64 SceneGraphTimelineModel::lastTimeMark() const
{
return d->eventList.last().startTime;
}
void SceneGraphTimelineModel::setExpanded(int category, bool expanded)
{
// TODO: uncomment this for PixMap
//if (category == QmlDebug::SceneGraphFrameEvent)
d->isExpanded = expanded;
}
int SceneGraphTimelineModel::categoryDepth(int categoryIndex) const
{
// TODO
if (isEmpty())
return 1;
return 3;
if (categoryIndex == QmlDebug::SceneGraphFrameEvent)
return 11;
return 1;
}
int SceneGraphTimelineModel::categoryCount() const
{
return 1;
}
const QString SceneGraphTimelineModel::categoryLabel(int categoryIndex) const
{
Q_UNUSED(categoryIndex);
return QLatin1String("SceneGraph");
}
int SceneGraphTimelineModel::findFirstIndex(qint64 startTime) const
{
// TODO properly
int candidate = -2;
for (int i=0; i < d->eventList.count(); i++)
if (d->eventList[i].startTime + d->eventList[i].duration > startTime) {
candidate = i;
break;
}
if (candidate == -1)
return 0;
if (candidate == -2)
return d->eventList.count() - 1;
return candidate;
}
int SceneGraphTimelineModel::findFirstIndexNoParents(qint64 startTime) const
{
// TODO properly
return findFirstIndex(startTime);
// int candidate = -1;
// // in the "endtime" list, find the first event that ends after startTime
// if (d->endTimeData.isEmpty())
// return 0; // -1
// if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime)
// candidate = 0;
// else
// if (d->endTimeData.last().endTime <= startTime)
// return 0; // -1
// if (candidate == -1) {
// int fromIndex = 0;
// int toIndex = d->endTimeData.count()-1;
// while (toIndex - fromIndex > 1) {
// int midIndex = (fromIndex + toIndex)/2;
// if (d->endTimeData[midIndex].endTime < startTime)
// fromIndex = midIndex;
// else
// toIndex = midIndex;
// }
// candidate = toIndex;
// }
// int ndx = d->endTimeData[candidate].startTimeIndex;
// return ndx;
}
int SceneGraphTimelineModel::findLastIndex(qint64 endTime) const
{
// TODO properly
int candidate = 0;
for (int i = d->eventList.count()-1; i >= 0; i--)
if (d->eventList[i].startTime < endTime) {
candidate = i;
break;
}
return candidate;
}
int SceneGraphTimelineModel::getEventType(int index) const
{
// TODO fix
return QmlDebug::PixmapCacheEvent;
//return QmlDebug::SceneGraphFrameEvent;
// return 0;
}
int SceneGraphTimelineModel::getEventRow(int index) const
{
return d->eventList[index].sgEventType + 1;
if (d->isExpanded)
return d->eventList[index].sgEventType + 1;
else
return 0;
}
qint64 SceneGraphTimelineModel::getDuration(int index) const
{
return d->eventList[index].duration;
}
qint64 SceneGraphTimelineModel::getStartTime(int index) const
{
return d->eventList[index].startTime;
}
qint64 SceneGraphTimelineModel::getEndTime(int index) const
{
return getStartTime(index)+getDuration(index);
}
int SceneGraphTimelineModel::getEventId(int index) const
{
return d->eventList[index].sgEventType;
}
QColor SceneGraphTimelineModel::getColor(int index) const
{
// get duration in seconds
double eventDuration = getDuration(index) / 1e9;
// supposedly never above 60 frames per second
// limit it in that case
if (eventDuration < 1/60.0)
eventDuration = 1/60.0;
// generate hue based on fraction of the 60fps
double fpsFraction = 1 / (eventDuration * 60.0);
if (fpsFraction > 1.0)
fpsFraction = 1.0;
return QColor::fromHsl((fpsFraction*96)+10, 76, 166);
}
float SceneGraphTimelineModel::getHeight(int index) const
{
return 1.0f;
}
QString labelForSGType(int t)
{
switch ((SceneGraphCategoryType)t) {
case SceneGraphRenderThread: return QLatin1String("Renderer Thread");
case SceneGraphGUIThread: return QLatin1String("GUI Thread");
}
}
const QVariantList SceneGraphTimelineModel::getLabelsForCategory(int category) const
{
QVariantList result;
for (int i = 0; i < MaximumSceneGraphCategoryType; i++) {
QVariantMap element;
element.insert(QLatin1String("displayName"), QVariant(labelForSGType(i)));
element.insert(QLatin1String("description"), QVariant(labelForSGType(i)));
element.insert(QLatin1String("id"), QVariant(i));
result << element;
}
return result;
}
QString SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::displayTime(double time)
{
if (time < 1e6)
return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
if (time < 1e9)
return QString::number(time/1e6,'f',3) + tr(" ms");
return QString::number(time/1e9,'f',3) + tr(" s");
}
void SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::addVP(QVariantList &l, QString label, qint64 time)
{
if (time > 0) {
QVariantMap res;
res.insert(label, QVariant(displayTime(time)));
l << res;
}
}
const QVariantList SceneGraphTimelineModel::getEventDetails(int index) const
{
QVariantList result;
SceneGraphEvent *ev = &d->eventList[index];
{
QVariantMap res;
res.insert(tr("title"), QVariant(labelForSGType(ev->sgEventType)));
result << res;
}
d->addVP(result, tr("duration"), ev->duration );
if (ev->sgEventType == SceneGraphRenderThread) {
d->addVP(result, tr("polish"), ev->timing[14]);
d->addVP(result, tr("sync"), ev->timing[0]);
d->addVP(result, tr("preprocess"), ev->timing[1]);
d->addVP(result, tr("upload"), ev->timing[2]);
d->addVP(result, tr("swizzle"), ev->timing[3]);
d->addVP(result, tr("convert"), ev->timing[4]);
d->addVP(result, tr("mipmap"), ev->timing[5]);
d->addVP(result, tr("bind"), ev->timing[6]);
d->addVP(result, tr("material"), ev->timing[7]);
d->addVP(result, tr("glyph render"), ev->timing[8]);
d->addVP(result, tr("glyph store"), ev->timing[9]);
d->addVP(result, tr("update"), ev->timing[10]);
d->addVP(result, tr("binding"), ev->timing[11]);
d->addVP(result, tr("render"), ev->timing[12]);
d->addVP(result, tr("swap"), ev->timing[13]);
d->addVP(result, tr("animations"), ev->timing[15]);
}
if (ev->sgEventType == SceneGraphGUIThread) {
d->addVP(result, tr("polish"), ev->timing[0]);
d->addVP(result, tr("wait"), ev->timing[1]);
d->addVP(result, tr("sync"), ev->timing[2]);
d->addVP(result, tr("animations"), ev->timing[3]);
}
return result;
}
const QVariantMap SceneGraphTimelineModel::getEventLocation(int /*index*/) const
{
QVariantMap map;
return map;
}
bool compareStartTimes(const SceneGraphTimelineModel::SceneGraphEvent&t1, const SceneGraphTimelineModel::SceneGraphEvent &t2)
{
return t1.startTime < t2.startTime;
}
void SceneGraphTimelineModel::loadData()
{
clear();
QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
if (simpleModel->isEmpty())
return;
int lastRenderEvent = -1;
// combine the data of several eventtypes into two rows
foreach (const QmlProfilerSimpleModel::QmlEventData &event, simpleModel->getEvents()) {
if (!eventAccepted(event))
continue;
if (event.bindingType == SceneGraphRenderLoopFrame) {
SceneGraphEvent newEvent;
newEvent.sgEventType = SceneGraphRenderThread;
newEvent.duration = event.numericData1 + event.numericData2 + event.numericData3;
newEvent.startTime = event.startTime - newEvent.duration;
for (int i=0; i < timingFieldCount; i++)
newEvent.timing[i] = 0;
d->eventList << newEvent;
lastRenderEvent = d->eventList.count()-1;
}
if (lastRenderEvent >= 0) {
switch ((SceneGraphEventType)event.bindingType) {
case SceneGraphRendererFrame: {
d->eventList[lastRenderEvent].timing[1] = event.numericData1;
d->eventList[lastRenderEvent].timing[10] = event.numericData2;
d->eventList[lastRenderEvent].timing[11] = event.numericData3;
d->eventList[lastRenderEvent].timing[12] = event.numericData4;
break;
}
case SceneGraphAdaptationLayerFrame: {
d->eventList[lastRenderEvent].timing[8] = event.numericData2;
d->eventList[lastRenderEvent].timing[9] = event.numericData3;
break;
}
case SceneGraphContextFrame: {
d->eventList[lastRenderEvent].timing[7] = event.numericData1;
break;
}
case SceneGraphRenderLoopFrame: {
d->eventList[lastRenderEvent].timing[0] = event.numericData1;
d->eventList[lastRenderEvent].timing[13] = event.numericData3;
break;
}
case SceneGraphTexturePrepare: {
d->eventList[lastRenderEvent].timing[2] = event.numericData4;
d->eventList[lastRenderEvent].timing[3] = event.numericData3;
d->eventList[lastRenderEvent].timing[4] = event.numericData2;
d->eventList[lastRenderEvent].timing[5] = event.numericData5;
d->eventList[lastRenderEvent].timing[6] = event.numericData1;
break;
}
case SceneGraphPolishAndSync: {
// GUI thread
SceneGraphEvent newEvent;
newEvent.sgEventType = SceneGraphGUIThread;
newEvent.duration = event.numericData1 + event.numericData2 + event.numericData3 + event.numericData4;
newEvent.startTime = event.startTime - newEvent.duration;
for (int i=0; i < timingFieldCount; i++)
newEvent.timing[i] = 0;
newEvent.timing[0] = event.numericData1;
newEvent.timing[1] = event.numericData2;
newEvent.timing[2] = event.numericData3;
newEvent.timing[3] = event.numericData4;
d->eventList << newEvent;
break;
}
case SceneGraphWindowsAnimations: {
d->eventList[lastRenderEvent].timing[14] = event.numericData1;
break;
}
case SceneGraphWindowsPolishFrame: {
d->eventList[lastRenderEvent].timing[15] = event.numericData1;
break;
}
}
}
}
qSort(d->eventList.begin(), d->eventList.end(), compareStartTimes);
}
void SceneGraphTimelineModel::clear()
{
d->eventList.clear();
}
void SceneGraphTimelineModel::dataChanged()
{
if (m_modelManager->state() == QmlProfilerDataState::Done)
loadData();
if (m_modelManager->state() == QmlProfilerDataState::Empty)
clear();
emit stateChanged();
emit dataAvailable();
emit emptyChanged();
return;
}
} // namespace Internal
} // namespace QmlProfilerExtended