QmlProfiler: Make overview declarative

Using the TimelineRenderer's render passes we can as well draw the
overview in a nicer way without procedural painting.

Change-Id: I9464f10c52988a6af10c849878e678e4958a1057
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
Ulf Hermann
2014-12-09 18:27:56 +01:00
parent cf80b93be0
commit 32c4d554e7
10 changed files with 279 additions and 292 deletions

View File

@@ -102,7 +102,6 @@ Rectangle {
selectionRangeMode = false;
zoomSlider.externalUpdate = true;
zoomSlider.value = zoomSlider.minimumValue;
overview.clear();
}
function enableButtonsBar(enable) {

View File

@@ -1,186 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2014 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://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
.pragma library
var models = 0;
var notes = 0;
var zoomControl = 0;
//draw background of the graph
function drawGraph(canvas, ctxt)
{
ctxt.fillStyle = "#eaeaea";
ctxt.fillRect(0, 0, canvas.width, canvas.height);
}
//draw the actual data to be graphed
function drawData(canvas, ctxt)
{
if (!zoomControl || !models)
return;
for (var modelIndex = 0; modelIndex < models.length; ++modelIndex) {
var model = models[modelIndex];
for (var ii = canvas.offset; ii < model.count; ii += canvas.increment) {
var xx = (model.startTime(ii) - zoomControl.traceStart) * canvas.spacing;
var eventWidth = model.duration(ii) * canvas.spacing;
if (eventWidth < 1)
eventWidth = 1;
xx = Math.round(xx);
var itemHeight = model.relativeHeight(ii) * canvas.blockHeight;
var yy = (modelIndex + 1) * canvas.blockHeight - itemHeight ;
ctxt.fillStyle = model.color(ii);
ctxt.fillRect(xx, canvas.bump + yy, eventWidth, itemHeight);
}
}
}
function drawBindingLoops(canvas, ctxt) {
ctxt.strokeStyle = "orange";
ctxt.lineWidth = 2;
var radius = 1;
for (var modelIndex = 0; modelIndex < models.length; ++modelIndex) {
var model = models[modelIndex];
for (var ii = canvas.offset - canvas.increment; ii < model.count; ii += canvas.increment) {
if (model.bindingLoopDest(ii) >= 0) {
var xcenter = Math.round(model.startTime(ii) + model.duration(ii) / 2 -
zoomControl.traceStart) * canvas.spacing;
var ycenter = Math.round(canvas.bump + canvas.blockHeight * modelIndex +
canvas.blockHeight / 2);
ctxt.beginPath();
ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
ctxt.stroke();
}
}
}
}
function drawNotes(canvas, ctxt)
{
if (!zoomControl || !models || !notes || notes.count === 0)
return;
var modelsById = models.reduce(function(prev, model) {
prev[model.modelId] = model;
return prev;
}, {});
// divide canvas height in 7 parts: margin, 3*line, space, dot, margin
var vertSpace = (canvas.height - canvas.bump) / 7;
ctxt.strokeStyle = "orange";
ctxt.lineWidth = 2;
for (var i = 0; i < notes.count; ++i) {
var timelineModel = notes.timelineModel(i);
var timelineIndex = notes.timelineIndex(i);
if (timelineIndex === -1)
continue;
var start = Math.max(modelsById[timelineModel].startTime(timelineIndex),
zoomControl.traceStart);
var end = Math.min(modelsById[timelineModel].endTime(timelineIndex),
zoomControl.traceEnd);
var annoX = Math.round(((start + end) / 2 - zoomControl.traceStart) * canvas.spacing);
ctxt.moveTo(annoX, canvas.bump + vertSpace)
ctxt.lineTo(annoX, canvas.bump + vertSpace * 4)
ctxt.stroke();
ctxt.moveTo(annoX, canvas.bump + vertSpace * 5);
ctxt.lineTo(annoX, canvas.bump + vertSpace * 6);
ctxt.stroke();
}
}
function drawTimeBar(canvas, ctxt)
{
if (!zoomControl)
return;
var width = canvas.width;
var height = 10;
var initialBlockLength = 120;
var timePerBlock = Math.pow(2, Math.floor( Math.log( zoomControl.traceDuration / width *
initialBlockLength ) / Math.LN2 ) );
var pixelsPerBlock = timePerBlock * canvas.spacing;
var pixelsPerSection = pixelsPerBlock / 5;
var blockCount = width / pixelsPerBlock;
var realStartTime = Math.floor(zoomControl.traceStart / timePerBlock) * timePerBlock;
var realStartPos = (zoomControl.traceStart - realStartTime) * canvas.spacing;
var timePerPixel = timePerBlock/pixelsPerBlock;
ctxt.fillStyle = "#000000";
ctxt.font = "6px sans-serif";
ctxt.fillStyle = "#cccccc";
ctxt.fillRect(0, 0, width, height);
for (var ii = 0; ii < blockCount+1; ii++) {
var x = Math.floor(ii*pixelsPerBlock - realStartPos);
// block boundary
ctxt.strokeStyle = "#525252";
ctxt.beginPath();
ctxt.moveTo(x, height/2);
ctxt.lineTo(x, height);
ctxt.stroke();
// block time label
ctxt.fillStyle = "#000000";
var timeString = prettyPrintTime((ii+0.5)*timePerBlock + realStartTime);
ctxt.textAlign = "center";
ctxt.fillText(timeString, x + pixelsPerBlock/2, height/2 + 3);
}
ctxt.fillStyle = "#808080";
ctxt.fillRect(0, height-1, width, 1);
}
function prettyPrintTime( t )
{
if (t <= 0) return "0";
if (t<1000) return t+" ns";
t = t/1000;
if (t<1000) return t+" μs";
t = Math.floor(t/100)/10;
if (t<1000) return t+" ms";
t = Math.floor(t)/1000;
if (t<60) return t+" s";
var m = Math.floor(t/60);
t = Math.floor(t - m*60);
return m+"m"+t+"s";
}

View File

@@ -29,39 +29,17 @@
****************************************************************************/
import QtQuick 2.1
import "Overview.js" as Plotter
import TimelineOverviewRenderer 1.0
Canvas {
id: canvas
Rectangle {
id: overview
objectName: "Overview"
contextType: "2d"
color: parent.color
property QtObject modelProxy
property QtObject zoomer
readonly property int eventsPerPass: 512
property int increment: -1
property int offset: -1
readonly property int bump: 10;
readonly property int blockHeight: (height - bump) / modelProxy.models.length;
readonly property double spacing: width / zoomer.traceDuration
// ***** properties
height: 50
property bool dataReady: false
property bool recursionGuard: false
onWidthChanged: offset = -1
// ***** functions
function clear()
{
dataReady = false;
increment = -1;
offset = -1;
requestPaint();
}
function updateRange() {
if (recursionGuard)
return;
@@ -75,11 +53,6 @@ Canvas {
recursionGuard = false;
}
function clamp(val, min, max) {
return Math.min(Math.max(val, min), max);
}
// ***** connections to external objects
Connections {
target: zoomer
onRangeChanged: {
@@ -100,90 +73,95 @@ Canvas {
}
}
Connections {
target: modelProxy
onDataAvailable: {
dataReady = true;
increment = 0;
var models = modelProxy.models;
for (var i = 0; i < models.length; ++i)
increment += models[i].count;
increment = Math.ceil(increment / eventsPerPass);
offset = -1;
requestPaint();
}
TimeDisplay {
id: timebar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
textMargin: 2
bottomBorderHeight: 0
topBorderHeight: 1
height: 10
fontSize: 6
labelsHeight: 10
color1: "#cccccc"
color2: "#cccccc"
windowStart: zoomer.traceStart
alignedWindowStart: zoomer.traceStart
rangeDuration: zoomer.traceDuration
contentX: 0
offsetX: 0
}
Connections {
target: modelProxy.notes
onChanged: notes.doPaint = true
}
Timer {
id: paintTimer
repeat: true
running: offset >= 0
interval: offset == 0 ? 1000 : 14 // Larger initial delay to avoid flickering on resize
onTriggered: canvas.requestPaint()
}
Column {
anchors.top: timebar.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
// ***** slots
onPaint: {
var context = (canvas.context === null) ? getContext("2d") : canvas.context;
id: column
Plotter.models = modelProxy.models;
Plotter.zoomControl = zoomer;
Plotter.notes = modelProxy.notes;
if (offset < 0) {
context.reset();
Plotter.drawGraph(canvas, context);
if (dataReady) {
Plotter.drawTimeBar(canvas, context);
offset = 0;
Repeater {
model: modelProxy.models
TimelineOverviewRenderer {
model: modelData
zoomer: overview.zoomer
notes: modelProxy.notes
width: column.width
height: column.height / modelProxy.models.length
}
} else if (offset < increment) {
Plotter.drawData(canvas, context);
++offset;
} else if (offset < 2 * increment) {
Plotter.drawBindingLoops(canvas, context);
++offset;
} else {
notes.doPaint = true;
offset = -1;
}
}
Canvas {
property alias bump: canvas.bump
property alias spacing: canvas.spacing
property bool doPaint: false
onDoPaintChanged: {
if (doPaint)
requestPaint();
}
Repeater {
id: noteSigns
property var modelsById: modelProxy.models.reduce(function(prev, model) {
prev[model.modelId] = model;
return prev;
}, {});
id: notes
anchors.fill: parent
onPaint: {
if (doPaint) {
var context = (notes.context === null) ? getContext("2d") : notes.context;
context.reset();
Plotter.drawNotes(notes, context);
doPaint = false;
property int vertSpace: column.height / 7
property color noteColor: "orange"
readonly property double spacing: parent.width / zoomer.traceDuration
model: modelProxy.notes.count
Item {
property int timelineIndex: modelProxy.notes.timelineIndex(index)
property int timelineModel: modelProxy.notes.timelineModel(index)
property double startTime: noteSigns.modelsById[timelineModel].startTime(timelineIndex)
property double endTime: noteSigns.modelsById[timelineModel].endTime(timelineIndex)
x: ((startTime + endTime) / 2 - zoomer.traceStart) * noteSigns.spacing
y: timebar.height + noteSigns.vertSpace
height: noteSigns.vertSpace * 5
width: 2
Rectangle {
color: noteSigns.noteColor
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: noteSigns.vertSpace * 3
}
Rectangle {
color: noteSigns.noteColor
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: noteSigns.vertSpace
}
}
}
// ***** child items
MouseArea {
anchors.fill: canvas
anchors.fill: parent
function jumpTo(posX) {
var newX = posX - rangeMover.rangeWidth / 2;
if (newX < 0)
newX = 0;
if (newX + rangeMover.rangeWidth > canvas.width)
newX = canvas.width - rangeMover.rangeWidth;
if (newX + rangeMover.rangeWidth > overview.width)
newX = overview.width - rangeMover.rangeWidth;
if (newX < rangeMover.rangeLeft) {
// Changing left border will change width, so precompute right border here.
@@ -206,9 +184,9 @@ Canvas {
RangeMover {
id: rangeMover
visible: dataReady
onRangeLeftChanged: canvas.updateRange()
onRangeRightChanged: canvas.updateRange()
visible: modelProxy.height > 0
onRangeLeftChanged: overview.updateRange()
onRangeRightChanged: overview.updateRange()
}
Rectangle {

View File

@@ -14,7 +14,6 @@
<file>ico_edit.png</file>
<file>TimeMarks.qml</file>
<file>Overview.qml</file>
<file>Overview.js</file>
<file>SelectionRange.qml</file>
<file>SelectionRangeDetails.qml</file>
<file>arrow_down.png</file>

View File

@@ -42,7 +42,8 @@ SOURCES += \
timelinerenderpass.cpp \
timelinerenderstate.cpp \
timelinenotesmodel.cpp \
timelineabstractrenderer.cpp
timelineabstractrenderer.cpp \
timelineoverviewrenderer.cpp
HEADERS += \
abstractqmlprofilerrunner.h \
@@ -91,7 +92,9 @@ HEADERS += \
timelinerenderer_p.h \
timelinerenderstate_p.h \
timelineabstractrenderer.h \
timelineabstractrenderer_p.h
timelineabstractrenderer_p.h \
timelineoverviewrenderer_p.h \
timelineoverviewrenderer.h
RESOURCES += \
qml/qmlprofiler.qrc

View File

@@ -57,6 +57,8 @@ QtcPlugin {
"timelinemodelaggregator.cpp", "timelinemodelaggregator.h",
"timelinenotesmodel.cpp", "timelinenotesmodel.h", "timelinenotesmodel_p.h",
"timelinenotesrenderpass.cpp", "timelinenotesrenderpass.h",
"timelineoverlayrenderer.cpp", "timelineoverlayrenderer.h",
"timelineoverlayrenderer_p.h",
"timelinerenderer.cpp", "timelinerenderer.h", "timelinerenderer_p.h",
"timelinerenderpass.cpp", "timelinerenderpass.h",
"timelinerenderstate.cpp", "timelinerenderstate.h", "timelinerenderstate_p.h",
@@ -73,7 +75,6 @@ QtcPlugin {
"Detail.qml",
"CategoryLabel.qml",
"MainView.qml",
"Overview.js",
"Overview.qml",
"RangeDetails.qml",
"RangeMover.qml",

View File

@@ -37,6 +37,7 @@
#include "qmlprofileranimationsmodel.h"
#include "qmlprofilerrangemodel.h"
#include "qmlprofilerplugin.h"
#include "timelineoverviewrenderer.h"
// Needed for the load&save actions in the context menu
#include <analyzerbase/ianalyzertool.h>
@@ -100,6 +101,8 @@ QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerT
groupLayout->setSpacing(0);
qmlRegisterType<Timeline::TimelineRenderer>("TimelineRenderer", 1, 0, "TimelineRenderer");
qmlRegisterType<Timeline::TimelineOverviewRenderer>("TimelineOverviewRenderer", 1, 0,
"TimelineOverviewRenderer");
qmlRegisterType<Timeline::TimelineZoomControl>();
qmlRegisterType<Timeline::TimelineModel>();
qmlRegisterType<Timeline::TimelineNotesModel>();

View File

@@ -0,0 +1,91 @@
/****************************************************************************
**
** Copyright (C) 2014 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://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "timelineoverviewrenderer_p.h"
#include "timelinerenderstate.h"
namespace Timeline {
TimelineOverviewRenderer::TimelineOverviewRenderer(QQuickItem *parent) :
TimelineAbstractRenderer(*(new TimelineOverviewRendererPrivate), parent)
{
Q_D(TimelineOverviewRenderer);
setFlag(QQuickItem::ItemHasContents);
d->renderState = 0;
}
TimelineOverviewRenderer::~TimelineOverviewRenderer()
{
Q_D(TimelineOverviewRenderer);
delete d->renderState;
delete d;
}
QSGNode *TimelineOverviewRenderer::updatePaintNode(QSGNode *oldNode,
UpdatePaintNodeData *updatePaintNodeData)
{
Q_D(TimelineOverviewRenderer);
Q_UNUSED(updatePaintNodeData)
if (d->modelDirty) {
delete d->renderState;
d->renderState = 0;
d->modelDirty = false;
}
if (d->renderState == 0) {
d->renderState = new TimelineRenderState(d->zoomer->traceStart(), d->zoomer->traceEnd(),
1.0, d->renderPasses.size());
}
qreal xSpacing = width() / d->zoomer->traceDuration();
qreal ySpacing = height() / (d->model->collapsedRowCount() * TimelineModel::defaultRowHeight());
for (int i = 0; i < d->renderPasses.length(); ++i) {
d->renderState->setPassState(i, d->renderPasses[i]->update(this, d->renderState,
d->renderState->passState(i),
0, d->model->count(), true,
xSpacing));
}
if (d->renderState->isEmpty())
d->renderState->assembleNodeTree(d->model, d->model->height(), 0);
d->modelDirty = false;
d->notesDirty = false;
d->rowHeightsDirty = false;
QMatrix4x4 matrix;
matrix.scale(xSpacing, ySpacing, 1);
return d->renderState->finalize(oldNode, false, matrix);
}
}

View File

@@ -0,0 +1,55 @@
/****************************************************************************
**
** Copyright (C) 2014 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://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef TIMELINEOVERVIEWRENDERER_H
#define TIMELINEOVERVIEWRENDERER_H
#include "timelineabstractrenderer.h"
namespace Timeline {
class TimelineOverviewRenderer : public TimelineAbstractRenderer
{
public:
TimelineOverviewRenderer(QQuickItem *parent = 0);
~TimelineOverviewRenderer();
protected:
virtual QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
class TimelineOverviewRendererPrivate;
Q_DECLARE_PRIVATE(TimelineOverviewRenderer)
};
} // namespace Timeline
QML_DECLARE_TYPE(Timeline::TimelineOverviewRenderer)
#endif // TIMELINEOVERVIEWRENDERER_H

View File

@@ -1,5 +1,49 @@
#ifndef TIMELINEOVERLAYRENDERER_P_H
#define TIMELINEOVERLAYRENDERER_P_H
/****************************************************************************
**
** Copyright (C) 2014 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://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#endif // TIMELINEOVERLAYRENDERER_P_H
#ifndef TIMELINEOVERVIEWRENDERER_P_H
#define TIMELINEOVERVIEWRENDERER_P_H
#include "timelineoverviewrenderer.h"
#include "timelineabstractrenderer_p.h"
namespace Timeline {
class TimelineOverviewRenderer::TimelineOverviewRendererPrivate :
public TimelineAbstractRenderer::TimelineAbstractRendererPrivate
{
public:
TimelineRenderState *renderState;
};
} // namespace Timeline
#endif // TIMELINEOVERVIEWRENDERER_P_H