From af151a0a683806b79e98e917e9c0a6749bc8cbae Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 30 Jun 2014 12:47:49 +0200 Subject: Change the scene graph profiler to give more accurate information The scene graph profiler was pretty barren. The ranges it showed were only vaguely related to actual events and the numbers shown in the labels weren't really informative. This change shows every stage in GUI thread and rendering thread as its own range, so that you can easily see now if a particular stage takes unusually long. This comes at a cost, though: The color is used for showing event types instead of frame rates now and there is no 1:1 mapping of rows to threads anymore. The first row is still reserved for the GUI thread and the second one for the render thread. Further rows are used as needed, though, and you can meaningfully expand the category now. Task-number: QTBUG-39876 Change-Id: Ib91ba454dd0e3e7482ca2afe6d3c80d9868bcce3 Reviewed-by: Kai Koehne --- .../scenegraphtimelinemodel.cpp | 376 +++++++++++---------- .../qmlprofilerextension/scenegraphtimelinemodel.h | 8 +- 2 files changed, 209 insertions(+), 175 deletions(-) diff --git a/plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp b/plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp index ccb27cb7b4..b6eb1a263e 100644 --- a/plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp +++ b/plugins/qmlprofilerextension/scenegraphtimelinemodel.cpp @@ -30,19 +30,30 @@ namespace Internal { using namespace QmlProfiler; -enum SceneGraphEventType { - SceneGraphRendererFrame, - SceneGraphAdaptationLayerFrame, - SceneGraphContextFrame, - SceneGraphRenderLoopFrame, - SceneGraphTexturePrepare, - SceneGraphTextureDeletion, - SceneGraphPolishAndSync, - SceneGraphWindowsRenderShow, - SceneGraphWindowsAnimations, - SceneGraphWindowsPolishFrame, - - MaximumSceneGraphFrameType +static const char *ThreadLabels[] = { + "GUI Thread", + "Render Thread" +}; + +static const char *StageLabels[] = { + "Polish", + "Wait", + "Sync", + "Animations", + "Sync", + "Swap", + "Material Compile", + "Render Preprocess", + "Render Update", + "Render Bind", + "Render", + "Glyph Render", + "Glyph Upload", + "Texture Bind", + "Texture Convert", + "Texture Swizzle", + "Texture Upload", + "Texture Mipmap" }; enum SceneGraphCategoryType { @@ -52,23 +63,63 @@ enum SceneGraphCategoryType { MaximumSceneGraphCategoryType }; +enum SceneGraphStage { + Polish = 0, + Wait, + GUIThreadSync, + Animations, + MaximumGUIThreadStage, + + RenderThreadSync = MaximumGUIThreadStage, + Swap, + Material, + MaximumRenderThreadStage, + + RenderPreprocess = MaximumRenderThreadStage, + RenderUpdate, + RenderBind, + RenderRender, + MaximumRenderStage, + + GlyphRender = MaximumRenderStage, + GlyphStore, + MaximumGlyphStage, + + TextureBind = MaximumGlyphStage, + TextureConvert, + TextureSwizzle, + TextureUpload, + TextureMipmap, + MaximumTextureStage, + + MaximumSceneGraphStage = MaximumTextureStage +}; + + +Q_STATIC_ASSERT(sizeof(StageLabels) == MaximumSceneGraphStage * sizeof(const char *)); + class SceneGraphTimelineModel::SceneGraphTimelineModelPrivate : public SortedTimelineModel { public: - void addVP(QVariantMap &result, QString label, qint64 time) const; + SceneGraphTimelineModelPrivate(); + int collapsedRowCount; + void flattenLoads(); + private: - bool seenPolishAndSync; Q_DECLARE_PUBLIC(SceneGraphTimelineModel) }; +SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::SceneGraphTimelineModelPrivate() : + collapsedRowCount(1) +{ +} + SceneGraphTimelineModel::SceneGraphTimelineModel(QObject *parent) : AbstractTimelineModel(new SceneGraphTimelineModelPrivate, tr("Scene Graph"), QmlDebug::SceneGraphFrame, QmlDebug::MaximumRangeType, parent) { - Q_D(SceneGraphTimelineModel); - d->seenPolishAndSync = false; } int SceneGraphTimelineModel::rowCount() const @@ -76,47 +127,24 @@ int SceneGraphTimelineModel::rowCount() const Q_D(const SceneGraphTimelineModel); if (isEmpty()) return 1; - return d->seenPolishAndSync ? 3 : 2; + return expanded() ? (MaximumSceneGraphStage + 1) : d->collapsedRowCount; } int SceneGraphTimelineModel::row(int index) const { Q_D(const SceneGraphTimelineModel); - return d->seenPolishAndSync ? d->range(index).sgEventType + 1 : 1; + return expanded() ? (d->range(index).stage + 1) : d->range(index).rowNumberCollapsed; } int SceneGraphTimelineModel::eventId(int index) const { Q_D(const SceneGraphTimelineModel); - return d->seenPolishAndSync ? d->range(index).sgEventType : SceneGraphGUIThread; + return d->range(index).stage; } QColor SceneGraphTimelineModel::color(int index) const { - // get duration in seconds - double eventDuration = duration(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 colorByFraction(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(); - } + return colorByEventId(eventId(index)); } QVariantList SceneGraphTimelineModel::labels() const @@ -124,20 +152,13 @@ QVariantList SceneGraphTimelineModel::labels() const Q_D(const SceneGraphTimelineModel); QVariantList result; - static QVariant renderThreadLabel(labelForSGType(SceneGraphRenderThread)); - static QVariant guiThreadLabel(labelForSGType(SceneGraphGUIThread)); - if (d->expanded && !isEmpty()) { - { + for (int i = 0; i < MaximumSceneGraphStage; ++i) { 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); + element.insert(QLatin1String("displayName"), tr(ThreadLabels[i < MaximumGUIThreadStage ? + SceneGraphGUIThread : SceneGraphRenderThread])); + element.insert(QLatin1String("description"), tr(StageLabels[i])); + element.insert(QLatin1String("id"), i); result << element; } } @@ -145,15 +166,6 @@ QVariantList SceneGraphTimelineModel::labels() const return result; } -void SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::addVP(QVariantMap &result, - QString label, - qint64 time) const -{ - if (time > 0) - result.insert(label, QmlProfilerBaseModel::formatTime(time)); -} - - QVariantMap SceneGraphTimelineModel::details(int index) const { Q_D(const SceneGraphTimelineModel); @@ -162,34 +174,12 @@ QVariantMap SceneGraphTimelineModel::details(int index) const AbstractTimelineModel::AbstractTimelineModelPrivate>::Range *ev = &d->range(index); - result.insert(QLatin1String("displayName"), labelForSGType( - d->seenPolishAndSync ? ev->sgEventType : SceneGraphGUIThread)); - 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]); - } + result.insert(QLatin1String("displayName"), tr(ThreadLabels[ev->stage < MaximumGUIThreadStage ? + SceneGraphGUIThread : SceneGraphRenderThread])); + result.insert(tr("Stage"), tr(StageLabels[ev->stage])); + result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(ev->duration)); + if (ev->glyphCount >= 0) + result.insert(tr("Glyph Count"), QString::number(ev->glyphCount)); return result; } @@ -202,8 +192,6 @@ void SceneGraphTimelineModel::loadData() 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()) { @@ -211,99 +199,145 @@ void SceneGraphTimelineModel::loadData() if (!accepted(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); + switch ((QmlDebug::SceneGraphFrameType)type.detailType) { + case QmlDebug::SceneGraphRendererFrame: { + qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - + event.numericData3 - event.numericData4; + d->insert(startTime, event.numericData1, SceneGraphEvent(RenderPreprocess)); + startTime += event.numericData1; + d->insert(startTime, event.numericData2, SceneGraphEvent(RenderUpdate)); + startTime += event.numericData2; + d->insert(startTime, event.numericData3, SceneGraphEvent(RenderBind)); + startTime += event.numericData3; + d->insert(startTime, event.numericData4, SceneGraphEvent(RenderRender)); + break; } - - 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; - } + case QmlDebug::SceneGraphAdaptationLayerFrame: { + qint64 startTime = event.startTime - event.numericData2 - event.numericData3; + d->insert(startTime, event.numericData2, + SceneGraphEvent(GlyphRender, event.numericData1)); + startTime += event.numericData2; + d->insert(startTime, event.numericData3, + SceneGraphEvent(GlyphStore, event.numericData1)); + break; + } + case QmlDebug::SceneGraphContextFrame: { + d->insert(event.startTime - event.numericData1, event.numericData1, + SceneGraphEvent(Material)); + break; + } + case QmlDebug::SceneGraphRenderLoopFrame: { + qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - + event.numericData3; + d->insert(startTime, event.numericData1, SceneGraphEvent(RenderThreadSync)); + startTime += event.numericData1 + event.numericData2; + // Skip actual rendering. We get a SceneGraphRendererFrame for that + d->insert(startTime, event.numericData3, SceneGraphEvent(Swap)); + break; + } + case QmlDebug::SceneGraphTexturePrepare: { + qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - + event.numericData3 - event.numericData4 - event.numericData5; + + d->insert(startTime, event.numericData1, SceneGraphEvent(TextureBind)); + startTime += event.numericData1; + d->insert(startTime, event.numericData2, SceneGraphEvent(TextureConvert)); + startTime += event.numericData2; + d->insert(startTime, event.numericData3, SceneGraphEvent(TextureSwizzle)); + startTime += event.numericData3; + d->insert(startTime, event.numericData4, SceneGraphEvent(TextureUpload)); + startTime += event.numericData4; + d->insert(startTime, event.numericData4, SceneGraphEvent(TextureMipmap)); + break; + } + case QmlDebug::SceneGraphPolishAndSync: { + qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - + event.numericData3 - event.numericData4; + + d->insert(startTime, event.numericData1, SceneGraphEvent(Polish)); + startTime += event.numericData1; + d->insert(startTime, event.numericData2, SceneGraphEvent(Wait)); + startTime += event.numericData2; + d->insert(startTime, event.numericData3, SceneGraphEvent(GUIThreadSync)); + startTime += event.numericData3; + d->insert(startTime, event.numericData4, SceneGraphEvent(Animations)); + break; + } + case QmlDebug::SceneGraphWindowsAnimations: { + // GUI thread, separate animations stage + d->insert(event.startTime - event.numericData1, event.numericData1, + SceneGraphEvent(Animations)); + break; + } + case QmlDebug::SceneGraphPolishFrame: { + // GUI thread, separate polish stage + d->insert(event.startTime - event.numericData1, event.numericData1, + SceneGraphEvent(Polish)); + break; + } + default: break; } d->modelManager->modelProxyCountUpdated(d->modelId, d->count(), simpleModel->getEvents().count()); } d->computeNesting(); + d->flattenLoads(); d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1); } +void SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::flattenLoads() +{ + collapsedRowCount = 0; + + // computes "compressed row" + QVector eventEndTimes; + + for (int i = 0; i < count(); i++) { + SceneGraphEvent &event = data(i); + const Range &start = range(i); + // Don't try to put render thread events in GUI row and vice versa. + // Rows below those are free for all. + event.rowNumberCollapsed = (event.stage < MaximumGUIThreadStage ? SceneGraphGUIThread : + SceneGraphRenderThread); + while (eventEndTimes.count() > event.rowNumberCollapsed && + eventEndTimes[event.rowNumberCollapsed] > start.start) { + ++event.rowNumberCollapsed; + if (event.stage < MaximumGUIThreadStage) { + if (event.rowNumberCollapsed == SceneGraphRenderThread) + ++event.rowNumberCollapsed; + } else if (event.rowNumberCollapsed == SceneGraphGUIThread) { + ++event.rowNumberCollapsed; + } + } + + while (eventEndTimes.count() <= event.rowNumberCollapsed) + eventEndTimes << 0; // increase stack length, proper value added below + eventEndTimes[event.rowNumberCollapsed] = start.start + start.duration; + + // readjust to account for category empty row + event.rowNumberCollapsed++; + if (event.rowNumberCollapsed > collapsedRowCount) + collapsedRowCount = event.rowNumberCollapsed; + } + + // Starting from 0, count is maxIndex+1 + collapsedRowCount++; +} + void SceneGraphTimelineModel::clear() { Q_D(SceneGraphTimelineModel); d->clear(); - d->seenPolishAndSync = false; d->expanded = false; + d->collapsedRowCount = 1; d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1); } +SceneGraphTimelineModel::SceneGraphEvent::SceneGraphEvent(int stage, int glyphCount) : + stage(stage), rowNumberCollapsed(-1), glyphCount(glyphCount) +{ +} + } // namespace Internal } // namespace QmlProfilerExtension diff --git a/plugins/qmlprofilerextension/scenegraphtimelinemodel.h b/plugins/qmlprofilerextension/scenegraphtimelinemodel.h index 6a9ebfd76c..71ef32d20a 100644 --- a/plugins/qmlprofilerextension/scenegraphtimelinemodel.h +++ b/plugins/qmlprofilerextension/scenegraphtimelinemodel.h @@ -29,16 +29,16 @@ namespace QmlProfilerExtension { namespace Internal { -#define timingFieldCount 16 - class SceneGraphTimelineModel : public QmlProfiler::AbstractTimelineModel { Q_OBJECT public: struct SceneGraphEvent { - int sgEventType; - qint64 timing[timingFieldCount]; + SceneGraphEvent(int stage = -1, int glyphCount = -1); + int stage; + int rowNumberCollapsed; + int glyphCount; // only used for one event type }; SceneGraphTimelineModel(QObject *parent = 0); -- cgit v1.2.3