Motiv
Marvelous OTF2 Traces Interactive Visualizer
Loading...
Searching...
No Matches
TimelineView.cpp
1/*
2 * Marvelous OTF2 Traces Interactive Visualizer (MOTIV)
3 * Copyright (C) 2023 Florian Gallrein, Björn Gehrke
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include "TimelineView.hpp"
19#include "src/ui/views/CommunicationIndicator.hpp"
20#include "src/ui/views/SlotIndicator.hpp"
21#include "src/ui/Constants.hpp"
22#include "CollectiveCommunicationIndicator.hpp"
23
24#include <QGraphicsRectItem>
25#include <QApplication>
26#include <QWheelEvent>
27
28TimelineView::TimelineView(TraceDataProxy *data, QWidget *parent) : QGraphicsView(parent), data(data) {
29 auto scene = new QGraphicsScene();
30 this->setAlignment(Qt::AlignTop | Qt::AlignLeft);
31 this->setAutoFillBackground(false);
32 this->setStyleSheet("background: transparent");
33 this->setScene(scene);
34 this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
35
36 // @formatter:off
37 connect(this->data, SIGNAL(selectionChanged(types::TraceTime,types::TraceTime)), this, SLOT(updateView()));
38 connect(this->data, SIGNAL(filterChanged(Filter)), this, SLOT(updateView()));
39 // @formatter:on
40}
41
42
43void TimelineView::populateScene(QGraphicsScene *scene) {
44 auto width = scene->width();
45 auto selection = this->data->getSelection();
46 auto runtime = selection->getRuntime().count();
47 auto runtimeR = static_cast<qreal>(runtime);
48 auto begin = this->data->getBegin().count();
49 auto beginR = static_cast<qreal>(begin);
50 auto end = begin + runtime;
51 auto endR = static_cast<qreal>(end);
52
53 QPen arrowPen(Qt::black, 1);
54 QPen collectiveCommunicationPen(colors::COLOR_COLLECTIVE_COMMUNICATION, 2);
55
56
57 auto onTimedElementSelected = [this](TimedElement *element) { this->data->setTimeElementSelection(element); };
58 auto onTimedElementDoubleClicked = [this](TimedElement *element) {
59 this->data->setSelection(element->getStartTime(), element->getEndTime());
60 };
61
62
63 auto top = 20;
64 auto ROW_HEIGHT = 30;
65 for (const auto &item: selection->getSlots()) {
66 // Display slots
67 for (const auto &slot: item.second) {
68 if (!(slot->getKind() & data->getSettings()->getFilter().getSlotKinds())) continue;
69
70 auto region = slot->region;
71 auto regionName = region->name();
72 auto regionNameStr = regionName.str();
73 auto startTime = slot->startTime.count();
74 auto endTime = slot->endTime.count();
75
76
77 // Ensures slots starting before `begin` (like main) are considered to start at begin
78 auto effectiveStartTime = qMax(begin, startTime);
79 // Ensures slots ending after `end` (like main) are considered to end at end
80 auto effectiveEndTime = qMin(end, endTime);
81
82 auto slotBeginPos = (static_cast<qreal>(effectiveStartTime - begin) / static_cast<qreal>(runtime)) * width;
83 auto slotRuntime = static_cast<qreal>(effectiveEndTime - effectiveStartTime);
84 auto rectWidth = (slotRuntime / static_cast<qreal>(runtime)) * width;
85
86 QRectF rect(slotBeginPos, top, qMax(rectWidth, 5.0), ROW_HEIGHT);
87 auto rectItem = new SlotIndicator(rect, slot);
88 rectItem->setOnDoubleClick(onTimedElementDoubleClicked);
89 rectItem->setOnSelected(onTimedElementSelected);
90 rectItem->setToolTip(regionNameStr.c_str());
91
92 // Determine color based on name
93 QColor rectColor;
94 switch (slot->getKind()) {
95 case ::MPI:
96 rectColor = colors::COLOR_SLOT_MPI;
97 rectItem->setZValue(layers::Z_LAYER_SLOTS_MIN_PRIORITY + 2);
98 break;
99 case ::OpenMP:
100 rectColor = colors::COLOR_SLOT_OPEN_MP;
101 rectItem->setZValue(layers::Z_LAYER_SLOTS_MIN_PRIORITY + 1);
102 break;
103 case ::None:
104 case ::Plain:
105 default:
106 rectColor = colors::COLOR_SLOT_PLAIN;
107 rectItem->setZValue(layers::Z_LAYER_SLOTS_MIN_PRIORITY + 0);
108 break;
109 }
110 rectItem->setBrush(rectColor);
111 scene->addItem(rectItem);
112 }
113
114 top += ROW_HEIGHT;
115 }
116
117 for (const auto &communication: selection->getCommunications()) {
118 const CommunicationEvent *startEvent = communication->getStartEvent();
119 auto startEventEnd = static_cast<qreal>(startEvent->getEndTime().count());
120 auto startEventStart = static_cast<qreal>(startEvent->getStartTime().count());
121
122
123 const CommunicationEvent *endEvent = communication->getEndEvent();
124 auto endEventEnd = static_cast<qreal>(startEvent->getEndTime().count());
125 auto endEventStart = static_cast<qreal>(startEvent->getStartTime().count());
126
127
128 auto fromTime = startEventStart + (startEventEnd - startEventStart) / 2;
129 auto effectiveFromTime = qMax(beginR, fromTime) - beginR;
130
131 auto toTime = endEventStart + (endEventEnd - endEventStart) / 2;
132 auto effectiveToTime = qMin(endR, toTime) - beginR;
133
134 auto fromRank = startEvent->getLocation()->ref().get();
135 auto toRank = endEvent->getLocation()->ref().get();
136
137 auto fromX = effectiveFromTime / runtimeR * width;
138 auto fromY = static_cast<qreal>(fromRank * ROW_HEIGHT) + .5 * ROW_HEIGHT + 20;
139
140 auto toX = effectiveToTime / runtimeR * width;
141 auto toY = static_cast<qreal> (toRank * ROW_HEIGHT) + .5 * ROW_HEIGHT + 20;
142
143 auto arrow = new CommunicationIndicator(communication, fromX, fromY, toX, toY);
144 arrow->setOnSelected(onTimedElementSelected);
145 arrow->setOnDoubleClick(onTimedElementDoubleClicked);
146 arrow->setPen(arrowPen);
147 arrow->setZValue(layers::Z_LAYER_P2P_COMMUNICATIONS);
148 scene->addItem(arrow);
149 }
150
151 for (const auto &communication: selection->getCollectiveCommunications()) {
152 auto fromTime = static_cast<qreal>(communication->getStartTime().count());
153 auto effectiveFromTime = qMax(beginR, fromTime) - beginR;
154
155 auto toTime = static_cast<qreal>(communication->getEndTime().count());
156 auto effectiveToTime = qMin(endR, toTime) - beginR;
157
158 auto fromX = (effectiveFromTime / runtimeR) * width;
159 auto fromY = 10;
160
161 auto toX = (effectiveToTime / runtimeR) * width;
162 auto toY = top + 10;
163
164 auto rectItem = new CollectiveCommunicationIndicator(communication);
165 rectItem->setOnSelected(onTimedElementSelected);
166 rectItem->setRect(QRectF(QPointF(fromX, fromY), QPointF(toX, toY)));
167 rectItem->setPen(collectiveCommunicationPen);
168 rectItem->setZValue(layers::Z_LAYER_COLLECTIVE_COMMUNICATIONS);
169 scene->addItem(rectItem);
170 }
171
172}
173
174
175void TimelineView::resizeEvent(QResizeEvent *event) {
176 this->updateView();
177 QGraphicsView::resizeEvent(event);
178}
179
181 // TODO it might be more performant to keep track of items and add/remove new/leaving items and resizing them
182 this->scene()->clear();
183
184 auto ROW_HEIGHT = 30;
185 auto sceneHeight = this->data->getSelection()->getSlots().size() * ROW_HEIGHT;
186 auto sceneRect = this->rect();
187 sceneRect.setHeight(sceneHeight);
188
189 this->scene()->setSceneRect(sceneRect);
190 this->populateScene(this->scene());
191}
192
193void TimelineView::wheelEvent(QWheelEvent *event) {
194 // Calculation according to https://doc.qt.io/qt-6/qwheelevent.html#angleDelta:
195 // @c angleDelta is in eights of a degree and most mouse wheels work in steps of 15 degrees.
196 QPoint numDegrees = event->angleDelta() / 8;
197
198 if (!numDegrees.isNull() && QApplication::keyboardModifiers() & (Qt::CTRL | Qt::SHIFT)) {
199 // See documentation and comment above
200 QPoint numSteps = numDegrees / 15;
201 auto stepSize = data->getSelection()->getRuntime() / data->getSettings()->getZoomQuotient();
202 auto deltaDuration = stepSize * numSteps.y();
203 auto delta = static_cast<double>(deltaDuration.count());
204
205 types::TraceTime newBegin;
206 types::TraceTime newEnd;
207 if (QApplication::keyboardModifiers() == Qt::CTRL) {
208 // Calculate the position of the mouse relative to the scene to zoom to where the mouse is pointed
209 auto originFactor = event->scenePosition().x() / this->scene()->width();
210
211 auto leftDelta = types::TraceTime(static_cast<long>(originFactor * 2 * delta));
212 auto rightDelta = types::TraceTime(static_cast<long>((1 - originFactor) * 2 * delta));
213
214 newBegin = data->getSelection()->getStartTime() + leftDelta;
215 newEnd = data->getSelection()->getStartTime() + data->getSelection()->getRuntime() - rightDelta;
216 } else {
217 // Calculate new absolute times (might be negative or to large)
218 auto newBeginAbs = data->getSelection()->getStartTime() - deltaDuration;
219 auto newEndAbs = data->getSelection()->getStartTime() + data->getSelection()->getRuntime() - deltaDuration;
220
221 // Limit the times to their boundaries (0 for start and end of entire trace for end)
222 auto newBeginBounded = qMax(newBeginAbs, types::TraceTime(0));
223 auto newEndBounded = qMin(newEndAbs, data->getTotalRuntime());
224
225 // If one time exceeds the bounds reject the changes
226 newBegin = qMin(newBeginBounded, newEndBounded - data->getSelection()->getRuntime());
227 newEnd = qMax(newEndBounded, newBeginBounded + data->getSelection()->getRuntime());
228 }
229
230 data->setSelection(newBegin, newEnd);
231 event->accept();
232 }
233
234 QGraphicsView::wheelEvent(event);
235}
Indicator for collective communications.
Abstract class for generic Communication events.
virtual otf2::definition::location * getLocation() const =0
otf2::chrono::duration getEndTime() const override=0
otf2::chrono::duration getStartTime() const override=0
Class containing options to filter the view.
Definition: Filter.hpp:28
SlotKind getSlotKinds() const
Returns the kinds of slots that should be rendered.
Definition: Filter.cpp:20
Indicator for collective communications.
A base class for all elements with a start and end time.
virtual types::TraceTime getStartTime() const =0
Returns the start time of the current object.
void updateView()
Updates the view to reflect the current selection of the TraceDataProxy.
void resizeEvent(QResizeEvent *event) override
TimelineView(TraceDataProxy *data, QWidget *parent=nullptr)
Creates a new instance of the TimelineView class.
void wheelEvent(QWheelEvent *event) override
Model class providing access to data and pub/sub architecture of change events.
ViewSettings * getSettings() const
Returns the current view settings.
types::TraceTime getTotalRuntime() const
types::TraceTime getBegin() const
Returns the selected start time.
Trace * getSelection() const
Returns the current selection.
void setTimeElementSelection(TimedElement *newSlot)
void setSelection(types::TraceTime newBegin, types::TraceTime newEnd)
virtual otf2::chrono::duration getRuntime() const =0
Returns the runtime of the current trace.
virtual std::map< otf2::definition::location_group *, Range< Slot * >, LocationGroupCmp > getSlots() const =0
Returns a map of slots of the current trace.
int getZoomQuotient() const
Returns the reciprocal of the current zoom factor.
Filter getFilter() const
Returns the current filter.