/**************************************************************************** ** ** 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 #include namespace QmlProfilerExtension { namespace Internal { using namespace QmlProfiler; static const char *ThreadLabels[] = { QT_TRANSLATE_NOOP("MainView", "GUI Thread"), QT_TRANSLATE_NOOP("MainView", "Render Thread"), QT_TRANSLATE_NOOP("MainView", "Render Thread Details") }; static const char *StageLabels[] = { QT_TRANSLATE_NOOP("MainView", "Polish"), QT_TRANSLATE_NOOP("MainView", "Wait"), QT_TRANSLATE_NOOP("MainView", "GUI Thread Sync"), QT_TRANSLATE_NOOP("MainView", "Animations"), QT_TRANSLATE_NOOP("MainView", "Render Thread Sync"), QT_TRANSLATE_NOOP("MainView", "Render"), QT_TRANSLATE_NOOP("MainView", "Swap"), QT_TRANSLATE_NOOP("MainView", "Render Preprocess"), QT_TRANSLATE_NOOP("MainView", "Render Update"), QT_TRANSLATE_NOOP("MainView", "Render Bind"), QT_TRANSLATE_NOOP("MainView", "Render Render"), QT_TRANSLATE_NOOP("MainView", "Material Compile"), QT_TRANSLATE_NOOP("MainView", "Glyph Render"), QT_TRANSLATE_NOOP("MainView", "Glyph Upload"), QT_TRANSLATE_NOOP("MainView", "Texture Bind"), QT_TRANSLATE_NOOP("MainView", "Texture Convert"), QT_TRANSLATE_NOOP("MainView", "Texture Swizzle"), QT_TRANSLATE_NOOP("MainView", "Texture Upload"), QT_TRANSLATE_NOOP("MainView", "Texture Mipmap"), QT_TRANSLATE_NOOP("MainView", "Texture Delete") }; enum SceneGraphCategoryType { SceneGraphGUIThread, SceneGraphRenderThread, SceneGraphRenderThreadDetails, MaximumSceneGraphCategoryType }; Q_STATIC_ASSERT(sizeof(StageLabels) == SceneGraphTimelineModel::MaximumSceneGraphStage * sizeof(const char *)); SceneGraphTimelineModel::SceneGraphTimelineModel(QmlProfilerModelManager *manager, QObject *parent) : QmlProfilerTimelineModel(manager, tr(QmlProfilerModelManager::featureName(QmlDebug::ProfileSceneGraph)), QmlDebug::SceneGraphFrame, QmlDebug::MaximumRangeType, parent) { announceFeatures(1 << QmlDebug::ProfileSceneGraph); } int SceneGraphTimelineModel::row(int index) const { return expanded() ? (selectionId(index) + 1) : m_data[index].rowNumberCollapsed; } int SceneGraphTimelineModel::typeId(int index) const { return m_data[index].typeId; } QColor SceneGraphTimelineModel::color(int index) const { return colorBySelectionId(index); } QVariantList SceneGraphTimelineModel::labels() const { QVariantList result; if (expanded() && !hidden() && !isEmpty()) { for (SceneGraphStage i = MinimumSceneGraphStage; i < MaximumSceneGraphStage; i = static_cast(i + 1)) { QVariantMap element; element.insert(QLatin1String("displayName"), tr(threadLabel(i))); element.insert(QLatin1String("description"), tr(StageLabels[i])); element.insert(QLatin1String("id"), i); result << element; } } return result; } QVariantMap SceneGraphTimelineModel::details(int index) const { QVariantMap result; const SceneGraphStage stage = static_cast(selectionId(index)); result.insert(QLatin1String("displayName"), tr(threadLabel(stage))); result.insert(tr("Stage"), tr(StageLabels[stage])); result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(duration(index))); const int glyphCount = m_data[index].glyphCount; if (glyphCount >= 0) result.insert(tr("Glyphs"), QString::number(glyphCount)); return result; } void SceneGraphTimelineModel::loadData() { clear(); QmlProfilerDataModel *simpleModel = modelManager()->qmlModel(); if (simpleModel->isEmpty()) return; // 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 (!accepted(type)) continue; switch ((QmlDebug::SceneGraphFrameType)type.detailType) { case QmlDebug::SceneGraphRendererFrame: { // Breakdown of render times. We repeat "render" here as "net" render time. It would // look incomplete if that was left out as the printf profiler lists it, too, and people // are apparently comparing that. Unfortunately it is somewhat redundant as the other // parts of the breakdown are usually very short. qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - event.numericData3 - event.numericData4; startTime += insert(startTime, event.numericData1, event.typeIndex, RenderPreprocess); startTime += insert(startTime, event.numericData2, event.typeIndex, RenderUpdate); startTime += insert(startTime, event.numericData3, event.typeIndex, RenderBind); insert(startTime, event.numericData4, event.typeIndex, RenderRender); break; } case QmlDebug::SceneGraphAdaptationLayerFrame: { qint64 startTime = event.startTime - event.numericData2 - event.numericData3; startTime += insert(startTime, event.numericData2, event.typeIndex, GlyphRender, event.numericData1); insert(startTime, event.numericData3, event.typeIndex, GlyphStore, event.numericData1); break; } case QmlDebug::SceneGraphContextFrame: { insert(event.startTime - event.numericData1, event.numericData1, event.typeIndex, Material); break; } case QmlDebug::SceneGraphRenderLoopFrame: { qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - event.numericData3; startTime += insert(startTime, event.numericData1, event.typeIndex, RenderThreadSync); startTime += insert(startTime, event.numericData2, event.typeIndex, Render); insert(startTime, event.numericData3, event.typeIndex, Swap); break; } case QmlDebug::SceneGraphTexturePrepare: { qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - event.numericData3 - event.numericData4 - event.numericData5; startTime += insert(startTime, event.numericData1, event.typeIndex, TextureBind); startTime += insert(startTime, event.numericData2, event.typeIndex, TextureConvert); startTime += insert(startTime, event.numericData3, event.typeIndex, TextureSwizzle); startTime += insert(startTime, event.numericData4, event.typeIndex, TextureUpload); insert(startTime, event.numericData5, event.typeIndex, TextureMipmap); break; } case QmlDebug::SceneGraphTextureDeletion: { insert(event.startTime - event.numericData1, event.numericData1, event.typeIndex, TextureDeletion); break; } case QmlDebug::SceneGraphPolishAndSync: { qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - event.numericData3 - event.numericData4; startTime += insert(startTime, event.numericData1, event.typeIndex, Polish); startTime += insert(startTime, event.numericData2, event.typeIndex, Wait); startTime += insert(startTime, event.numericData3, event.typeIndex, GUIThreadSync); insert(startTime, event.numericData4, event.typeIndex, Animations); break; } case QmlDebug::SceneGraphWindowsAnimations: { // GUI thread, separate animations stage insert(event.startTime - event.numericData1, event.numericData1, event.typeIndex, Animations); break; } case QmlDebug::SceneGraphPolishFrame: { // GUI thread, separate polish stage insert(event.startTime - event.numericData1, event.numericData1, event.typeIndex, Polish); break; } default: break; } updateProgress(count(), simpleModel->getEvents().count()); } computeNesting(); flattenLoads(); updateProgress(1, 1); } void SceneGraphTimelineModel::flattenLoads() { int collapsedRowCount = 0; // computes "compressed row" QVector eventEndTimes; for (int i = 0; i < count(); i++) { SceneGraphEvent &event = m_data[i]; int stage = selectionId(i); // Don't try to put render thread events in GUI row and vice versa. // Rows below those are free for all. if (stage < MaximumGUIThreadStage) event.rowNumberCollapsed = SceneGraphGUIThread; else if (stage < MaximumRenderThreadStage) event.rowNumberCollapsed = SceneGraphRenderThread; else event.rowNumberCollapsed = SceneGraphRenderThreadDetails; while (eventEndTimes.count() > event.rowNumberCollapsed && eventEndTimes[event.rowNumberCollapsed] > startTime(i)) ++event.rowNumberCollapsed; while (eventEndTimes.count() <= event.rowNumberCollapsed) eventEndTimes << 0; // increase stack length, proper value added below eventEndTimes[event.rowNumberCollapsed] = endTime(i); // readjust to account for category empty row event.rowNumberCollapsed++; if (event.rowNumberCollapsed > collapsedRowCount) collapsedRowCount = event.rowNumberCollapsed; } // Starting from 0, count is maxIndex+1 setCollapsedRowCount(collapsedRowCount + 1); setExpandedRowCount(MaximumSceneGraphStage + 1); } /*! * Inserts an event characterized by \a start time, \a duration, \a typeIndex, \a stage and possibly * \a glyphCount (if it's a \c GlyphRender or \c GlyphStore event) into the scene graph model if its * \a duration is greater than 0. Returns \a duration in that case; otherwise returns 0. */ qint64 SceneGraphTimelineModel::insert(qint64 start, qint64 duration, int typeIndex, SceneGraphStage stage, int glyphCount) { if (duration <= 0) return 0; m_data.insert(QmlProfilerTimelineModel::insert(start, duration, stage), SceneGraphEvent(typeIndex, glyphCount)); return duration; } const char *SceneGraphTimelineModel::threadLabel(SceneGraphStage stage) { if (stage < MaximumGUIThreadStage) return ThreadLabels[SceneGraphGUIThread]; else if (stage < MaximumRenderThreadStage) return ThreadLabels[SceneGraphRenderThread]; else return ThreadLabels[SceneGraphRenderThreadDetails]; } void SceneGraphTimelineModel::clear() { m_data.clear(); QmlProfilerTimelineModel::clear(); } SceneGraphTimelineModel::SceneGraphEvent::SceneGraphEvent(int typeId, int glyphCount) : typeId(typeId), rowNumberCollapsed(-1), glyphCount(glyphCount) { } } // namespace Internal } // namespace QmlProfilerExtension