Improve appearance of Scene Graph profiler.

It shouldn't display 0-size timings and it should distinguish the
threads more clearly. Also, we display the gross render time in
addition to the render time breakdown. This seems to be rather
important information for users.

Change-Id: I3c1d94f1489902a815c20831e8e1059c815ef129
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
This commit is contained in:
Ulf Hermann
2014-10-09 12:39:14 +02:00
parent f70cbad9b1
commit 7ae08a61b1

View File

@@ -31,21 +31,23 @@ using namespace QmlProfiler;
static const char *ThreadLabels[] = { static const char *ThreadLabels[] = {
"GUI Thread", "GUI Thread",
"Render Thread" "Render Thread",
"Render Thread Details"
}; };
static const char *StageLabels[] = { static const char *StageLabels[] = {
"Polish", "Polish",
"Wait", "Wait",
"Sync", "GUI Thread Sync",
"Animations", "Animations",
"Sync", "Render Thread Sync",
"Render",
"Swap", "Swap",
"Material Compile",
"Render Preprocess", "Render Preprocess",
"Render Update", "Render Update",
"Render Bind", "Render Bind",
"Render", "Render Render",
"Material Compile",
"Glyph Render", "Glyph Render",
"Glyph Upload", "Glyph Upload",
"Texture Bind", "Texture Bind",
@@ -58,20 +60,22 @@ static const char *StageLabels[] = {
enum SceneGraphCategoryType { enum SceneGraphCategoryType {
SceneGraphGUIThread, SceneGraphGUIThread,
SceneGraphRenderThread, SceneGraphRenderThread,
SceneGraphRenderThreadDetails,
MaximumSceneGraphCategoryType MaximumSceneGraphCategoryType
}; };
enum SceneGraphStage { enum SceneGraphStage {
Polish = 0, MinimumSceneGraphStage = 0,
Polish = MinimumSceneGraphStage,
Wait, Wait,
GUIThreadSync, GUIThreadSync,
Animations, Animations,
MaximumGUIThreadStage, MaximumGUIThreadStage,
RenderThreadSync = MaximumGUIThreadStage, RenderThreadSync = MaximumGUIThreadStage,
Render,
Swap, Swap,
Material,
MaximumRenderThreadStage, MaximumRenderThreadStage,
RenderPreprocess = MaximumRenderThreadStage, RenderPreprocess = MaximumRenderThreadStage,
@@ -80,7 +84,10 @@ enum SceneGraphStage {
RenderRender, RenderRender,
MaximumRenderStage, MaximumRenderStage,
GlyphRender = MaximumRenderStage, Material = MaximumRenderStage,
MaximumMaterialStage,
GlyphRender = MaximumMaterialStage,
GlyphStore, GlyphStore,
MaximumGlyphStage, MaximumGlyphStage,
@@ -104,6 +111,10 @@ public:
void flattenLoads(); void flattenLoads();
QVector<SceneGraphEvent> data; QVector<SceneGraphEvent> data;
qint64 insert(qint64 start, qint64 duration, int typeIndex, SceneGraphStage stage,
int glyphCount = -1);
static const char *threadLabel(SceneGraphStage stage);
private: private:
Q_DECLARE_PUBLIC(SceneGraphTimelineModel) Q_DECLARE_PUBLIC(SceneGraphTimelineModel)
}; };
@@ -143,10 +154,10 @@ QVariantList SceneGraphTimelineModel::labels() const
QVariantList result; QVariantList result;
if (d->expanded && !d->hidden && !isEmpty()) { if (d->expanded && !d->hidden && !isEmpty()) {
for (int i = 0; i < MaximumSceneGraphStage; ++i) { for (SceneGraphStage i = MinimumSceneGraphStage; i < MaximumSceneGraphStage;
i = static_cast<SceneGraphStage>(i + 1)) {
QVariantMap element; QVariantMap element;
element.insert(QLatin1String("displayName"), tr(ThreadLabels[i < MaximumGUIThreadStage ? element.insert(QLatin1String("displayName"), tr(d->threadLabel(i)));
SceneGraphGUIThread : SceneGraphRenderThread]));
element.insert(QLatin1String("description"), tr(StageLabels[i])); element.insert(QLatin1String("description"), tr(StageLabels[i]));
element.insert(QLatin1String("id"), i); element.insert(QLatin1String("id"), i);
result << element; result << element;
@@ -162,8 +173,8 @@ QVariantMap SceneGraphTimelineModel::details(int index) const
QVariantMap result; QVariantMap result;
const SceneGraphEvent *ev = &d->data[index]; const SceneGraphEvent *ev = &d->data[index];
result.insert(QLatin1String("displayName"), tr(ThreadLabels[ev->stage < MaximumGUIThreadStage ? result.insert(QLatin1String("displayName"),
SceneGraphGUIThread : SceneGraphRenderThread])); tr(d->threadLabel(static_cast<SceneGraphStage>(ev->stage))));
result.insert(tr("Stage"), tr(StageLabels[ev->stage])); result.insert(tr("Stage"), tr(StageLabels[ev->stage]));
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration)); result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration));
if (ev->glyphCount >= 0) if (ev->glyphCount >= 0)
@@ -189,93 +200,72 @@ void SceneGraphTimelineModel::loadData()
switch ((QmlDebug::SceneGraphFrameType)type.detailType) { switch ((QmlDebug::SceneGraphFrameType)type.detailType) {
case QmlDebug::SceneGraphRendererFrame: { 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 - qint64 startTime = event.startTime - event.numericData1 - event.numericData2 -
event.numericData3 - event.numericData4; event.numericData3 - event.numericData4;
d->data.insert(insert(startTime, event.numericData1, event.typeIndex), startTime += d->insert(startTime, event.numericData1, event.typeIndex,
SceneGraphEvent(RenderPreprocess)); RenderPreprocess);
startTime += event.numericData1; startTime += d->insert(startTime, event.numericData2, event.typeIndex, RenderUpdate);
d->data.insert(insert(startTime, event.numericData2, event.typeIndex), startTime += d->insert(startTime, event.numericData3, event.typeIndex, RenderBind);
SceneGraphEvent(RenderUpdate)); d->insert(startTime, event.numericData4, event.typeIndex, RenderRender);
startTime += event.numericData2;
d->data.insert(insert(startTime, event.numericData3, event.typeIndex),
SceneGraphEvent(RenderBind));
startTime += event.numericData3;
d->data.insert(insert(startTime, event.numericData4, event.typeIndex),
SceneGraphEvent(RenderRender));
break; break;
} }
case QmlDebug::SceneGraphAdaptationLayerFrame: { case QmlDebug::SceneGraphAdaptationLayerFrame: {
qint64 startTime = event.startTime - event.numericData2 - event.numericData3; qint64 startTime = event.startTime - event.numericData2 - event.numericData3;
d->data.insert(insert(startTime, event.numericData2, event.typeIndex), startTime += d->insert(startTime, event.numericData2, event.typeIndex, GlyphRender,
SceneGraphEvent(GlyphRender, event.numericData1)); event.numericData1);
startTime += event.numericData2; d->insert(startTime, event.numericData3, event.typeIndex, GlyphStore,
d->data.insert(insert(startTime, event.numericData3, event.typeIndex), event.numericData1);
SceneGraphEvent(GlyphStore, event.numericData1));
break; break;
} }
case QmlDebug::SceneGraphContextFrame: { case QmlDebug::SceneGraphContextFrame: {
d->data.insert(insert(event.startTime - event.numericData1, event.numericData1, d->insert(event.startTime - event.numericData1, event.numericData1, event.typeIndex,
event.typeIndex), SceneGraphEvent(Material)); Material);
break; break;
} }
case QmlDebug::SceneGraphRenderLoopFrame: { case QmlDebug::SceneGraphRenderLoopFrame: {
qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - qint64 startTime = event.startTime - event.numericData1 - event.numericData2 -
event.numericData3; event.numericData3;
d->data.insert(insert(startTime, event.numericData1, event.typeIndex), startTime += d->insert(startTime, event.numericData1, event.typeIndex,
SceneGraphEvent(RenderThreadSync)); RenderThreadSync);
startTime += event.numericData1 + event.numericData2; startTime += d->insert(startTime, event.numericData2, event.typeIndex,
// Skip actual rendering. We get a SceneGraphRendererFrame for that Render);
d->data.insert(insert(startTime, event.numericData3, event.typeIndex), d->insert(startTime, event.numericData3, event.typeIndex, Swap);
SceneGraphEvent(Swap));
break; break;
} }
case QmlDebug::SceneGraphTexturePrepare: { case QmlDebug::SceneGraphTexturePrepare: {
qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - qint64 startTime = event.startTime - event.numericData1 - event.numericData2 -
event.numericData3 - event.numericData4 - event.numericData5; event.numericData3 - event.numericData4 - event.numericData5;
startTime += d->insert(startTime, event.numericData1, event.typeIndex, TextureBind);
d->data.insert(insert(startTime, event.numericData1, event.typeIndex), startTime += d->insert(startTime, event.numericData2, event.typeIndex, TextureConvert);
SceneGraphEvent(TextureBind)); startTime += d->insert(startTime, event.numericData3, event.typeIndex, TextureSwizzle);
startTime += event.numericData1; startTime += d->insert(startTime, event.numericData4, event.typeIndex, TextureUpload);
d->data.insert(insert(startTime, event.numericData2, event.typeIndex), d->insert(startTime, event.numericData5, event.typeIndex, TextureMipmap);
SceneGraphEvent(TextureConvert));
startTime += event.numericData2;
d->data.insert(insert(startTime, event.numericData3, event.typeIndex),
SceneGraphEvent(TextureSwizzle));
startTime += event.numericData3;
d->data.insert(insert(startTime, event.numericData4, event.typeIndex),
SceneGraphEvent(TextureUpload));
startTime += event.numericData4;
d->data.insert(insert(startTime, event.numericData5, event.typeIndex),
SceneGraphEvent(TextureMipmap));
break; break;
} }
case QmlDebug::SceneGraphPolishAndSync: { case QmlDebug::SceneGraphPolishAndSync: {
qint64 startTime = event.startTime - event.numericData1 - event.numericData2 - qint64 startTime = event.startTime - event.numericData1 - event.numericData2 -
event.numericData3 - event.numericData4; event.numericData3 - event.numericData4;
d->data.insert(insert(startTime, event.numericData1, event.typeIndex), startTime += d->insert(startTime, event.numericData1, event.typeIndex, Polish);
SceneGraphEvent(Polish)); startTime += d->insert(startTime, event.numericData2, event.typeIndex, Wait);
startTime += event.numericData1; startTime += d->insert(startTime, event.numericData3, event.typeIndex, GUIThreadSync);
d->data.insert(insert(startTime, event.numericData2, event.typeIndex), d->insert(startTime, event.numericData4, event.typeIndex, Animations);
SceneGraphEvent(Wait));
startTime += event.numericData2;
d->data.insert(insert(startTime, event.numericData3, event.typeIndex),
SceneGraphEvent(GUIThreadSync));
startTime += event.numericData3;
d->data.insert(insert(startTime, event.numericData4, event.typeIndex),
SceneGraphEvent(Animations));
break; break;
} }
case QmlDebug::SceneGraphWindowsAnimations: { case QmlDebug::SceneGraphWindowsAnimations: {
// GUI thread, separate animations stage // GUI thread, separate animations stage
d->data.insert(insert(event.startTime - event.numericData1, event.numericData1, d->insert(event.startTime - event.numericData1, event.numericData1, event.typeIndex,
event.typeIndex), SceneGraphEvent(Animations)); Animations);
break; break;
} }
case QmlDebug::SceneGraphPolishFrame: { case QmlDebug::SceneGraphPolishFrame: {
// GUI thread, separate polish stage // GUI thread, separate polish stage
d->data.insert(insert(event.startTime - event.numericData1, event.numericData1, d->insert(event.startTime - event.numericData1, event.numericData1, event.typeIndex,
event.typeIndex), SceneGraphEvent(Polish)); Polish);
break; break;
} }
default: break; default: break;
@@ -302,18 +292,16 @@ void SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::flattenLoads()
const Range &start = q->range(i); const Range &start = q->range(i);
// Don't try to put render thread events in GUI row and vice versa. // Don't try to put render thread events in GUI row and vice versa.
// Rows below those are free for all. // Rows below those are free for all.
event.rowNumberCollapsed = (event.stage < MaximumGUIThreadStage ? SceneGraphGUIThread : if (event.stage < MaximumGUIThreadStage)
SceneGraphRenderThread); event.rowNumberCollapsed = SceneGraphGUIThread;
else if (event.stage < MaximumRenderThreadStage)
event.rowNumberCollapsed = SceneGraphRenderThread;
else
event.rowNumberCollapsed = SceneGraphRenderThreadDetails;
while (eventEndTimes.count() > event.rowNumberCollapsed && while (eventEndTimes.count() > event.rowNumberCollapsed &&
eventEndTimes[event.rowNumberCollapsed] > start.start) { eventEndTimes[event.rowNumberCollapsed] > start.start)
++event.rowNumberCollapsed; ++event.rowNumberCollapsed;
if (event.stage < MaximumGUIThreadStage) {
if (event.rowNumberCollapsed == SceneGraphRenderThread)
++event.rowNumberCollapsed;
} else if (event.rowNumberCollapsed == SceneGraphGUIThread) {
++event.rowNumberCollapsed;
}
}
while (eventEndTimes.count() <= event.rowNumberCollapsed) while (eventEndTimes.count() <= event.rowNumberCollapsed)
eventEndTimes << 0; // increase stack length, proper value added below eventEndTimes << 0; // increase stack length, proper value added below
@@ -330,6 +318,34 @@ void SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::flattenLoads()
expandedRowCount = MaximumSceneGraphStage + 1; expandedRowCount = 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::SceneGraphTimelineModelPrivate::insert(qint64 start,
qint64 duration, int typeIndex, SceneGraphStage stage, int glyphCount)
{
if (duration <= 0)
return 0;
Q_Q(SceneGraphTimelineModel);
data.insert(q->insert(start, duration, typeIndex), SceneGraphEvent(stage, glyphCount));
return duration;
}
const char *SceneGraphTimelineModel::SceneGraphTimelineModelPrivate::threadLabel(
SceneGraphStage stage)
{
if (stage < MaximumGUIThreadStage)
return ThreadLabels[SceneGraphGUIThread];
else if (stage < MaximumRenderThreadStage)
return ThreadLabels[SceneGraphRenderThread];
else
return ThreadLabels[SceneGraphRenderThreadDetails];
}
void SceneGraphTimelineModel::clear() void SceneGraphTimelineModel::clear()
{ {
Q_D(SceneGraphTimelineModel); Q_D(SceneGraphTimelineModel);