/****************************************************************************
**
** Copyright (C) 2013 Digia Plc
** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com
**
** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
**
** Licensees holding valid Qt Enterprise licenses may use this file in
** accordance with the Qt Enterprise License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.digia.com
**
****************************************************************************/
#include "scenegraphtimelinemodel.h"
#include "qmldebug/qmlprofilereventtypes.h"
#include "qmlprofiler/qmlprofilermodelmanager.h"
#include "qmlprofiler/sortedtimelinemodel.h"
#include "qmlprofiler/abstracttimelinemodel_p.h"
#include
#include
namespace QmlProfilerExtension {
namespace Internal {
using namespace QmlProfiler;
enum SceneGraphEventType {
SceneGraphRendererFrame,
SceneGraphAdaptationLayerFrame,
SceneGraphContextFrame,
SceneGraphRenderLoopFrame,
SceneGraphTexturePrepare,
SceneGraphTextureDeletion,
SceneGraphPolishAndSync,
SceneGraphWindowsRenderShow,
SceneGraphWindowsAnimations,
SceneGraphWindowsPolishFrame,
MaximumSceneGraphFrameType
};
enum SceneGraphCategoryType {
SceneGraphGUIThread,
SceneGraphRenderThread,
MaximumSceneGraphCategoryType
};
class SceneGraphTimelineModel::SceneGraphTimelineModelPrivate :
public SortedTimelineModel
{
public:
void addVP(QVariantList &l, QString label, qint64 time) const;
private:
bool seenPolishAndSync;
Q_DECLARE_PUBLIC(SceneGraphTimelineModel)
};
SceneGraphTimelineModel::SceneGraphTimelineModel(QObject *parent)
: AbstractTimelineModel(new SceneGraphTimelineModelPrivate,
QLatin1String("SceneGraphTimeLineModel"), tr("Scene Graph"),
QmlDebug::SceneGraphFrame, QmlDebug::MaximumRangeType, parent)
{
Q_D(SceneGraphTimelineModel);
d->seenPolishAndSync = false;
}
int SceneGraphTimelineModel::rowCount() const
{
Q_D(const SceneGraphTimelineModel);
if (isEmpty())
return 1;
return d->seenPolishAndSync ? 3 : 2;
}
int SceneGraphTimelineModel::getEventRow(int index) const
{
Q_D(const SceneGraphTimelineModel);
return d->seenPolishAndSync ? d->range(index).sgEventType + 1 : 1;
}
int SceneGraphTimelineModel::getEventId(int index) const
{
Q_D(const SceneGraphTimelineModel);
return d->seenPolishAndSync ? d->range(index).sgEventType : SceneGraphGUIThread;
}
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 getFractionColor(fpsFraction);
}
QString labelForSGType(int t)
{
switch ((SceneGraphCategoryType)t) {
case SceneGraphRenderThread:
return QCoreApplication::translate("SceneGraphTimelineModel", "Render Thread");
case SceneGraphGUIThread:
return QCoreApplication::translate("SceneGraphTimelineModel", "GUI Thread");
default: return QString();
}
}
const QVariantList SceneGraphTimelineModel::getLabels() const
{
Q_D(const SceneGraphTimelineModel);
QVariantList result;
static QVariant renderThreadLabel(labelForSGType(SceneGraphRenderThread));
static QVariant guiThreadLabel(labelForSGType(SceneGraphGUIThread));
if (d->expanded && !isEmpty()) {
{
QVariantMap element;
element.insert(QLatin1String("description"), guiThreadLabel);
element.insert(QLatin1String("id"), SceneGraphGUIThread);
result << element;
}
if (d->seenPolishAndSync) {
QVariantMap element;
element.insert(QLatin1String("description"), renderThreadLabel);
element.insert(QLatin1String("id"), SceneGraphRenderThread);
result << element;
}
}
return result;
}
void SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::addVP(QVariantList &l, QString label, qint64 time) const
{
if (time > 0) {
QVariantMap res;
res.insert(label, QVariant(QmlProfilerBaseModel::formatTime(time)));
l << res;
}
}
const QVariantList SceneGraphTimelineModel::getEventDetails(int index) const
{
Q_D(const SceneGraphTimelineModel);
QVariantList result;
const SortedTimelineModel::Range *ev =
&d->range(index);
{
QVariantMap res;
res.insert(QLatin1String("title"), QVariant(labelForSGType(
d->seenPolishAndSync ? ev->sgEventType : SceneGraphGUIThread)));
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;
}
void SceneGraphTimelineModel::loadData()
{
Q_D(SceneGraphTimelineModel);
clear();
QmlProfilerDataModel *simpleModel = d->modelManager->qmlModel();
if (simpleModel->isEmpty())
return;
int lastRenderEvent = -1;
// combine the data of several eventtypes into two rows
const QVector &types = simpleModel->getEventTypes();
foreach (const QmlProfilerDataModel::QmlEventData &event, simpleModel->getEvents()) {
const QmlProfilerDataModel::QmlEventTypeData &type = types[event.typeIndex];
if (!eventAccepted(type))
continue;
if (type.detailType == SceneGraphRenderLoopFrame) {
SceneGraphEvent newEvent;
newEvent.sgEventType = SceneGraphRenderThread;
qint64 duration = event.numericData1 + event.numericData2 + event.numericData3;
qint64 startTime = event.startTime - duration;
for (int i=0; i < timingFieldCount; i++)
newEvent.timing[i] = 0;
// Filter out events with incorrect timings due to interrupted thread on server side
if (duration > 0 && startTime > 0)
lastRenderEvent = d->insert(startTime, duration, newEvent);
}
if (lastRenderEvent >= 0) {
qint64 *timing = d->data(lastRenderEvent).timing;
switch ((SceneGraphEventType)type.detailType) {
case SceneGraphRendererFrame: {
timing[1] = event.numericData1;
timing[10] = event.numericData2;
timing[11] = event.numericData3;
timing[12] = event.numericData4;
break;
}
case SceneGraphAdaptationLayerFrame: {
timing[8] = event.numericData2;
timing[9] = event.numericData3;
break;
}
case SceneGraphContextFrame: {
timing[7] = event.numericData1;
break;
}
case SceneGraphRenderLoopFrame: {
timing[0] = event.numericData1;
timing[13] = event.numericData3;
break;
}
case SceneGraphTexturePrepare: {
timing[2] = event.numericData4;
timing[3] = event.numericData3;
timing[4] = event.numericData2;
timing[5] = event.numericData5;
timing[6] = event.numericData1;
break;
}
case SceneGraphPolishAndSync: {
d->seenPolishAndSync = true;
// GUI thread
SceneGraphEvent newEvent;
newEvent.sgEventType = SceneGraphGUIThread;
qint64 duration = event.numericData1 + event.numericData2 + event.numericData3 + event.numericData4;
qint64 startTime = event.startTime - 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;
// Filter out events with incorrect timings due to interrupted thread on server side
if (duration > 0 && startTime > 0)
d->insert(startTime, duration, newEvent);
break;
}
case SceneGraphWindowsAnimations: {
timing[15] = event.numericData1;
break;
}
case SceneGraphWindowsPolishFrame: {
timing[14] = event.numericData1;
break;
}
default: break;
}
}
d->modelManager->modelProxyCountUpdated(d->modelId, d->count(), simpleModel->getEvents().count());
}
d->computeNesting();
d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1);
}
void SceneGraphTimelineModel::clear()
{
Q_D(SceneGraphTimelineModel);
d->clear();
d->seenPolishAndSync = false;
d->expanded = false;
d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1);
}
} // namespace Internal
} // namespace QmlProfilerExtension