QMLProfiler: Use scene graph for painting events in timeline

By using the scene graph we can retain the geometry for events in
the timeline on the GPU and potentially speed up the rendering for
large amounts of items.

Change-Id: I2cfbb8ef4ebc7b56f1977ec1facb4f2e7f2002ee
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
Ulf Hermann
2014-11-18 18:32:43 +01:00
parent 62d21d123c
commit ef064154a4
30 changed files with 2395 additions and 390 deletions

View File

@@ -416,41 +416,23 @@ Rectangle {
signal clearChildren
signal select(int modelIndex, int eventIndex)
DelegateModel {
id: timelineModel
model: qmlProfilerModelProxy.models
delegate: Item {
id: spacer
height: modelData.height
width: flick.contentWidth
property int visualIndex: DelegateModel.itemsIndex
TimelineRenderer {
delegate: TimelineRenderer {
id: renderer
model: modelData
notes: qmlProfilerModelProxy.notes
zoomer: zoomControl
selectionLocked: root.selectionLocked
x: flick.contentX
x: 0
height: modelData.height
property int visualIndex: DelegateModel.itemsIndex
// paint "under" the vertical scrollbar, so that it always matches with the
// timemarks
width: scroller.width
property int yScrollStartDiff: flick.contentY - parent.y
property int yScrollEndDiff: flick.height - parent.height + yScrollStartDiff
y: Math.min(parent.height, Math.max(0, yScrollStartDiff))
height: {
if (yScrollStartDiff > 0) {
return Math.max(0, Math.min(flick.height,
parent.height - yScrollStartDiff));
} else if (yScrollEndDiff < 0) {
return Math.max(0, Math.min(flick.height,
parent.height + yScrollEndDiff));
} else {
return parent.height;
}
}
width: flick.contentWidth
Connections {
target: timelineView
@@ -487,18 +469,11 @@ Rectangle {
zoomer.traceEnd));
}
if (spacer.y + spacer.height < flick.contentY)
flick.contentY = spacer.y + spacer.height;
else if (spacer.y - flick.height > flick.contentY)
flick.contentY = spacer.y - flick.height;
var row = modelData.row(selectedItem);
var rowStart = modelData.rowOffset(row);
var rowStart = modelData.rowOffset(row) + y;
var rowEnd = rowStart + modelData.rowHeight(row);
if (rowStart < y)
flick.contentY -= y - rowStart;
else if (rowEnd > y + height)
flick.contentY += rowEnd - y - height;
if (rowStart < flick.contentY || rowEnd - scroller.height > flick.contentY)
flick.contentY = (rowStart + rowEnd - scroller.height) / 2;
}
onSelectedItemChanged: {
@@ -519,7 +494,6 @@ Rectangle {
}
}
}
}
Repeater {
id: repeater

View File

@@ -0,0 +1,35 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
vec4 orange = vec4(1.0, 165.0 / 255.0, 0.0, 1.0);
void main()
{
gl_FragColor = orange;
}

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
attribute vec4 vertexCoord;
attribute vec2 postScaleOffset;
uniform mat4 matrix;
void main()
{
gl_Position = matrix * vertexCoord;
gl_Position.x += postScaleOffset.x * 0.005;
gl_Position.y += postScaleOffset.y * 0.01;
gl_Position.z -= 0.1;
gl_Position.w = 1.0;
}

View File

@@ -0,0 +1,37 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
vec4 orange = vec4(1.0, 165.0 / 255.0, 0.0, 1.0);
varying float d;
void main()
{
gl_FragColor = orange * float(d < (2.0 / 3.0) || d > (5.0 / 6.0));
}

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
attribute vec4 vertexCoord;
attribute float distanceFromTop;
uniform mat4 matrix;
varying float d;
void main()
{
gl_Position = matrix * vertexCoord;
gl_Position.z -= 0.1;
gl_Position.w = 1.0;
d = distanceFromTop;
}

View File

@@ -30,5 +30,11 @@
<file>ico_note.png</file>
<file>ButtonsBar.qml</file>
<file>ico_filter.png</file>
<file>timelineitems.vert</file>
<file>timelineitems.frag</file>
<file>bindingloops.vert</file>
<file>bindingloops.frag</file>
<file>notes.vert</file>
<file>notes.frag</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,46 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
varying lowp vec3 edgeColor;
varying lowp vec3 color;
varying lowp vec2 barycentric;
vec4 zero = vec4(0.0);
void main()
{
vec2 d = fwidth(barycentric) * 5.0;
vec4 edge_closeness = smoothstep(zero, vec4(d.x, d.y, d.x, d.y),
vec4(barycentric.x, barycentric.y, 1.0 - barycentric.x, 1.0 - barycentric.y));
float total = min(min(edge_closeness[0], edge_closeness[1]),
min(edge_closeness[2], edge_closeness[3]));
// square to make lines sharper
total = total > 0.5 ? (1.0 - (1.0 - total) * (1.0 - total) * 2.0) : total * total * 2.0;
gl_FragColor.rgb = mix(edgeColor, color, total);
}

View File

@@ -0,0 +1,64 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
attribute vec4 vertexCoord;
attribute vec2 rectSize;
attribute float selectionId;
attribute vec4 vertexColor;
uniform vec2 scale;
uniform mat4 matrix;
uniform vec4 selectionColor;
uniform float selectedItem;
varying vec3 color;
varying vec3 edgeColor;
varying vec2 barycentric;
void main()
{
gl_Position = matrix * vertexCoord;
// Make very narrow events somewhat wider so that they don't collapse into 0 pixels
highp float scaledWidth = scale.x * rectSize.x;
highp float shift = sign(scaledWidth) * max(0, 3.0 - abs(scaledWidth)) * 0.0005;
gl_Position.x += shift;
// Ditto for events with very small height
highp float scaledHeight = scale.y * rectSize.y;
gl_Position.y += float(rectSize.y > 0.0) * max(0, 3.0 - scaledHeight) * 0.003;
barycentric = vec2(rectSize.x > 0.0 ? 1.0 : 0.0, rectSize.y > 0.0 ? 1.0 : 0.0);
color = vertexColor.rgb;
float selected = min(1.0, abs(selectionId - selectedItem));
edgeColor = mix(selectionColor.rgb, vertexColor.rgb, selected);
gl_Position.z += mix(0.0, (shift + 0.0015) / 10.0, selected);
gl_Position.w = 1.0;
}

View File

@@ -34,7 +34,13 @@ SOURCES += \
timelinemodel.cpp \
timelinemodelaggregator.cpp \
timelinerenderer.cpp \
timelinezoomcontrol.cpp
timelinezoomcontrol.cpp \
timelineitemsrenderpass.cpp \
qmlprofilerbindingloopsrenderpass.cpp \
timelineselectionrenderpass.cpp \
timelinenotesrenderpass.cpp \
timelinerenderpass.cpp \
timelinerenderstate.cpp
HEADERS += \
abstractqmlprofilerrunner.h \
@@ -71,7 +77,13 @@ HEADERS += \
timelinemodel_p.h \
timelinemodelaggregator.h \
timelinerenderer.h \
timelinezoomcontrol.h
timelinezoomcontrol.h \
timelineitemsrenderpass.h \
qmlprofilerbindingloopsrenderpass.h \
timelineselectionrenderpass.h \
timelinenotesrenderpass.h \
timelinerenderpass.h \
timelinerenderstate.h
RESOURCES += \
qml/qmlprofiler.qrc
@@ -87,4 +99,10 @@ DISTFILES += \
qml/TimeMarks.qml \
qml/SelectionRange.qml \
qml/SelectionRangeDetails.qml \
qml/Overview.qml
qml/Overview.qml \
qml/timelineitems.frag \
qml/timelineitems.vert \
qml/bindingloops.frag \
qml/bindingloops.vert \
qml/notes.frag \
qml/notes.vert

View File

@@ -26,6 +26,7 @@ QtcPlugin {
"qmlprofileranimationsmodel.h", "qmlprofileranimationsmodel.cpp",
"qmlprofilerattachdialog.cpp", "qmlprofilerattachdialog.h",
"qmlprofilerbasemodel.cpp", "qmlprofilerbasemodel.h", "qmlprofilerbasemodel_p.h",
"qmlprofilerbindingloopsrenderpass.cpp","qmlprofilerbindingloopsrenderpass.h",
"qmlprofilerclientmanager.cpp", "qmlprofilerclientmanager.h",
"qmlprofilerconstants.h",
"qmlprofilerdatamodel.cpp", "qmlprofilerdatamodel.h",
@@ -49,9 +50,14 @@ QtcPlugin {
"qmlprofilerviewmanager.cpp", "qmlprofilerviewmanager.h",
"qv8profilerdatamodel.cpp", "qv8profilerdatamodel.h",
"qv8profilereventview.h", "qv8profilereventview.cpp",
"timelineitemsrenderpass.cpp", "timelineitemsrenderpass.h",
"timelinemodel.cpp", "timelinemodel.h", "timelinemodel_p.h",
"timelinemodelaggregator.cpp", "timelinemodelaggregator.h",
"timelinenotesrenderpass.cpp", "timelinenotesrenderpass.h",
"timelinerenderer.cpp", "timelinerenderer.h",
"timelinerenderpass.cpp", "timelinerenderpass.h",
"timelinerenderstate.cpp", "timelinerenderstate.h",
"timelineselectionrenderpass.cpp", "timelineselectionrenderpass.h",
"timelinezoomcontrol.cpp", "timelinezoomcontrol.h"
]
}
@@ -72,7 +78,15 @@ QtcPlugin {
"SelectionRangeDetails.qml",
"TimeDisplay.qml",
"TimeMarks.qml",
"qmlprofiler.qrc",
"bindingloops.frag",
"bindingloops.vert",
"notes.frag",
"notes.vert",
"timelineitems.frag",
"timelineitems.vert"
]
}
}

View File

@@ -0,0 +1,356 @@
/****************************************************************************
**
** 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 "qmlprofilerbindingloopsrenderpass.h"
namespace QmlProfiler {
namespace Internal {
class BindingLoopMaterial : public QSGMaterial {
public:
QSGMaterialType *type() const;
QSGMaterialShader *createShader() const;
BindingLoopMaterial();
};
struct BindingLoopsRenderPassState : public TimelineRenderPass::State {
BindingLoopsRenderPassState() : indexFrom(std::numeric_limits<int>::max()), indexTo(-1) {}
BindingLoopMaterial material;
int indexFrom;
int indexTo;
};
struct Point2DWithOffset {
float x, y, x2, y2;
void set(float nx, float ny, float nx2, float ny2);
};
struct BindlingLoopsGeometry {
static const QSGGeometry::AttributeSet &point2DWithOffset();
static const int maxEventsPerNode = 0xffff / 18;
BindlingLoopsGeometry() : allocatedVertices(0), usedVertices(0), currentY(-1), node(0) {}
uint allocatedVertices;
uint usedVertices;
float currentY;
QSGGeometryNode *node;
Point2DWithOffset *vertexData();
void allocate(QSGMaterial *material);
void addExpandedEvent(float itemCenter);
void addCollapsedEvent(float horizontalCenterSource, float horizontalCenterTarget,
float verticalCenterSource, float verticalCenterTarget);
};
const QmlProfilerBindingLoopsRenderPass *QmlProfilerBindingLoopsRenderPass::instance()
{
static const QmlProfilerBindingLoopsRenderPass pass;
return &pass;
}
QmlProfilerBindingLoopsRenderPass::QmlProfilerBindingLoopsRenderPass()
{
}
void updateNodes(const QmlProfilerRangeModel *model, int from, int to,
const TimelineRenderState *parentState, BindingLoopsRenderPassState *state)
{
QVector<BindlingLoopsGeometry> expandedPerRow(model->expandedRowCount());
BindlingLoopsGeometry collapsed;
for (int i = from; i < to; ++i) {
int bindingLoopDest = model->bindingLoopDest(i);
if (bindingLoopDest == -1)
continue;
qint64 start = qMax(parentState->start(), model->startTime(i));
qint64 end = qMin(parentState->end(), model->startTime(i) + model->duration(i));
if (start > end)
continue;
expandedPerRow[model->expandedRow(i)].usedVertices += 4;
collapsed.usedVertices += 18;
}
for (int i = 0; i < model->expandedRowCount(); ++i) {
BindlingLoopsGeometry &row = expandedPerRow[i];
if (row.usedVertices > 0) {
row.allocate(&state->material);
state->expandedRows[i]->appendChildNode(row.node);
}
}
if (collapsed.usedVertices > 0) {
collapsed.allocate(&state->material);
state->collapsedOverlay->appendChildNode(collapsed.node);
}
int rowHeight = TimelineModel::defaultRowHeight();
for (int i = from; i < to; ++i) {
int bindingLoopDest = model->bindingLoopDest(i);
if (bindingLoopDest == -1)
continue;
if (model->startTime(i) > parentState->end() || model->endTime(i) < parentState->start())
continue;
qint64 center = qMax(parentState->start(), qMin(parentState->end(),
(model->startTime(i) + model->endTime(i)) /
(qint64)2));
float itemCenter = (center - parentState->start()) * parentState->scale();
expandedPerRow[model->expandedRow(i)].addExpandedEvent(itemCenter);
center = qMax(parentState->start(), qMin(parentState->end(),
(model->startTime(bindingLoopDest) +
model->endTime(bindingLoopDest)) / (qint64)2));
float itemCenterTarget = (center - parentState->start()) * parentState->scale();
collapsed.addCollapsedEvent(itemCenter, itemCenterTarget,
(model->collapsedRow(i) + 0.5) * rowHeight,
(model->collapsedRow(bindingLoopDest) + 0.5) * rowHeight);
}
}
TimelineRenderPass::State *QmlProfilerBindingLoopsRenderPass::update(
const TimelineRenderer *renderer, const TimelineRenderState *parentState, State *oldState,
int indexFrom, int indexTo, bool stateChanged, qreal spacing) const
{
Q_UNUSED(stateChanged);
Q_UNUSED(spacing);
BindingLoopsRenderPassState *state;
if (oldState == 0)
state = new BindingLoopsRenderPassState;
else
state = static_cast<BindingLoopsRenderPassState *>(oldState);
const QmlProfilerRangeModel *model = qobject_cast<const QmlProfilerRangeModel *>(
renderer->model());
if (!model)
return state;
if (state->expandedRows.isEmpty()) {
state->expandedRows.reserve(model->expandedRowCount());
for (int i = 0; i < model->expandedRowCount(); ++i)
state->expandedRows << new QSGNode;
}
if (state->collapsedOverlay == 0)
state->collapsedOverlay = new QSGNode;
if (indexFrom < 0 || indexTo > model->count())
return state;
if (state->indexFrom < state->indexTo) {
if (indexFrom < state->indexFrom) {
for (int i = indexFrom; i < state->indexFrom;
i += BindlingLoopsGeometry::maxEventsPerNode)
updateNodes(model, i, qMin(i + BindlingLoopsGeometry::maxEventsPerNode,
state->indexFrom), parentState, state);
}
if (indexTo > state->indexTo) {
for (int i = state->indexTo; i < indexTo; i+= BindlingLoopsGeometry::maxEventsPerNode)
updateNodes(model, i, qMin(i + BindlingLoopsGeometry::maxEventsPerNode, indexTo),
parentState, state);
}
} else {
for (int i = indexFrom; i < indexTo; i+= BindlingLoopsGeometry::maxEventsPerNode)
updateNodes(model, i, qMin(i + BindlingLoopsGeometry::maxEventsPerNode, indexTo),
parentState, state);
}
state->indexFrom = qMin(state->indexFrom, indexFrom);
state->indexTo = qMax(state->indexTo, indexTo);
return state;
}
const QSGGeometry::AttributeSet &BindlingLoopsGeometry::point2DWithOffset()
{
static QSGGeometry::Attribute data[] = {
QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
QSGGeometry::Attribute::create(1, 2, GL_FLOAT),
};
static QSGGeometry::AttributeSet attrs = {
2,
sizeof(Point2DWithOffset),
data
};
return attrs;
}
Point2DWithOffset *BindlingLoopsGeometry::vertexData()
{
QSGGeometry *geometry = node->geometry();
Q_ASSERT(geometry->attributeCount() == 2);
Q_ASSERT(geometry->sizeOfVertex() == sizeof(Point2DWithOffset));
const QSGGeometry::Attribute *attributes = geometry->attributes();
Q_ASSERT(attributes[0].position == 0);
Q_ASSERT(attributes[0].tupleSize == 2);
Q_ASSERT(attributes[0].type == GL_FLOAT);
Q_ASSERT(attributes[1].position == 1);
Q_ASSERT(attributes[1].tupleSize == 2);
Q_ASSERT(attributes[1].type == GL_FLOAT);
return static_cast<Point2DWithOffset *>(geometry->vertexData());
}
void BindlingLoopsGeometry::allocate(QSGMaterial *material)
{
QSGGeometry *geometry = new QSGGeometry(BindlingLoopsGeometry::point2DWithOffset(),
usedVertices);
geometry->setIndexDataPattern(QSGGeometry::StaticPattern);
geometry->setVertexDataPattern(QSGGeometry::StaticPattern);
node = new QSGGeometryNode;
node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry, true);
node->setMaterial(material);
allocatedVertices = usedVertices;
usedVertices = 0;
}
void BindlingLoopsGeometry::addExpandedEvent(float itemCenter)
{
float verticalCenter = TimelineModel::defaultRowHeight() / 2.0;
Point2DWithOffset *v = vertexData() + usedVertices;
v[0].set(itemCenter, verticalCenter, -1.0f, currentY);
v[1].set(itemCenter, verticalCenter, +1.0f, currentY);
currentY = -currentY;
v[2].set(itemCenter, verticalCenter, -1.0f, currentY);
v[3].set(itemCenter, verticalCenter, +1.0f, currentY);
usedVertices += 4;
}
void BindlingLoopsGeometry::addCollapsedEvent(float horizontalCenterSource,
float horizontalCenterTarget,
float verticalCenterSource,
float verticalCenterTarget)
{
if (verticalCenterSource < verticalCenterTarget) {
qSwap(verticalCenterSource, verticalCenterTarget);
qSwap(horizontalCenterSource, horizontalCenterTarget);
}
float tilt = horizontalCenterSource < horizontalCenterTarget ? +0.3 : -0.3;
Point2DWithOffset *v = vertexData() + usedVertices;
v[0].set(horizontalCenterSource, verticalCenterSource, -0.3f, tilt);
v[1].set(horizontalCenterSource, verticalCenterSource, -0.3f, tilt);
v[2].set(horizontalCenterSource, verticalCenterSource, +0.3f, -tilt);
v[3].set(horizontalCenterTarget, verticalCenterTarget, -0.3f, tilt);
v[4].set(horizontalCenterTarget, verticalCenterTarget, +0.3f, -tilt);
v[5].set(horizontalCenterTarget, verticalCenterTarget, -1.0f, -1.0f);
v[6].set(horizontalCenterTarget, verticalCenterTarget, +1.0f, -1.0f);
v[7].set(horizontalCenterTarget, verticalCenterTarget, -1.0f, +1.0f);
v[8].set(horizontalCenterTarget, verticalCenterTarget, +1.0f, +1.0f);
v[9].set(horizontalCenterTarget, verticalCenterTarget, -0.3f, tilt);
v[10].set(horizontalCenterTarget, verticalCenterTarget, +0.3f, -tilt);
v[11].set(horizontalCenterSource, verticalCenterSource, -0.3f, tilt);
v[12].set(horizontalCenterSource, verticalCenterSource, +0.3f, -tilt);
v[13].set(horizontalCenterSource, verticalCenterSource, -1.0f, +1.0f);
v[14].set(horizontalCenterSource, verticalCenterSource, +1.0f, +1.0f);
v[15].set(horizontalCenterSource, verticalCenterSource, -1.0f, -1.0f);
v[16].set(horizontalCenterSource, verticalCenterSource, +1.0f, -1.0f);
v[17].set(horizontalCenterSource, verticalCenterSource, +1.0f, -1.0f);
usedVertices += 18;
}
class BindingLoopMaterialShader : public QSGMaterialShader
{
public:
BindingLoopMaterialShader();
virtual void updateState(const RenderState &state, QSGMaterial *newEffect,
QSGMaterial *oldEffect);
virtual char const *const *attributeNames() const;
private:
virtual void initialize();
int m_matrix_id;
int m_z_range_id;
};
BindingLoopMaterialShader::BindingLoopMaterialShader()
: QSGMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qmlprofiler/bindingloops.vert"));
setShaderSourceFile(QOpenGLShader::Fragment,
QStringLiteral(":/qmlprofiler/bindingloops.frag"));
}
void BindingLoopMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *)
{
if (state.isMatrixDirty()) {
program()->setUniformValue(m_matrix_id, state.combinedMatrix());
program()->setUniformValue(m_z_range_id, GLfloat(1.0));
}
}
char const *const *BindingLoopMaterialShader::attributeNames() const
{
static const char *const attr[] = {"vertexCoord", "postScaleOffset", 0};
return attr;
}
void BindingLoopMaterialShader::initialize()
{
m_matrix_id = program()->uniformLocation("matrix");
m_z_range_id = program()->uniformLocation("_qt_zRange");
}
BindingLoopMaterial::BindingLoopMaterial()
{
setFlag(QSGMaterial::Blending, false);
}
QSGMaterialType *BindingLoopMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
QSGMaterialShader *BindingLoopMaterial::createShader() const
{
return new BindingLoopMaterialShader;
}
void Point2DWithOffset::set(float nx, float ny, float nx2, float ny2)
{
x = nx; y = ny; x2 = nx2; y2 = ny2;
}
}
}

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** 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 QMLPROFILERBINDINGLOOPSRENDERPASS_H
#define QMLPROFILERBINDINGLOOPSRENDERPASS_H
#include "timelinerenderer.h"
#include "timelinerenderpass.h"
#include "timelinerenderstate.h"
#include "qmlprofilerrangemodel.h"
#include <QSGMaterial>
namespace QmlProfiler {
namespace Internal {
class QmlProfilerBindingLoopsRenderPass : public TimelineRenderPass
{
public:
static const QmlProfilerBindingLoopsRenderPass *instance();
State *update(const TimelineRenderer *renderer, const TimelineRenderState *parentState,
State *oldState, int indexFrom, int indexTo, bool stateChanged,
qreal spacing) const;
protected:
QmlProfilerBindingLoopsRenderPass();
};
}
}
#endif // QMLPROFILERBINDINGLOOPSRENDERPASS_H

View File

@@ -31,6 +31,10 @@
#include "qmlprofilerrangemodel.h"
#include "qmlprofilermodelmanager.h"
#include "qmlprofilerdatamodel.h"
#include "qmlprofilerbindingloopsrenderpass.h"
#include "timelinenotesrenderpass.h"
#include "timelineitemsrenderpass.h"
#include "timelineselectionrenderpass.h"
#include <QCoreApplication>
#include <QVector>
@@ -61,6 +65,11 @@ void QmlProfilerRangeModel::clear()
QmlProfilerTimelineModel::clear();
}
bool QmlProfilerRangeModel::supportsBindingLoops() const
{
return rangeType() == QmlDebug::Binding || rangeType() == QmlDebug::HandlingSignal;
}
void QmlProfilerRangeModel::loadData()
{
clear();
@@ -97,6 +106,7 @@ void QmlProfilerRangeModel::loadData()
updateProgress(4, 6);
if (supportsBindingLoops())
findBindingLoops();
updateProgress(5, 6);
@@ -152,9 +162,6 @@ void QmlProfilerRangeModel::computeExpandedLevels()
void QmlProfilerRangeModel::findBindingLoops()
{
if (rangeType() != QmlDebug::Binding && rangeType() != QmlDebug::HandlingSignal)
return;
typedef QPair<int, int> CallStackEntry;
QStack<CallStackEntry> callStack;
@@ -277,7 +284,20 @@ int QmlProfilerRangeModel::selectionIdForLocation(const QString &filename, int l
return -1;
}
QList<const TimelineRenderPass *> QmlProfilerRangeModel::supportedRenderPasses() const
{
if (supportsBindingLoops()) {
QList<const TimelineRenderPass *> passes;
passes << TimelineItemsRenderPass::instance()
<< QmlProfilerBindingLoopsRenderPass::instance()
<< TimelineSelectionRenderPass::instance()
<< TimelineNotesRenderPass::instance();
return passes;
} else {
return QmlProfilerTimelineModel::supportedRenderPasses();
}
}
}
}

View File

@@ -78,11 +78,15 @@ public:
int typeId(int index) const;
int selectionIdForLocation(const QString &filename, int line, int column) const;
virtual QList<const TimelineRenderPass *> supportedRenderPasses() const;
protected:
void loadData();
void clear();
private:
bool supportsBindingLoops() const;
void computeNestingContracted();
void computeExpandedLevels();
void findBindingLoops();

View File

@@ -102,7 +102,7 @@ QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerT
groupLayout->setSpacing(0);
qmlRegisterType<TimelineZoomControl>();
qmlRegisterType<QmlProfilerTimelineModel>();
qmlRegisterType<TimelineModel>();
qmlRegisterType<QmlProfilerNotesModel>();
d->m_mainView = new QmlProfilerQuickView(this);

View File

@@ -0,0 +1,457 @@
/****************************************************************************
**
** 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 "timelineitemsrenderpass.h"
#include "timelinerenderstate.h"
#include <QSGSimpleRectNode>
#include <QSGVertexColorMaterial>
#include <QtAlgorithms>
namespace QmlProfiler {
namespace Internal {
class TimelineItemsMaterial : public QSGMaterial {
public:
TimelineItemsMaterial();
QVector2D scale() const;
void setScale(QVector2D scale);
float selectedItem() const;
void setSelectedItem(float selectedItem);
QColor selectionColor() const;
void setSelectionColor(QColor selectionColor);
QSGMaterialType *type() const;
QSGMaterialShader *createShader() const;
private:
QVector2D m_scale;
float m_selectedItem;
QColor m_selectionColor;
};
struct TimelineItemsRenderPassState : public TimelineRenderPass::State {
TimelineItemsRenderPassState() : indexFrom(std::numeric_limits<int>::max()), indexTo(-1) {}
int indexFrom;
int indexTo;
TimelineItemsMaterial collapsedRowMaterial;
};
struct OpaqueColoredPoint2DWithSize {
float x, y, w, h, id;
unsigned char r, g, b, a;
void set(float nx, float ny, float nw, float nh, float nid, uchar nr, uchar ng, uchar nb);
};
struct TimelineItemsGeometry {
// Alternating nodes with with 7 and 4 vertices; and vertex indices are 16bit
static const int maxEventsPerNode = 0xffff * 2 / (7 + 4);
static const QSGGeometry::AttributeSet &opaqueColoredPoint2DWithSize();
TimelineItemsGeometry() : allocatedVertices(0), usedVertices(0), currentY(0), node(0) {}
uint allocatedVertices;
uint usedVertices;
float currentY;
QSGGeometryNode *node;
OpaqueColoredPoint2DWithSize *vertexData();
void allocate(QSGMaterial *material);
void addVertices(float itemTop);
void addEvent(float itemLeft, float itemTop, float itemWidth, float selectionId, uchar red,
uchar green, uchar blue);
};
void TimelineItemsGeometry::addEvent(float itemLeft, float itemTop, float itemWidth,
float selectionId, uchar red, uchar green,
uchar blue)
{
float rowHeight = TimelineModel::defaultRowHeight();
float itemHeight = rowHeight - itemTop;
OpaqueColoredPoint2DWithSize *v = vertexData();
if (currentY == rowHeight) {
// "Z" form, bottom to top
v[usedVertices++].set(itemLeft, rowHeight, -itemWidth, -itemHeight, selectionId, red, green,
blue);
v[usedVertices++].set(itemLeft + itemWidth, rowHeight, itemWidth, -itemHeight, selectionId,
red, green, blue);
v[usedVertices++].set(itemLeft, itemTop, -itemWidth, itemHeight, selectionId, red, green,
blue);
v[usedVertices++].set(itemLeft + itemWidth, itemTop, itemWidth, itemHeight, selectionId,
red, green, blue);
currentY = itemTop;
} else {
if (currentY != itemTop) {
// 3 extra vertices to degenerate the surplus triangles
v[usedVertices++].set(itemLeft, currentY, -itemWidth, rowHeight - currentY, selectionId,
red, green, blue);
v[usedVertices++].set(itemLeft, currentY, -itemWidth, rowHeight - currentY, selectionId,
red, green, blue);
v[usedVertices++].set(itemLeft, itemTop, -itemWidth, itemHeight, selectionId, red,
green, blue);
}
// "Z" form, top to bottom
v[usedVertices++].set(itemLeft, itemTop, -itemWidth, itemHeight, selectionId, red, green,
blue);
v[usedVertices++].set(itemLeft + itemWidth, itemTop, itemWidth, itemHeight, selectionId,
red, green, blue);
v[usedVertices++].set(itemLeft, rowHeight, - itemWidth, -itemHeight, selectionId, red,
green, blue);
v[usedVertices++].set(itemLeft + itemWidth, rowHeight, itemWidth, -itemHeight, selectionId,
red, green, blue);
currentY = rowHeight;
}
}
OpaqueColoredPoint2DWithSize *TimelineItemsGeometry::vertexData()
{
QSGGeometry *geometry = node->geometry();
Q_ASSERT(geometry->attributeCount() == 4);
Q_ASSERT(geometry->sizeOfVertex() == sizeof(OpaqueColoredPoint2DWithSize));
const QSGGeometry::Attribute *attributes = geometry->attributes();
Q_ASSERT(attributes[0].position == 0);
Q_ASSERT(attributes[0].tupleSize == 2);
Q_ASSERT(attributes[0].type == GL_FLOAT);
Q_ASSERT(attributes[1].position == 1);
Q_ASSERT(attributes[1].tupleSize == 2);
Q_ASSERT(attributes[1].type == GL_FLOAT);
Q_ASSERT(attributes[2].position == 2);
Q_ASSERT(attributes[2].tupleSize == 1);
Q_ASSERT(attributes[2].type == GL_FLOAT);
Q_ASSERT(attributes[3].position == 3);
Q_ASSERT(attributes[3].tupleSize == 4);
Q_ASSERT(attributes[3].type == GL_UNSIGNED_BYTE);
return static_cast<OpaqueColoredPoint2DWithSize *>(node->geometry()->vertexData());
}
void TimelineItemsGeometry::allocate(QSGMaterial *material)
{
QSGGeometry *geometry = new QSGGeometry(opaqueColoredPoint2DWithSize(), usedVertices);
geometry->setIndexDataPattern(QSGGeometry::StaticPattern);
geometry->setVertexDataPattern(QSGGeometry::StaticPattern);
node = new QSGGeometryNode;
node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry, true);
node->setMaterial(material);
allocatedVertices = usedVertices;
usedVertices = 0;
currentY = 0;
}
void TimelineItemsGeometry::addVertices(float itemTop)
{
if (currentY == TimelineModel::defaultRowHeight()) {
usedVertices += 4;
currentY = itemTop;
} else {
usedVertices += (currentY != itemTop ? 7 : 4);
currentY = TimelineModel::defaultRowHeight();
}
}
class TimelineExpandedRowNode : public QSGNode {
public:
TimelineItemsMaterial material;
virtual ~TimelineExpandedRowNode() {}
};
static void updateNodes(int from, int to, const TimelineRenderer *renderer,
const TimelineRenderState *parentState, TimelineItemsRenderPassState *state)
{
const TimelineModel *model = renderer->model();
float defaultRowHeight = TimelineModel::defaultRowHeight();
QVector<TimelineItemsGeometry> expandedPerRow(model->expandedRowCount());
QVector<TimelineItemsGeometry> collapsedPerRow(model->collapsedRowCount());
for (int i = from; i < to; ++i) {
qint64 start = qMax(parentState->start(), model->startTime(i));
qint64 end = qMin(parentState->end(), model->startTime(i) + model->duration(i));
if (start > end)
continue;
float itemTop = (1.0 - model->relativeHeight(i)) * defaultRowHeight;
expandedPerRow[model->expandedRow(i)].addVertices(itemTop);
collapsedPerRow[model->collapsedRow(i)].addVertices(itemTop);
}
for (int i = 0; i < model->expandedRowCount(); ++i) {
TimelineItemsGeometry &row = expandedPerRow[i];
if (row.usedVertices > 0) {
row.allocate(&static_cast<TimelineExpandedRowNode *>(state->expandedRows[i])->material);
state->expandedRows[i]->appendChildNode(row.node);
}
}
for (int i = 0; i < model->collapsedRowCount(); ++i) {
TimelineItemsGeometry &row = collapsedPerRow[i];
if (row.usedVertices > 0) {
row.allocate(&state->collapsedRowMaterial);
state->collapsedRows[i]->appendChildNode(row.node);
}
}
for (int i = from; i < to; ++i) {
qint64 start = qMax(parentState->start(), model->startTime(i));
qint64 end = qMin(parentState->end(), model->startTime(i) + model->duration(i));
if (start > end)
continue;
QColor color = model->color(i);
uchar red = color.red();
uchar green = color.green();
uchar blue = color.blue();
float itemWidth = (end - start) * parentState->scale();
float itemLeft = (start - parentState->start()) * parentState->scale();
// This has to be the exact same expression as above, to guarantee determinism.
float itemTop = (1.0 - model->relativeHeight(i)) * defaultRowHeight;
float selectionId = model->selectionId(i);
expandedPerRow[model->expandedRow(i)].addEvent(itemLeft, itemTop, itemWidth, selectionId,
red, green, blue);
collapsedPerRow[model->collapsedRow(i)].addEvent(itemLeft, itemTop, itemWidth, selectionId,
red, green, blue);
}
}
const QSGGeometry::AttributeSet &TimelineItemsGeometry::opaqueColoredPoint2DWithSize()
{
static QSGGeometry::Attribute data[] = {
QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
QSGGeometry::Attribute::create(1, 2, GL_FLOAT),
QSGGeometry::Attribute::create(2, 1, GL_FLOAT),
QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE)
};
static QSGGeometry::AttributeSet attrs = {
4,
sizeof(OpaqueColoredPoint2DWithSize),
data
};
return attrs;
}
const TimelineItemsRenderPass *TimelineItemsRenderPass::instance()
{
static const TimelineItemsRenderPass pass;
return &pass;
}
TimelineRenderPass::State *TimelineItemsRenderPass::update(const TimelineRenderer *renderer,
const TimelineRenderState *parentState,
State *oldState, int indexFrom,
int indexTo, bool stateChanged,
qreal spacing) const
{
Q_UNUSED(stateChanged);
QColor selectionColor = (renderer->selectionLocked() ? QColor(96,0,255) :
QColor(Qt::blue)).lighter(130);
TimelineItemsRenderPassState *state;
if (oldState == 0)
state = new TimelineItemsRenderPassState;
else
state = static_cast<TimelineItemsRenderPassState *>(oldState);
const TimelineModel *model = renderer->model();
float selectedItem = renderer->selectedItem() == -1 ? -1 :
model->selectionId(renderer->selectedItem());
state->collapsedRowMaterial.setScale(QVector2D(spacing / parentState->scale(), 1));
state->collapsedRowMaterial.setSelectedItem(selectedItem);
state->collapsedRowMaterial.setSelectionColor(selectionColor);
if (state->expandedRows.isEmpty()) {
state->expandedRows.reserve(model->expandedRowCount());
state->collapsedRows.reserve(model->collapsedRowCount());
for (int i = 0; i < model->expandedRowCount(); ++i)
state->expandedRows << new TimelineExpandedRowNode;
for (int i = 0; i < model->collapsedRowCount(); ++i)
state->collapsedRows << new QSGNode;
}
if (indexFrom < 0 || indexTo > model->count())
return state;
if (state->indexFrom < state->indexTo) {
if (indexFrom < state->indexFrom) {
for (int i = indexFrom; i < state->indexFrom;
i+= TimelineItemsGeometry::maxEventsPerNode)
updateNodes(i, qMin(i + TimelineItemsGeometry::maxEventsPerNode, state->indexFrom),
renderer, parentState, state);
}
if (indexTo > state->indexTo) {
for (int i = state->indexTo; i < indexTo; i+= TimelineItemsGeometry::maxEventsPerNode)
updateNodes(i, qMin(i + TimelineItemsGeometry::maxEventsPerNode, indexTo), renderer,
parentState, state);
}
} else {
for (int i = indexFrom; i < indexTo; i+= TimelineItemsGeometry::maxEventsPerNode)
updateNodes(i, qMin(i + TimelineItemsGeometry::maxEventsPerNode, indexTo), renderer,
parentState, state);
}
if (model->expanded()) {
for (int row = 0; row < model->expandedRowCount(); ++row) {
TimelineExpandedRowNode *rowNode = static_cast<TimelineExpandedRowNode *>(
state->expandedRows[row]);
rowNode->material.setScale(
QVector2D(spacing / parentState->scale(),
static_cast<qreal>(model->expandedRowHeight(row))) /
static_cast<qreal>(TimelineModel::defaultRowHeight()));
rowNode->material.setSelectedItem(selectedItem);
rowNode->material.setSelectionColor(selectionColor);
}
}
state->indexFrom = qMin(state->indexFrom, indexFrom);
state->indexTo = qMax(state->indexTo, indexTo);
return state;
}
TimelineItemsRenderPass::TimelineItemsRenderPass()
{
}
class TimelineItemsMaterialShader : public QSGMaterialShader
{
public:
TimelineItemsMaterialShader();
virtual void updateState(const RenderState &state, QSGMaterial *newEffect,
QSGMaterial *oldEffect);
virtual char const *const *attributeNames() const;
private:
virtual void initialize();
int m_matrix_id;
int m_scale_id;
int m_selection_color_id;
int m_selected_item_id;
int m_z_range_id;
};
TimelineItemsMaterialShader::TimelineItemsMaterialShader()
: QSGMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qmlprofiler/timelineitems.vert"));
setShaderSourceFile(QOpenGLShader::Fragment,
QStringLiteral(":/qmlprofiler/timelineitems.frag"));
}
void TimelineItemsMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *)
{
if (state.isMatrixDirty()) {
TimelineItemsMaterial *material = static_cast<TimelineItemsMaterial *>(newMaterial);
program()->setUniformValue(m_matrix_id, state.combinedMatrix());
program()->setUniformValue(m_scale_id, material->scale());
program()->setUniformValue(m_selection_color_id, material->selectionColor());
program()->setUniformValue(m_selected_item_id, material->selectedItem());
program()->setUniformValue(m_z_range_id, GLfloat(1.0));
}
}
char const *const *TimelineItemsMaterialShader::attributeNames() const
{
static const char *const attr[] = {"vertexCoord", "rectSize", "selectionId", "vertexColor", 0};
return attr;
}
void TimelineItemsMaterialShader::initialize()
{
m_matrix_id = program()->uniformLocation("matrix");
m_scale_id = program()->uniformLocation("scale");
m_selection_color_id = program()->uniformLocation("selectionColor");
m_selected_item_id = program()->uniformLocation("selectedItem");
m_z_range_id = program()->uniformLocation("_qt_zRange");
}
TimelineItemsMaterial::TimelineItemsMaterial() : m_selectedItem(-1)
{
setFlag(QSGMaterial::Blending, false);
}
QVector2D TimelineItemsMaterial::scale() const
{
return m_scale;
}
void TimelineItemsMaterial::setScale(QVector2D scale)
{
m_scale = scale;
}
float TimelineItemsMaterial::selectedItem() const
{
return m_selectedItem;
}
void TimelineItemsMaterial::setSelectedItem(float selectedItem)
{
m_selectedItem = selectedItem;
}
QColor TimelineItemsMaterial::selectionColor() const
{
return m_selectionColor;
}
void TimelineItemsMaterial::setSelectionColor(QColor selectionColor)
{
m_selectionColor = selectionColor;
}
QSGMaterialType *TimelineItemsMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
QSGMaterialShader *TimelineItemsMaterial::createShader() const
{
return new TimelineItemsMaterialShader;
}
void OpaqueColoredPoint2DWithSize::set(float nx, float ny, float nw, float nh, float nid,
uchar nr, uchar ng, uchar nb) {
x = nx; y = ny; w = nw; h = nh; id = nid;
r = nr; g = ng, b = nb; a = 255;
}
}
}

View File

@@ -0,0 +1,56 @@
/****************************************************************************
**
** 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 TIMELINEITEMSRENDERPASS_H
#define TIMELINEITEMSRENDERPASS_H
#include "timelinerenderer.h"
#include "timelinerenderpass.h"
#include <QSGMaterial>
namespace QmlProfiler {
namespace Internal {
class TimelineItemsRenderPass : public TimelineRenderPass
{
public:
static const TimelineItemsRenderPass *instance();
State *update(const TimelineRenderer *renderer, const TimelineRenderState *parentState,
State *state, int firstIndex, int lastIndex, bool stateChanged,
qreal spacing) const;
protected:
TimelineItemsRenderPass();
};
}
}
#endif // TIMELINEITEMSRENDERPASS_H

View File

@@ -30,6 +30,9 @@
#include "timelinemodel.h"
#include "timelinemodel_p.h"
#include "timelineitemsrenderpass.h"
#include "timelineselectionrenderpass.h"
#include "timelinenotesrenderpass.h"
#include <QLinkedList>
@@ -400,6 +403,15 @@ int TimelineModel::defaultRowHeight()
return TimelineModelPrivate::DefaultRowHeight;
}
QList<const TimelineRenderPass *> TimelineModel::supportedRenderPasses() const
{
QList<const TimelineRenderPass *> passes;
passes << TimelineItemsRenderPass::instance()
<< TimelineSelectionRenderPass::instance()
<< TimelineNotesRenderPass::instance();
return passes;
}
QColor TimelineModel::colorBySelectionId(int index) const
{
return colorByHue(selectionId(index) * TimelineModelPrivate::SelectionIdHueMultiplier);

View File

@@ -35,6 +35,7 @@
#include "qmlprofiler_global.h"
#include "qmlprofilermodelmanager.h"
#include "qmlprofilerdatamodel.h"
#include "timelinerenderpass.h"
#include <QVariant>
#include <QColor>
@@ -119,6 +120,7 @@ public:
Q_INVOKABLE int prevItemByTypeId(int typeId, qint64 time, int currentItem) const;
static int defaultRowHeight();
virtual QList<const TimelineRenderPass *> supportedRenderPasses() const;
signals:
void expandedChanged();

View File

@@ -0,0 +1,259 @@
/****************************************************************************
**
** 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 "timelinenotesrenderpass.h"
#include "timelinerenderstate.h"
#include "qmlprofilernotesmodel.h"
namespace QmlProfiler {
namespace Internal {
struct Point2DWithDistanceFromTop {
float x, y, d;
void set(float nx, float ny, float nd);
};
class NotesMaterial : public QSGMaterial
{
public:
QSGMaterialType *type() const;
QSGMaterialShader *createShader() const;
};
struct NotesGeometry
{
static const int maxNotes;
static const QSGGeometry::AttributeSet &point2DWithDistanceFromTop();
static QSGGeometry *createGeometry(QVector<int> &ids, const TimelineModel *model,
const TimelineRenderState *parentState, bool collapsed);
};
const int NotesGeometry::maxNotes = 0xffff / 2;
struct TimelineNotesRenderPassState : public TimelineRenderPass::State
{
TimelineNotesRenderPassState(int expandedRows);
QSGGeometryNode *createNode();
NotesMaterial material;
QSGGeometry nullGeometry;
};
const QSGGeometry::AttributeSet &NotesGeometry::point2DWithDistanceFromTop()
{
static QSGGeometry::Attribute data[] = {
QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
QSGGeometry::Attribute::create(1, 1, GL_FLOAT),
};
static QSGGeometry::AttributeSet attrs = {
2,
sizeof(Point2DWithDistanceFromTop),
data
};
return attrs;
}
const TimelineNotesRenderPass *TimelineNotesRenderPass::instance()
{
static const TimelineNotesRenderPass pass;
return &pass;
}
TimelineNotesRenderPass::TimelineNotesRenderPass()
{
}
TimelineRenderPass::State *TimelineNotesRenderPass::update(const TimelineRenderer *renderer,
const TimelineRenderState *parentState,
State *oldState, int firstIndex,
int lastIndex, bool stateChanged,
qreal spacing) const
{
Q_UNUSED(firstIndex);
Q_UNUSED(lastIndex);
Q_UNUSED(spacing);
const QmlProfilerNotesModel *notes = renderer->notes();
const TimelineModel *model = renderer->model();
TimelineNotesRenderPassState *state;
if (oldState == 0) {
state = new TimelineNotesRenderPassState(model->expandedRowCount());
} else {
if (!stateChanged && !renderer->notesDirty())
return oldState;
state = static_cast<TimelineNotesRenderPassState *>(oldState);
}
QVector<QVector<int> > expanded(model->expandedRowCount());
QVector<int> collapsed;
for (int i = 0; i < qMin(notes->count(), NotesGeometry::maxNotes); ++i) {
if (notes->timelineModel(i) != model->modelId())
continue;
int timelineIndex = notes->timelineIndex(i);
if (model->startTime(timelineIndex) > parentState->end() ||
model->endTime(timelineIndex) < parentState->start())
continue;
expanded[model->expandedRow(timelineIndex)] << timelineIndex;
collapsed << timelineIndex;
}
QSGGeometryNode *collapsedNode = static_cast<QSGGeometryNode *>(state->collapsedOverlay);
if (collapsed.count() > 0) {
collapsedNode->setGeometry(NotesGeometry::createGeometry(collapsed, model, parentState,
true));
collapsedNode->setFlag(QSGGeometryNode::OwnsGeometry, true);
} else {
collapsedNode->setGeometry(&state->nullGeometry);
collapsedNode->setFlag(QSGGeometryNode::OwnsGeometry, false);
}
for (int row = 0; row < model->expandedRowCount(); ++row) {
QSGGeometryNode *rowNode = static_cast<QSGGeometryNode *>(state->expandedRows[row]);
if (expanded[row].isEmpty()) {
rowNode->setGeometry(&state->nullGeometry);
rowNode->setFlag(QSGGeometryNode::OwnsGeometry, false);
} else {
rowNode->setGeometry(NotesGeometry::createGeometry(expanded[row], model, parentState,
false));
collapsedNode->setFlag(QSGGeometryNode::OwnsGeometry, true);
}
}
return state;
}
TimelineNotesRenderPassState::TimelineNotesRenderPassState(int numExpandedRows) :
nullGeometry(NotesGeometry::point2DWithDistanceFromTop(), 0)
{
material.setFlag(QSGMaterial::Blending, true);
expandedRows.reserve(numExpandedRows);
for (int i = 0; i < numExpandedRows; ++i)
expandedRows << createNode();
collapsedOverlay = createNode();
}
QSGGeometryNode *TimelineNotesRenderPassState::createNode()
{
QSGGeometryNode *node = new QSGGeometryNode;
node->setGeometry(&nullGeometry);
node->setMaterial(&material);
return node;
}
QSGGeometry *NotesGeometry::createGeometry(QVector<int> &ids, const TimelineModel *model,
const TimelineRenderState *parentState, bool collapsed)
{
float rowHeight = TimelineModel::defaultRowHeight();
QSGGeometry *geometry = new QSGGeometry(point2DWithDistanceFromTop(),
ids.count() * 2);
geometry->setDrawingMode(GL_LINES);
geometry->setLineWidth(3);
Point2DWithDistanceFromTop *v =
static_cast<Point2DWithDistanceFromTop *>(geometry->vertexData());
for (int i = 0; i < ids.count(); ++i) {
int timelineIndex = ids[i];
float horizontalCenter = ((model->startTime(timelineIndex) +
model->endTime(timelineIndex)) / (qint64)2 -
parentState->start()) * parentState->scale();
float verticalStart = (collapsed ? (model->collapsedRow(timelineIndex) + 0.1) : 0.1) *
rowHeight;
float verticalEnd = verticalStart + 0.8 * rowHeight;
v[i * 2].set(horizontalCenter, verticalStart, 0);
v[i * 2 + 1].set(horizontalCenter, verticalEnd, 1);
}
return geometry;
}
class NotesMaterialShader : public QSGMaterialShader
{
public:
NotesMaterialShader();
virtual void updateState(const RenderState &state, QSGMaterial *newEffect,
QSGMaterial *oldEffect);
virtual char const *const *attributeNames() const;
private:
virtual void initialize();
int m_matrix_id;
int m_z_range_id;
};
NotesMaterialShader::NotesMaterialShader()
: QSGMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qmlprofiler/notes.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qmlprofiler/notes.frag"));
}
void NotesMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *)
{
if (state.isMatrixDirty()) {
program()->setUniformValue(m_matrix_id, state.combinedMatrix());
program()->setUniformValue(m_z_range_id, GLfloat(1.0));
}
}
char const *const *NotesMaterialShader::attributeNames() const
{
static const char *const attr[] = {"vertexCoord", "distanceFromTop", 0};
return attr;
}
void NotesMaterialShader::initialize()
{
m_matrix_id = program()->uniformLocation("matrix");
m_z_range_id = program()->uniformLocation("_qt_zRange");
}
QSGMaterialType *NotesMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
QSGMaterialShader *NotesMaterial::createShader() const
{
return new NotesMaterialShader;
}
void Point2DWithDistanceFromTop::set(float nx, float ny, float nd)
{
x = nx; y = ny; d = nd;
}
}
}

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** 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 TIMELINENOTESRENDERPASS_H
#define TIMELINENOTESRENDERPASS_H
#include "timelinerenderer.h"
#include <QSGMaterial>
namespace QmlProfiler {
namespace Internal {
class TimelineNotesRenderPass : public TimelineRenderPass
{
public:
static const TimelineNotesRenderPass *instance();
State *update(const TimelineRenderer *renderer, const TimelineRenderState *parentState,
State *oldState, int firstIndex, int lastIndex, bool stateChanged,
qreal spacing) const;
private:
TimelineNotesRenderPass();
};
}
}
#endif // TIMELINENOTESRENDERPASS_H

View File

@@ -29,65 +29,80 @@
****************************************************************************/
#include "timelinerenderer.h"
#include "timelinerenderpass.h"
#include "qmlprofilernotesmodel.h"
#include "timelineitemsrenderpass.h"
#include "qmlprofilerbindingloopsrenderpass.h"
#include "timelineselectionrenderpass.h"
#include "timelinenotesrenderpass.h"
#include <QElapsedTimer>
#include <QQmlContext>
#include <QQmlProperty>
#include <QTimer>
#include <QPixmap>
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QVarLengthArray>
#include <QSGTransformNode>
#include <QSGSimpleRectNode>
#include <math.h>
using namespace QmlProfiler;
using namespace QmlProfiler::Internal;
TimelineRenderer::TimelineRenderer(QQuickPaintedItem *parent) :
QQuickPaintedItem(parent), m_spacing(0), m_spacedDuration(0),
m_model(0), m_zoomer(0), m_notes(0), m_selectedItem(-1), m_selectionLocked(true)
TimelineRenderer::TimelineRenderer(QQuickItem *parent) :
QQuickItem(parent), m_model(0), m_zoomer(0), m_notes(0),
m_selectedItem(-1), m_selectionLocked(true), m_modelDirty(false),
m_rowHeightsDirty(false), m_rowCountsDirty(false), m_lastState(0)
{
setFlag(QQuickItem::ItemHasContents);
resetCurrentSelection();
setAcceptedMouseButtons(Qt::LeftButton);
setAcceptHoverEvents(true);
connect(this, &QQuickItem::yChanged, this, &TimelineRenderer::requestPaint);
connect(this, &QQuickItem::xChanged, this, &TimelineRenderer::requestPaint);
connect(this, &QQuickItem::widthChanged, this, &TimelineRenderer::requestPaint);
connect(this, &QQuickItem::heightChanged, this, &TimelineRenderer::requestPaint);
}
void TimelineRenderer::setModel(QmlProfilerTimelineModel *model)
void TimelineRenderer::setModel(TimelineModel *model)
{
if (m_model == model)
return;
if (m_model) {
disconnect(m_model, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
disconnect(m_model, SIGNAL(hiddenChanged()), this, SLOT(requestPaint()));
disconnect(m_model, SIGNAL(expandedRowHeightChanged(int,int)), this, SLOT(requestPaint()));
disconnect(m_model, SIGNAL(expandedChanged()), this, SLOT(update()));
disconnect(m_model, SIGNAL(hiddenChanged()), this, SLOT(update()));
disconnect(m_model, SIGNAL(expandedRowHeightChanged(int,int)),
this, SLOT(setRowHeightsDirty()));
disconnect(m_model, SIGNAL(emptyChanged()), this, SLOT(setModelDirty()));
disconnect(m_model, SIGNAL(expandedRowCountChanged()), this, SLOT(setRowCountsDirty()));
disconnect(m_model, SIGNAL(collapsedRowCountChanged()), this, SLOT(setRowCountsDirty()));
}
m_model = model;
if (m_model) {
connect(m_model, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
connect(m_model, SIGNAL(hiddenChanged()), this, SLOT(requestPaint()));
connect(m_model, SIGNAL(expandedRowHeightChanged(int,int)), this, SLOT(requestPaint()));
connect(m_model, SIGNAL(expandedChanged()), this, SLOT(update()));
connect(m_model, SIGNAL(hiddenChanged()), this, SLOT(update()));
connect(m_model, SIGNAL(expandedRowHeightChanged(int,int)),
this, SLOT(setRowHeightsDirty()));
connect(m_model, SIGNAL(emptyChanged()), this, SLOT(setModelDirty()));
connect(m_model, SIGNAL(expandedRowCountChanged()), this, SLOT(setRowCountsDirty()));
connect(m_model, SIGNAL(collapsedRowCountChanged()), this, SLOT(setRowCountsDirty()));
m_renderPasses = model->supportedRenderPasses();
}
setModelDirty();
setRowHeightsDirty();
setRowCountsDirty();
emit modelChanged(m_model);
update();
}
void TimelineRenderer::setZoomer(TimelineZoomControl *zoomer)
{
if (zoomer != m_zoomer) {
if (m_zoomer != 0)
disconnect(m_zoomer, SIGNAL(rangeChanged(qint64,qint64)), this, SLOT(requestPaint()));
disconnect(m_zoomer, SIGNAL(windowChanged(qint64,qint64)), this, SLOT(update()));
m_zoomer = zoomer;
if (m_zoomer != 0)
connect(m_zoomer, SIGNAL(rangeChanged(qint64,qint64)), this, SLOT(requestPaint()));
connect(m_zoomer, SIGNAL(windowChanged(qint64,qint64)), this, SLOT(update()));
emit zoomerChanged(zoomer);
update();
}
@@ -99,47 +114,31 @@ void TimelineRenderer::setNotes(QmlProfilerNotesModel *notes)
return;
if (m_notes)
disconnect(m_notes, &QmlProfilerNotesModel::changed, this, &TimelineRenderer::requestPaint);
disconnect(m_notes, &QmlProfilerNotesModel::changed,
this, &TimelineRenderer::setNotesDirty);
m_notes = notes;
if (m_notes)
connect(m_notes, &QmlProfilerNotesModel::changed, this, &TimelineRenderer::requestPaint);
connect(m_notes, &QmlProfilerNotesModel::changed,
this, &TimelineRenderer::setNotesDirty);
emit notesChanged(m_notes);
update();
}
void TimelineRenderer::requestPaint()
bool TimelineRenderer::modelDirty() const
{
update();
return m_modelDirty;
}
inline void TimelineRenderer::getItemXExtent(int i, int &currentX, int &itemWidth)
bool TimelineRenderer::notesDirty() const
{
qint64 start = m_model->startTime(i) - m_zoomer->rangeStart();
return m_notesDirty;
}
// avoid integer overflows by using floating point for width calculations. m_spacing is qreal,
// too, so for some intermediate calculations we have to use floats anyway.
qreal rawWidth;
if (start > 0) {
currentX = static_cast<int>(start * m_spacing);
rawWidth = m_model->duration(i) * m_spacing;
} else {
currentX = -OutOfScreenMargin;
// Explicitly round the "start" part down, away from 0, to match the implicit rounding of
// currentX in the > 0 case. If we don't do that we get glitches where a pixel is added if
// the element starts outside the screen and subtracted if it starts inside the screen.
rawWidth = m_model->duration(i) * m_spacing +
floor(start * m_spacing) + OutOfScreenMargin;
}
if (rawWidth < MinimumItemWidth) {
currentX -= static_cast<int>((MinimumItemWidth - rawWidth) / 2);
itemWidth = MinimumItemWidth;
} else if (rawWidth > m_spacedDuration - static_cast<qreal>(currentX - OutOfScreenMargin)) {
itemWidth = static_cast<int>(m_spacedDuration) - currentX + OutOfScreenMargin;
} else {
itemWidth = static_cast<int>(rawWidth);
}
bool TimelineRenderer::rowHeightsDirty() const
{
return m_rowHeightsDirty;
}
void TimelineRenderer::resetCurrentSelection()
@@ -150,26 +149,156 @@ void TimelineRenderer::resetCurrentSelection()
m_currentSelection.eventIndex = -1;
}
void TimelineRenderer::paint(QPainter *p)
TimelineRenderState *TimelineRenderer::findRenderState()
{
if (height() <= 0 || m_zoomer->rangeDuration() <= 0)
return;
int newLevel = 0;
int newOffset = 0;
int level;
int offset;
m_spacing = width() / m_zoomer->rangeDuration();
m_spacedDuration = width() + 2 * OutOfScreenMargin;
qint64 newStart = m_zoomer->traceStart();
qint64 newEnd = m_zoomer->traceEnd();
qint64 start;
qint64 end;
do {
level = newLevel;
offset = newOffset;
start = newStart;
end = newEnd;
p->setPen(Qt::transparent);
newLevel = level + 1;
qint64 range = m_zoomer->traceDuration() >> newLevel;
newOffset = (m_zoomer->windowStart() - m_zoomer->traceStart() + range / 2) / range;
newStart = m_zoomer->traceStart() + newOffset * range - range / 2;
newEnd = newStart + range;
} while (newStart < m_zoomer->windowStart() && newEnd > m_zoomer->windowEnd());
int lastIndex = m_model->lastIndex(m_zoomer->rangeEnd());
if (lastIndex >= 0 && lastIndex < m_model->count()) {
int firstIndex = m_model->firstIndex(m_zoomer->rangeStart());
if (firstIndex >= 0) {
drawItemsToPainter(p, firstIndex, lastIndex);
drawSelectionBoxes(p, firstIndex, lastIndex);
drawBindingLoopMarkers(p, firstIndex, lastIndex);
if (m_renderStates.length() <= level)
m_renderStates.resize(level + 1);
if (m_renderStates[level].length() <= offset)
m_renderStates[level].resize(offset + 1);
TimelineRenderState *state = m_renderStates[level][offset];
if (state == 0) {
state = new TimelineRenderState(start, end, 1.0 / static_cast<qreal>(SafeFloatMax),
m_renderPasses.size());
m_renderStates[level][offset] = state;
}
return state;
}
QSGNode *TimelineRenderer::updatePaintNode(QSGNode *node,
UpdatePaintNodeData *updatePaintNodeData)
{
Q_UNUSED(updatePaintNodeData)
if (!m_model || m_model->hidden() || m_model->isEmpty() || m_zoomer->windowDuration() <= 0) {
delete node;
return 0;
} else if (node == 0) {
node = new QSGTransformNode;
}
qreal spacing = width() / m_zoomer->windowDuration();
if (m_modelDirty || m_rowCountsDirty) {
node->removeAllChildNodes();
foreach (QVector<TimelineRenderState *> stateVector, m_renderStates)
qDeleteAll(stateVector);
m_renderStates.clear();
m_lastState = 0;
}
TimelineRenderState *state = findRenderState();
int lastIndex = m_model->lastIndex(m_zoomer->windowEnd());
int firstIndex = m_model->firstIndex(m_zoomer->windowStart());
for (int i = 0; i < m_renderPasses.length(); ++i)
state->setPassState(i, m_renderPasses[i]->update(this, state, state->passState(i),
firstIndex, lastIndex + 1,
state != m_lastState, spacing));
if (state->isEmpty()) { // new state
for (int pass = 0; pass < m_renderPasses.length(); ++pass) {
const TimelineRenderPass::State *passState = state->passState(pass);
if (!passState)
continue;
if (passState->expandedOverlay)
state->expandedOverlayRoot()->appendChildNode(passState->expandedOverlay);
if (passState->collapsedOverlay)
state->collapsedOverlayRoot()->appendChildNode(passState->collapsedOverlay);
}
int row = 0;
for (int i = 0; i < m_model->expandedRowCount(); ++i) {
QSGTransformNode *rowNode = new QSGTransformNode;
for (int pass = 0; pass < m_renderPasses.length(); ++pass) {
const TimelineRenderPass::State *passState = state->passState(pass);
if (passState && passState->expandedRows.length() > row) {
QSGNode *rowChildNode = passState->expandedRows[row];
if (rowChildNode)
rowNode->appendChildNode(rowChildNode);
}
}
drawNotes(p);
state->expandedRowRoot()->appendChildNode(rowNode);
++row;
}
for (int row = 0; row < m_model->collapsedRowCount(); ++row) {
QSGTransformNode *rowNode = new QSGTransformNode;
QMatrix4x4 matrix;
matrix.translate(0, row * TimelineModel::defaultRowHeight(), 0);
rowNode->setMatrix(matrix);
for (int pass = 0; pass < m_renderPasses.length(); ++pass) {
const TimelineRenderPass::State *passState = state->passState(pass);
if (passState && passState->collapsedRows.length() > row) {
QSGNode *rowChildNode = passState->collapsedRows[row];
if (rowChildNode)
rowNode->appendChildNode(rowChildNode);
}
}
state->collapsedRowRoot()->appendChildNode(rowNode);
}
}
if (m_rowHeightsDirty || state != m_lastState) {
int row = 0;
qreal offset = 0;
for (QSGNode *rowNode = state->expandedRowRoot()->firstChild(); rowNode != 0;
rowNode = rowNode->nextSibling()) {
qreal rowHeight = m_model->expandedRowHeight(row++);
QMatrix4x4 matrix;
matrix.translate(0, offset, 0);
matrix.scale(1, rowHeight / TimelineModel::defaultRowHeight(), 1);
offset += rowHeight;
static_cast<QSGTransformNode *>(rowNode)->setMatrix(matrix);
}
}
m_modelDirty = false;
m_notesDirty = false;
m_rowCountsDirty = false;
m_rowHeightsDirty = false;
m_lastState = state;
QSGNode *rowNode = m_model->expanded() ? state->expandedRowRoot() : state->collapsedRowRoot();
QSGNode *overlayNode = m_model->expanded() ? state->expandedOverlayRoot() :
state->collapsedOverlayRoot();
QMatrix4x4 matrix;
matrix.translate((state->start() - m_zoomer->windowStart()) * spacing, 0, 0);
matrix.scale(spacing / state->scale(), 1, 1);
QSGTransformNode *transform = static_cast<QSGTransformNode *>(node);
transform->setMatrix(matrix);
if (node->firstChild() != rowNode || node->lastChild() != overlayNode) {
node->removeAllChildNodes();
node->appendChildNode(rowNode);
node->appendChildNode(overlayNode);
}
return node;
}
void TimelineRenderer::mousePressEvent(QMouseEvent *event)
@@ -177,204 +306,6 @@ void TimelineRenderer::mousePressEvent(QMouseEvent *event)
Q_UNUSED(event);
}
void TimelineRenderer::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
{
p->save();
p->setPen(Qt::transparent);
for (int i = fromIndex; i <= toIndex; i++) {
int currentX, currentY, itemWidth, itemHeight;
int rowNumber = m_model->row(i);
currentY = m_model->rowOffset(rowNumber) - y();
if (currentY >= height())
continue;
int rowHeight = m_model->rowHeight(rowNumber);
itemHeight = rowHeight * m_model->relativeHeight(i);
currentY += rowHeight - itemHeight;
if (currentY + itemHeight < 0)
continue;
getItemXExtent(i, currentX, itemWidth);
// normal events
p->setBrush(m_model->color(i));
p->drawRect(currentX, currentY, itemWidth, itemHeight);
}
p->restore();
}
void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
{
const uint strongLineWidth = 3;
const uint lightLineWidth = 2;
static const QColor strongColor = Qt::blue;
static const QColor lockedStrongColor = QColor(96,0,255);
static const QColor lightColor = strongColor.lighter(130);
static const QColor lockedLightColor = lockedStrongColor.lighter(130);
if (m_selectedItem == -1)
return;
int id = m_model->selectionId(m_selectedItem);
p->save();
QPen strongPen(m_selectionLocked ? lockedStrongColor : strongColor, strongLineWidth);
strongPen.setJoinStyle(Qt::MiterJoin);
QPen lightPen(m_selectionLocked ? lockedLightColor : lightColor, lightLineWidth);
lightPen.setJoinStyle(Qt::MiterJoin);
p->setPen(lightPen);
p->setBrush(Qt::transparent);
int currentX, currentY, itemWidth;
for (int i = fromIndex; i <= toIndex; i++) {
if (m_model->selectionId(i) != id)
continue;
int row = m_model->row(i);
int rowHeight = m_model->rowHeight(row);
int itemHeight = rowHeight * m_model->relativeHeight(i);
currentY = m_model->rowOffset(row) + rowHeight - itemHeight - y();
if (currentY + itemHeight < 0 || height() < currentY)
continue;
getItemXExtent(i, currentX, itemWidth);
if (i == m_selectedItem)
p->setPen(strongPen);
// Draw the lines at the right offsets. The lines have a width and we don't want them to
// bleed into the previous or next row as that may belong to a different model and get cut
// off.
int lineWidth = p->pen().width();
itemWidth -= lineWidth;
itemHeight -= lineWidth;
currentX += lineWidth / 2;
currentY += lineWidth / 2;
// If it's only a line or point, draw it left/top aligned.
if (itemWidth > 0) {
if (itemHeight > 0) {
p->drawRect(currentX, currentY, itemWidth, itemHeight);
} else {
p->drawLine(currentX, currentY + itemHeight, currentX + itemWidth,
currentY + itemHeight);
}
} else if (itemHeight > 0) {
p->drawLine(currentX + itemWidth, currentY, currentX + itemWidth,
currentY + itemHeight);
} else {
p->drawPoint(currentX + itemWidth, currentY + itemHeight);
}
if (i == m_selectedItem)
p->setPen(lightPen);
}
p->restore();
}
void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
{
int destindex;
int xfrom, xto, width;
int yfrom, yto;
int radius = 10;
QPen shadowPen = QPen(QColor("grey"),2);
QPen markerPen = QPen(QColor("orange"),2);
QBrush shadowBrush = QBrush(QColor("grey"));
QBrush markerBrush = QBrush(QColor("orange"));
p->save();
for (int i = fromIndex; i <= toIndex; i++) {
destindex = m_model->bindingLoopDest(i);
if (destindex >= 0) {
// to
getItemXExtent(destindex, xto, width);
xto += width / 2;
yto = getYPosition(destindex) + m_model->rowHeight(m_model->row(destindex)) / 2 - y();
// from
getItemXExtent(i, xfrom, width);
xfrom += width / 2;
yfrom = getYPosition(i) + m_model->rowHeight(m_model->row(i)) / 2 - y();
// radius (derived from width of origin event)
radius = 5;
if (radius * 2 > width)
radius = width / 2;
if (radius < 2)
radius = 2;
int shadowoffset = 2;
if ((yfrom + radius + shadowoffset < 0 && yto + radius + shadowoffset < 0) ||
(yfrom - radius >= height() && yto - radius >= height()))
continue;
// shadow
p->setPen(shadowPen);
p->setBrush(shadowBrush);
p->drawEllipse(QPoint(xfrom, yfrom + shadowoffset), radius, radius);
p->drawEllipse(QPoint(xto, yto + shadowoffset), radius, radius);
p->drawLine(QPoint(xfrom, yfrom + shadowoffset), QPoint(xto, yto + shadowoffset));
// marker
p->setPen(markerPen);
p->setBrush(markerBrush);
p->drawEllipse(QPoint(xfrom, yfrom), radius, radius);
p->drawEllipse(QPoint(xto, yto), radius, radius);
p->drawLine(QPoint(xfrom, yfrom), QPoint(xto, yto));
}
}
p->restore();
}
void TimelineRenderer::drawNotes(QPainter *p)
{
static const QColor shadowBrush("grey");
static const QColor markerBrush("orange");
static const int annotationWidth = 4;
static const int annotationHeight1 = 16;
static const int annotationHeight2 = 4;
static const int annotationSpace = 4;
static const int shadowOffset = 2;
for (int i = 0; i < m_notes->count(); ++i) {
int modelId = m_notes->timelineModel(i);
if (modelId == -1 || modelId != m_model->modelId())
continue;
int eventIndex = m_notes->timelineIndex(i);
int row = m_model->row(eventIndex);
int rowHeight = m_model->rowHeight(row);
int currentY = m_model->rowOffset(row) - y();
if (currentY + rowHeight < 0 || height() < currentY)
continue;
int currentX;
int itemWidth;
getItemXExtent(eventIndex, currentX, itemWidth);
// shadow
int annoX = currentX + (itemWidth - annotationWidth) / 2;
int annoY = currentY + rowHeight / 2 -
(annotationHeight1 + annotationHeight2 + annotationSpace) / 2;
p->setBrush(shadowBrush);
p->drawRect(annoX, annoY + shadowOffset, annotationWidth, annotationHeight1);
p->drawRect(annoX, annoY + annotationHeight1 + annotationSpace + shadowOffset,
annotationWidth, annotationHeight2);
// marker
p->setBrush(markerBrush);
p->drawRect(annoX, annoY, annotationWidth, annotationHeight1);
p->drawRect(annoX, annoY + annotationHeight1 + annotationSpace,
annotationWidth, annotationHeight2);
}
}
int TimelineRenderer::rowFromPosition(int y)
{
if (!m_model->expanded())
@@ -432,15 +363,15 @@ void TimelineRenderer::manageClicked()
void TimelineRenderer::manageHovered(int mouseX, int mouseY)
{
qint64 duration = m_zoomer->rangeDuration();
qint64 duration = m_zoomer->windowDuration();
if (duration <= 0)
return;
// Make the "selected" area 3 pixels wide by adding/subtracting 1 to catch very narrow events.
qint64 startTime = (mouseX - 1) * duration / width() + m_zoomer->rangeStart();
qint64 endTime = (mouseX + 1) * duration / width() + m_zoomer->rangeStart();
qint64 startTime = (mouseX - 1) * duration / width() + m_zoomer->windowStart();
qint64 endTime = (mouseX + 1) * duration / width() + m_zoomer->windowStart();
qint64 exactTime = (startTime + endTime) / 2;
int row = rowFromPosition(mouseY + y());
int row = rowFromPosition(mouseY);
// already covered? Only recheck selectionLocked and make sure m_selectedItem is correct.
if (m_currentSelection.eventIndex != -1 &&
@@ -489,22 +420,11 @@ void TimelineRenderer::manageHovered(int mouseX, int mouseY)
void TimelineRenderer::clearData()
{
m_spacing = 0;
m_spacedDuration = 0;
resetCurrentSelection();
setSelectedItem(-1);
setSelectionLocked(true);
}
int TimelineRenderer::getYPosition(int index) const
{
Q_ASSERT(m_model);
if (index >= m_model->count())
return 0;
return m_model->rowOffset(m_model->row(index));
}
void TimelineRenderer::selectNextFromSelectionId(int selectionId)
{
setSelectedItem(m_model->nextItemBySelectionId(selectionId, m_zoomer->rangeStart(),
@@ -516,3 +436,27 @@ void TimelineRenderer::selectPrevFromSelectionId(int selectionId)
setSelectedItem(m_model->prevItemBySelectionId(selectionId, m_zoomer->rangeStart(),
m_selectedItem));
}
void TimelineRenderer::setModelDirty()
{
m_modelDirty = true;
update();
}
void TimelineRenderer::setRowHeightsDirty()
{
m_rowHeightsDirty = true;
update();
}
void TimelineRenderer::setNotesDirty()
{
m_notesDirty = true;
update();
}
void TimelineRenderer::setRowCountsDirty()
{
m_rowCountsDirty = true;
}

View File

@@ -31,26 +31,31 @@
#ifndef TIMELINERENDERER_H
#define TIMELINERENDERER_H
#include <QQuickPaintedItem>
#include <QJSValue>
#include <QSGTransformNode>
#include <QQuickItem>
#include "timelinezoomcontrol.h"
#include "timelinemodel.h"
#include "qmlprofilernotesmodel.h"
#include "timelinerenderpass.h"
namespace QmlProfiler {
namespace Internal {
class TimelineRenderer : public QQuickPaintedItem
class TimelineRenderPass;
class TimelineRenderState;
class TimelineRenderer : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QmlProfiler::QmlProfilerTimelineModel *model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(QmlProfiler::TimelineModel *model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(QmlProfiler::TimelineZoomControl *zoomer READ zoomer WRITE setZoomer NOTIFY zoomerChanged)
Q_PROPERTY(QmlProfiler::QmlProfilerNotesModel *notes READ notes WRITE setNotes NOTIFY notesChanged)
Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged)
Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged)
public:
explicit TimelineRenderer(QQuickPaintedItem *parent = 0);
explicit TimelineRenderer(QQuickItem *parent = 0);
bool selectionLocked() const
{
@@ -62,8 +67,8 @@ public:
return m_selectedItem;
}
QmlProfilerTimelineModel *model() const { return m_model; }
void setModel(QmlProfilerTimelineModel *model);
TimelineModel *model() const { return m_model; }
void setModel(TimelineModel *model);
TimelineZoomControl *zoomer() const { return m_zoomer; }
void setZoomer(TimelineZoomControl *zoomer);
@@ -71,13 +76,18 @@ public:
QmlProfilerNotesModel *notes() const { return m_notes; }
void setNotes(QmlProfilerNotesModel *notes);
Q_INVOKABLE int getYPosition(int index) const;
bool modelDirty() const;
bool notesDirty() const;
bool rowHeightsDirty() const;
Q_INVOKABLE void selectNextFromSelectionId(int selectionId);
Q_INVOKABLE void selectPrevFromSelectionId(int selectionId);
// TODO: We could add some Q_INVOKABLE functions to enable or disable render passes when the the
// need arises.
signals:
void modelChanged(TimelineModel *model);
void modelChanged(const TimelineModel *model);
void zoomerChanged(TimelineZoomControl *zoomer);
void notesChanged(QmlProfilerNotesModel *notes);
@@ -87,7 +97,6 @@ signals:
public slots:
void clearData();
void requestPaint();
void setSelectedItem(int itemIndex)
{
@@ -107,34 +116,32 @@ public slots:
}
}
private slots:
void setModelDirty();
void setRowHeightsDirty();
void setNotesDirty();
void setRowCountsDirty();
protected:
virtual void paint(QPainter *);
virtual QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void hoverMoveEvent(QHoverEvent *event);
private:
void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex);
void drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex);
void drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex);
void drawNotes(QPainter *p);
int rowFromPosition(int y);
void manageClicked();
void manageHovered(int mouseX, int mouseY);
static const int OutOfScreenMargin = 3; // margin to make sure the rectangles stay invisible
static const int MinimumItemWidth = 3;
static const int SafeFloatMax = 1 << 12;
inline void getItemXExtent(int i, int &currentX, int &itemWidth);
void resetCurrentSelection();
qreal m_spacing;
qreal m_spacedDuration;
TimelineRenderState *findRenderState();
QmlProfilerTimelineModel *m_model;
TimelineModel *m_model;
TimelineZoomControl *m_zoomer;
QmlProfilerNotesModel *m_notes;
@@ -147,6 +154,14 @@ private:
int m_selectedItem;
bool m_selectionLocked;
bool m_modelDirty;
bool m_rowHeightsDirty;
bool m_notesDirty;
bool m_rowCountsDirty;
QList<const TimelineRenderPass *> m_renderPasses;
QVector<QVector<TimelineRenderState *> > m_renderStates;
TimelineRenderState *m_lastState;
};
} // namespace Internal

View File

@@ -0,0 +1,37 @@
/****************************************************************************
**
** 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 "timelinerenderpass.h"
namespace QmlProfiler {
namespace Internal {
}
}

View File

@@ -0,0 +1,62 @@
/****************************************************************************
**
** 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 TIMELINERENDERPASS_H
#define TIMELINERENDERPASS_H
#include <QVector>
QT_FORWARD_DECLARE_CLASS(QSGNode)
namespace QmlProfiler {
namespace Internal {
class TimelineRenderer;
class TimelineRenderState;
class TimelineRenderPass {
public:
struct State {
State() : expandedOverlay(0), collapsedOverlay(0) {}
QVector<QSGNode *> expandedRows;
QVector<QSGNode *> collapsedRows;
QSGNode *expandedOverlay;
QSGNode *collapsedOverlay;
};
virtual ~TimelineRenderPass() {}
virtual State *update(const TimelineRenderer *renderer, const TimelineRenderState *parentState,
State *state, int indexFrom, int indexTo, bool stateChanged,
qreal spacing) const = 0;
};
}
}
#endif // TIMELINERENDERPASS_H

View File

@@ -0,0 +1,112 @@
/****************************************************************************
**
** 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 "timelinerenderstate.h"
namespace QmlProfiler {
namespace Internal {
TimelineRenderState::TimelineRenderState(qint64 start, qint64 end, qreal scale, int numPasses) :
m_expandedRowRoot(new QSGNode), m_collapsedRowRoot(new QSGNode),
m_expandedOverlayRoot(new QSGNode), m_collapsedOverlayRoot(new QSGNode),
m_start(start), m_end(end), m_scale(scale), m_passes(numPasses)
{
m_expandedRowRoot->setFlag(QSGNode::OwnedByParent, false);
m_collapsedRowRoot->setFlag(QSGNode::OwnedByParent, false);
m_expandedOverlayRoot->setFlag(QSGNode::OwnedByParent, false);
m_collapsedOverlayRoot->setFlag(QSGNode::OwnedByParent, false);
}
TimelineRenderState::~TimelineRenderState()
{
delete m_expandedRowRoot;
delete m_collapsedRowRoot;
delete m_expandedOverlayRoot;
delete m_collapsedOverlayRoot;
}
qint64 TimelineRenderState::start() const
{
return m_start;
}
qint64 TimelineRenderState::end() const
{
return m_end;
}
qreal TimelineRenderState::scale() const
{
return m_scale;
}
QSGNode *TimelineRenderState::expandedRowRoot() const
{
return m_expandedRowRoot;
}
QSGNode *TimelineRenderState::collapsedRowRoot() const
{
return m_collapsedRowRoot;
}
QSGNode *TimelineRenderState::expandedOverlayRoot() const
{
return m_expandedOverlayRoot;
}
QSGNode *TimelineRenderState::collapsedOverlayRoot() const
{
return m_collapsedOverlayRoot;
}
bool TimelineRenderState::isEmpty() const
{
return m_collapsedRowRoot->childCount() == 0 && m_expandedRowRoot->childCount() == 0 &&
m_collapsedOverlayRoot->childCount() == 0 && m_expandedOverlayRoot->childCount() == 0;
}
TimelineRenderPass::State *TimelineRenderState::passState(int i)
{
return m_passes[i];
}
const TimelineRenderPass::State *TimelineRenderState::passState(int i) const
{
return m_passes[i];
}
void TimelineRenderState::setPassState(int i, TimelineRenderPass::State *state)
{
m_passes[i] = state;
}
}
}

View File

@@ -0,0 +1,77 @@
/****************************************************************************
**
** 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 TIMELINERENDERSTATE_H
#define TIMELINERENDERSTATE_H
#include <QSGNode>
#include "timelinerenderpass.h"
namespace QmlProfiler {
namespace Internal {
class TimelineRenderState {
public:
TimelineRenderState(qint64 start, qint64 end, qreal scale, int numPasses);
~TimelineRenderState();
qint64 start() const;
qint64 end() const;
qreal scale() const;
TimelineRenderPass::State *passState(int i);
const TimelineRenderPass::State *passState(int i) const;
void setPassState(int i, TimelineRenderPass::State *state);
QSGNode *expandedRowRoot() const;
QSGNode *collapsedRowRoot() const;
QSGNode *expandedOverlayRoot() const;
QSGNode *collapsedOverlayRoot() const;
bool isEmpty() const;
private:
QSGNode *m_expandedRowRoot;
QSGNode *m_collapsedRowRoot;
QSGNode *m_expandedOverlayRoot;
QSGNode *m_collapsedOverlayRoot;
qint64 m_start;
qint64 m_end;
qreal m_scale; // "native" scale, this stays the same through the life time of a state
QVector<TimelineRenderPass::State *> m_passes;
};
}
}
#endif // TIMELINERENDERSTATE_H

View File

@@ -0,0 +1,138 @@
/****************************************************************************
**
** 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 "timelineselectionrenderpass.h"
#include <QSGSimpleRectNode>
namespace QmlProfiler {
namespace Internal {
QSGSimpleRectNode *createSelectionNode()
{
QSGSimpleRectNode *selectionNode = new QSGSimpleRectNode;
selectionNode->material()->setFlag(QSGMaterial::Blending, false);
selectionNode->setRect(0, 0, 0, 0);
QSGSimpleRectNode *selectionChild = new QSGSimpleRectNode;
selectionChild->material()->setFlag(QSGMaterial::Blending, false);
selectionChild->setRect(0, 0, 0, 0);
selectionNode->appendChildNode(selectionChild);
return selectionNode;
}
TimelineRenderPass::State *TimelineSelectionRenderPass::update(const TimelineRenderer *renderer,
const TimelineRenderState *parentState, State *state, int firstIndex, int lastIndex,
bool stateChanged, qreal spacing) const
{
Q_UNUSED(stateChanged);
if (state == 0)
state = new TimelineRenderPass::State;
if (state->expandedOverlay == 0) {
state->expandedOverlay = createSelectionNode();
state->collapsedOverlay = createSelectionNode();
}
const TimelineModel *model = renderer->model();
QSGSimpleRectNode *selectionNode = static_cast<QSGSimpleRectNode *>(model->expanded() ?
state->expandedOverlay :
state->collapsedOverlay);
QSGSimpleRectNode *child = static_cast<QSGSimpleRectNode *>(selectionNode->firstChild());
int selectedItem = renderer->selectedItem();
if (selectedItem != -1 && selectedItem >= firstIndex && selectedItem < lastIndex) {
qreal top = 0;
qreal height = 0;
if (model->expanded()) {
int row = model->expandedRow(selectedItem);
int rowHeight = model->expandedRowHeight(row);
height = rowHeight * model->relativeHeight(selectedItem);
top = model->expandedRowOffset(row) - height + rowHeight;
} else {
int row = model->collapsedRow(selectedItem);
height = TimelineModel::defaultRowHeight() * model->relativeHeight(selectedItem);
top = TimelineModel::defaultRowHeight() * (row + 1) - height;
}
qreal left = qMax(model->startTime(selectedItem) - parentState->start(), (qint64)0);
qreal right = qMin(parentState->end() - parentState->start(),
model->endTime(selectedItem) - parentState->start());
// Construct from upper left and lower right for better precision. When constructing from
// left and width the error on the left border is inherited by the right border. Like this
// they're independent.
QRectF outer(QPointF(floor(left * parentState->scale()), top),
QPointF(ceil(right * parentState->scale()), top + height));
float scaleConversion = parentState->scale() / spacing;
float missing = 3.0 - outer.width() / scaleConversion;
if (missing > 0.0) {
outer.setLeft(outer.left() - missing * scaleConversion / 2.0);
outer.setRight(outer.right() + missing * scaleConversion / 2.0);
}
missing = 3.0 - outer.height();
if (missing > 0.0)
outer.setTop(outer.top() - missing);
selectionNode->setRect(outer);
selectionNode->setColor(renderer->selectionLocked() ? QColor(96,0,255) : Qt::blue);
float childWidthThreshold = 6.0 * scaleConversion;
if (outer.width() > childWidthThreshold && outer.height() > 6.0) {
// Construct from upper left and lower right for better precision
child->setRect(QRectF(QPointF(outer.left() + childWidthThreshold / 2.0,
outer.top() + 3.0),
QPointF(outer.right() - childWidthThreshold / 2.0,
outer.bottom() - 3.0)));
child->setColor(model->color(selectedItem));
} else {
child->setRect(0, 0, 0, 0);
}
} else {
selectionNode->setRect(0, 0, 0, 0);
child->setRect(0, 0, 0, 0);
}
return state;
}
const TimelineSelectionRenderPass *TimelineSelectionRenderPass::instance()
{
static const TimelineSelectionRenderPass pass;
return &pass;
}
TimelineSelectionRenderPass::TimelineSelectionRenderPass()
{
}
}
}

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** 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 TIMELINESELECTIONRENDERPASS_H
#define TIMELINESELECTIONRENDERPASS_H
#include "timelinerenderer.h"
#include "timelinerenderpass.h"
#include "timelinerenderstate.h"
namespace QmlProfiler {
namespace Internal {
class TimelineSelectionRenderPass : public TimelineRenderPass
{
public:
static const TimelineSelectionRenderPass *instance();
State *update(const TimelineRenderer *renderer, const TimelineRenderState *parentState,
State *state, int firstIndex, int lastIndex, bool stateChanged,
qreal spacing) const;
protected:
TimelineSelectionRenderPass();
};
}
}
#endif // TIMELINESELECTIONRENDERPASS_H

View File

@@ -53,7 +53,7 @@ class TimelineZoomControl : public QObject {
Q_PROPERTY(bool windowLocked READ windowLocked WRITE setWindowLocked NOTIFY windowLockedChanged)
public:
static const qint64 MAX_ZOOM_FACTOR = 1 << 12;
static const qint64 MAX_ZOOM_FACTOR = 1 << 10;
TimelineZoomControl(QObject *parent = 0);
qint64 traceStart() const { return m_traceStart; }