1 #include "graphwidget.h"
2 #include "timelinewidget.h"
3 #include "profiledialog.h"
10 #include <QApplication>
12 typedef trace::Profile::Call Call;
13 typedef trace::Profile::Frame Frame;
14 typedef trace::Profile::Program Program;
16 GraphWidget::GraphWidget(QWidget *parent)
21 m_axisForeground(Qt::black),
22 m_axisBackground(Qt::lightGray)
24 setBackgroundRole(QPalette::Base);
25 setAutoFillBackground(true);
26 setMouseTracking(true);
28 m_graphGradientGpu.setColorAt(0.9, QColor(255, 0, 0));
29 m_graphGradientGpu.setColorAt(0.0, QColor(255, 200, 200));
31 m_graphGradientCpu.setColorAt(0.9, QColor(0, 0, 255));
32 m_graphGradientCpu.setColorAt(0.0, QColor(200, 200, 255));
37 * Setup graph data from profile results
39 void GraphWidget::setProfile(trace::Profile* profile, GraphType type)
45 /* Find longest call to use as y axis */
46 if (m_type == GraphGpu) {
47 for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
48 const Call& call = *itr;
50 if (call.gpuDuration > m_maxTime) {
51 m_maxTime = call.gpuDuration;
55 for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
56 const Call& call = *itr;
58 if (call.cpuDuration > m_maxTime) {
59 m_maxTime = call.cpuDuration;
65 m_maxCall = m_profile->calls.size();
68 m_maxCallWidth = m_maxCall - m_minCall;
71 m_callWidth = m_maxCallWidth;
78 * Slot to synchronise with other graph views
80 void GraphWidget::changeView(int call, int width)
89 * Calculate the maxTime variable when model is updated
91 void GraphWidget::update()
95 if (m_type == GraphGpu) {
96 for (int i = m_call; i < m_call + m_callWidth; ++i) {
97 const Call& call = m_profile->calls[i];
99 if (call.gpuDuration > m_maxTime) {
100 m_maxTime = call.gpuDuration;
104 for (int i = m_call; i < m_call + m_callWidth; ++i) {
105 const Call& call = m_profile->calls[i];
107 if (call.cpuDuration > m_maxTime) {
108 m_maxTime = call.cpuDuration;
118 * Find the call at (x, y) position
120 const Call* GraphWidget::callAtPosition(const QPoint& pos)
122 int left, right, size;
129 int posX = qMax(0, pos.x() - m_axisWidth);
130 int posY = qMax(0, pos.y() - m_axisHeight);
132 time = ((m_graphHeight - posY) / (double)m_graphHeight) * m_maxTime;
133 time -= (2 * m_maxTime) / m_graphHeight;
135 size = m_callWidth / (double)m_graphWidth;
137 left = m_call + (posX / (double)m_graphWidth) * m_callWidth;
138 left = qMax(m_minCall, left - size);
140 right = qMin(m_maxCall - 1, left + size * 2);
142 if (m_type == GraphGpu) {
143 const Call* longest = NULL;
145 for (int i = left; i <= right; ++i) {
146 const Call& call = m_profile->calls[i];
148 if (call.pixels >= 0) {
149 if (!longest || call.gpuDuration > longest->gpuDuration) {
155 if (longest && time < longest->gpuDuration) {
159 const Call* longest = NULL;
161 for (int i = left; i <= right; ++i) {
162 const Call& call = m_profile->calls[i];
164 if (!longest || call.cpuDuration > longest->cpuDuration) {
169 if (longest && time < longest->cpuDuration) {
178 void GraphWidget::mousePressEvent(QMouseEvent *e)
180 if (e->button() == Qt::LeftButton) {
181 m_mousePressPosition = e->pos();
182 m_mousePressCall = m_call;
187 void GraphWidget::mouseMoveEvent(QMouseEvent *e)
193 if (e->pos().x() < m_axisWidth || e->pos().y() < m_axisHeight) {
197 if (e->buttons().testFlag(Qt::LeftButton)) {
198 /* Horizontal scroll */
199 double dcdx = m_callWidth / (double)m_graphWidth;
200 dcdx *= m_mousePressPosition.x() - e->pos().x();
202 m_call = m_mousePressCall + dcdx;
203 m_call = qBound(m_minCall, m_call, m_maxCall - m_callWidth);
205 emit viewChanged(m_call, m_callWidth);
209 const Call* call = callAtPosition(e->pos());
211 if (e->button() == Qt::NoButton && call) {
213 text = QString::fromStdString(call->name);
214 text += QString("\nCall: %1").arg(call->no);
215 text += QString("\nCPU Duration: %1").arg(getTimeString(call->cpuDuration));
217 if (call->pixels >= 0) {
218 text += QString("\nGPU Duration: %1").arg(getTimeString(call->gpuDuration));
219 text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call->pixels));
222 QToolTip::showText(e->globalPos(), text);
224 QToolTip::hideText();
229 void GraphWidget::wheelEvent(QWheelEvent *e)
235 if (e->pos().x() < m_axisWidth || e->pos().y() < m_axisHeight) {
239 int zoomPercent = 10;
241 /* If holding Ctrl key then zoom 2x faster */
242 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
246 /* Zoom view by adjusting width */
247 double dt = m_callWidth;
248 double size = dt * -e->delta();
250 /* Zoom deltas normally come in increments of 120 */
251 size /= 120 * (100 / zoomPercent);
254 m_callWidth = qBound(m_minCallWidth, m_callWidth, m_maxCallWidth);
256 /* Scroll view to zoom around mouse */
258 dt *= e->x() - m_axisWidth;
261 m_call = dt + m_call;
262 m_call = qBound(m_minCall, m_call, m_maxCall - m_callWidth);
264 emit viewChanged(m_call, m_callWidth);
269 void GraphWidget::mouseDoubleClickEvent(QMouseEvent *e)
271 const Call* call = callAtPosition(e->pos());
274 emit jumpToCall(call->no);
279 void GraphWidget::resizeEvent(QResizeEvent *e)
281 m_graphWidth = qMax(0, width() - m_axisWidth);
282 m_graphHeight = qMax(0, height() - m_axisHeight);
284 m_graphGradientGpu.setStart(0, m_graphHeight);
285 m_graphGradientCpu.setStart(0, m_graphHeight);
290 * Draw the vertical axis of time
292 void GraphWidget::paintVerticalAxis(QPainter& painter)
294 int height = painter.fontMetrics().height();
295 int ticks = m_graphHeight / (height * 2);
297 double step = m_maxTime / (double)ticks;
298 double step10 = qPow(10.0, qFloor(qLn(step) / qLn(10.0)));
299 step = qFloor((step / step10) * 2) * (step10 / 2);
301 painter.resetTransform();
302 painter.translate(0, m_axisHeight);
303 painter.setPen(m_axisForeground);
305 for (double tick = 0; tick <= m_maxTime; tick += step) {
306 int y = m_graphHeight - ((tick / m_maxTime) * m_graphHeight);
308 painter.drawLine(m_axisWidth - 8, y, m_axisWidth - 1, y);
311 qBound(0, y - height / 2, m_graphHeight - height),
314 Qt::AlignRight | Qt::AlignVCenter,
315 getTimeString(tick, m_maxTime));
321 * Draw horizontal axis of frame numbers
323 void GraphWidget::paintHorizontalAxis(QPainter& painter)
325 double dxdc = m_graphWidth / (double)m_callWidth;
326 double scroll = dxdc * m_call;
327 int lastLabel = -9999;
329 painter.resetTransform();
330 painter.fillRect(0, 0, width(), m_axisHeight, m_axisBackground);
331 painter.fillRect(0, 0, m_axisWidth, height(), m_axisBackground);
333 painter.setPen(m_axisForeground);
334 painter.drawLine(0, m_axisHeight - 1, width(), m_axisHeight - 1);
335 painter.drawLine(m_axisWidth - 1, 0, m_axisWidth - 1, height());
337 painter.translate(m_axisWidth, 0);
339 for (std::vector<Frame>::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) {
340 static const int padding = 4;
341 const Frame& frame = *itr;
345 if (frame.calls.begin > m_call + m_callWidth) {
349 if (frame.calls.end < m_call) {
353 double left = dxdc * frame.calls.begin;
354 double right = dxdc * frame.calls.end;
355 QString text = QString("%1").arg(frame.no);
357 width = painter.fontMetrics().width(text) + padding * 2;
359 if (left + width > scroll)
362 /* Draw a frame number if we have space since the last one */
363 if (left - lastLabel > width) {
364 lastLabel = left + width;
369 if (left < scroll && right - left > width) {
370 if (right - scroll > width) {
373 textX = right - scroll - width;
376 textX = left - scroll;
379 /* Draw frame number and major ruler marking */
380 painter.drawText(textX + padding, 0, width - padding, m_axisHeight - 5, Qt::AlignLeft | Qt::AlignVCenter, text);
381 painter.drawLine(left - scroll, m_axisHeight / 2, left - scroll, m_axisHeight - 1);
384 /* Draw a minor ruler marking */
385 painter.drawLine(left - scroll, m_axisHeight - (m_axisHeight / 4), left - scroll, m_axisHeight - 1);
391 void GraphWidget::paintEvent(QPaintEvent *e)
397 QPainter painter(this);
401 paintHorizontalAxis(painter);
402 paintVerticalAxis(painter);
405 painter.resetTransform();
406 painter.fillRect(0, 0, m_axisWidth - 1, m_axisHeight - 1, Qt::lightGray);
408 if (m_type == GraphGpu) {
409 painter.drawText(0, 0, m_axisWidth, m_axisHeight, Qt::AlignHCenter | Qt::AlignVCenter, "GPU");
411 painter.drawText(0, 0, m_axisWidth, m_axisHeight, Qt::AlignHCenter | Qt::AlignVCenter, "CPU");
415 if (m_type == GraphGpu) {
416 brush = QBrush(m_graphGradientGpu);
418 brush = QBrush(m_graphGradientCpu);
421 painter.setBrush(brush);
422 painter.setPen(QPen(brush, 1));
423 painter.translate(m_axisWidth, m_axisHeight);
426 double dydt = m_graphHeight / (double)m_maxTime;
427 double dxdc = m_graphWidth / (double)m_callWidth;
430 /* Less than 1 pixel per call, draw the longest call in a pixel */
434 if (m_type == GraphGpu) {
435 for (int i = m_call; i < m_call + m_callWidth; ++i) {
436 const Call& call = m_profile->calls[i];
438 if (call.gpuDuration > longest) {
439 longest = call.gpuDuration;
444 if (lastX != (int)x) {
445 painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (longest * dydt));
451 for (int i = m_call; i < m_call + m_callWidth; ++i) {
452 const Call& call = m_profile->calls[i];
454 if (call.cpuDuration > longest) {
455 longest = call.cpuDuration;
460 if (lastX != (int)x) {
461 painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (longest * dydt));
468 /* At least 1 pixel per call, draw rects */
469 if (m_type == GraphGpu) {
470 for (int i = m_call; i < m_call + m_callWidth; ++i) {
471 const Call& call = m_profile->calls[i];
473 if (call.pixels >= 0) {
474 int y = qMax<int>(1, call.gpuDuration * dydt);
475 painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), brush);
481 for (int i = m_call; i < m_call + m_callWidth; ++i) {
482 const Call& call = m_profile->calls[i];
484 int y = qMax<int>(1, call.cpuDuration * dydt);
485 painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), brush);
493 #include "graphwidget.moc"