apitracemodel.cpp
argumentseditor.cpp
glsledit.cpp
- graphwidget.cpp
imageviewer.cpp
jumpwidget.cpp
mainwindow.cpp
traceloader.cpp
traceprocess.cpp
trimprocess.cpp
- timelinewidget.cpp
vertexdatainterpreter.cpp
+ graphing/frameaxiswidget.cpp
+ graphing/graphwidget.cpp
+ graphing/graphaxiswidget.cpp
+ graphing/graphview.cpp
+ graphing/heatmapview.cpp
+ graphing/heatmapverticalaxiswidget.cpp
+ graphing/histogramview.cpp
+ graphing/timeaxiswidget.cpp
)
qt4_automoc(${qapitrace_SRCS})
--- /dev/null
+#ifndef CALLDURATIONGRAPH_H
+#define CALLDURATIONGRAPH_H
+
+#include "graphing/graphwidget.h"
+#include "trace_profiler.hpp"
+#include "profiling.h"
+
+/**
+ * Wrapper for call duration graphs.
+ *
+ * This implements the transformSelectionIn and transformSelectionOut to
+ * allow sharing the selection between the graphs and the heatmap as they
+ * are using different scales. The duration graphs have call.no on the X-axis
+ * whereas the heatmap has time on the X axis.
+ */
+class CallDurationGraph : public GraphWidget {
+public:
+ CallDurationGraph(QWidget* parent = 0) :
+ GraphWidget(parent),
+ m_profile(NULL)
+ {
+ }
+
+ void setProfile(const trace::Profile* profile)
+ {
+ m_profile = profile;
+ }
+
+protected:
+ /* Transform from time-based horizontal selection to call no based. */
+ virtual SelectionState transformSelectionIn(SelectionState state)
+ {
+ if (!m_profile || state.type != SelectionState::Horizontal) {
+ return state;
+ }
+
+ qint64 timeStart = state.start;
+ qint64 timeEnd = state.end;
+
+ std::vector<trace::Profile::Call>::const_iterator itr;
+
+ itr =
+ Profiling::binarySearchTimespan<
+ trace::Profile::Call,
+ &trace::Profile::Call::cpuStart,
+ &trace::Profile::Call::cpuDuration>
+ (m_profile->calls.begin(), m_profile->calls.end(), timeStart, true);
+
+ state.start = itr - m_profile->calls.begin();
+
+ itr =
+ Profiling::binarySearchTimespan<
+ trace::Profile::Call,
+ &trace::Profile::Call::cpuStart,
+ &trace::Profile::Call::cpuDuration>
+ (m_profile->calls.begin(), m_profile->calls.end(), timeEnd, true);
+
+ state.end = itr - m_profile->calls.begin();
+
+ return state;
+ }
+
+ virtual SelectionState transformSelectionOut(SelectionState state)
+ {
+ if (!m_profile || state.type != SelectionState::Horizontal) {
+ return state;
+ }
+
+ qint64 start = qMax<qint64>(0, state.start);
+ qint64 end = qMin<qint64>(state.end, m_profile->calls.size());
+
+ /* Call based -> time based */
+ state.start = m_profile->calls[start].cpuStart;
+ state.end = m_profile->calls[end].cpuStart + m_profile->calls[end].cpuDuration;
+
+ return state;
+ }
+
+private:
+ const trace::Profile* m_profile;
+};
+
+/* Data provider for call duration graphs */
+class CallDurationDataProvider : public GraphDataProvider {
+public:
+ CallDurationDataProvider(const trace::Profile* profile, bool gpu) :
+ m_gpu(gpu),
+ m_profile(profile),
+ m_selectionState(NULL)
+ {
+ }
+
+ virtual qint64 size() const
+ {
+ return m_profile ? m_profile->calls.size() : 0;
+ }
+
+ virtual bool selected(qint64 index) const
+ {
+ if (m_selectionState) {
+ if (m_selectionState->type == SelectionState::Horizontal) {
+ if (m_selectionState->start <= index && index < m_selectionState->end) {
+ return true;
+ }
+ } else if (m_selectionState->type == SelectionState::Vertical) {
+ return m_profile->calls[index].program == m_selectionState->start;
+ }
+ }
+
+ return false;
+ }
+
+ virtual void setSelectionState(SelectionState* state)
+ {
+ m_selectionState = state;
+ }
+
+ virtual qint64 value(qint64 index) const
+ {
+ if (m_gpu) {
+ return m_profile->calls[index].gpuDuration;
+ } else {
+ return m_profile->calls[index].cpuDuration;
+ }
+ }
+
+ virtual void itemDoubleClicked(qint64 index) const
+ {
+ if (!m_profile) {
+ return;
+ }
+
+ if (index < 0 || index >= m_profile->calls.size()) {
+ return;
+ }
+
+ const trace::Profile::Call& call = m_profile->calls[index];
+ Profiling::jumpToCall(call.no);
+ }
+
+ virtual QString itemTooltip(qint64 index) const
+ {
+ if (!m_profile) {
+ return QString();
+ }
+
+ if (index < 0 || index >= m_profile->calls.size()) {
+ return QString();
+ }
+
+ const trace::Profile::Call& call = m_profile->calls[index];
+
+ QString text;
+ text = QString::fromStdString(call.name);
+ text += QString("\nCall: %1").arg(call.no);
+ text += QString("\nCPU Duration: %1").arg(Profiling::getTimeString(call.cpuDuration));
+
+ if (call.pixels >= 0) {
+ text += QString("\nGPU Duration: %1").arg(Profiling::getTimeString(call.gpuDuration));
+ text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call.pixels));
+ text += QString("\nProgram: %1").arg(call.program);
+ }
+
+ return text;
+ }
+
+private:
+ bool m_gpu;
+ const trace::Profile* m_profile;
+ SelectionState* m_selectionState;
+};
+
+#endif
--- /dev/null
+#include "frameaxiswidget.h"
+
+#include <QPainter>
+
+FrameAxisWidget::FrameAxisWidget(QWidget* parent) :
+ GraphAxisWidget(parent),
+ m_data(NULL)
+{
+ setSelectable(GraphAxisWidget::Range);
+}
+
+void FrameAxisWidget::setDataProvider(FrameDataProvider* data)
+{
+ delete m_data;
+ m_data = data;
+}
+
+void FrameAxisWidget::paintEvent(QPaintEvent *)
+{
+ if (!m_data || m_orientation != GraphAxisWidget::Horizontal) {
+ /* TODO: Vertical axis support */
+ return;
+ }
+
+ QPainter painter(this);
+ painter.setPen(Qt::black);
+ painter.setBrush(Qt::lightGray);
+ painter.drawRect(0, 0, width() - 1, height() - 1);
+
+ qint64 range = m_valueEnd - m_valueBegin;
+ double dxdv = width() / (double)range;
+ double scroll = dxdv * m_valueBegin;
+ int lastLabel = -9999;
+
+ /* Iterate over frames, drawing a label when there is space to do so */
+ for (unsigned i = 0; i < m_data->size(); ++i) {
+ static const int padding = 4;
+ qint64 start = m_data->frameStart(i);
+ qint64 end = m_data->frameEnd(i);
+ bool visible = false;
+
+ if (start > m_valueEnd) {
+ break;
+ }
+
+ if (end < m_valueBegin) {
+ visible = false;
+ }
+
+ double left = dxdv * start;
+ double right = dxdv * end;
+ QString text = QString("%1").arg(i);
+
+ int width = painter.fontMetrics().width(text) + padding * 2;
+
+ if (right > scroll) {
+ visible = true;
+ }
+
+ if (left - lastLabel > width) {
+ lastLabel = left + width;
+
+ if (visible) {
+ int textX;
+
+ if (left < scroll && right - left > width) {
+ if (right - scroll > width) {
+ textX = 0;
+ } else {
+ textX = right - scroll - width;
+ }
+ } else {
+ textX = left - scroll;
+ }
+
+ painter.drawText(textX + padding, 0, width - padding, height() - 5, Qt::AlignLeft | Qt::AlignVCenter, text);
+ painter.drawLine(left - scroll, height() / 2, left - scroll, height() - 1);
+ }
+ } else if (visible) {
+ painter.drawLine(left - scroll, height() * 3/4.0, left - scroll, height() - 1);
+ }
+ }
+
+ /* Draw selection */
+ if (hasSelection()) {
+ double left = (dxdv * m_selectionState->start) - scroll;
+ double right = (dxdv * m_selectionState->end) - scroll;
+
+ painter.setPen(Qt::green);
+
+ if (left >= 0 && left <= width()) {
+ painter.drawLine(left, 0, left, height());
+ }
+
+ if (right >= 0 && right <= width()) {
+ painter.drawLine(right, 0, right, height());
+ }
+ }
+}
--- /dev/null
+#ifndef FRAMEAXISWIDGET_H
+#define FRAMEAXISWIDGET_H
+
+#include "graphaxiswidget.h"
+
+class FrameDataProvider {
+public:
+ /* Number of frames */
+ virtual unsigned size() const = 0;
+
+ /* Start and end values of frame */
+ virtual qint64 frameStart(unsigned frame) const = 0;
+ virtual qint64 frameEnd(unsigned frame) const = 0;
+};
+
+/**
+ * A generic axis which will draw frame numbers over a period of values.
+ * Frames designated by start / end values.
+ */
+class FrameAxisWidget : public GraphAxisWidget {
+public:
+ FrameAxisWidget(QWidget* parent = 0);
+
+ void setDataProvider(FrameDataProvider* data);
+
+ virtual void paintEvent(QPaintEvent *e);
+
+protected:
+ FrameDataProvider* m_data;
+};
+
+#endif
--- /dev/null
+#include "graphaxiswidget.h"
+
+#include <QMouseEvent>
+
+GraphAxisWidget::GraphAxisWidget(QWidget* parent) :
+ QWidget(parent),
+ m_selectable(None),
+ m_selectionState(NULL)
+{
+}
+
+
+bool GraphAxisWidget::hasSelection()
+{
+ if (!m_selectionState) {
+ return false;
+ }
+
+ if (m_selectionState->type == SelectionState::Horizontal && m_orientation == GraphAxisWidget::Horizontal) {
+ return true;
+ }
+
+ if (m_selectionState->type == SelectionState::Vertical && m_orientation == GraphAxisWidget::Vertical) {
+ return true;
+ }
+
+ return false;
+}
+
+
+void GraphAxisWidget::setSelectable(SelectionStyle selectable)
+{
+ m_selectable = selectable;
+}
+
+
+void GraphAxisWidget::setSelectionState(SelectionState* state)
+{
+ m_selectionState = state;
+}
+
+
+void GraphAxisWidget::setOrientation(Orientation v)
+{
+ m_orientation = v;
+
+ if (m_orientation == Horizontal) {
+ setMinimumWidth(60);
+ } else {
+ setMinimumHeight(60);
+ }
+}
+
+
+void GraphAxisWidget::mouseMoveEvent(QMouseEvent *e)
+{
+ if (m_selectable == None) {
+ return;
+ }
+
+ int pos, max;
+
+ if (m_orientation == Horizontal) {
+ pos = e->x();
+ max = width();
+ } else {
+ pos = e->y();
+ max = height();
+ }
+
+ double value = m_valueEnd - m_valueBegin;
+ value *= pos / (double)max;
+ value += m_valueBegin;
+
+ if (e->buttons().testFlag(Qt::LeftButton)) {
+ m_selectionState->start = qMin<qint64>(m_mousePressValue, value);
+ m_selectionState->end = qMax<qint64>(m_mousePressValue, value);
+ m_selectionState->type = m_orientation == Horizontal ? SelectionState::Horizontal : SelectionState::Vertical;
+ emit selectionChanged();
+ update();
+ }
+}
+
+
+void GraphAxisWidget::mousePressEvent(QMouseEvent *e)
+{
+ if (m_selectable == None) {
+ return;
+ }
+
+ int pos, max;
+
+ if (m_orientation == Horizontal) {
+ pos = e->x();
+ max = width();
+ } else {
+ pos = e->y();
+ max = height();
+ }
+
+ double value = m_valueEnd - m_valueBegin;
+ value *= pos / (double)max;
+ value += m_valueBegin;
+
+ m_mousePressPosition = e->pos();
+ m_mousePressValue = value;
+}
+
+
+void GraphAxisWidget::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (m_selectable == None) {
+ return;
+ }
+
+ int dx = qAbs(m_mousePressPosition.x() - e->x());
+ int dy = qAbs(m_mousePressPosition.y() - e->y());
+
+ if (dx + dy < 2) {
+ m_selectionState->type = SelectionState::None;
+ emit selectionChanged();
+ }
+}
+
+
+void GraphAxisWidget::setRange(qint64 min, qint64 max)
+{
+ m_valueMin = min;
+ m_valueMax = max;
+ update();
+}
+
+
+void GraphAxisWidget::setView(qint64 start, qint64 end)
+{
+ m_valueBegin = start;
+ m_valueEnd = end;
+ update();
+}
+
+#include "graphaxiswidget.moc"
--- /dev/null
+#ifndef GRAPHAXISWIDGET_H
+#define GRAPHAXISWIDGET_H
+
+#include "graphing.h"
+
+#include <QWidget>
+
+/**
+ * The generic base class of all graph axes.
+ *
+ * Handles orientation, simple selections, and view area.
+ */
+class GraphAxisWidget : public QWidget {
+ Q_OBJECT
+public:
+ enum Orientation {
+ Horizontal,
+ Vertical
+ };
+
+ enum SelectionStyle {
+ None,
+ Single,
+ Range
+ };
+
+public:
+ GraphAxisWidget(QWidget* parent = 0);
+ virtual ~GraphAxisWidget(){}
+
+ /* Is this axis part of the active selection */
+ bool hasSelection();
+
+ void setSelectable(SelectionStyle selectable);
+ void setSelectionState(SelectionState* state);
+
+ void setOrientation(Orientation v);
+
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+
+public slots:
+ /* The minimum and maximum values of this axis */
+ void setRange(qint64 min, qint64 max);
+
+ /* The currently visible range of values */
+ void setView(qint64 start, qint64 end);
+
+signals:
+ void selectionChanged();
+
+protected:
+ Orientation m_orientation;
+
+ /* The min/max value of this axis */
+ qint64 m_valueMin;
+ qint64 m_valueMax;
+
+ /* The highest and lowest currently visible value */
+ qint64 m_valueBegin;
+ qint64 m_valueEnd;
+
+ /* Selection */
+ SelectionStyle m_selectable;
+ SelectionState* m_selectionState;
+
+ /* Mouse tracking */
+ QPoint m_mousePressPosition;
+ qint64 m_mousePressValue;
+};
+
+#endif
--- /dev/null
+#ifndef GRAPHING_H
+#define GRAPHING_H
+
+#include <QString>
+
+/**
+ * A simple struct to hold a horizontal or vertical selection
+ */
+struct SelectionState {
+ enum SelectionType {
+ None,
+ Horizontal,
+ Vertical
+ };
+
+ SelectionType type;
+ qint64 start;
+ qint64 end;
+};
+
+
+/**
+ * Fairly generic data provider for graphs
+ */
+class GraphDataProvider {
+public:
+ virtual ~GraphDataProvider(){}
+
+ /* Number of elements in graph */
+ virtual qint64 size() const = 0;
+
+ /* Returns value for index */
+ virtual qint64 value(qint64 index) const = 0;
+
+ /* Is the item at index selected */
+ virtual bool selected(qint64 index) const = 0;
+
+ /* Get mouse hover tooltip for item */
+ virtual QString itemTooltip(qint64 index) const = 0;
+
+ /* Called on item double click */
+ virtual void itemDoubleClicked(qint64 index) const = 0;
+
+ /* Set pointer to selection state */
+ virtual void setSelectionState(SelectionState* state) = 0;
+};
+
+#endif
--- /dev/null
+#ifndef GRAPHLABELWIDGET_H
+#define GRAPHLABELWIDGET_H
+
+#include <QWidget>
+#include <QPainter>
+
+/**
+ * A very simple label widget, basically a box with text in.
+ */
+class GraphLabelWidget : public QWidget {
+public:
+ GraphLabelWidget(QString text = QString(), QWidget* parent = 0) :
+ QWidget(parent),
+ m_flags(Qt::AlignHCenter | Qt::AlignVCenter),
+ m_text(text)
+ {
+ }
+
+ void setText(const QString& text)
+ {
+ m_text = text;
+ }
+
+ void setFlags(int flags)
+ {
+ m_flags = flags;
+ }
+
+ virtual void paintEvent(QPaintEvent *)
+ {
+ QPainter painter(this);
+ painter.setPen(Qt::black);
+ painter.fillRect(rect(), Qt::lightGray);
+ painter.drawText(rect(), m_flags, m_text);
+ }
+
+protected:
+ int m_flags;
+ QString m_text;
+};
+
+#endif
--- /dev/null
+#include "graphview.h"
+
+#include <QMouseEvent>
+#include <QApplication>
+
+GraphView::GraphView(QWidget* parent) :
+ QWidget(parent),
+ m_selectionState(NULL),
+ m_viewLeft(0),
+ m_viewRight(0),
+ m_viewBottom(0),
+ m_viewTop(0),
+ m_graphLeft(0),
+ m_graphRight(0),
+ m_graphBottom(0),
+ m_graphTop(0),
+ m_viewWidth(0),
+ m_viewWidthMin(0),
+ m_viewWidthMax(0),
+ m_viewHeight(0),
+ m_viewHeightMin(0),
+ m_viewHeightMax(0)
+{
+ memset(&m_previous, -1, sizeof(m_previous));
+}
+
+void GraphView::update()
+{
+ if (m_graphLeft != m_previous.m_graphLeft || m_graphRight != m_previous.m_graphRight) {
+ m_previous.m_graphLeft = m_graphLeft;
+ m_previous.m_graphRight = m_graphRight;
+
+ emit horizontalRangeChanged(m_graphLeft, m_graphRight);
+ }
+
+ if (m_viewLeft != m_previous.m_viewLeft || m_viewRight != m_previous.m_viewRight) {
+ m_previous.m_viewLeft = m_viewLeft;
+ m_previous.m_viewRight = m_viewRight;
+
+ emit horizontalViewChanged(m_viewLeft, m_viewRight);
+ }
+
+ if (m_graphBottom != m_previous.m_graphBottom || m_graphTop != m_previous.m_graphTop) {
+ m_previous.m_graphBottom = m_graphBottom;
+ m_previous.m_graphTop = m_graphTop;
+
+ emit verticalRangeChanged(m_graphBottom, m_graphTop);
+ }
+
+ if (m_viewBottom != m_previous.m_viewBottom || m_viewTop != m_previous.m_viewTop) {
+ m_previous.m_viewBottom = m_viewBottom;
+ m_previous.m_viewTop = m_viewTop;
+
+ emit verticalViewChanged(m_viewBottom, m_viewTop);
+ }
+
+ QWidget::update();
+}
+
+void GraphView::resizeEvent(QResizeEvent *)
+{
+ m_viewHeight = height();
+ m_viewHeightMin = m_viewHeight;
+ m_viewHeightMax = m_viewHeight;
+
+ m_viewTop = m_viewBottom + m_viewHeight;
+
+ update();
+}
+
+void GraphView::wheelEvent(QWheelEvent *e)
+{
+ int zoomPercent = 10;
+
+ /* If holding Ctrl key then zoom 2x faster */
+ if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
+ zoomPercent = 20;
+ }
+
+ /* Zoom view by adjusting width */
+ double dt = m_viewWidth;
+ double size = dt;
+ size *= -e->delta();
+
+ /* Zoom deltas normally come in increments of 120 */
+ size /= 120 * (100 / zoomPercent);
+
+ m_viewWidth += size;
+ m_viewWidth = qBound(m_viewWidthMin, m_viewWidth, m_viewWidthMax);
+
+ /* Scroll view to zoom around mouse */
+ dt -= m_viewWidth;
+ dt *= e->x();
+ dt /= width();
+
+ m_viewLeft = dt + m_viewLeft;
+ m_viewLeft = qBound(m_graphLeft, m_viewLeft, m_graphRight - m_viewWidth);
+ m_viewRight = m_viewLeft + m_viewWidth;
+
+ update();
+}
+
+void GraphView::mouseMoveEvent(QMouseEvent *e)
+{
+ if (e->buttons().testFlag(Qt::LeftButton)) {
+ /* Horizontal scroll */
+ double dvdx = m_viewWidth / (double)width();
+ dvdx *= m_mousePressPosition.x() - e->pos().x();
+
+ m_viewLeft = m_mousePressViewLeft + dvdx;
+ m_viewLeft = qBound(m_graphLeft, m_viewLeft, m_graphRight - m_viewWidth);
+ m_viewRight = m_viewLeft + m_viewWidth;
+
+ /* Vertical scroll */
+ double dvdy = m_viewHeight / (double)height();
+ dvdy *= m_mousePressPosition.y() - e->pos().y();
+
+ m_viewBottom = m_mousePressViewBottom + dvdy;
+ m_viewBottom = qBound(m_graphBottom, m_viewBottom, m_graphTop - m_viewHeight);
+ m_viewTop = m_viewBottom + m_viewHeight;
+
+ update();
+ }
+}
+
+void GraphView::mousePressEvent(QMouseEvent *e)
+{
+ m_mousePressPosition = e->pos();
+ m_mousePressViewLeft = m_viewLeft;
+ m_mousePressViewBottom = m_viewBottom;
+}
+
+void GraphView::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (m_selectionState) {
+ m_selectionState->type = SelectionState::None;
+ emit selectionChanged();
+ }
+}
+
+void GraphView::setSelectionState(SelectionState* state)
+{
+ m_selectionState = state;
+}
+
+void GraphView::setHorizontalView(qint64 start, qint64 end)
+{
+ m_viewLeft = qBound(m_graphLeft, start, m_graphRight - (end - start));
+ m_viewRight = qBound(m_graphLeft, end, m_graphRight);
+ m_viewWidth = m_viewRight - m_viewLeft;
+ update();
+}
+
+void GraphView::setVerticalView(qint64 start, qint64 end)
+{
+ m_viewBottom = qBound(m_graphBottom, start, m_graphTop - (end - start));
+ m_viewTop = qBound(m_graphBottom, end, m_graphTop);
+ m_viewHeight = m_viewTop - m_viewBottom;
+ update();
+}
+
+void GraphView::setDefaultView(qint64 min, qint64 max)
+{
+ m_graphLeft = min;
+ m_graphRight = max;
+ m_viewWidth = max - min;
+
+ m_viewWidthMin = 1;
+ m_viewWidthMax = m_viewWidth;
+
+ m_viewLeft = min;
+ m_viewRight = max;
+
+ m_viewHeight = height();
+ m_viewHeightMin = m_viewHeight;
+ m_viewHeightMax = m_viewHeight;
+
+ m_viewBottom = 0;
+ m_viewTop = m_viewHeight;
+
+ m_graphBottom = 0;
+ m_graphTop = m_viewHeight;
+
+ update();
+}
+
+#include "graphview.moc"
--- /dev/null
+#ifndef GRAPHVIEW_H
+#define GRAPHVIEW_H
+
+#include "graphing.h"
+
+#include <QWidget>
+
+/**
+ * The generic base class for a graph's view, this is the component that
+ * displays the actual data for the graph.
+ *
+ * - Stores the view area within the graph
+ * - Simple user interaction such as translating and zooming with mouse
+ * - Selection tracking synchronised with axis
+ */
+class GraphView : public QWidget {
+ Q_OBJECT
+public:
+ GraphView(QWidget* parent = 0);
+ virtual ~GraphView(){}
+
+ virtual void update();
+
+ virtual void resizeEvent(QResizeEvent *);
+
+ virtual void wheelEvent(QWheelEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+ virtual void setSelectionState(SelectionState* state);
+
+ void setHorizontalView(qint64 start, qint64 end);
+ void setVerticalView(qint64 start, qint64 end);
+
+protected:
+ void setDefaultView(qint64 min, qint64 max);
+
+signals:
+ void selectionChanged();
+
+ void verticalViewChanged(qint64 start, qint64 end);
+ void verticalRangeChanged(qint64 min, qint64 max);
+
+ void horizontalRangeChanged(qint64 min, qint64 max);
+ void horizontalViewChanged(qint64 start, qint64 end);
+
+protected:
+ /* Viewport area */
+ qint64 m_viewLeft;
+ qint64 m_viewRight;
+ qint64 m_viewBottom;
+ qint64 m_viewTop;
+
+ /* Graph limits */
+ qint64 m_graphLeft;
+ qint64 m_graphRight;
+ qint64 m_graphBottom;
+ qint64 m_graphTop;
+
+ /* Viewport width (m_viewRight - m_viewLeft), used for zoom */
+ qint64 m_viewWidth;
+ qint64 m_viewWidthMin;
+ qint64 m_viewWidthMax;
+
+ /* Viewport height (m_viewTop - m_viewBottom), used for zoom */
+ qint64 m_viewHeight;
+ qint64 m_viewHeightMin;
+ qint64 m_viewHeightMax;
+
+ /* Mouse tracking */
+ QPoint m_mousePressPosition;
+ qint64 m_mousePressViewLeft;
+ qint64 m_mousePressViewBottom;
+
+ /* Selection */
+ SelectionState* m_selectionState;
+
+ /* State from the last update() call */
+ struct PreviousUpdate {
+ qint64 m_viewLeft;
+ qint64 m_viewRight;
+ qint64 m_viewBottom;
+ qint64 m_viewTop;
+
+ qint64 m_graphLeft;
+ qint64 m_graphRight;
+ qint64 m_graphBottom;
+ qint64 m_graphTop;
+ } m_previous;
+};
+
+#endif
--- /dev/null
+#include "graphwidget.h"
+
+#include <QScrollBar>
+
+GraphWidget::GraphWidget(QWidget* parent) :
+ QWidget(parent),
+ m_view(NULL),
+ m_label(NULL),
+ m_axisTop(NULL),
+ m_axisLeft(NULL),
+ m_axisRight(NULL),
+ m_axisBottom(NULL),
+ m_horizontalScrollbar(NULL),
+ m_horizontalMin(0),
+ m_horizontalMax(0),
+ m_horizontalStart(0),
+ m_horizontalEnd(0),
+ m_horizontalScrollbarPolicy(Qt::ScrollBarAlwaysOff),
+ m_verticalScrollbar(NULL),
+ m_verticalMin(0),
+ m_verticalMax(0),
+ m_verticalStart(0),
+ m_verticalEnd(0),
+ m_verticalScrollbarPolicy(Qt::ScrollBarAlwaysOff)
+{
+ m_selection.type = SelectionState::None;
+
+ m_verticalScrollbar = new QScrollBar(this);
+ m_verticalScrollbar->setOrientation(Qt::Vertical);
+ m_verticalScrollbar->hide();
+ m_verticalScrollbar->resize(m_verticalScrollbar->sizeHint());
+
+ m_horizontalScrollbar = new QScrollBar(this);
+ m_horizontalScrollbar->setOrientation(Qt::Horizontal);
+ m_horizontalScrollbar->hide();
+ m_horizontalScrollbar->resize(m_horizontalScrollbar->sizeHint());
+
+ updateLayout();
+ setAutoFillBackground(true);
+}
+
+
+GraphView* GraphWidget::view()
+{
+ return m_view;
+}
+
+
+GraphLabelWidget* GraphWidget::label()
+{
+ return m_label;
+}
+
+
+GraphAxisWidget* GraphWidget::axis(AxisPosition pos)
+{
+ switch(pos) {
+ case AxisTop:
+ return m_axisTop;
+
+ case AxisLeft:
+ return m_axisLeft;
+
+ case AxisRight:
+ return m_axisRight;
+
+ case AxisBottom:
+ return m_axisBottom;
+
+ default:
+ return NULL;
+ }
+}
+
+
+void GraphWidget::setView(GraphView* view)
+{
+ delete m_view;
+ m_view = view;
+
+ updateConnections();
+
+ m_view->setSelectionState(&m_selection);
+ m_view->show();
+ m_view->update();
+}
+
+
+void GraphWidget::setLabel(GraphLabelWidget* label)
+{
+ delete m_label;
+ m_label = label;
+}
+
+
+void GraphWidget::setAxis(AxisPosition pos, GraphAxisWidget* axis)
+{
+ switch(pos) {
+ case AxisTop:
+ delete m_axisTop;
+ m_axisTop = axis;
+ m_axisTop->setOrientation(GraphAxisWidget::Horizontal);
+ m_axisTop->setSelectionState(&m_selection);
+ break;
+
+ case AxisLeft:
+ delete m_axisLeft;
+ m_axisLeft = axis;
+ m_axisLeft->setOrientation(GraphAxisWidget::Vertical);
+ m_axisLeft->setSelectionState(&m_selection);
+ break;
+
+ case AxisRight:
+ delete m_axisRight;
+ m_axisRight = axis;
+ m_axisRight->setOrientation(GraphAxisWidget::Vertical);
+ m_axisRight->setSelectionState(&m_selection);
+ break;
+
+ case AxisBottom:
+ delete m_axisBottom;
+ m_axisBottom = axis;
+ m_axisBottom->setOrientation(GraphAxisWidget::Horizontal);
+ m_axisBottom->setSelectionState(&m_selection);
+ break;
+ }
+
+ updateConnections();
+ updateSelection();
+ axis->show();
+}
+
+
+void GraphWidget::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
+{
+ m_horizontalScrollbarPolicy = policy;
+ updateScrollbars();
+}
+
+
+void GraphWidget::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
+{
+ m_verticalScrollbarPolicy = policy;
+ updateScrollbars();
+}
+
+
+void GraphWidget::resizeEvent(QResizeEvent *e)
+{
+ updateLayout();
+ update();
+}
+
+
+/* Used if a selection would be shared between graphs with different axis */
+SelectionState GraphWidget::transformSelectionIn(SelectionState state)
+{
+ return state;
+}
+
+
+/* Used if a selection would be shared between graphs with different axis */
+SelectionState GraphWidget::transformSelectionOut(SelectionState state)
+{
+ return state;
+}
+
+
+/* Update the scrollbars based on current view */
+void GraphWidget::updateScrollbars()
+{
+ /* Vertical scroll bar */
+ qint64 size = (m_verticalMax - m_verticalMin) - (m_verticalEnd - m_verticalStart);
+
+ if (size <= INT_MAX) {
+ m_verticalScrollbar->setValue(m_verticalStart - m_verticalMin);
+ m_verticalScrollbar->setPageStep(m_verticalEnd - m_verticalStart);
+ m_verticalScrollbar->setRange(0, size);
+ } else {
+ /* QScrollBar only supports up to INT_MAX values,
+ * here we must scale our values to match this */
+ double curSize = m_verticalEnd - m_verticalStart;
+ double pages = (m_verticalMax - m_verticalMin) / curSize;
+ double value = (m_verticalStart - m_verticalMin) / curSize;
+
+ m_verticalScrollbar->setValue(value);
+ m_verticalScrollbar->setPageStep(1);
+ m_verticalScrollbar->setRange(0, pages);
+ }
+
+ /* Adhere to scrollbar policy */
+ bool visible = false;
+
+ if (m_verticalScrollbarPolicy == Qt::ScrollBarAlwaysOn) {
+ visible = true;
+ } else if (m_verticalScrollbarPolicy == Qt::ScrollBarAlwaysOff) {
+ visible = false;
+ } else if (m_verticalScrollbarPolicy == Qt::ScrollBarAsNeeded) {
+ visible = m_verticalMin != m_verticalStart || m_verticalMax != m_verticalEnd;
+ }
+
+ if (visible != m_verticalScrollbar->isVisible()) {
+ m_verticalScrollbar->setVisible(visible);
+ updateLayout();
+ }
+
+ /* Horizontal scroll bar */
+ size = (m_horizontalMax - m_horizontalMin) - (m_horizontalEnd - m_horizontalStart);
+
+ if (size <= INT_MAX) {
+ m_horizontalScrollbar->setValue(m_horizontalStart - m_horizontalMin);
+ m_horizontalScrollbar->setPageStep(m_horizontalEnd - m_horizontalStart);
+ m_horizontalScrollbar->setRange(0, size);
+ } else {
+ /* QScrollBar only supports up to INT_MAX values,
+ * here we must scale our values to match this */
+ double dxdv = INT_MAX / (double)size;
+ double value = (m_horizontalStart - m_horizontalMin) * dxdv;
+ double pageStep = (m_horizontalEnd - m_horizontalStart) * dxdv;
+
+ m_horizontalScrollbar->setValue((int)value);
+ m_horizontalScrollbar->setPageStep((int)pageStep);
+ m_horizontalScrollbar->setRange(0, INT_MAX);
+ }
+
+ /* Adhere to scrollbar policy */
+ visible = false;
+
+ if (m_horizontalScrollbarPolicy == Qt::ScrollBarAlwaysOn) {
+ visible = true;
+ } else if (m_horizontalScrollbarPolicy == Qt::ScrollBarAlwaysOff) {
+ visible = false;
+ } else if (m_horizontalScrollbarPolicy == Qt::ScrollBarAsNeeded) {
+ visible = m_horizontalMin != m_horizontalStart || m_horizontalMax != m_horizontalEnd;
+ }
+
+ if (visible != m_horizontalScrollbar->isVisible()) {
+ m_horizontalScrollbar->setVisible(visible);
+ updateLayout();
+ }
+}
+
+
+/* Update all signal / slot connections */
+void GraphWidget::updateConnections()
+{
+ if (m_view) {
+ connect(m_view, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+
+ connect(m_view, SIGNAL(horizontalViewChanged(qint64,qint64)), this, SLOT(horizontalViewChange(qint64,qint64)), Qt::UniqueConnection);
+ connect(m_view, SIGNAL(horizontalRangeChanged(qint64,qint64)), this, SLOT(horizontalRangeChange(qint64,qint64)), Qt::UniqueConnection);
+
+ connect(m_view, SIGNAL(verticalViewChanged(qint64,qint64)), this, SLOT(verticalViewChange(qint64,qint64)), Qt::UniqueConnection);
+ connect(m_view, SIGNAL(verticalRangeChanged(qint64,qint64)), this, SLOT(verticalRangeChange(qint64,qint64)), Qt::UniqueConnection);
+ }
+
+ if (m_axisTop) {
+ if (m_view) {
+ connect(m_view, SIGNAL(horizontalViewChanged(qint64,qint64)), m_axisTop, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+ connect(m_view, SIGNAL(horizontalRangeChanged(qint64,qint64)), m_axisTop, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+ }
+
+ connect(m_axisTop, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+ }
+
+ if (m_axisLeft) {
+ if (m_view) {
+ connect(m_view, SIGNAL(verticalViewChanged(qint64,qint64)), m_axisLeft, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+ connect(m_view, SIGNAL(verticalRangeChanged(qint64,qint64)), m_axisLeft, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+ }
+
+ connect(m_axisLeft, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+ }
+
+ if (m_axisRight) {
+ if (m_view) {
+ connect(m_view, SIGNAL(verticalViewChanged(qint64,qint64)), m_axisRight, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+ connect(m_view, SIGNAL(verticalRangeChanged(qint64,qint64)), m_axisRight, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+ }
+
+ connect(m_axisRight, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+ }
+
+ if (m_axisBottom) {
+ if (m_view) {
+ connect(m_view, SIGNAL(horizontalViewChanged(qint64,qint64)), m_axisBottom, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+ connect(m_view, SIGNAL(horizontalRangeChanged(qint64,qint64)), m_axisBottom, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+ }
+
+ connect(m_axisBottom, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+ }
+
+ if (m_horizontalScrollbar) {
+ connect(m_horizontalScrollbar, SIGNAL(actionTriggered(int)), this, SLOT(horizontalScrollAction(int)));
+ }
+
+ if (m_verticalScrollbar) {
+ connect(m_verticalScrollbar, SIGNAL(actionTriggered(int)), this, SLOT(verticalScrollAction(int)));
+ }
+}
+
+
+/* Recalculate the layout */
+void GraphWidget::updateLayout()
+{
+ int x, y;
+ int padX = 0, padY = 0;
+
+ if (m_axisTop) {
+ padY += m_axisTop->height();
+ }
+
+ if (m_axisBottom) {
+ padY += m_axisBottom->height();
+ }
+
+ if (m_axisLeft) {
+ padX += m_axisLeft->width();
+ }
+
+ if (m_axisRight) {
+ padX += m_axisRight->width();
+ }
+
+ if (m_horizontalScrollbar->isVisible()) {
+ padY += m_horizontalScrollbar->height();
+ }
+
+ if (m_verticalScrollbar->isVisible()) {
+ padX += m_verticalScrollbar->width();
+ }
+
+ if (m_axisTop) {
+ x = m_axisLeft ? m_axisLeft->width() : 0;
+ y = 0;
+
+ m_axisTop->move(x, y);
+ m_axisTop->resize(width() - padX, m_axisTop->height());
+ }
+
+ if (m_axisBottom) {
+ x = m_axisLeft ? m_axisLeft->width() : 0;
+ y = height() - m_axisBottom->height();
+
+ if (m_horizontalScrollbar->isVisible()) {
+ y -= m_horizontalScrollbar->height();
+ }
+
+ m_axisBottom->move(x, y);
+ m_axisBottom->resize(width() - padX, m_axisBottom->height());
+ }
+
+ if (m_axisLeft) {
+ x = 0;
+ y = m_axisTop ? m_axisTop->height() : 0;
+
+ m_axisLeft->move(x, y);
+ m_axisLeft->resize(m_axisLeft->width(), height() - padY);
+ }
+
+ if (m_axisRight) {
+ x = width() - m_axisRight->width();
+ y = m_axisTop ? m_axisTop->height() : 0;
+
+ if (m_verticalScrollbar->isVisible()) {
+ x -= m_verticalScrollbar->width();
+ }
+
+ m_axisRight->move(x, y);
+ m_axisRight->resize(m_axisRight->width(), height() - padY);
+ }
+
+ if (m_view) {
+ x = m_axisLeft ? m_axisLeft->width() : 0;
+ y = m_axisTop ? m_axisTop->height() : 0;
+
+ m_view->move(x, y);
+ m_view->resize(width() - padX, height() - padY);
+ }
+
+ if (m_label) {
+ if (m_axisTop && m_axisLeft) {
+ m_label->move(0, 0);
+ m_label->resize(m_axisLeft->width(), m_axisTop->height());
+ }
+ }
+
+ if (m_verticalScrollbar) {
+ m_verticalScrollbar->move(width() - m_verticalScrollbar->width(), 0);
+
+ if (m_horizontalScrollbar) {
+ m_verticalScrollbar->resize(m_verticalScrollbar->width(), height() - m_horizontalScrollbar->height());
+ } else {
+ m_verticalScrollbar->resize(m_verticalScrollbar->width(), height());
+ }
+ }
+
+ if (m_horizontalScrollbar) {
+ m_horizontalScrollbar->move(0, height() - m_horizontalScrollbar->height());
+
+ if (m_verticalScrollbar) {
+ m_horizontalScrollbar->resize(width() - m_verticalScrollbar->width(), m_horizontalScrollbar->height());
+ } else {
+ m_horizontalScrollbar->resize(width(), m_horizontalScrollbar->height());
+ }
+ }
+}
+
+
+void GraphWidget::setSelection(SelectionState state)
+{
+ m_selection = transformSelectionIn(state);
+ updateSelection(false);
+}
+
+
+void GraphWidget::setHorizontalView(qint64 start, qint64 end)
+{
+ if (m_view) {
+ m_view->setHorizontalView(start, end);
+ }
+}
+
+
+void GraphWidget::setVerticalView(qint64 start, qint64 end)
+{
+ if (m_view) {
+ m_view->setVerticalView(start, end);
+ }
+}
+
+
+/* Called when the view is translated / zoomed */
+void GraphWidget::verticalViewChange(qint64 start, qint64 end)
+{
+ m_verticalStart = start;
+ m_verticalEnd = end;
+ updateScrollbars();
+
+ emit verticalViewChanged(start, end);
+}
+
+
+void GraphWidget::verticalRangeChange(qint64 start, qint64 end)
+{
+ m_verticalMin = start;
+ m_verticalMax = end;
+ updateScrollbars();
+
+ emit verticalRangeChanged(start, end);
+}
+
+
+void GraphWidget::horizontalViewChange(qint64 start, qint64 end)
+{
+ m_horizontalStart = start;
+ m_horizontalEnd = end;
+ updateScrollbars();
+
+ emit horizontalViewChanged(start, end);
+}
+
+
+void GraphWidget::horizontalRangeChange(qint64 start, qint64 end)
+{
+ m_horizontalMin = start;
+ m_horizontalMax = end;
+ updateScrollbars();
+
+ emit horizontalRangeChanged(start, end);
+}
+
+
+/* User interaction with horizontal scroll bar */
+void GraphWidget::horizontalScrollAction(int /*action*/)
+{
+ int value = m_horizontalScrollbar->sliderPosition();
+ qint64 size = (m_horizontalMax - m_horizontalMin) - (m_horizontalEnd - m_horizontalStart);
+
+ /* Calculate the new scroll values */
+ if (size <= INT_MAX) {
+ m_horizontalEnd -= m_horizontalStart;
+ m_horizontalStart = value + m_horizontalMin;
+ m_horizontalEnd += value;
+ } else {
+ /* QScrollBar only supports up to INT_MAX values, here we must scale
+ * our values to match this */
+ double dxdv = INT_MAX / (double)size;
+
+ size = m_horizontalEnd - m_horizontalStart;
+ m_horizontalStart = value / dxdv + m_horizontalMin;
+ m_horizontalEnd = m_horizontalStart + size;
+ }
+
+ /* Update view */
+ if (m_view) {
+ m_view->setHorizontalView(m_horizontalStart, m_horizontalEnd);
+ }
+
+ /* Update horizontal axes */
+ if (m_axisTop) {
+ m_axisTop->setView(m_horizontalStart, m_horizontalEnd);
+ }
+
+ if (m_axisBottom) {
+ m_axisBottom->setView(m_horizontalStart, m_horizontalEnd);
+ }
+
+ /* Inform the world of our changes! */
+ emit horizontalViewChanged(m_horizontalStart, m_horizontalEnd);
+}
+
+
+/* User interaction with vertical scroll bar */
+void GraphWidget::verticalScrollAction(int /*action*/)
+{
+ int value = m_verticalScrollbar->sliderPosition();
+ qint64 size = (m_verticalMax - m_verticalMin) - (m_verticalEnd - m_verticalStart);
+
+ /* Calculate the new scroll values */
+ if (size <= INT_MAX) {
+ m_verticalEnd -= m_verticalStart;
+ m_verticalStart = value + m_verticalMin;
+ m_verticalEnd += value;
+ } else {
+ /* QScrollBar only supports up to INT_MAX values, here we must scale
+ * our values to match this */
+ double dxdv = INT_MAX / (double)size;
+
+ size = m_verticalEnd - m_verticalStart;
+ m_verticalStart = value / dxdv + m_verticalMin;
+ m_verticalEnd = m_verticalStart + size;
+ }
+
+ /* Update view */
+ if (m_view) {
+ m_view->setVerticalView(m_verticalStart, m_verticalEnd);
+ }
+
+ /* Update vertical axes */
+ if (m_axisLeft) {
+ m_axisLeft->setView(m_verticalStart, m_verticalEnd);
+ }
+
+ if (m_axisRight) {
+ m_axisRight->setView(m_verticalStart, m_verticalEnd);
+ }
+
+ /* Inform the world of our changes! */
+ emit verticalViewChanged(m_verticalStart, m_verticalEnd);
+}
+
+
+/* Update child elements when selection changes */
+void GraphWidget::updateSelection(bool emitSignal)
+{
+ if (m_view) {
+ m_view->update();
+ }
+
+ if (m_axisTop) {
+ m_axisTop->update();
+ }
+
+ if (m_axisLeft) {
+ m_axisLeft->update();
+ }
+
+ if (m_axisRight) {
+ m_axisRight->update();
+ }
+
+ if (m_axisBottom) {
+ m_axisBottom->update();
+ }
+
+ if (emitSignal) {
+ emit selectionChanged(transformSelectionOut(m_selection));
+ }
+}
+
+
+#include "graphwidget.moc"
--- /dev/null
+#ifndef GRAPHWIDGET_H
+#define GRAPHWIDGET_H
+
+#include "graphview.h"
+#include "graphaxiswidget.h"
+#include "graphlabelwidget.h"
+
+class QScrollBar;
+
+/**
+ * The generic GraphWidget class which combines the elements of a graph,
+ * the axis, view, scrollbars and label.
+ */
+class GraphWidget : public QWidget {
+ Q_OBJECT
+public:
+ enum AxisPosition {
+ AxisTop,
+ AxisLeft,
+ AxisRight,
+ AxisBottom
+ };
+
+public:
+ GraphWidget(QWidget* parent = 0);
+ virtual ~GraphWidget(){}
+
+ GraphView* view();
+ GraphLabelWidget* label();
+ GraphAxisWidget* axis(AxisPosition pos);
+
+ void setView(GraphView* view);
+ void setLabel(GraphLabelWidget* label);
+ void setAxis(AxisPosition pos, GraphAxisWidget* axis);
+
+ void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy);
+ void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy);
+
+ virtual void resizeEvent(QResizeEvent *e);
+
+protected:
+ /* Used if a selection would be shared between graphs with different axis */
+ virtual SelectionState transformSelectionIn(SelectionState state);
+ virtual SelectionState transformSelectionOut(SelectionState state);
+
+ /* Update the scrollbars based on current view */
+ void updateScrollbars();
+
+ /* Update all signal / slot connections */
+ void updateConnections();
+
+ /* Recalculate child widget layout */
+ void updateLayout();
+
+public slots:
+ void setSelection(SelectionState state);
+
+ /* Set view areas */
+ void setHorizontalView(qint64 start, qint64 end);
+ void setVerticalView(qint64 start, qint64 end);
+
+protected slots:
+ /* View changed by translation / zooming */
+ void verticalViewChange(qint64 start, qint64 end);
+ void verticalRangeChange(qint64 start, qint64 end);
+ void horizontalViewChange(qint64 start, qint64 end);
+ void horizontalRangeChange(qint64 start, qint64 end);
+
+ /* User interaction with scroll bars */
+ void horizontalScrollAction(int action);
+ void verticalScrollAction(int action);
+
+ /* Update child elements when selection changes */
+ void updateSelection(bool emitSignal = true);
+
+signals:
+ void selectionChanged(SelectionState state);
+
+ void verticalViewChanged(qint64 start, qint64 end);
+ void verticalRangeChanged(qint64 start, qint64 end);
+
+ void horizontalViewChanged(qint64 start, qint64 end);
+ void horizontalRangeChanged(qint64 start, qint64 end);
+
+protected:
+ SelectionState m_selection;
+
+ GraphView* m_view;
+
+ GraphLabelWidget* m_label;
+
+ GraphAxisWidget* m_axisTop;
+ GraphAxisWidget* m_axisLeft;
+ GraphAxisWidget* m_axisRight;
+ GraphAxisWidget* m_axisBottom;
+
+ QScrollBar* m_horizontalScrollbar;
+ qint64 m_horizontalMin;
+ qint64 m_horizontalMax;
+ qint64 m_horizontalStart;
+ qint64 m_horizontalEnd;
+ Qt::ScrollBarPolicy m_horizontalScrollbarPolicy;
+
+ QScrollBar* m_verticalScrollbar;
+ qint64 m_verticalMin;
+ qint64 m_verticalMax;
+ qint64 m_verticalStart;
+ qint64 m_verticalEnd;
+ Qt::ScrollBarPolicy m_verticalScrollbarPolicy;
+};
+
+#endif
--- /dev/null
+#include "heatmapverticalaxiswidget.h"
+
+#include <qmath.h>
+#include <QPainter>
+#include <QMouseEvent>
+
+HeatmapVerticalAxisWidget::HeatmapVerticalAxisWidget(QWidget* parent) :
+ GraphAxisWidget(parent),
+ m_data(NULL)
+{
+ m_rowHeight = 20;
+}
+
+void HeatmapVerticalAxisWidget::setDataProvider(HeatmapDataProvider* data)
+{
+ delete m_data;
+ m_data = data;
+
+ m_valueMin = 0;
+ m_valueMax = (data->dataRows() + data->headerRows()) * m_rowHeight;
+
+ m_valueBegin = m_valueMin;
+ m_valueEnd = m_valueMax;
+
+ update();
+}
+
+void HeatmapVerticalAxisWidget::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (e->pos().y() < m_data->headerRows() * m_rowHeight) {
+ m_selectionState->type = SelectionState::None;
+ emit selectionChanged();
+ } else {
+ int row = e->pos().y();
+ row -= m_data->headerRows() * m_rowHeight;
+ row += m_valueBegin;
+ row /= m_rowHeight;
+
+ if (row >= m_data->dataRows()) {
+ m_selectionState->type = SelectionState::None;
+ emit selectionChanged();
+ } else {
+ m_selectionState->type = SelectionState::Vertical;
+ m_selectionState->start = m_data->dataRowAt(row);
+ m_selectionState->end = m_selectionState->start;
+ emit selectionChanged();
+ }
+ }
+}
+
+void HeatmapVerticalAxisWidget::paintEvent(QPaintEvent *)
+{
+ if (!m_data) {
+ return;
+ }
+
+ QPainter painter(this);
+ painter.setPen(Qt::black);
+ painter.setBrush(Qt::lightGray);
+ painter.drawRect(0, 0, width() - 1, height() - 1);
+
+ /* Draw scrollable rows */
+ painter.translate(0, m_data->headerRows() * m_rowHeight - m_valueBegin % m_rowHeight);
+ int rowStart = m_valueBegin / m_rowHeight;
+ int rowEnd = qMin<int>(qCeil(m_valueEnd / (double)m_rowHeight), m_data->dataRows());
+
+ for (unsigned i = rowStart; i < rowEnd; ++i) {
+ painter.drawText(0, 0, width(), m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, m_data->dataLabel(i));
+ painter.drawLine(0, m_rowHeight, width(), m_rowHeight);
+ painter.translate(0, m_rowHeight);
+ }
+
+ /* Draw fixed position headers */
+ painter.resetTransform();
+ painter.drawRect(0, 0, width() - 1, m_data->headerRows() * m_rowHeight);
+
+ for (unsigned i = 0; i < m_data->headerRows(); ++i) {
+ painter.drawText(0, 0, width(), m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, m_data->headerLabel(i));
+ painter.drawLine(0, m_rowHeight, width(), m_rowHeight);
+ painter.translate(0, m_rowHeight);
+ }
+}
--- /dev/null
+#ifndef HEATMAPVERTICALAXISWIDGET_H
+#define HEATMAPVERTICALAXISWIDGET_H
+
+#include "heatmapview.h"
+#include "graphaxiswidget.h"
+
+/**
+ * Vertical axis specifically for heatmap displaying header and data rows
+ */
+class HeatmapVerticalAxisWidget : public GraphAxisWidget {
+public:
+ HeatmapVerticalAxisWidget(QWidget* parent);
+
+ void setDataProvider(HeatmapDataProvider* data);
+
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+ virtual void paintEvent(QPaintEvent *);
+
+protected:
+ int m_rowHeight;
+ HeatmapDataProvider* m_data;
+};
+
+#endif
--- /dev/null
+#include "heatmapview.h"
+
+#include <qmath.h>
+#include <QToolTip>
+#include <QPainter>
+#include <QMouseEvent>
+
+HeatmapView::HeatmapView(QWidget* parent) :
+ GraphView(parent),
+ m_data(NULL)
+{
+ m_rowHeight = 20;
+ setMouseTracking(true);
+}
+
+
+void HeatmapView::setDataProvider(HeatmapDataProvider* data)
+{
+ delete m_data;
+ m_data = data;
+
+ if (m_data) {
+ m_data->setSelectionState(m_selectionState);
+
+ setDefaultView(m_data->start(), m_data->end());
+
+ m_viewWidthMin = 1000;
+
+ m_graphBottom = 0;
+ m_graphTop = (m_data->headerRows() + m_data->dataRows()) * m_rowHeight;
+ } else {
+ setDefaultView(0, 0);
+ m_graphBottom = m_graphTop = 0;
+ }
+
+ update();
+}
+
+
+void HeatmapView::setSelectionState(SelectionState* state)
+{
+ if (m_data) {
+ m_data->setSelectionState(state);
+ }
+
+ GraphView::setSelectionState(state);
+}
+
+
+void HeatmapView::mouseMoveEvent(QMouseEvent *e)
+{
+ GraphView::mouseMoveEvent(e);
+
+ if (e->buttons() || !m_data) {
+ QToolTip::hideText();
+ return;
+ }
+
+ qint64 index = itemAtPosition(e->pos());
+
+ if (index >= 0) {
+ QToolTip::showText(e->globalPos(), m_data->itemTooltip(index));
+ } else {
+ QToolTip::hideText();
+ }
+}
+
+
+void HeatmapView::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (m_data && e->button() == Qt::LeftButton) {
+ qint64 index = itemAtPosition(e->pos());
+
+ if (index >= 0) {
+ m_data->itemDoubleClicked(index);
+ return;
+ }
+ }
+
+ GraphView::mouseDoubleClickEvent(e);
+}
+
+
+void HeatmapView::paintEvent(QPaintEvent *)
+{
+ if (!m_data) {
+ return;
+ }
+
+ QPainter painter(this);
+ painter.fillRect(0, m_data->headerRows() * m_rowHeight, width(), height(), Qt::white);
+
+ /* Draw data rows */
+ painter.translate(0, m_data->headerRows() * m_rowHeight - m_viewBottom % m_rowHeight);
+ int rowStart = m_viewBottom / m_rowHeight;
+ int rowEnd = qMin<int>(qCeil(m_viewTop / (double)m_rowHeight), m_data->dataRows());
+
+ for (unsigned i = rowStart; i < rowEnd; ++i) {
+ HeatmapRowIterator* itr = m_data->dataRowIterator(i, m_viewLeft, m_viewRight, width());
+ paintRow(painter, itr);
+ painter.translate(0, m_rowHeight);
+ delete itr;
+ }
+
+ /* Draw Header */
+ painter.resetTransform();
+ painter.fillRect(0, 0, width(), m_data->headerRows() * m_rowHeight, Qt::white);
+
+ for (unsigned i = 0; i < m_data->headerRows(); ++i) {
+ HeatmapRowIterator* itr = m_data->headerRowIterator(i, m_viewLeft, m_viewRight, width());
+ paintRow(painter, itr);
+ painter.translate(0, m_rowHeight);
+ delete itr;
+ }
+
+ /* Draw Axis Lines */
+ painter.resetTransform();
+ painter.setPen(Qt::black);
+ painter.drawLine(0, m_rowHeight, width(), m_rowHeight);
+ painter.drawLine(0, m_rowHeight * 2, width(), m_rowHeight * 2);
+
+ painter.setPen(QColor(240, 240, 240));
+ painter.translate(0, m_data->headerRows() * m_rowHeight - m_viewBottom % m_rowHeight);
+
+ for (unsigned i = rowStart; i < rowEnd; ++i) {
+ painter.drawLine(0, m_rowHeight, width(), m_rowHeight);
+ painter.translate(0, m_rowHeight);
+ }
+
+ /* Draw selection borders */
+ painter.resetTransform();
+ painter.setPen(Qt::green);
+
+ if (m_selectionState->type == SelectionState::Horizontal) {
+ double dxdt = width() / (double)m_viewWidth;
+ double scroll = m_viewLeft * dxdt;
+ double left = (m_selectionState->start * dxdt) - scroll;
+ double right = (m_selectionState->end * dxdt) - scroll;
+
+ /* Highlight time */
+ if (left >= 0 && left <= width()) {
+ painter.drawLine(left, 0, left, height());
+ }
+
+ if (right >= 0 && right <= width()) {
+ painter.drawLine(right, 0, right, height());
+ }
+ } else if (m_selectionState->type == SelectionState::Vertical) {
+ /* Highlight row */
+ int row = m_selectionState->start;
+
+ for (unsigned i = rowStart; i < rowEnd; ++i) {
+ if (m_data->dataRowAt(i) == row) {
+ row = i - rowStart;
+
+ painter.translate(0, m_data->headerRows() * m_rowHeight - m_viewBottom % m_rowHeight);
+ painter.drawLine(0, (row + 1) * m_rowHeight, width(), (row + 1) * m_rowHeight);
+
+ if (row > 0) {
+ painter.drawLine(0, row * m_rowHeight, width(), row * m_rowHeight);
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+
+void HeatmapView::paintRow(QPainter& painter, HeatmapRowIterator* itr)
+{
+ bool selection = m_selectionState ? m_selectionState->type != SelectionState::None : false;
+
+ while (itr->next())
+ {
+ double heat = itr->heat();
+ int width = itr->width();
+ int x = itr->step();
+
+ /* Gamma correction */
+ heat = qPow(heat, 1.0 / 2.0);
+
+ if (width == 1) {
+ /* Draw single line */
+ if (selection) {
+ double selectedHeat = itr->selectedHeat();
+
+ if (selectedHeat >= 0.999) {
+ heat = 255.0 - qBound(0.0, heat * 255.0, 255.0);
+
+ if (itr->isGpu()) {
+ painter.setPen(QColor(255, heat, heat));
+ } else {
+ painter.setPen(QColor(heat, heat, 255));
+ }
+
+ painter.drawLine(x, 0, x, m_rowHeight - 1);
+ } else {
+ heat = 255.0 - qBound(0.0, heat * 100.0, 100.0);
+ painter.setPen(QColor(heat, heat, heat));
+ painter.drawLine(x, 0, x, m_rowHeight - 1);
+
+ if (selectedHeat > 0.001) {
+ selectedHeat = qPow(selectedHeat, 1.0 / 2.0);
+ selectedHeat = qBound(0.0, selectedHeat * 255.0, 255.0);
+
+ if (itr->isGpu()) {
+ painter.setPen(QColor(255, 0, 0, selectedHeat));
+ } else {
+ painter.setPen(QColor(0, 0, 255, selectedHeat));
+ }
+
+ painter.drawLine(x, 0, x, m_rowHeight - 1);
+ }
+ }
+ } else {
+ heat = qBound(0.0, heat * 255.0, 255.0);
+
+ if (itr->isGpu()) {
+ painter.setPen(QColor(255, 255 - heat, 255 - heat));
+ } else {
+ painter.setPen(QColor(255 - heat, 255 - heat, 255));
+ }
+
+ painter.drawLine(x, 0, x, m_rowHeight - 1);
+ }
+ } else {
+ double selectedHeat = itr->selectedHeat();
+
+ if (selection && selectedHeat < 0.9) {
+ painter.fillRect(x, 0, width, m_rowHeight, QColor(255 - 100, 255 - 100, 255 - 100));
+ } else if (itr->isGpu()) {
+ painter.fillRect(x, 0, width, m_rowHeight, QColor(255, 0, 0));
+ } else {
+ painter.fillRect(x, 0, width, m_rowHeight, QColor(0, 0, 255));
+ }
+
+ if (width > 6) {
+ painter.setPen(Qt::white);
+ QString elided = painter.fontMetrics().elidedText(itr->label(), Qt::ElideRight, width - 1);
+
+ painter.drawText(x + 1, 0, width - 1, m_rowHeight - 1,
+ Qt::AlignLeft | Qt::AlignVCenter,
+ elided);
+ }
+ }
+ }
+}
+
+
+qint64 HeatmapView::itemAtPosition(QPoint pos)
+{
+ if (!m_data) {
+ return -1;
+ }
+
+ double t = m_viewWidth / (double)width();
+ t *= pos.x();
+ t += m_viewLeft;
+
+ qint64 time = (qint64)t;
+ qint64 index;
+
+ if (pos.y() < m_data->headerRows() * m_rowHeight) {
+ int row = pos.y() / m_rowHeight;
+ index = m_data->headerItemAt(row, time);
+ } else {
+ int row = pos.y();
+ row -= m_data->headerRows() * m_rowHeight;
+ row += m_viewBottom;
+ row /= m_rowHeight;
+ index = m_data->dataItemAt(row, time);
+ }
+
+ return index;
+}
--- /dev/null
+#ifndef HEATMAPVIEW_H
+#define HEATMAPVIEW_H
+
+#include "graphview.h"
+
+/**
+ * The heatmap iterator will only return data when there is something to draw,
+ * this allows much faster access to the data in the case where the view is
+ * zoomed out to the point of where there is multiple calls in one pixel,
+ * it automagically calculates the heat for that pixel.
+ */
+class HeatmapRowIterator {
+public:
+ virtual ~HeatmapRowIterator(){}
+
+ /* Go to the next visible heat map */
+ virtual bool next() = 0;
+
+ /* Is the current value GPU or CPU heat */
+ virtual bool isGpu() const = 0;
+
+ /* Current step (normally x coordinate) */
+ virtual int step() const = 0;
+
+ /* Current width (in steps) */
+ virtual int width() const = 0;
+
+ /* Current heat */
+ virtual float heat() const = 0;
+
+ /* Heat value for selected calls */
+ virtual float selectedHeat() const = 0;
+
+ /* Label only used when there is a single call in this heat */
+ virtual QString label() const = 0;
+};
+
+
+/**
+ * Data provider for the whole heatmap
+ */
+class HeatmapDataProvider {
+public:
+ virtual ~HeatmapDataProvider(){}
+
+ /* The start and end values (time on x-axis) for the heatmap */
+ virtual qint64 start() const = 0;
+ virtual qint64 end() const = 0;
+
+ /*
+ * Header rows (fixed at top of heatmap view)
+ */
+
+ /* Header row count */
+ virtual unsigned headerRows() const = 0;
+
+ /* Label to be used in the vertical axis */
+ virtual QString headerLabel(unsigned row) const = 0;
+
+ /* Get identifier (program no) for row */
+ virtual qint64 headerRowAt(unsigned row) const = 0;
+
+ /* Get item at row and time */
+ virtual qint64 headerItemAt(unsigned row, qint64 time) const = 0;
+
+ /* Get iterator for a row between start and end time for steps */
+ virtual HeatmapRowIterator* headerRowIterator(int row, qint64 start, qint64 end, int steps) const = 0;
+
+ /*
+ * Data rows (scrollable in heatmap view)
+ */
+
+ /* Data row count */
+ virtual unsigned dataRows() const = 0;
+
+ /* Label to be used in the vertical axis */
+ virtual QString dataLabel(unsigned row) const = 0;
+
+ /* Get identifier (program no) for row */
+ virtual qint64 dataRowAt(unsigned row) const = 0;
+
+ /* Get item at row and time */
+ virtual qint64 dataItemAt(unsigned row, qint64 time) const = 0;
+
+ /* Get iterator for a row between start and end time for steps */
+ virtual HeatmapRowIterator* dataRowIterator(int row, qint64 start, qint64 end, int steps) const = 0;
+
+ /* Handle double click on item */
+ virtual void itemDoubleClicked(qint64 index) const = 0;
+
+ /* Get mouse over tooltip for item */
+ virtual QString itemTooltip(qint64 index) const = 0;
+
+ /* Set the selection */
+ virtual void setSelectionState(SelectionState* state) = 0;
+};
+
+
+/**
+ * A not very generic heatmap for row based data
+ */
+class HeatmapView : public GraphView {
+public:
+ HeatmapView(QWidget* parent);
+
+ void setDataProvider(HeatmapDataProvider* data);
+ void setSelectionState(SelectionState* state);
+
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+ virtual void paintEvent(QPaintEvent *e);
+ virtual void paintRow(QPainter& painter, HeatmapRowIterator* itr);
+
+
+protected:
+ qint64 itemAtPosition(QPoint pos);
+
+protected:
+ int m_rowHeight;
+ HeatmapDataProvider* m_data;
+};
+
+#endif
--- /dev/null
+#include "histogramview.h"
+
+#include <QPen>
+#include <QBrush>
+#include <qmath.h>
+#include <QPainter>
+#include <QToolTip>
+#include <QMouseEvent>
+
+HistogramView::HistogramView(QWidget* parent) :
+ GraphView(parent),
+ m_data(NULL)
+{
+ setMouseTracking(true);
+
+ m_gradientUnselected.setColorAt(0.9, QColor(200, 200, 200));
+ m_gradientUnselected.setColorAt(0.0, QColor(220, 220, 220));
+
+ m_gradientSelected.setColorAt(0.9, QColor(0, 0, 210));
+ m_gradientSelected.setColorAt(0.0, QColor(130, 130, 255));
+}
+
+
+void HistogramView::setDataProvider(GraphDataProvider* data)
+{
+ delete m_data;
+ m_data = data;
+
+ if (m_data) {
+ m_data->setSelectionState(m_selectionState);
+ setDefaultView(0, m_data->size());
+ m_viewWidthMin = 10;
+ } else {
+ setDefaultView(0, 0);
+ }
+}
+
+
+void HistogramView::setSelectionState(SelectionState* state)
+{
+ if (m_data) {
+ m_data->setSelectionState(state);
+ }
+
+ GraphView::setSelectionState(state);
+}
+
+
+void HistogramView::setSelectedGradient(const QLinearGradient& gradient)
+{
+ m_gradientSelected = gradient;
+}
+
+
+void HistogramView::setUnelectedGradient(const QLinearGradient& gradient)
+{
+ m_gradientUnselected = gradient;
+}
+
+
+void HistogramView::mouseMoveEvent(QMouseEvent *e)
+{
+ GraphView::mouseMoveEvent(e);
+
+ if (e->buttons() || !m_data) {
+ QToolTip::hideText();
+ return;
+ }
+
+ qint64 index = itemAtPosition(e->pos());
+ qint64 time = valueAtPosition(e->pos());
+
+ if (m_data->value(index) >= time) {
+ QToolTip::showText(e->globalPos(), m_data->itemTooltip(index));
+ } else {
+ QToolTip::hideText();
+ }
+}
+
+
+void HistogramView::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ if (e->button() == Qt::LeftButton) {
+ qint64 index = itemAtPosition(e->pos());
+ qint64 time = valueAtPosition(e->pos());
+
+ if (m_data->value(index) >= time) {
+ m_data->itemDoubleClicked(index);
+ return;
+ }
+ }
+
+ GraphView::mouseDoubleClickEvent(e);
+}
+
+
+void HistogramView::update()
+{
+ m_graphBottom = 0;
+ m_graphTop = 0;
+
+ if (m_data) {
+ for (qint64 i = m_viewLeft; i < m_viewRight; ++i) {
+ qint64 value = m_data->value(i);
+
+ if (value > m_graphTop) {
+ m_graphTop = value;
+ }
+ }
+ }
+
+ GraphView::update();
+}
+
+
+void HistogramView::resizeEvent(QResizeEvent *)
+{
+ m_gradientSelected.setStart(0, height());
+ m_gradientUnselected.setStart(0, height());
+}
+
+
+/* Draw the histogram
+ *
+ * When the view is zoomed such that there is more than one item occupying a single pixel
+ * the one with the highest value will be displayed.
+ */
+void HistogramView::paintEvent(QPaintEvent *)
+{
+ if (!m_data) {
+ return;
+ }
+
+ QBrush selectedBrush = QBrush(m_gradientSelected);
+ QPen selectedPen = QPen(selectedBrush, 1);
+
+ QBrush unselectedBrush = QBrush(m_gradientUnselected);
+ QPen unselectedPen = QPen(unselectedBrush, 1);
+
+ QPainter painter(this);
+ painter.fillRect(0, 0, width(), height(), Qt::white);
+
+ double dydv = height() / (double)m_graphTop;
+ double dxdv = width() / (double)(m_viewRight - m_viewLeft);
+ bool selection = m_selectionState && m_selectionState->type != SelectionState::None;
+
+ if (dxdv < 1.0) {
+ /* Less than one pixel per item */
+ qint64 longestValue = m_graphBottom;
+ qint64 longestSelected = m_graphBottom;
+ int lastX = 0;
+ double x = 0;
+
+ if (selection) {
+ painter.setPen(unselectedPen);
+ } else {
+ painter.setPen(selectedPen);
+ }
+
+ for (qint64 i = m_viewLeft; i < m_viewRight; ++i) {
+ qint64 value = m_data->value(i);
+ int ix;
+
+ if (value > longestValue) {
+ longestValue = value;
+ }
+
+ if (selection && m_data->selected(i) && value > longestSelected) {
+ longestSelected = value;
+ }
+
+ x += dxdv;
+ ix = (int)x;
+
+ if (lastX != ix) {
+ painter.drawLine(lastX, height(), lastX, height() - (longestValue * dydv));
+
+ if (selection && longestSelected > m_graphBottom) {
+ painter.setPen(selectedPen);
+ painter.drawLine(lastX, height(), lastX, height() - (longestSelected * dydv));
+ painter.setPen(unselectedPen);
+ longestSelected = m_graphBottom;
+ }
+
+ longestValue = m_graphBottom;
+ lastX = ix;
+ }
+ }
+ } else {
+ /* Draw rectangles for graph */
+ double x = 0;
+
+ for (qint64 i = m_viewLeft; i < m_viewRight; ++i, x += dxdv) {
+ qint64 value = m_data->value(i);
+ int y = qMax<int>(1, value * dydv);
+
+ if (!selection || m_data->selected(i)) {
+ painter.fillRect(x, height() - y, dxdv, y, selectedBrush);
+ } else {
+ painter.fillRect(x, height() - y, dxdv, y, unselectedBrush);
+ }
+ }
+ }
+
+ /* Draw the borders for the selection */
+ if (m_selectionState && m_selectionState->type == SelectionState::Horizontal) {
+ double dxdt = width() / (double)m_viewWidth;
+ double scroll = m_viewLeft * dxdt;
+ double left = (m_selectionState->start * dxdt) - scroll;
+ double right = (m_selectionState->end * dxdt) - scroll;
+
+ painter.setPen(Qt::green);
+
+ if (left >= 0 && left <= width()) {
+ painter.drawLine(left, 0, left, height());
+ }
+
+ if (right >= 0 && right <= width()) {
+ painter.drawLine(right, 0, right, height());
+ }
+ }
+}
+
+
+/* Find the item with the highest value at pos.x() +/- 1,
+ * the mouse must be within the bar height-wise.
+ */
+qint64 HistogramView::itemAtPosition(QPoint pos) {
+ double dvdx = m_viewWidth / (double)width();
+
+ qint64 left = qFloor(dvdx) * (pos.x() - 1) + m_viewLeft;
+ qint64 right = qCeil(dvdx) * (pos.x() + 1) + m_viewLeft;
+
+ qint64 longestIndex = 0;
+ qint64 longestValue = 0;
+
+ left = qBound<qint64>(0, left, m_data->size() - 1);
+ right = qBound<qint64>(0, right, m_data->size() - 1);
+
+ for (qint64 i = left; i <= right; ++i) {
+ if (m_data->value(i) > longestValue) {
+ longestValue = m_data->value(i);
+ longestIndex = i;
+ }
+ }
+
+ return longestIndex;
+}
+
+
+/* Return the value at position */
+qint64 HistogramView::valueAtPosition(QPoint pos) {
+ double value = m_graphTop / (double)height();
+ value *= height() - pos.y();
+ value += m_graphBottom;
+ return (qint64)value;
+}
+
--- /dev/null
+#ifndef HISTOGRAMVIEW_H
+#define HISTOGRAMVIEW_H
+
+#include "graphview.h"
+
+/**
+ * Histogram graph view.
+ *
+ * When the view is zoomed such that there is more than one item occupying
+ * a single pixel the one with the highest value will be displayed.
+ */
+class HistogramView : public GraphView {
+public:
+ HistogramView(QWidget* parent);
+
+ void setDataProvider(GraphDataProvider* data);
+ void setSelectionState(SelectionState* state);
+
+ /* Gradient colours for selected and unselected items */
+ void setSelectedGradient(const QLinearGradient& gradient);
+ void setUnelectedGradient(const QLinearGradient& gradient);
+
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+ virtual void update();
+ virtual void resizeEvent(QResizeEvent *e);
+ virtual void paintEvent(QPaintEvent *e);
+
+protected:
+ qint64 itemAtPosition(QPoint pos);
+ qint64 valueAtPosition(QPoint pos);
+
+protected:
+ QLinearGradient m_gradientSelected;
+ QLinearGradient m_gradientUnselected;
+
+ GraphDataProvider* m_data;
+};
+
+#endif
--- /dev/null
+#include "timeaxiswidget.h"
+#include "profiling.h"
+
+#include <qmath.h>
+#include <QPainter>
+
+TimeAxisWidget::TimeAxisWidget(QWidget* parent) :
+ GraphAxisWidget(parent)
+{
+}
+
+void TimeAxisWidget::paintEvent(QPaintEvent *)
+{
+ /* TODO: Support selections? */
+ /* TODO: Vertical scrolling? */
+
+ QPainter painter(this);
+ painter.setPen(Qt::black);
+ painter.setBrush(Qt::lightGray);
+ painter.drawRect(0, 0, width() - 1, height() - 1);
+
+ if (m_orientation == GraphAxisWidget::Vertical) {
+ int axisHeight = height() - 1;
+ int fontHeight = painter.fontMetrics().height();
+ int ticks = axisHeight / (fontHeight * 2);
+
+ double range = m_valueMax - m_valueMin;
+ double step = range / (double)ticks;
+ double step10 = qPow(10.0, qFloor(qLn(step) / qLn(10.0)));
+ step = qFloor((step / step10) * 2) * (step10 / 2);
+
+ if (step <= 0) {
+ return;
+ }
+
+ painter.resetTransform();
+
+ for (double tick = 0; tick <= range; tick += step) {
+ int y = axisHeight - ((tick / range) * axisHeight);
+
+ painter.drawLine(width() - 8, y, width(), y);
+
+ painter.drawText(0,
+ qBound(0, y - fontHeight / 2, axisHeight - fontHeight),
+ width() - 10,
+ fontHeight,
+ Qt::AlignRight | Qt::AlignVCenter,
+ Profiling::getTimeString(tick, m_valueMax));
+ }
+ } else {
+ int axisWidth = width() - 1;
+ int fontWidth = painter.fontMetrics().width("0.000 ns");
+ int fontHeight= painter.fontMetrics().height();
+ int ticks = axisWidth / (fontWidth * 2);
+
+ double range = m_valueMax - m_valueMin;
+ double step = range / (double)ticks;
+ double step10 = qPow(10.0, qFloor(qLn(step) / qLn(10.0)));
+ step = qFloor((step / step10) * 2) * (step10 / 2);
+
+ if (step <= 0) {
+ return;
+ }
+
+ painter.resetTransform();
+
+ for (double tick = 0; tick <= range; tick += step) {
+ int x = (tick / range) * axisWidth;
+
+ painter.drawLine(x, 0, x, 8);
+
+ painter.drawText(qBound(0, x - fontWidth / 2, axisWidth - fontWidth),
+ 8,
+ fontWidth,
+ fontHeight,
+ Qt::AlignHCenter | Qt::AlignTop,
+ Profiling::getTimeString(tick, m_valueMax));
+ }
+ }
+}
--- /dev/null
+#ifndef TIMEAXISWIDGET_H
+#define TIMEAXISWIDGET_H
+
+#include "graphaxiswidget.h"
+
+/**
+ * A simple axis that draws text using getTimeString to nicely format time values
+ */
+class TimeAxisWidget : public GraphAxisWidget {
+public:
+ TimeAxisWidget(QWidget* parent = 0);
+
+ virtual void paintEvent(QPaintEvent *e);
+};
+
+#endif
+++ /dev/null
-#include "graphwidget.h"
-#include "timelinewidget.h"
-#include "profiledialog.h"
-
-#include <qmath.h>
-#include <QLocale>
-#include <QPainter>
-#include <QToolTip>
-#include <QMouseEvent>
-#include <QApplication>
-
-typedef trace::Profile::Call Call;
-typedef trace::Profile::Frame Frame;
-typedef trace::Profile::Program Program;
-
-GraphWidget::GraphWidget(QWidget *parent)
- : QWidget(parent),
- m_profile(0),
- m_axisWidth(80),
- m_axisHeight(30),
- m_axisForeground(Qt::black),
- m_axisBackground(Qt::lightGray)
-{
- m_selection.type = SelectNone;
-
- m_graphGradientGpu.setColorAt(0.9, QColor(210, 0, 0));
- m_graphGradientGpu.setColorAt(0.0, QColor(255, 130, 130));
-
- m_graphGradientCpu.setColorAt(0.9, QColor(0, 0, 210));
- m_graphGradientCpu.setColorAt(0.0, QColor(130, 130, 255));
-
- m_graphGradientDeselected.setColorAt(0.9, QColor(200, 200, 200));
- m_graphGradientDeselected.setColorAt(0.0, QColor(220, 220, 220));
-
- setMouseTracking(true);
- setAutoFillBackground(true);
- setBackgroundRole(QPalette::Base);
-}
-
-
-/**
- * Setup graph data from profile results
- */
-void GraphWidget::setProfile(trace::Profile* profile, GraphType type)
-{
- m_type = type;
- m_profile = profile;
- m_timeMax = 0;
-
- /* Find longest call to use as y axis */
- if (m_type == GraphGpu) {
- for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
- const Call& call = *itr;
-
- if (call.gpuDuration > m_timeMax) {
- m_timeMax = call.gpuDuration;
- }
- }
- } else {
- for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
- const Call& call = *itr;
-
- if (call.cpuDuration > m_timeMax) {
- m_timeMax = call.cpuDuration;
- }
- }
- }
-
- m_callMin = 0;
- m_callMax = m_profile->calls.size();
-
- m_callWidthMin = 10;
- m_callWidthMax = m_callMax - m_callMin;
-
- m_call = m_callMin;
- m_callWidth = m_callWidthMax;
-
- selectNone();
- update();
-}
-
-
-/**
- * Set selection to nothing
- */
-void GraphWidget::selectNone(bool notify)
-{
- m_selection.type = SelectNone;
-
- if (notify) {
- emit selectedNone();
- }
-
- update();
-}
-
-
-/**
- * Set selection to a time period
- */
-void GraphWidget::selectTime(int64_t start, int64_t end, bool notify)
-{
- m_selection.timeStart = start;
- m_selection.timeEnd = end;
- m_selection.type = (start == end) ? SelectNone : SelectTime;
-
- if (notify) {
- emit selectedTime(start, end);
- }
-
- update();
-}
-
-
-/**
- * Set selection to a program
- */
-void GraphWidget::selectProgram(unsigned program, bool notify)
-{
- m_selection.program = program;
- m_selection.type = SelectProgram;
-
- if (notify) {
- emit selectedProgram(program);
- }
-
- update();
-}
-
-
-/**
- * Slot to synchronise with other graph views
- */
-void GraphWidget::changeView(int call, int width)
-{
- m_call = call;
- m_callWidth = width;
- update();
-}
-
-
-/**
- * Calculate the maxTime variable when model is updated
- */
-void GraphWidget::update()
-{
- m_timeMax = 0;
-
- if (m_type == GraphGpu) {
- for (int i = m_call; i < m_call + m_callWidth; ++i) {
- const Call& call = m_profile->calls[i];
-
- if (call.gpuDuration > m_timeMax) {
- m_timeMax = call.gpuDuration;
- }
- }
- } else {
- for (int i = m_call; i < m_call + m_callWidth; ++i) {
- const Call& call = m_profile->calls[i];
-
- if (call.cpuDuration > m_timeMax) {
- m_timeMax = call.cpuDuration;
- }
- }
- }
-
- QWidget::update();
-}
-
-
-/**
- * Find the call at (x, y) position
- */
-const Call* GraphWidget::callAtPosition(const QPoint& pos)
-{
- int left, right, size;
- int64_t time;
-
- if (!m_profile) {
- return NULL;
- }
-
- int posX = qMax(0, pos.x() - m_axisWidth);
- int posY = qMax(0, pos.y() - m_axisHeight);
-
- time = ((m_graphHeight - posY) / (double)m_graphHeight) * m_timeMax;
- time -= (2 * m_timeMax) / m_graphHeight;
-
- size = m_callWidth / (double)m_graphWidth;
-
- left = m_call + (posX / (double)m_graphWidth) * m_callWidth;
- left = qMax(m_callMin, left - size);
-
- right = qMin(m_callMax - 1, left + size * 2);
-
- if (m_type == GraphGpu) {
- const Call* longest = NULL;
-
- for (int i = left; i <= right; ++i) {
- const Call& call = m_profile->calls[i];
-
- if (call.pixels >= 0) {
- if (!longest || call.gpuDuration > longest->gpuDuration) {
- longest = &call;
- }
- }
- }
-
- if (longest && time < longest->gpuDuration) {
- return longest;
- }
- } else {
- const Call* longest = NULL;
-
- for (int i = left; i <= right; ++i) {
- const Call& call = m_profile->calls[i];
-
- if (!longest || call.cpuDuration > longest->cpuDuration) {
- longest = &call;
- }
- }
-
- if (longest && time < longest->cpuDuration) {
- return longest;
- }
- }
-
- return NULL;
-}
-
-
-void GraphWidget::mousePressEvent(QMouseEvent *e)
-{
- if (e->button() == Qt::LeftButton) {
- m_mousePressPosition = e->pos();
- m_mousePressCall = m_call;
- }
-}
-
-
-void GraphWidget::mouseReleaseEvent(QMouseEvent *e)
-{
- if (!m_profile) {
- return;
- }
-
- if (e->button() == Qt::LeftButton) {
- int dxy = qAbs(m_mousePressPosition.x() - e->pos().x()) + qAbs(m_mousePressPosition.y() - e->pos().y());
-
- if (dxy <= 2) {
- int x = qMax(m_axisWidth, e->pos().x());
- double dcdx = m_callWidth / (double)m_graphWidth;
-
- int call = m_mousePressCall + dcdx * (x - m_axisWidth);
-
- int64_t start = m_profile->calls[call].cpuStart;
- int64_t end = m_profile->calls[call].cpuStart + m_profile->calls[call].cpuDuration;
-
- if (start < m_selection.timeStart || end > m_selection.timeEnd) {
- selectNone(true);
- }
- }
- }
-}
-
-
-void GraphWidget::mouseDoubleClickEvent(QMouseEvent *e)
-{
- const Call* call = callAtPosition(e->pos());
-
- if (call) {
- emit jumpToCall(call->no);
- }
-}
-
-
-void GraphWidget::mouseMoveEvent(QMouseEvent *e)
-{
- if (!m_profile) {
- return;
- }
-
- if (e->buttons().testFlag(Qt::LeftButton)) {
- double dcdx = m_callWidth / (double)m_graphWidth;
-
- if (m_mousePressPosition.y() > m_axisHeight) {
- dcdx *= m_mousePressPosition.x() - e->pos().x();
-
- /* Horizontal scroll */
- m_call = m_mousePressCall + dcdx;
- m_call = qBound(m_callMin, m_call, m_callMax - m_callWidth);
-
- emit viewChanged(m_call, m_callWidth);
- update();
- } else {
- int x = qMax(m_axisWidth, e->pos().x());
-
- int down = m_mousePressCall + dcdx * (m_mousePressPosition.x() - m_axisWidth);
- int up = m_mousePressCall + dcdx * (x - m_axisWidth);
-
- int left = qMax(qMin(down, up), 0);
- int right = qMin<int>(qMax(down, up), m_profile->calls.size() - 1);
-
- selectTime(m_profile->calls[left].cpuStart, m_profile->calls[right].cpuStart + m_profile->calls[right].cpuDuration, true);
- }
- }
-
- const Call* call = callAtPosition(e->pos());
-
- if (e->button() == Qt::NoButton && call) {
- QString text;
- text = QString::fromStdString(call->name);
- text += QString("\nCall: %1").arg(call->no);
- text += QString("\nCPU Duration: %1").arg(getTimeString(call->cpuDuration));
-
- if (call->pixels >= 0) {
- text += QString("\nGPU Duration: %1").arg(getTimeString(call->gpuDuration));
- text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call->pixels));
- text += QString("\nProgram: %1").arg(call->program);
- }
-
- QToolTip::showText(e->globalPos(), text);
- } else {
- QToolTip::hideText();
- }
-}
-
-
-void GraphWidget::wheelEvent(QWheelEvent *e)
-{
- if (!m_profile) {
- return;
- }
-
- if (e->pos().x() < m_axisWidth) {
- return;
- }
-
- int zoomPercent = 10;
-
- /* If holding Ctrl key then zoom 2x faster */
- if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
- zoomPercent = 20;
- }
-
- /* Zoom view by adjusting width */
- double dt = m_callWidth;
- double size = dt * -e->delta();
-
- /* Zoom deltas normally come in increments of 120 */
- size /= 120 * (100 / zoomPercent);
-
- m_callWidth += size;
- m_callWidth = qBound(m_callWidthMin, m_callWidth, m_callWidthMax);
-
- /* Scroll view to zoom around mouse */
- dt -= m_callWidth;
- dt *= e->x() - m_axisWidth;
- dt /= m_graphWidth;
-
- m_call = dt + m_call;
- m_call = qBound(m_callMin, m_call, m_callMax - m_callWidth);
-
- emit viewChanged(m_call, m_callWidth);
- update();
-}
-
-
-void GraphWidget::resizeEvent(QResizeEvent *e)
-{
- m_graphWidth = qMax(0, width() - m_axisWidth);
- m_graphHeight = qMax(0, height() - m_axisHeight);
-
- m_graphGradientGpu.setStart(0, m_graphHeight);
- m_graphGradientCpu.setStart(0, m_graphHeight);
- m_graphGradientDeselected.setStart(0, m_graphHeight);
-}
-
-
-/**
- * Draw the vertical axis of time
- */
-void GraphWidget::paintVerticalAxis(QPainter& painter)
-{
- int height = painter.fontMetrics().height();
- int ticks = m_graphHeight / (height * 2);
-
- double step = m_timeMax / (double)ticks;
- double step10 = qPow(10.0, qFloor(qLn(step) / qLn(10.0)));
- step = qFloor((step / step10) * 2) * (step10 / 2);
-
- if (step <= 0) {
- return;
- }
-
- painter.resetTransform();
- painter.translate(0, m_axisHeight);
- painter.setPen(m_axisForeground);
-
- for (double tick = 0; tick <= m_timeMax; tick += step) {
- int y = m_graphHeight - ((tick / m_timeMax) * m_graphHeight);
-
- painter.drawLine(m_axisWidth - 8, y, m_axisWidth - 1, y);
-
- painter.drawText(0,
- qBound(0, y - height / 2, m_graphHeight - height),
- m_axisWidth - 10,
- height,
- Qt::AlignRight | Qt::AlignVCenter,
- getTimeString(tick, m_timeMax));
- }
-}
-
-
-/**
- * Draw horizontal axis of frame numbers
- */
-void GraphWidget::paintHorizontalAxis(QPainter& painter)
-{
- double dxdc = m_graphWidth / (double)m_callWidth;
- double scroll = dxdc * m_call;
- int lastLabel = -9999;
-
- painter.resetTransform();
- painter.fillRect(0, 0, width(), m_axisHeight, m_axisBackground);
- painter.fillRect(0, 0, m_axisWidth, height(), m_axisBackground);
-
- painter.setPen(m_axisForeground);
- painter.drawLine(0, m_axisHeight - 1, width(), m_axisHeight - 1);
- painter.drawLine(m_axisWidth - 1, 0, m_axisWidth - 1, height());
-
- painter.translate(m_axisWidth, 0);
-
- for (std::vector<Frame>::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) {
- static const int padding = 4;
- const Frame& frame = *itr;
- bool draw = true;
- int width;
-
- if (frame.calls.begin > m_call + m_callWidth) {
- break;
- }
-
- if (frame.calls.end < m_call) {
- draw = false;
- }
-
- double left = dxdc * frame.calls.begin;
- double right = dxdc * frame.calls.end;
- QString text = QString("%1").arg(frame.no);
-
- width = painter.fontMetrics().width(text) + padding * 2;
-
- if (left + width > scroll)
- draw = true;
-
- /* Draw a frame number if we have space since the last one */
- if (left - lastLabel > width) {
- lastLabel = left + width;
-
- if (draw) {
- int textX;
-
- if (left < scroll && right - left > width) {
- if (right - scroll > width) {
- textX = 0;
- } else {
- textX = right - scroll - width;
- }
- } else {
- textX = left - scroll;
- }
-
- /* Draw frame number and major ruler marking */
- painter.drawText(textX + padding, 0, width - padding, m_axisHeight - 5, Qt::AlignLeft | Qt::AlignVCenter, text);
- painter.drawLine(left - scroll, m_axisHeight / 2, left - scroll, m_axisHeight - 1);
- }
- } else if (draw) {
- /* Draw a minor ruler marking */
- painter.drawLine(left - scroll, m_axisHeight - (m_axisHeight / 4), left - scroll, m_axisHeight - 1);
- }
- }
-}
-
-
-void GraphWidget::paintEvent(QPaintEvent *e)
-{
- if (!m_profile) {
- return;
- }
-
- QPainter painter(this);
- QBrush deselectBrush;
- QPen deselectPen;
- QBrush brush;
- QPen pen;
-
- /* Draw axes */
- paintHorizontalAxis(painter);
- paintVerticalAxis(painter);
-
- /* Draw the label */
- painter.resetTransform();
- painter.fillRect(0, 0, m_axisWidth - 1, m_axisHeight - 1, Qt::lightGray);
-
- if (m_type == GraphGpu) {
- painter.drawText(0, 0, m_axisWidth, m_axisHeight, Qt::AlignHCenter | Qt::AlignVCenter, "GPU");
- } else {
- painter.drawText(0, 0, m_axisWidth, m_axisHeight, Qt::AlignHCenter | Qt::AlignVCenter, "CPU");
- }
-
- /* Draw graph */
- deselectBrush = QBrush(m_graphGradientDeselected);
-
- if (m_type == GraphGpu) {
- brush = QBrush(m_graphGradientGpu);
- } else {
- brush = QBrush(m_graphGradientCpu);
- }
-
- pen = QPen(brush, 1);
- deselectPen = QPen(deselectBrush, 1);
-
- painter.setBrush(brush);
- painter.setPen(pen);
- painter.translate(m_axisWidth, m_axisHeight);
-
- double x = 0;
- double dydt = m_graphHeight / (double)m_timeMax;
- double dxdc = m_graphWidth / (double)m_callWidth;
-
- int selectLeft = m_call + m_callWidth;
- int selectRight = -1;
-
- if (m_selection.type == SelectProgram) {
- painter.setPen(deselectPen);
- }
-
- if (dxdc < 1.0) {
- /* Draw the longest call in a pixel */
- int64_t selectedLongest = 0;
- int64_t pixelLongest = 0;
- int lastX = 0;
-
- for (int i = m_call; i < m_call + m_callWidth; ++i) {
- const Call& call = m_profile->calls[i];
- int ix;
-
- if (m_type == GraphGpu) {
- if (call.gpuDuration > pixelLongest) {
- pixelLongest = call.gpuDuration;
- }
-
- if (m_selection.type == SelectProgram && call.program == m_selection.program) {
- if (call.gpuDuration > selectedLongest) {
- selectedLongest = call.gpuDuration;
- }
- }
- } else {
- if (call.cpuDuration > pixelLongest) {
- pixelLongest = call.cpuDuration;
- }
-
- if (m_selection.type == SelectProgram && call.program == m_selection.program) {
- if (call.cpuDuration > selectedLongest) {
- selectedLongest = call.cpuDuration;
- }
- }
- }
-
- x += dxdc;
- ix = (int)x;
-
- if (lastX != ix) {
- if (m_selection.type == SelectTime) {
- if (call.cpuStart < m_selection.timeStart || call.cpuStart > m_selection.timeEnd) {
- painter.setPen(deselectPen);
- } else {
- if (ix < selectLeft) {
- selectLeft = ix;
- }
-
- if (ix > selectRight) {
- selectRight = ix;
- }
-
- painter.setPen(pen);
- }
- }
-
- painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (pixelLongest * dydt));
- pixelLongest = 0;
-
- if (selectedLongest > 0) {
- painter.setPen(pen);
- painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (selectedLongest * dydt));
- painter.setPen(deselectPen);
- selectedLongest = 0;
- }
-
- lastX = ix;
- }
- }
- } else {
- /* Draw rectangles for graph */
- for (int i = m_call; i < m_call + m_callWidth; ++i, x += dxdc) {
- const Call& call = m_profile->calls[i];
- int y;
-
- if (m_type == GraphGpu) {
- y = qMax<int>(1, call.gpuDuration * dydt);
- } else {
- y = qMax<int>(1, call.cpuDuration * dydt);
- }
-
- if (m_selection.type == SelectTime) {
- if (call.cpuStart < m_selection.timeStart || call.cpuStart > m_selection.timeEnd) {
- if (m_type == GraphCpu || call.pixels >= 0) {
- painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), deselectBrush);
- }
-
- continue;
- } else {
- if (x < selectLeft) {
- selectLeft = x;
- }
-
- if (x + dxdc > selectRight) {
- selectRight = x + dxdc;
- }
- }
- }
-
- if (m_type == GraphCpu || call.pixels >= 0) {
- if (m_selection.type == SelectProgram && call.program != m_selection.program) {
- painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), deselectBrush);
- } else {
- painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), brush);
- }
- }
- }
- }
-
- /* Draw the selection borders */
- if (m_selection.type == SelectTime && selectLeft < selectRight) {
- selectLeft += m_axisWidth;
- selectRight += m_axisWidth;
-
- painter.resetTransform();
- painter.setPen(Qt::green);
-
- if (m_profile->calls[m_call].cpuStart <= m_selection.timeStart) {
- painter.drawLine(selectLeft, 0, selectLeft, height());
- }
-
- if (m_profile->calls[m_call + m_callWidth - 1].cpuStart >= m_selection.timeEnd) {
- painter.drawLine(selectRight, 0, selectRight, height());
- }
-
- selectLeft = qBound(m_axisWidth, selectLeft, width());
- selectRight = qBound(m_axisWidth, selectRight, width());
- painter.drawLine(selectLeft, m_axisHeight - 1, selectRight, m_axisHeight - 1);
- }
-}
-
-#include "graphwidget.moc"
+++ /dev/null
-#ifndef GRAPHWIDGET_H
-#define GRAPHWIDGET_H
-
-#include <QPen>
-#include <QWidget>
-#include <QPainter>
-#include <QLinearGradient>
-#include "trace_profiler.hpp"
-
-enum GraphType {
- GraphGpu,
- GraphCpu
-};
-
-enum SelectType {
- SelectNone,
- SelectTime,
- SelectProgram
-};
-
-class GraphWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- GraphWidget(QWidget *parent = 0);
-
- void setProfile(trace::Profile* profile, GraphType type);
- const trace::Profile::Call* callAtPosition(const QPoint& pos);
-
- void selectNone(bool notify = false);
- void selectTime(int64_t start, int64_t end, bool notify = false);
- void selectProgram(unsigned program, bool notify = false);
-
-protected:
- virtual void paintEvent(QPaintEvent *e);
- virtual void resizeEvent(QResizeEvent *e);
-
- virtual void wheelEvent(QWheelEvent *e);
- virtual void mouseMoveEvent(QMouseEvent *e);
- virtual void mousePressEvent(QMouseEvent *e);
- virtual void mouseReleaseEvent(QMouseEvent *e);
- virtual void mouseDoubleClickEvent(QMouseEvent *e);
-
-signals:
- void jumpToCall(int no);
- void viewChanged(int call, int width);
-
- void selectedNone();
- void selectedProgram(unsigned program);
- void selectedTime(int64_t start, int64_t end);
-
-public slots:
- void changeView(int call, int width);
-
-private:
- void update();
-
- void paintVerticalAxis(QPainter& painter);
- void paintHorizontalAxis(QPainter& painter);
-
-private:
- /* Data */
- trace::Profile* m_profile;
- GraphType m_type;
-
- /* Vertical Axis */
- int64_t m_timeMax;
-
- /* Horizontal axis */
- int m_call;
- int m_callMin;
- int m_callMax;
- int m_callWidth;
- int m_callWidthMin;
- int m_callWidthMax;
-
- /* Viewport */
- int m_graphWidth;
- int m_graphHeight;
-
- /* Mouse track data */
- int m_mousePressCall;
- QPoint m_mousePressPosition;
-
- /* Style */
- int m_axisWidth;
- int m_axisHeight;
- QPen m_axisForeground;
- QBrush m_axisBackground;
- QLinearGradient m_graphGradientGpu;
- QLinearGradient m_graphGradientCpu;
- QLinearGradient m_graphGradientDeselected;
-
- struct {
- SelectType type;
-
- unsigned program;
-
- int64_t timeStart;
- int64_t timeEnd;
- } m_selection;
-};
-
-#endif // GRAPHWIDGET_H
#include "profiletablemodel.h"
#include <QSortFilterProxyModel>
-ProfileDialog::ProfileDialog(QWidget *parent)
- : QDialog(parent),
- m_profile(0)
-{
- setupUi(this);
-
- connect(m_timeline, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int)));
- connect(m_gpuGraph, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int)));
- connect(m_cpuGraph, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int)));
-}
+#include "graphing/histogramview.h"
+#include "graphing/timeaxiswidget.h"
+#include "graphing/frameaxiswidget.h"
+#include "graphing/heatmapverticalaxiswidget.h"
+#include "profileheatmap.h"
+/* Handy function to allow selection of a call in main window */
+ProfileDialog* g_profileDialog = 0;
-ProfileDialog::~ProfileDialog()
+void Profiling::jumpToCall(int index)
{
- delete m_profile;
+ if (g_profileDialog) {
+ g_profileDialog->showCall(index);
+ }
}
+/* Provides frame numbers based off call index */
+class FrameCallDataProvider : public FrameDataProvider {
+public:
+ FrameCallDataProvider(const trace::Profile* profile)
+ {
+ m_profile = profile;
+ }
-void ProfileDialog::tableDoubleClicked(const QModelIndex& index)
-{
- ProfileTableModel* model = (ProfileTableModel*)m_table->model();
- const trace::Profile::Call* call = model->getJumpCall(index);
+ unsigned size() const {
+ return m_profile->frames.size();
+ }
- if (call) {
- emit jumpToCall(call->no);
- } else {
- unsigned program = model->getProgram(index);
- m_timeline->selectProgram(program);
- m_cpuGraph->selectProgram(program);
- m_gpuGraph->selectProgram(program);
+ qint64 frameStart(unsigned index) const {
+ return m_profile->frames[index].calls.begin;
}
-}
+ qint64 frameEnd(unsigned index) const {
+ return m_profile->frames[index].calls.end;
+ }
-void ProfileDialog::setProfile(trace::Profile* profile)
-{
- delete m_profile;
+private:
+ const trace::Profile* m_profile;
+};
- if (profile->frames.size() == 0) {
- m_profile = NULL;
- } else {
+/* Provides frame numbers based off time */
+class FrameTimeDataProvider : public FrameDataProvider {
+public:
+ FrameTimeDataProvider(const trace::Profile* profile)
+ {
m_profile = profile;
- m_timeline->setProfile(m_profile);
- m_gpuGraph->setProfile(m_profile, GraphGpu);
- m_cpuGraph->setProfile(m_profile, GraphCpu);
+ }
- ProfileTableModel* model = new ProfileTableModel(m_table);
- model->setProfile(m_profile);
+ unsigned size() const {
+ return m_profile->frames.size();
+ }
- delete m_table->model();
- m_table->setModel(model);
- m_table->update(QModelIndex());
- m_table->sortByColumn(1, Qt::DescendingOrder);
- m_table->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
- m_table->resizeColumnsToContents();
+ qint64 frameStart(unsigned index) const {
+ return m_profile->frames[index].cpuStart;
}
-}
+ qint64 frameEnd(unsigned index) const {
+ return m_profile->frames[index].cpuStart + m_profile->frames[index].cpuDuration;
+ }
+
+private:
+ const trace::Profile* m_profile;
+};
-void ProfileDialog::selectNone()
+ProfileDialog::ProfileDialog(QWidget *parent)
+ : QDialog(parent),
+ m_profile(0)
{
- QObject* src = QObject::sender();
+ setupUi(this);
+ g_profileDialog = this;
- /* Update table model */
- ProfileTableModel* model = (ProfileTableModel*)m_table->model();
- model->selectNone();
- m_table->reset();
+ /* Gradients for call duration histograms */
+ QLinearGradient cpuGradient;
+ cpuGradient.setColorAt(0.9, QColor(0, 0, 210));
+ cpuGradient.setColorAt(0.0, QColor(130, 130, 255));
- /* Update graphs */
- if (src != m_gpuGraph) {
- m_gpuGraph->selectNone();
- }
+ QLinearGradient gpuGradient;
+ gpuGradient.setColorAt(0.9, QColor(210, 0, 0));
+ gpuGradient.setColorAt(0.0, QColor(255, 130, 130));
- if (src != m_cpuGraph) {
- m_cpuGraph->selectNone();
- }
- /* Update timeline */
- if (src != m_timeline) {
- m_timeline->selectNone();
- }
-}
+ /* Setup heatmap timeline */
+ m_timeline->setLabel(new GraphLabelWidget("Frames ", m_timeline));
+ m_timeline->label()->setFlags(Qt::AlignVCenter | Qt::AlignRight);
+ m_timeline->setView(new HeatmapView(m_timeline));
-void ProfileDialog::selectProgram(unsigned program)
-{
- QObject* src = QObject::sender();
+ m_timeline->setAxis(GraphWidget::AxisTop, new FrameAxisWidget(m_timeline));
+ m_timeline->setAxis(GraphWidget::AxisLeft, new HeatmapVerticalAxisWidget(m_timeline));
+ m_timeline->axis(GraphWidget::AxisLeft)->resize(80, 0);
- /* Update table model */
- ProfileTableModel* model = (ProfileTableModel*)m_table->model();
- model->selectNone();
- m_table->reset();
- m_table->selectRow(model->getRowIndex(program));
+ m_timeline->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_timeline->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- /* Update graphs */
- if (src != m_gpuGraph) {
- m_gpuGraph->selectProgram(program);
- }
- if (src != m_cpuGraph) {
- m_cpuGraph->selectProgram(program);
- }
+ /* Setup Cpu call duration histogram */
+ m_cpuGraph->setLabel(new GraphLabelWidget("CPU", m_cpuGraph));
- /* Update timeline */
- if (src != m_timeline) {
- m_timeline->selectProgram(program);
- }
+ m_cpuGraph->setAxis(GraphWidget::AxisTop, new FrameAxisWidget(m_cpuGraph));
+ m_cpuGraph->setAxis(GraphWidget::AxisLeft, new TimeAxisWidget(m_cpuGraph));
+ m_cpuGraph->axis(GraphWidget::AxisLeft)->resize(80, 0);
+
+ HistogramView* cpuView = new HistogramView(m_cpuGraph);
+ cpuView->setSelectedGradient(cpuGradient);
+ m_cpuGraph->setView(cpuView);
+
+
+ /* Setup Gpu call duration histogram */
+ m_gpuGraph->setLabel(new GraphLabelWidget("GPU", m_gpuGraph));
+
+ m_gpuGraph->setAxis(GraphWidget::AxisTop, new FrameAxisWidget(m_gpuGraph));
+ m_gpuGraph->setAxis(GraphWidget::AxisLeft, new TimeAxisWidget(m_gpuGraph));
+ m_gpuGraph->axis(GraphWidget::AxisLeft)->resize(80, 0);
+
+ HistogramView* gpuView = new HistogramView(m_gpuGraph);
+ gpuView->setSelectedGradient(gpuGradient);
+ m_gpuGraph->setView(gpuView);
+
+
+ /* Synchronise selections */
+ connect(m_timeline, SIGNAL(selectionChanged(SelectionState)), m_cpuGraph, SLOT(setSelection(SelectionState)));
+ connect(m_timeline, SIGNAL(selectionChanged(SelectionState)), m_gpuGraph, SLOT(setSelection(SelectionState)));
+
+ connect(m_cpuGraph, SIGNAL(selectionChanged(SelectionState)), m_timeline, SLOT(setSelection(SelectionState)));
+ connect(m_cpuGraph, SIGNAL(selectionChanged(SelectionState)), m_gpuGraph, SLOT(setSelection(SelectionState)));
+
+ connect(m_gpuGraph, SIGNAL(selectionChanged(SelectionState)), m_timeline, SLOT(setSelection(SelectionState)));
+ connect(m_gpuGraph, SIGNAL(selectionChanged(SelectionState)), m_cpuGraph, SLOT(setSelection(SelectionState)));
+
+ connect(m_timeline, SIGNAL(selectionChanged(SelectionState)), this, SLOT(graphSelectionChanged(SelectionState)));
+ connect(m_cpuGraph, SIGNAL(selectionChanged(SelectionState)), this, SLOT(graphSelectionChanged(SelectionState)));
+ connect(m_gpuGraph, SIGNAL(selectionChanged(SelectionState)), this, SLOT(graphSelectionChanged(SelectionState)));
+
+
+ /* Synchronise views between cpuGraph and gpuGraph */
+ connect(m_cpuGraph, SIGNAL(horizontalViewChanged(qint64,qint64)), m_gpuGraph, SLOT(setHorizontalView(qint64,qint64)));
+ connect(m_gpuGraph, SIGNAL(horizontalViewChanged(qint64,qint64)), m_cpuGraph, SLOT(setHorizontalView(qint64,qint64)));
}
-void ProfileDialog::selectTime(int64_t start, int64_t end)
+ProfileDialog::~ProfileDialog()
{
- QObject* src = QObject::sender();
+ delete m_profile;
+}
- /* Update table model */
- ProfileTableModel* model = (ProfileTableModel*)m_table->model();
- model->selectTime(start, end);
- m_table->reset();
- /* Update graphs */
- if (src != m_gpuGraph) {
- m_gpuGraph->selectTime(start, end);
- }
+void ProfileDialog::showCall(int call)
+{
+ emit jumpToCall(call);
+}
- if (src != m_cpuGraph) {
- m_cpuGraph->selectTime(start, end);
- }
- /* Update timeline */
- if (src != m_timeline) {
- m_timeline->selectTime(start, end);
+void ProfileDialog::tableDoubleClicked(const QModelIndex& index)
+{
+ ProfileTableModel* model = (ProfileTableModel*)m_table->model();
+
+ if (!model) {
+ return;
}
-}
+ const trace::Profile::Call* call = model->getJumpCall(index);
-void ProfileDialog::setVerticalScrollMax(int max)
-{
- if (max <= 0) {
- m_verticalScrollBar->hide();
+ if (call) {
+ emit jumpToCall(call->no);
} else {
- m_verticalScrollBar->show();
- m_verticalScrollBar->setMinimum(0);
- m_verticalScrollBar->setMaximum(max);
+ unsigned program = model->getProgram(index);
+
+ SelectionState state;
+ state.type = SelectionState::Vertical;
+ state.end = state.start = program;
+
+ m_timeline->setSelection(state);
+ m_cpuGraph->setSelection(state);
+ m_gpuGraph->setSelection(state);
}
}
-void ProfileDialog::setHorizontalScrollMax(int max)
+void ProfileDialog::setProfile(trace::Profile* profile)
{
- if (max <= 0) {
- m_horizontalScrollBar->hide();
- } else {
- m_horizontalScrollBar->show();
- m_horizontalScrollBar->setMinimum(0);
- m_horizontalScrollBar->setMaximum(max);
+
+ if (profile && profile->frames.size()) {
+ HeatmapVerticalAxisWidget* programAxis;
+ FrameAxisWidget* frameAxis;
+ HistogramView* histogram;
+ HeatmapView* heatmap;
+
+
+ /* Setup data providers for Cpu graph */
+ m_cpuGraph->setProfile(profile);
+ histogram = (HistogramView*)m_cpuGraph->view();
+ frameAxis = (FrameAxisWidget*)m_cpuGraph->axis(GraphWidget::AxisTop);
+
+ histogram->setDataProvider(new CallDurationDataProvider(profile, false));
+ frameAxis->setDataProvider(new FrameCallDataProvider(profile));
+
+ /* Setup data provider for Gpu graph */
+ m_gpuGraph->setProfile(profile);
+ histogram = (HistogramView*)m_gpuGraph->view();
+ frameAxis = (FrameAxisWidget*)m_gpuGraph->axis(GraphWidget::AxisTop);
+
+ histogram->setDataProvider(new CallDurationDataProvider(profile, true));
+ frameAxis->setDataProvider(new FrameCallDataProvider(profile));
+
+ /* Setup data provider for heatmap timeline */
+ heatmap = (HeatmapView*)m_timeline->view();
+ frameAxis = (FrameAxisWidget*)m_timeline->axis(GraphWidget::AxisTop);
+ programAxis = (HeatmapVerticalAxisWidget*)m_timeline->axis(GraphWidget::AxisLeft);
+
+ heatmap->setDataProvider(new ProfileHeatmapDataProvider(profile));
+ frameAxis->setDataProvider(new FrameTimeDataProvider(profile));
+ programAxis->setDataProvider(new ProfileHeatmapDataProvider(profile));
+
+ /* Setup data model for table view */
+ ProfileTableModel* model = new ProfileTableModel(m_table);
+ model->setProfile(profile);
+
+ delete m_table->model();
+ m_table->setModel(model);
+ m_table->update(QModelIndex());
+ m_table->sortByColumn(2, Qt::DescendingOrder);
+ m_table->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
+ m_table->resizeColumnsToContents();
+
+ /* Reset selection */
+ SelectionState emptySelection;
+ emptySelection.type = SelectionState::None;
+ m_cpuGraph->setSelection(emptySelection);
+ m_gpuGraph->setSelection(emptySelection);
+ m_timeline->setSelection(emptySelection);
}
+
+ delete m_profile;
+ m_profile = profile;
}
-/**
- * Convert a CPU / GPU time to a textual representation.
- * This includes automatic unit selection.
- */
-QString getTimeString(int64_t time, int64_t unitTime)
+void ProfileDialog::graphSelectionChanged(SelectionState state)
{
- QString text;
- QString unit = " ns";
- double unitScale = 1;
+ ProfileTableModel* model = (ProfileTableModel*)m_table->model();
- if (unitTime == 0) {
- unitTime = time;
+ if (!model) {
+ return;
}
- if (unitTime >= 60e9) {
- int64_t mins = time / 60e9;
- text += QString("%1 m ").arg(mins);
-
- time -= mins * 60e9;
- unit = " s";
- unitScale = 1e9;
- } else if (unitTime >= 1e9) {
- unit = " s";
- unitScale = 1e9;
- } else if (unitTime >= 1e6) {
- unit = " ms";
- unitScale = 1e6;
- } else if (unitTime >= 1e3) {
- unit = QString::fromUtf8(" µs");
- unitScale = 1e3;
+ if (state.type == SelectionState::None) {
+ model->selectNone();
+ } else if (state.type == SelectionState::Horizontal) {
+ model->selectTime(state.start, state.end);
+ } else if (state.type == SelectionState::Vertical) {
+ model->selectProgram(state.start);
}
- /* 3 decimal places */
- text += QString("%1").arg(time / unitScale, 0, 'f', 3);
-
- /* Remove trailing 0 */
- while(text.endsWith('0'))
- text.truncate(text.length() - 1);
-
- /* Remove trailing decimal point */
- if (text.endsWith(QLocale::system().decimalPoint()))
- text.truncate(text.length() - 1);
+ m_table->reset();
- return text + unit;
+ if (state.type == SelectionState::Vertical) {
+ m_table->selectRow(model->getRowIndex(state.start));
+ }
}
-
#include "profiledialog.moc"
~ProfileDialog();
void setProfile(trace::Profile* profile);
+ void showCall(int call);
public slots:
- void setVerticalScrollMax(int max);
- void setHorizontalScrollMax(int max);
-
void tableDoubleClicked(const QModelIndex& index);
-
- void selectNone();
- void selectProgram(unsigned program);
- void selectTime(int64_t start, int64_t end);
+ void graphSelectionChanged(SelectionState state);
signals:
void jumpToCall(int call);
trace::Profile *m_profile;
};
-QString getTimeString(int64_t time, int64_t unitTime = 0);
-
#endif
--- /dev/null
+#ifndef PROFILEHEATMAP_H
+#define PROFILEHEATMAP_H
+
+#include "graphing/heatmapview.h"
+#include "profiling.h"
+
+/**
+ * Data providers for a heatmap based off the trace::Profile call data
+ */
+
+class ProfileHeatmapRowIterator : public HeatmapRowIterator {
+public:
+ ProfileHeatmapRowIterator(const trace::Profile* profile, qint64 start, qint64 end, int steps, bool gpu, int program = -1) :
+ m_profile(profile),
+ m_step(-1),
+ m_stepWidth(1),
+ m_stepCount(steps),
+ m_index(0),
+ m_timeStart(start),
+ m_timeEnd(end),
+ m_useGpu(gpu),
+ m_program(program),
+ m_selected(false),
+ m_timeSelection(false),
+ m_programSelection(false)
+ {
+ m_timeWidth = m_timeEnd - m_timeStart;
+ }
+
+ virtual bool next()
+ {
+ unsigned maxIndex = m_program == -1 ? m_profile->calls.size() : m_profile->programs[m_program].calls.size();
+
+ if (m_index >= maxIndex) {
+ return false;
+ }
+
+ double dtds = m_timeWidth / (double)m_stepCount;
+
+ qint64 heatDuration = 0;
+ qint64 programHeatDuration = 0;
+ m_heat = 0.0f;
+ m_step += m_stepWidth;
+ m_stepWidth = 1;
+
+ m_selected = false;
+
+ /* Iterator through calls until step != lastStep */
+ for (; m_index < maxIndex; ++m_index)
+ {
+ const trace::Profile::Call* call;
+
+ if (m_program == -1) {
+ call = &m_profile->calls[m_index];
+ } else {
+ call = &m_profile->calls[ m_profile->programs[m_program].calls[m_index] ];
+ }
+
+ qint64 start, duration, end;
+
+ if (m_useGpu) {
+ start = call->gpuStart;
+ duration = call->gpuDuration;
+
+ if (call->pixels < 0) {
+ continue;
+ }
+ } else {
+ start = call->cpuStart;
+ duration = call->cpuDuration;
+ }
+
+ end = start + duration;
+
+ if (end < m_timeStart) {
+ continue;
+ }
+
+ if (start > m_timeEnd) {
+ m_index = m_profile->calls.size();
+ break;
+ }
+
+ double left = timeToStep(start);
+ double right = timeToStep(end);
+
+ int leftStep = left;
+ int rightStep = right;
+
+ if (leftStep > m_step) {
+ break;
+ }
+
+ if (m_programSelection && call->program == m_programSel) {
+ m_selected = true;
+ }
+
+ if (rightStep - leftStep > 1) {
+ m_label = QString::fromStdString(call->name);
+ m_step = left;
+ m_stepWidth = rightStep - leftStep;
+ heatDuration = dtds;
+ ++m_index;
+ break;
+ }
+
+ if (leftStep < m_step) {
+ qint64 rightTime = stepToTime(rightStep);
+ heatDuration += end - rightTime;
+
+ if (m_programSelection && call->program == m_programSel) {
+ programHeatDuration += end - rightTime;
+ }
+ } else if (leftStep == rightStep) {
+ heatDuration += duration;
+
+ if (m_programSelection && call->program == m_programSel) {
+ programHeatDuration += duration;
+ }
+ } else if (rightStep - leftStep == 1) {
+ qint64 rightTime = stepToTime(rightStep);
+ heatDuration += rightTime - start;
+
+ if (m_programSelection && call->program == m_programSel) {
+ programHeatDuration += rightTime - start;
+ }
+
+ break;
+ }
+ }
+
+ m_heat = heatDuration / dtds;
+ m_programHeat = programHeatDuration / dtds;
+
+ if (m_timeSelection) {
+ qint64 time = stepToTime(m_step);
+
+ if (time >= m_timeSelStart && time <= m_timeSelEnd) {
+ m_programHeat = 1.0;
+ }
+ }
+
+ if (m_programSelection && (m_program == m_programSel || (m_selected && m_stepWidth > 1))) {
+ m_programHeat = 1.0;
+ }
+
+ if (m_programHeat > 0) {
+ m_selected = true;
+ }
+
+ return true;
+ }
+
+ virtual bool isGpu() const
+ {
+ return m_useGpu;
+ }
+
+ virtual float heat() const
+ {
+ return m_heat;
+ }
+
+ virtual float selectedHeat() const
+ {
+ return m_programHeat;
+ }
+
+ virtual int step() const
+ {
+ return m_step;
+ }
+
+ virtual int width() const
+ {
+ return m_stepWidth;
+ }
+
+ virtual QString label() const
+ {
+ return m_label;
+ }
+
+ void setProgramSelection(int program)
+ {
+ m_programSelection = true;
+ m_programSel = program;
+ }
+
+ void setTimeSelection(qint64 start, qint64 end)
+ {
+ m_timeSelection = true;
+ m_timeSelStart = start;
+ m_timeSelEnd = end;
+ }
+
+private:
+ double timeToStep(qint64 time) const
+ {
+ double pos = time;
+ pos -= m_timeStart;
+ pos /= m_timeWidth;
+ pos *= m_stepCount;
+ return pos;
+ }
+
+ qint64 stepToTime(int pos) const
+ {
+ double time = pos;
+ time /= m_stepCount;
+ time *= m_timeWidth;
+ time += m_timeStart;
+ return (qint64)time;
+ }
+
+private:
+ const trace::Profile* m_profile;
+
+ int m_step;
+ int m_stepWidth;
+ int m_stepCount;
+
+ unsigned m_index;
+
+ float m_heat;
+
+ qint64 m_timeStart;
+ qint64 m_timeEnd;
+ qint64 m_timeWidth;
+
+ bool m_useGpu;
+ int m_program;
+
+ QString m_label;
+
+ bool m_selected;
+
+ bool m_timeSelection;
+ qint64 m_timeSelStart;
+ qint64 m_timeSelEnd;
+
+ bool m_programSelection;
+ int m_programSel;
+
+ float m_programHeat;
+};
+
+class ProfileHeatmapDataProvider : public HeatmapDataProvider {
+protected:
+ enum SelectionType {
+ None,
+ Time,
+ Program
+ };
+
+public:
+ ProfileHeatmapDataProvider(trace::Profile* profile) :
+ m_profile(profile),
+ m_selectionState(NULL)
+ {
+ sortRows();
+ }
+
+ virtual qint64 start() const
+ {
+ return m_profile->frames.front().cpuStart;
+ }
+
+ virtual qint64 end() const
+ {
+ return m_profile->frames.back().cpuStart + m_profile->frames.back().cpuDuration;
+ }
+
+ virtual unsigned dataRows() const
+ {
+ return m_rowPrograms.size();
+ }
+
+ virtual QString dataLabel(unsigned row) const
+ {
+ if (row >= m_rowPrograms.size()) {
+ return QString();
+ } else {
+ return QString("%1").arg(m_rowPrograms[row]);
+ }
+ }
+
+ virtual qint64 dataRowAt(unsigned row) const
+ {
+ if (row >= m_rowPrograms.size()) {
+ return 0;
+ } else {
+ return m_rowPrograms[row];
+ }
+ }
+
+ virtual HeatmapRowIterator* dataRowIterator(int row, qint64 start, qint64 end, int steps) const
+ {
+ ProfileHeatmapRowIterator* itr = new ProfileHeatmapRowIterator(m_profile, start, end, steps, true, m_rowPrograms[row]);
+
+ if (m_selectionState) {
+ if (m_selectionState->type == SelectionState::Horizontal) {
+ itr->setTimeSelection(m_selectionState->start, m_selectionState->end);
+ } else if (m_selectionState->type == SelectionState::Vertical) {
+ itr->setProgramSelection(m_selectionState->start);
+ }
+ }
+
+ return itr;
+ }
+
+ virtual unsigned headerRows() const
+ {
+ return 2;
+ }
+
+ virtual qint64 headerRowAt(unsigned row) const
+ {
+ return row;
+ }
+
+ virtual QString headerLabel(unsigned row) const
+ {
+ if (row == 0) {
+ return "CPU";
+ } else if (row == 1) {
+ return "GPU";
+ } else {
+ return QString();
+ }
+ }
+
+ virtual HeatmapRowIterator* headerRowIterator(int row, qint64 start, qint64 end, int steps) const
+ {
+ ProfileHeatmapRowIterator* itr = new ProfileHeatmapRowIterator(m_profile, start, end, steps, row != 0);
+
+ if (m_selectionState) {
+ if (m_selectionState->type == SelectionState::Horizontal) {
+ itr->setTimeSelection(m_selectionState->start, m_selectionState->end);
+ } else if (m_selectionState->type == SelectionState::Vertical) {
+ itr->setProgramSelection(m_selectionState->start);
+ }
+ }
+
+ return itr;
+ }
+
+ virtual qint64 dataItemAt(unsigned row, qint64 time) const
+ {
+ if (row >= m_rowPrograms.size()) {
+ return -1;
+ }
+
+ unsigned program = m_rowPrograms[row];
+
+ std::vector<unsigned>::const_iterator item =
+ Profiling::binarySearchTimespanIndexed
+ (m_profile->calls, m_profile->programs[program].calls.begin(), m_profile->programs[program].calls.end(), time);
+
+ if (item == m_profile->programs[program].calls.end()) {
+ return -1;
+ }
+
+ return *item;
+ }
+
+ virtual qint64 headerItemAt(unsigned row, qint64 time) const
+ {
+ if (row >= m_rowPrograms.size()) {
+ return -1;
+ }
+
+ if (row == 0) {
+ /* CPU */
+ std::vector<trace::Profile::Call>::const_iterator item =
+ Profiling::binarySearchTimespan<trace::Profile::Call,
+ &trace::Profile::Call::cpuStart,
+ &trace::Profile::Call::cpuDuration>
+ (m_profile->calls.begin(), m_profile->calls.end(), time);
+
+ if (item != m_profile->calls.end()) {
+ return item - m_profile->calls.begin();
+ }
+ } else if (row == 1) {
+ /* GPU */
+ for (unsigned i = 0; i < m_rowPrograms.size(); ++i) {
+ qint64 index = dataItemAt(i, time);
+
+ if (index != -1) {
+ return index;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ virtual void itemDoubleClicked(qint64 index) const
+ {
+ if (index < 0 || index >= m_profile->calls.size()) {
+ return;
+ }
+
+ const trace::Profile::Call& call = m_profile->calls[index];
+ Profiling::jumpToCall(call.no);
+ }
+
+ virtual QString itemTooltip(qint64 index) const
+ {
+ if (index >= m_profile->calls.size()) {
+ return QString();
+ }
+
+ const trace::Profile::Call& call = m_profile->calls[index];
+
+ QString text;
+ text = QString::fromStdString(call.name);
+
+ text += QString("\nCall: %1").arg(call.no);
+ text += QString("\nCPU Start: %1").arg(Profiling::getTimeString(call.cpuStart));
+ text += QString("\nCPU Duration: %1").arg(Profiling::getTimeString(call.cpuDuration));
+
+ if (call.pixels >= 0) {
+ text += QString("\nGPU Start: %1").arg(Profiling::getTimeString(call.gpuStart));
+ text += QString("\nGPU Duration: %1").arg(Profiling::getTimeString(call.gpuDuration));
+ text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call.pixels));
+ }
+
+ return text;
+ }
+
+ virtual void setSelectionState(SelectionState* state)
+ {
+ m_selectionState = state;
+ }
+
+private:
+ void sortRows()
+ {
+ typedef QPair<quint64, unsigned> Pair;
+ std::vector<Pair> gpu;
+
+ /* Map shader to visible row */
+ for (std::vector<trace::Profile::Program>::const_iterator itr = m_profile->programs.begin(); itr != m_profile->programs.end(); ++itr) {
+ const trace::Profile::Program& program = *itr;
+ unsigned no = itr - m_profile->programs.begin();
+
+ if (program.gpuTotal > 0) {
+ gpu.push_back(Pair(program.gpuTotal, no));
+ }
+ }
+
+ /* Sort the shaders by most used gpu */
+ qSort(gpu);
+
+ /* Create row order */
+ m_rowPrograms.clear();
+
+ for (std::vector<Pair>::const_reverse_iterator itr = gpu.rbegin(); itr != gpu.rend(); ++itr) {
+ m_rowPrograms.push_back(itr->second);
+ }
+ }
+
+protected:
+ trace::Profile* m_profile;
+ std::vector<int> m_rowPrograms;
+ SelectionState* m_selectionState;
+};
+
+#endif
#include "profiletablemodel.h"
#include "profiledialog.h"
+#include "profiling.h"
#include <QLocale>
case COLUMN_USAGES:
return QLocale::system().toString(row.uses);
case COLUMN_GPU_TIME:
- return getTimeString(row.gpuTime);
+ return Profiling::getTimeString(row.gpuTime);
case COLUMN_CPU_TIME:
- return getTimeString(row.cpuTime);
+ return Profiling::getTimeString(row.cpuTime);
case COLUMN_PIXELS_DRAWN:
return QLocale::system().toString((qlonglong)row.pixels);
case COLUMN_GPU_AVERAGE:
- return getTimeString((row.uses <= 0) ? 0 : (row.gpuTime / row.uses));
+ return Profiling::getTimeString((row.uses <= 0) ? 0 : (row.gpuTime / row.uses));
case COLUMN_CPU_AVERAGE:
- return getTimeString((row.uses <= 0) ? 0 : (row.cpuTime / row.uses));
+ return Profiling::getTimeString((row.uses <= 0) ? 0 : (row.cpuTime / row.uses));
case COLUMN_PIXELS_AVERAGE:
return QLocale::system().toString((row.uses <= 0) ? 0 : (row.pixels / row.uses));
}
--- /dev/null
+#ifndef PROFILING_H
+#define PROFILING_H
+
+#include <QString>
+#include <QLocale>
+#include "trace_profiler.hpp"
+
+class Profiling {
+public:
+ /**
+ * Select and show the call in main window.
+ */
+ static void jumpToCall(int index);
+
+ /**
+ * Convert a CPU / GPU time to a textual representation.
+ * This includes automatic unit selection.
+ */
+ static QString getTimeString(int64_t time, int64_t unitTime = 0)
+ {
+ QString text;
+ QString unit = " ns";
+ double unitScale = 1;
+
+ if (unitTime == 0) {
+ unitTime = time;
+ }
+
+ if (unitTime >= 60e9) {
+ int64_t mins = time / 60e9;
+ text += QString("%1 m ").arg(mins);
+
+ time -= mins * 60e9;
+ unit = " s";
+ unitScale = 1e9;
+ } else if (unitTime >= 1e9) {
+ unit = " s";
+ unitScale = 1e9;
+ } else if (unitTime >= 1e6) {
+ unit = " ms";
+ unitScale = 1e6;
+ } else if (unitTime >= 1e3) {
+ unit = QString::fromUtf8(" µs");
+ unitScale = 1e3;
+ }
+
+ /* 3 decimal places */
+ text += QString("%1").arg(time / unitScale, 0, 'f', 3);
+
+ /* Remove trailing 0 */
+ while(text.endsWith('0'))
+ text.truncate(text.length() - 1);
+
+ /* Remove trailing decimal point */
+ if (text.endsWith(QLocale::system().decimalPoint()))
+ text.truncate(text.length() - 1);
+
+ return text + unit;
+ }
+
+ template<typename val_ty, int64_t val_ty::* mem_ptr_start, int64_t val_ty::* mem_ptr_dura>
+ static typename std::vector<val_ty>::const_iterator binarySearchTimespan(
+ typename std::vector<val_ty>::const_iterator begin,
+ typename std::vector<val_ty>::const_iterator end,
+ int64_t time,
+ bool nearest = false)
+ {
+ int lower = 0;
+ int upper = end - begin;
+ int pos = (lower + upper) / 2;
+ typename std::vector<val_ty>::const_iterator itr = begin + pos;
+
+ while (!((*itr).*mem_ptr_start <= time && (*itr).*mem_ptr_start + (*itr).*mem_ptr_dura > time) && (lower <= upper)) {
+ if ((*itr).*mem_ptr_start > time) {
+ upper = pos - 1;
+ } else {
+ lower = pos + 1;
+ }
+
+ pos = (lower + upper) / 2;
+ itr = begin + pos;
+ }
+
+ if (nearest || lower <= upper) {
+ return itr;
+ } else {
+ return end;
+ }
+ }
+
+ static std::vector<unsigned>::const_iterator binarySearchTimespanIndexed(
+ const std::vector<trace::Profile::Call>& calls,
+ std::vector<unsigned>::const_iterator begin,
+ std::vector<unsigned>::const_iterator end,
+ int64_t time,
+ bool nearest = false)
+ {
+ int lower = 0;
+ int upper = end - begin - 1;
+ int pos = (lower + upper) / 2;
+ std::vector<unsigned>::const_iterator itr = begin + pos;
+
+ while (lower <= upper) {
+ const trace::Profile::Call& call = calls[*itr];
+
+ if (call.gpuStart <= time && call.gpuStart + call.gpuDuration > time) {
+ break;
+ }
+
+ if (call.gpuStart > time) {
+ upper = pos - 1;
+ } else {
+ lower = pos + 1;
+ }
+
+ pos = (lower + upper) / 2;
+ itr = begin + pos;
+ }
+
+ if (nearest || lower <= upper) {
+ return itr;
+ } else {
+ return end;
+ }
+ }
+};
+
+#endif
+++ /dev/null
-#include "timelinewidget.h"
-#include "profiledialog.h"
-#include "trace_profiler.hpp"
-
-#include <qmath.h>
-#include <QColor>
-#include <QLocale>
-#include <QPainter>
-#include <QToolTip>
-#include <QMouseEvent>
-#include <QWheelEvent>
-#include <QApplication>
-
-typedef trace::Profile::Call Call;
-typedef trace::Profile::Frame Frame;
-typedef trace::Profile::Program Program;
-
-TimelineWidget::TimelineWidget(QWidget *parent)
- : QWidget(parent),
- m_profile(NULL),
- m_rowHeight(20),
- m_axisWidth(50),
- m_axisHeight(30),
- m_axisLine(QColor(240, 240, 240)),
- m_axisBorder(Qt::black),
- m_axisForeground(Qt::black),
- m_axisBackground(QColor(210, 210, 210)),
- m_itemBorder(Qt::red),
- m_itemGpuForeground(Qt::cyan),
- m_itemGpuBackground(Qt::red),
- m_itemCpuForeground(QColor(255, 255, 0)),
- m_itemCpuBackground(QColor(0, 0, 255)),
- m_itemDeselectedForeground(Qt::white),
- m_itemDeselectedBackground(QColor(155, 155, 155)),
- m_selectionBorder(Qt::green),
- m_selectionBackground(QColor(100, 255, 100, 8)),
- m_zoomBorder(QColor(255, 0, 255)),
- m_zoomBackground(QColor(255, 0, 255, 30))
-{
- setBackgroundRole(QPalette::Base);
- setAutoFillBackground(true);
- setMouseTracking(true);
-
- m_selection.type = SelectNone;
-}
-
-
-/**
- * Update horizontal view scroll based on scroll value
- */
-void TimelineWidget::setHorizontalScrollValue(int scrollValue)
-{
- if (!m_profile) {
- return;
- }
-
- /* Calculate time from scroll value */
- double time = scrollValue;
- time /= m_maxScrollX;
- time *= (m_timeMax - m_timeWidth) - m_timeMin;
- time += m_timeMin;
-
- setTimeScroll(time, false);
-}
-
-
-/**
- * Update vertical view scroll based on scroll value
- */
-void TimelineWidget::setVerticalScrollValue(int value)
-{
- if (!m_profile) {
- return;
- }
-
- setRowScroll(value, false);
-}
-
-
-/**
- * Set selection to nothing
- */
-void TimelineWidget::selectNone(bool notify)
-{
- m_selection.type = SelectNone;
-
- if (notify) {
- emit selectedNone();
- }
-
- update();
-}
-
-
-/**
- * Set selection to a program
- */
-void TimelineWidget::selectProgram(unsigned program, bool notify)
-{
- m_selection.program = program;
- m_selection.type = SelectProgram;
-
- if (notify) {
- emit selectedProgram(program);
- }
-
- update();
-}
-
-
-/**
- * Set selection to a period of time
- */
-void TimelineWidget::selectTime(int64_t start, int64_t end, bool notify)
-{
- m_selection.timeStart = start;
- m_selection.timeEnd = end;
- m_selection.type = SelectTime;
-
- if (notify) {
- emit selectedTime(start, end);
- }
-
- update();
-}
-
-
-/**
- * Convert time to view position
- */
-double TimelineWidget::timeToPosition(int64_t time)
-{
- double pos = time;
- pos -= m_time;
- pos /= m_timeWidth;
- pos *= m_viewWidth;
- return pos;
-}
-
-
-/**
- * Convert view position to time
- */
-int64_t TimelineWidget::positionToTime(int pos)
-{
- double time = pos;
- time /= m_viewWidth;
- time *= m_timeWidth;
- time += m_time;
- return (int64_t)time;
-}
-
-
-/**
- * Binary Search for a time in start+durations
- */
-template<typename val_ty, int64_t val_ty::* mem_ptr_start, int64_t val_ty::* mem_ptr_dura>
-typename std::vector<val_ty>::const_iterator binarySearchTimespan(
- typename std::vector<val_ty>::const_iterator begin,
- typename std::vector<val_ty>::const_iterator end,
- int64_t time)
-{
- int lower = 0;
- int upper = end - begin;
- int pos = (lower + upper) / 2;
- typename std::vector<val_ty>::const_iterator itr = begin + pos;
-
- while (!((*itr).*mem_ptr_start <= time && (*itr).*mem_ptr_start + (*itr).*mem_ptr_dura > time) && (lower <= upper)) {
- if ((*itr).*mem_ptr_start > time) {
- upper = pos - 1;
- } else {
- lower = pos + 1;
- }
-
- pos = (lower + upper) / 2;
- itr = begin + pos;
- }
-
- if (lower <= upper) {
- return itr;
- } else {
- return end;
- }
-}
-
-
-/**
- * Binary Search for a time in start+durations on an array of indices
- */
-std::vector<unsigned>::const_iterator binarySearchTimespanIndexed(
- const std::vector<Call>& calls,
- std::vector<unsigned>::const_iterator begin,
- std::vector<unsigned>::const_iterator end,
- int64_t time)
-{
- int lower = 0;
- int upper = end - begin - 1;
- int pos = (lower + upper) / 2;
- std::vector<unsigned>::const_iterator itr = begin + pos;
-
- while (lower <= upper) {
- const Call& call = calls[*itr];
-
- if (call.gpuStart <= time && call.gpuStart + call.gpuDuration > time) {
- break;
- }
-
- if (call.gpuStart > time) {
- upper = pos - 1;
- } else {
- lower = pos + 1;
- }
-
- pos = (lower + upper) / 2;
- itr = begin + pos;
- }
-
- if (lower <= upper) {
- return itr;
- } else {
- return end;
- }
-}
-
-
-/**
- * Find the frame at time
- */
-const Frame* TimelineWidget::frameAtTime(int64_t time)
-{
- if (!m_profile) {
- return NULL;
- }
-
- std::vector<Frame>::const_iterator res
- = binarySearchTimespan<Frame, &Frame::cpuStart, &Frame::cpuDuration>(
- m_profile->frames.begin(),
- m_profile->frames.end(),
- time);
-
- if (res != m_profile->frames.end()) {
- return &*res;
- }
-
- return NULL;
-}
-
-
-/**
- * Find the CPU call at time
- */
-const Call* TimelineWidget::cpuCallAtTime(int64_t time)
-{
- if (!m_profile) {
- return NULL;
- }
-
- std::vector<Call>::const_iterator res
- = binarySearchTimespan<Call, &Call::cpuStart, &Call::cpuDuration>(
- m_profile->calls.begin(),
- m_profile->calls.end(),
- time);
-
- if (res != m_profile->calls.end()) {
- return &*res;
- }
-
- return NULL;
-}
-
-
-/**
- * Find the draw call at time
- */
-const Call* TimelineWidget::drawCallAtTime(int64_t time)
-{
- if (!m_profile) {
- return NULL;
- }
-
- for (int i = 0; i < m_rowPrograms.size(); ++i) {
- const Call* call = drawCallAtTime(time, m_rowPrograms[i]);
-
- if (call) {
- return call;
- }
- }
-
- return NULL;
-}
-
-
-/**
- * Find the draw call at time for a selected program
- */
-const Call* TimelineWidget::drawCallAtTime(int64_t time, int program)
-{
- if (!m_profile) {
- return NULL;
- }
-
- std::vector<unsigned>::const_iterator res
- = binarySearchTimespanIndexed(
- m_profile->calls,
- m_profile->programs[program].calls.begin(),
- m_profile->programs[program].calls.end(),
- time);
-
- if (res != m_profile->programs[program].calls.end()) {
- return &m_profile->calls[*res];
- }
-
- return NULL;
-}
-
-
-/**
- * Calculate the row order by total gpu time per shader
- */
-void TimelineWidget::calculateRows()
-{
- typedef QPair<uint64_t, unsigned> Pair;
- std::vector<Pair> gpu;
-
- /* Map shader to visible row */
- for (std::vector<Program>::const_iterator itr = m_profile->programs.begin(); itr != m_profile->programs.end(); ++itr) {
- const Program& program = *itr;
- unsigned no = itr - m_profile->programs.begin();
-
- if (program.gpuTotal > 0) {
- gpu.push_back(Pair(program.gpuTotal, no));
- }
- }
-
- /* Sort the shaders by most used gpu */
- qSort(gpu);
-
- /* Create row order */
- m_rowPrograms.clear();
-
- for (std::vector<Pair>::const_reverse_iterator itr = gpu.rbegin(); itr != gpu.rend(); ++itr) {
- m_rowPrograms.push_back(itr->second);
- }
-
- m_rowCount = m_rowPrograms.size();
-}
-
-
-/**
- * Set the trace profile to use for the timeline
- */
-void TimelineWidget::setProfile(trace::Profile* profile)
-{
- if (!profile->frames.size())
- return;
-
- m_profile = profile;
- calculateRows();
-
- m_timeMin = m_profile->frames.front().cpuStart;
- m_timeMax = m_profile->frames.back().cpuStart + m_profile->frames.back().cpuDuration;
-
- m_time = m_timeMin;
- m_timeWidth = m_timeMax - m_timeMin;
-
- m_timeWidthMin = 1000;
- m_timeWidthMax = m_timeWidth;
-
- m_maxScrollX = 0;
- m_maxScrollY = qMax(0, (m_rowCount * m_rowHeight) - m_viewHeight);
-
- setTimeScroll(m_time);
- setRowScroll(0);
- selectNone();
- update();
-}
-
-
-/**
- * Set the horizontal scroll position to time
- */
-void TimelineWidget::setTimeScroll(int64_t time, bool notify)
-{
- time = qBound(m_timeMin, time, m_timeMax - m_timeWidth);
-
- m_time = time;
-
- if (m_timeWidth == m_timeWidthMax) {
- m_maxScrollX = 0;
- } else {
- m_maxScrollX = 10000;
- }
-
- if (notify) {
- double value = time - m_timeMin;
- value /= m_timeMax - m_timeWidth - m_timeMin;
- value *= m_maxScrollX;
- m_scrollX = value;
-
- emit horizontalScrollMaxChanged(m_maxScrollX);
- emit horizontalScrollValueChanged(m_scrollX);
- }
-
- update();
-}
-
-
-/**
- * Set the vertical scroll position to position
- */
-void TimelineWidget::setRowScroll(int position, bool notify)
-{
- position = qBound(0, position, m_maxScrollY);
-
- m_scrollY = position;
- m_row = m_scrollY / m_rowHeight;
-
- if (notify) {
- emit verticalScrollMaxChanged(m_maxScrollY);
- emit verticalScrollValueChanged(m_scrollY);
- }
-
- update();
-}
-
-
-void TimelineWidget::resizeEvent(QResizeEvent *e)
-{
- /* Update viewport size */
- m_viewWidth = qMax(0, width() - m_axisWidth);
- m_viewHeight = qMax(0, height() - m_axisHeight - m_rowHeight * 2);
-
- /* Update vertical scroll bar */
- if (m_profile) {
- m_maxScrollY = qMax(0, (m_rowCount * m_rowHeight) - m_viewHeight);
- emit verticalScrollMaxChanged(m_maxScrollY);
- setRowScroll(m_scrollY);
- }
-}
-
-
-void TimelineWidget::mouseMoveEvent(QMouseEvent *e)
-{
- bool tooltip = false;
- m_mousePosition = e->pos();
-
- if (!m_profile) {
- return;
- }
-
- /* Display tooltip if necessary */
- if (e->buttons() == Qt::NoButton) {
- if (m_mousePosition.x() > m_axisWidth && m_mousePosition.y() > m_axisHeight) {
- int64_t time = positionToTime(m_mousePosition.x() - m_axisWidth);
- int y = m_mousePosition.y() - m_axisHeight;
-
- if (y < m_rowHeight) {
- const Call* call = cpuCallAtTime(time);
-
- if (call) {
- QString text;
- text = QString::fromStdString(call->name);
- text += QString("\nCall: %1").arg(call->no);
- text += QString("\nCPU Start: %1").arg(getTimeString(call->cpuStart));
- text += QString("\nCPU Duration: %1").arg(getTimeString(call->cpuDuration));
-
- QToolTip::showText(e->globalPos(), text);
- tooltip = true;
- }
- } else {
- const Call* call = NULL;
-
- if (y < m_rowHeight * 2) {
- call = drawCallAtTime(time);
- } else {
- int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
-
- if (row < m_rowPrograms.size()) {
- call = drawCallAtTime(time, m_rowPrograms[row]);
- }
- }
-
- if (call) {
- QString text;
- text = QString::fromStdString(call->name);
- text += QString("\nCall: %1").arg(call->no);
- text += QString("\nCPU Start: %1").arg(getTimeString(call->cpuStart));
- text += QString("\nGPU Start: %1").arg(getTimeString(call->gpuStart));
- text += QString("\nCPU Duration: %1").arg(getTimeString(call->cpuDuration));
- text += QString("\nGPU Duration: %1").arg(getTimeString(call->gpuDuration));
- text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call->pixels));
-
- QToolTip::showText(e->globalPos(), text);
- tooltip = true;
- }
- }
- } else if (m_mousePosition.x() < m_axisWidth && m_mousePosition.y() > m_axisHeight) {
- int y = m_mousePosition.y() - m_axisHeight;
-
- if (y < m_rowHeight) {
- QToolTip::showText(e->globalPos(), "All CPU calls");
- tooltip = true;
- } else if (y < m_rowHeight * 2) {
- QToolTip::showText(e->globalPos(), "All GPU calls");
- tooltip = true;
- } else {
- int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
-
- if (row < m_rowPrograms.size()) {
- QToolTip::showText(e->globalPos(), QString("All calls in Shader Program %1").arg(m_rowPrograms[row]));
- tooltip = true;
- }
- }
- }
- } else if (e->buttons().testFlag(Qt::LeftButton)) {
- if (m_mousePressMode == DragView) {
- /* Horizontal scroll */
- double dt = m_timeWidth;
- dt /= m_viewWidth;
- dt *= m_mousePressPosition.x() - e->pos().x();
- setTimeScroll(m_mousePressTime + dt);
-
- /* Vertical scroll */
- int dy = m_mousePressPosition.y() - e->pos().y();
- setRowScroll(m_mousePressRow + dy);
- } else if (m_mousePressMode == RulerSelect) {
- /* Horizontal selection */
- int64_t down = positionToTime(m_mousePressPosition.x() - m_axisWidth);
- int64_t up = positionToTime(qMax(e->pos().x() - m_axisWidth, 0));
-
- selectTime(qMin(down, up), qMax(down, up), true);
- }
-
- update();
- }
-
- if (!tooltip) {
- QToolTip::hideText();
- }
-}
-
-
-void TimelineWidget::mousePressEvent(QMouseEvent *e)
-{
- if (e->buttons() & Qt::LeftButton) {
- if (e->pos().y() < m_axisHeight && e->pos().x() >= m_axisWidth) {
- if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
- m_mousePressMode = RulerZoom;
- } else {
- m_mousePressMode = RulerSelect;
- }
- } else if (e->pos().x() >= m_axisWidth) {
- m_mousePressMode = DragView;
- } else {
- m_mousePressMode = NoMousePress;
- }
-
- m_mousePressPosition = e->pos();
- m_mousePressTime = m_time;
- m_mousePressRow = m_scrollY;
-
- update();
- }
-}
-
-
-void TimelineWidget::mouseReleaseEvent(QMouseEvent *e)
-{
- if (!m_profile) {
- return;
- }
-
- /* Calculate new time view based on selected area */
- int dxy = qAbs(m_mousePressPosition.x() - e->pos().x()) + qAbs(m_mousePressPosition.y() - e->pos().y());
-
- int64_t down = positionToTime(m_mousePressPosition.x() - m_axisWidth);
- int64_t up = positionToTime(qMax(e->pos().x() - m_axisWidth, 0));
-
- int64_t left = qMin(down, up);
- int64_t right = qMax(down, up);
-
- if (m_mousePressMode == RulerZoom) {
- m_timeWidth = right - left;
- m_timeWidth = qBound(m_timeWidthMin, m_timeWidth, m_timeWidthMax);
-
- m_mousePressMode = NoMousePress;
- setTimeScroll(left);
- } else {
- if (dxy <= 2) {
- if (m_selection.type == SelectTime) {
- if (left < m_selection.timeStart || right > m_selection.timeEnd || e->pos().x() < m_axisWidth) {
- selectNone(true);
- }
- } else if (m_selection.type == SelectProgram) {
- int y = e->pos().y() - m_axisHeight;
- int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
-
- if (row < 0 || m_rowPrograms[row] != m_selection.program) {
- selectNone(true);
- }
- }
- } else if (m_mousePressMode == RulerSelect) {
- selectTime(left, right, true);
- }
- }
-}
-
-
-void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *e)
-{
- int64_t time = positionToTime(e->pos().x() - m_axisWidth);
-
- if (e->pos().x() > m_axisWidth) {
- int row = (e->pos().y() - m_axisHeight) / m_rowHeight;
-
- if (e->pos().y() < m_axisHeight) {
- /* Horizontal axis */
- const Frame* frame = frameAtTime(time);
-
- if (frame) {
- selectTime(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true);
- return;
- }
- } else if (row == 0) {
- /* CPU Calls */
- const Call* call = cpuCallAtTime(time);
-
- if (call) {
- emit jumpToCall(call->no);
- return;
- }
- } else if (row > 0) {
- /* Draw Calls */
- const Call* call = drawCallAtTime(time, 0);
-
- if (call) {
- emit jumpToCall(call->no);
- return;
- }
- }
- } else {
- int y = e->pos().y() - m_axisHeight;
- int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
-
- if (row >= 0 && row < m_rowPrograms.size()) {
- selectProgram(m_rowPrograms[row], true);
- }
- }
-}
-
-
-void TimelineWidget::wheelEvent(QWheelEvent *e)
-{
- if (!m_profile) {
- return;
- }
-
- if (e->pos().x() < m_axisWidth) {
- return;
- }
-
- int zoomPercent = 10;
-
- /* If holding Ctrl key then zoom 2x faster */
- if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
- zoomPercent = 20;
- }
-
- /* Zoom view by adjusting width */
- double dt = m_timeWidth;
- double size = m_timeWidth;
- size *= -e->delta();
-
- /* Zoom deltas normally come in increments of 120 */
- size /= 120 * (100 / zoomPercent);
-
- m_timeWidth += size;
- m_timeWidth = qBound(m_timeWidthMin, m_timeWidth, m_timeWidthMax);
-
- /* Scroll view to zoom around mouse */
- dt -= m_timeWidth;
- dt *= e->x() - m_axisWidth;
- dt /= m_viewWidth;
- setTimeScroll(dt + m_time);
-
- update();
-}
-
-
-/**
- * Paints a single pixel column of the heat map
- */
-void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool gpu, bool selected)
-{
- if (heat == 0) {
- return;
- }
-
- if (m_selection.type == SelectTime) {
- selected = x >= m_selectionLeft && x <= m_selectionRight;
- }
-
- double timePerPixel = m_timeWidth / (double)m_viewWidth;
- double colour = heat / timePerPixel;
-
- /* Gamma correction */
- colour = qPow(colour, 1.0 / 2.0);
-
- if (!selected) {
- colour = qBound(0.0, colour * 100.0, 100.0);
- painter.setPen(QColor(255 - colour, 255 - colour, 255 - colour));
- } else if (gpu) {
- colour = qBound(0.0, colour * 255.0, 255.0);
- painter.setPen(QColor(255, 255 - colour, 255 - colour));
- } else {
- colour = qBound(0.0, colour * 255.0, 255.0);
- painter.setPen(QColor(255 - colour, 255 - colour, 255));
- }
-
- painter.drawLine(x, 0, x, m_rowHeight - 1);
-}
-
-
-/**
- * Draws a call on the heatmap
- */
-bool TimelineWidget::drawCall(QPainter& painter, const trace::Profile::Call& call, int& lastX, int64_t& heat, bool gpu)
-{
- int64_t start, duration, end;
-
- if (gpu) {
- start = call.gpuStart;
- duration = call.gpuDuration;
- } else {
- start = call.cpuStart;
- duration = call.cpuDuration;
- }
-
- end = start + duration;
-
- if (start > m_timeEnd) {
- return false;
- }
-
- if (end < m_time) {
- return true;
- }
-
- double left = timeToPosition(start);
- double right = timeToPosition(end);
-
- int leftX = left;
- int rightX = right;
-
- bool selected = true;
-
- if (m_selection.type == SelectProgram) {
- selected = call.program == m_selection.program;
- }
-
- /* Draw last heat if needed */
- if (leftX != lastX) {
- drawHeat(painter, lastX, heat, gpu, selected);
- lastX = leftX;
- heat = 0;
- }
-
- if (rightX <= leftX + 1) {
- if (rightX == lastX) {
- /* Fully contained in this X */
- heat += duration;
- } else {
- /* Split call time between the two pixels it occupies */
- int64_t time = positionToTime(rightX);
- heat += time - start;
-
- drawHeat(painter, lastX, heat, gpu, selected);
-
- heat = end - time;
- lastX = rightX;
- }
- } else {
- QRect rect;
- rect.setLeft(left + 0.5);
- rect.setWidth(right - left);
- rect.setTop(0);
- rect.setHeight(m_rowHeight);
-
- if (m_selection.type == SelectTime) {
- selected = (start >= m_selection.timeStart && start <= m_selection.timeEnd)
- || (end >= m_selection.timeStart && end <= m_selection.timeEnd);
- }
-
- /* Draw background rect */
- if (selected) {
- if (gpu) {
- painter.fillRect(rect, m_itemGpuBackground);
- } else {
- painter.fillRect(rect, m_itemCpuBackground);
- }
- } else {
- painter.fillRect(rect, m_itemDeselectedBackground);
- }
-
- /* If wide enough, draw text */
- if (rect.width() > 6) {
- rect.adjust(1, 0, -1, -2);
-
- if (selected) {
- if (gpu) {
- painter.setPen(m_itemGpuForeground);
- } else {
- painter.setPen(m_itemCpuForeground);
- }
- } else {
- painter.setPen(m_itemDeselectedForeground);
- }
-
- painter.drawText(rect,
- Qt::AlignLeft | Qt::AlignVCenter,
- painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width()));
- }
- }
-
- return true;
-}
-
-
-/**
- * Render the whole widget
- */
-void TimelineWidget::paintEvent(QPaintEvent *e)
-{
- if (!m_profile)
- return;
-
- QPainter painter(this);
-
- int rowEnd = qMin(m_row + qCeil(m_viewHeight / (double)m_rowHeight) + 1, m_rowCount);
- int64_t heatGPU = 0, heatCPU = 0;
- int lastCpuX = 0, lastGpuX = 0;
- int widgetHeight = height();
- int widgetWidth = width();
-
- m_timeEnd = m_time + m_timeWidth;
- m_selectionLeft = timeToPosition(m_selection.timeStart);
- m_selectionRight = (timeToPosition(m_selection.timeEnd) + 0.5);
-
-
- /* Draw program rows */
- painter.translate(m_axisWidth, m_axisHeight + m_rowHeight * 2 - (m_scrollY % m_rowHeight));
-
- for (int row = m_row; row < rowEnd; ++row) {
- Program& program = m_profile->programs[m_rowPrograms[row]];
- lastGpuX = 0;
- heatGPU = 0;
-
- for (std::vector<unsigned>::const_iterator itr = program.calls.begin(); itr != program.calls.end(); ++itr) {
- const Call& call = m_profile->calls[*itr];
-
- if (!drawCall(painter, call, lastGpuX, heatGPU, true)) {
- break;
- }
- }
-
- painter.translate(0, m_rowHeight);
- }
-
-
- /* Draw CPU/GPU rows */
- painter.resetTransform();
- painter.translate(m_axisWidth, m_axisHeight);
- painter.fillRect(0, 0, m_viewWidth, m_rowHeight * 2, Qt::white);
-
- lastCpuX = lastGpuX = 0;
- heatCPU = heatGPU = 0;
-
- for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
- const Call& call = *itr;
-
- /* Draw gpu row */
- if (call.pixels >= 0) {
- painter.translate(0, m_rowHeight);
- drawCall(painter, call, lastGpuX, heatGPU, true);
- painter.translate(0, -m_rowHeight);
- }
-
- /* Draw cpu row */
- if (!drawCall(painter, call, lastCpuX, heatCPU, false)) {
- break;
- }
- }
-
-
- /* Draw axis */
- painter.resetTransform();
- painter.setPen(m_axisBorder);
-
- /* Top Rect */
- painter.fillRect(m_axisWidth - 1, 0, widgetWidth, m_axisHeight - 1, m_axisBackground);
- painter.drawLine(0, m_axisHeight - 1, widgetWidth, m_axisHeight - 1);
-
- /* Left Rect */
- painter.fillRect(0, m_axisHeight - 1, m_axisWidth - 1, widgetHeight, m_axisBackground);
- painter.drawLine(m_axisWidth - 1, 0, m_axisWidth - 1, widgetHeight);
-
-
- /* Draw the program numbers */
- painter.translate(0, m_axisHeight + m_rowHeight * 2);
-
- for (int row = m_row; row < rowEnd; ++row) {
- int y = (row - m_row) * m_rowHeight - (m_scrollY % m_rowHeight);
-
- painter.setPen(m_axisForeground);
- painter.drawText(0, y, m_axisWidth, m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, QString("%1").arg(m_rowPrograms[row]));
-
- if (m_selection.type == SelectProgram && m_selection.program == m_rowPrograms[row]) {
- painter.setPen(m_selectionBorder);
- painter.drawLine(0, qMax(0, y - 1), widgetWidth, qMax(0, y - 1));
- painter.drawLine(0, y + m_rowHeight - 1, widgetWidth, y + m_rowHeight - 1);
- painter.drawLine(m_axisWidth - 1, y - 1, m_axisWidth - 1, y + m_rowHeight - 1);
- } else {
- painter.setPen(m_axisBorder);
- painter.drawLine(0, y + m_rowHeight - 1, m_axisWidth - 1, y + m_rowHeight - 1);
-
- painter.setPen(m_axisLine);
- painter.drawLine(m_axisWidth, y + m_rowHeight - 1, widgetWidth, y + m_rowHeight - 1);
- }
- }
-
-
- /* Draw the "CPU" axis label */
- painter.resetTransform();
- painter.translate(0, m_axisHeight);
-
- painter.setPen(m_axisBorder);
- painter.setBrush(m_axisBackground);
- painter.drawRect(-1, -1, m_axisWidth, m_rowHeight);
-
- painter.setPen(m_axisForeground);
- painter.drawText(0, 0, m_axisWidth - 1, m_rowHeight - 1, Qt::AlignHCenter | Qt::AlignVCenter, "CPU");
-
- painter.setPen(m_axisBorder);
- painter.drawLine(m_axisWidth, m_rowHeight - 1, widgetWidth, m_rowHeight - 1);
-
-
- /* Draw the "GPU" axis label */
- painter.translate(0, m_rowHeight);
-
- painter.setPen(m_axisBorder);
- painter.setBrush(m_axisBackground);
- painter.drawRect(-1, -1, m_axisWidth, m_rowHeight);
-
- painter.setPen(m_axisForeground);
- painter.drawText(0, 0, m_axisWidth - 1, m_rowHeight - 1, Qt::AlignHCenter | Qt::AlignVCenter, "GPU");
-
- painter.setPen(m_axisBorder);
- painter.drawLine(m_axisWidth, m_rowHeight - 1, widgetWidth, m_rowHeight - 1);
-
-
- /* Draw the frame numbers */
- painter.resetTransform();
-
- painter.setPen(m_axisForeground);
- painter.translate(m_axisWidth, 0);
-
- int lastLabel = -999; /* Ensure first label gets drawn */
-
- double scroll = m_time;
- scroll /= m_timeWidth;
- scroll *= m_viewWidth;
-
- for (std::vector<Frame>::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) {
- static const int padding = 4;
- const Frame& frame = *itr;
- bool draw = true;
- int width;
-
- if (frame.cpuStart > m_timeEnd) {
- break;
- }
-
- if (frame.cpuStart + frame.cpuDuration < m_time) {
- draw = false;
- }
-
- double left = frame.cpuStart;
- left /= m_timeWidth;
- left *= m_viewWidth;
-
- double right = frame.cpuStart + frame.cpuDuration;
- right /= m_timeWidth;
- right *= m_viewWidth;
-
- QString text = QString("%1").arg(frame.no);
-
- width = painter.fontMetrics().width(text) + padding * 2;
-
- if (left + width > scroll)
- draw = true;
-
- /* Draw a frame number if we have space since the last one */
- if (left - lastLabel > width) {
- lastLabel = left + width;
-
- if (draw) {
- int textX;
- painter.setPen(m_axisForeground);
-
- if (left < scroll && right - left > width) {
- if (right - scroll > width) {
- textX = 0;
- } else {
- textX = right - scroll - width;
- }
- } else {
- textX = left - scroll;
- }
-
- /* Draw frame number and major ruler marking */
- painter.drawText(textX + padding, 0, width - padding, m_axisHeight - 5, Qt::AlignLeft | Qt::AlignVCenter, text);
- painter.drawLine(left - scroll, m_axisHeight / 2, left - scroll, m_axisHeight - 1);
- }
- } else if (draw) {
- /* Draw a minor ruler marking */
- painter.drawLine(left - scroll, m_axisHeight - (m_axisHeight / 4), left - scroll, m_axisHeight - 1);
- }
- }
-
-
- /* Draw "Frame" axis label */
- painter.resetTransform();
-
- painter.setPen(m_axisBorder);
- painter.setBrush(m_axisBackground);
- painter.drawRect(-1, -1, m_axisWidth, m_axisHeight);
-
- painter.setPen(m_axisForeground);
- painter.drawText(0, 0, m_axisWidth - 1, m_axisHeight - 1, Qt::AlignHCenter | Qt::AlignVCenter, "Frame");
-
-
- /* Draw the active selection border */
- if (m_selection.type == SelectTime) {
- painter.setPen(m_selectionBorder);
-
- m_selectionLeft += m_axisWidth;
- m_selectionRight += m_axisWidth;
-
- if (m_selectionLeft >= m_axisWidth && m_selectionLeft < widgetWidth) {
- painter.drawLine(m_selectionLeft, 0, m_selectionLeft, widgetHeight);
- }
-
- if (m_selectionRight >= m_axisWidth && m_selectionRight < widgetWidth) {
- painter.drawLine(m_selectionRight, 0, m_selectionRight, widgetHeight);
- }
-
- m_selectionLeft = qBound(m_axisWidth, m_selectionLeft, widgetWidth);
- m_selectionRight = qBound(m_axisWidth, m_selectionRight, widgetWidth);
-
- painter.drawLine(m_selectionLeft, m_axisHeight - 1, m_selectionRight, m_axisHeight - 1);
- painter.fillRect(m_selectionLeft, 0, m_selectionRight - m_selectionLeft, widgetHeight, m_selectionBackground);
- }
-
-
- /* Draw the ruler zoom */
- if (m_mousePressMode == RulerZoom) {
- int x1 = m_mousePressPosition.x();
- int x2 = qMax(m_mousePosition.x(), m_axisWidth);
-
- painter.setPen(m_zoomBorder);
- painter.drawLine(x1, 0, x1, widgetHeight);
- painter.drawLine(x2, 0, x2, widgetHeight);
- painter.drawLine(x1, m_axisHeight - 1, x2, m_axisHeight - 1);
- painter.fillRect(x1, m_axisHeight, x2 - x1, widgetHeight, m_zoomBackground);
- }
-}
-
-#include "timelinewidget.moc"
+++ /dev/null
-#ifndef TIMELINEWIDGET_H
-#define TIMELINEWIDGET_H
-
-#include <QWidget>
-#include <QList>
-#include <QPen>
-#include "trace_profiler.hpp"
-
-class TimelineWidget : public QWidget
-{
- Q_OBJECT
-
- enum MousePressMode {
- NoMousePress,
- DragView,
- RulerZoom,
- RulerSelect
- };
-
- enum SelectType {
- SelectNone,
- SelectTime,
- SelectProgram
- };
-
-public:
- TimelineWidget(QWidget *parent = 0);
-
- void setProfile(trace::Profile* profile);
-
- void selectNone(bool notify = false);
- void selectProgram(unsigned program, bool notify = false);
- void selectTime(int64_t start, int64_t end, bool notify = false);
-
-protected:
- virtual void wheelEvent(QWheelEvent *e);
- virtual void mousePressEvent(QMouseEvent *e);
- virtual void mouseMoveEvent(QMouseEvent *e);
- virtual void mouseReleaseEvent(QMouseEvent *e);
- virtual void mouseDoubleClickEvent(QMouseEvent *e);
-
- virtual void paintEvent(QPaintEvent *e);
- virtual void resizeEvent(QResizeEvent *e);
-
-public slots:
- void setHorizontalScrollValue(int value);
- void setVerticalScrollValue(int value);
-
-signals:
- void verticalScrollMaxChanged(int max);
- void verticalScrollValueChanged(int value);
-
- void horizontalScrollMaxChanged(int max);
- void horizontalScrollValueChanged(int value);
-
- void jumpToCall(int call);
-
- void selectedNone();
- void selectedProgram(unsigned program);
- void selectedTime(int64_t start, int64_t end);
-
-private:
- void setRowScroll(int position, bool notify = true);
- void setTimeScroll(int64_t time, bool notify = true);
-
- bool drawCall(QPainter& painter, const trace::Profile::Call& call, int &lastX, int64_t &heat, bool gpu);
- void drawHeat(QPainter& painter, int x, int64_t heat, bool gpu, bool selected);
-
- double timeToPosition(int64_t time);
- int64_t positionToTime(int pos);
-
- void calculateRows();
-
- const trace::Profile::Frame* frameAtTime(int64_t time);
- const trace::Profile::Call* cpuCallAtTime(int64_t time);
- const trace::Profile::Call* drawCallAtTime(int64_t time);
- const trace::Profile::Call* drawCallAtTime(int64_t time, int program);
-
-private:
- /* Data */
- trace::Profile* m_profile;
- std::vector<int> m_rowPrograms;
-
- /* Scrollbars */
- int m_scrollX;
- int m_scrollY;
- int m_maxScrollX;
- int m_maxScrollY;
-
- /* Viewport */
- int m_viewWidth;
- int m_viewHeight;
-
- /* Visible Times */
- int64_t m_time;
- int64_t m_timeEnd;
- int64_t m_timeMin;
- int64_t m_timeMax;
- int64_t m_timeWidth;
- int64_t m_timeWidthMin;
- int64_t m_timeWidthMax;
-
- int m_selectionLeft;
- int m_selectionRight;
-
- /* Visible Rows */
- int m_row;
- int m_rowCount;
-
- /* Mouse data */
- int m_mousePressRow;
- int64_t m_mousePressTime;
- QPoint m_mousePosition;
- QPoint m_mousePressPosition;
- MousePressMode m_mousePressMode;
-
- /* Style */
- int m_rowHeight;
- int m_axisWidth;
- int m_axisHeight;
- QPen m_axisLine;
- QPen m_axisBorder;
- QPen m_axisForeground;
- QBrush m_axisBackground;
- QPen m_itemBorder;
- QPen m_itemGpuForeground;
- QBrush m_itemGpuBackground;
- QPen m_itemCpuForeground;
- QBrush m_itemCpuBackground;
- QPen m_itemDeselectedForeground;
- QBrush m_itemDeselectedBackground;
- QPen m_selectionBorder;
- QBrush m_selectionBackground;
- QPen m_zoomBorder;
- QBrush m_zoomBackground;
-
- /* Selection */
- struct {
- SelectType type;
-
- unsigned program;
-
- int64_t timeStart;
- int64_t timeEnd;
- } m_selection;
-};
-
-#endif // TIMELINEWIDGET_H
<property name="opaqueResize">
<bool>false</bool>
</property>
- <widget class="QWidget" name="timelineContainer" native="true">
+ <widget class="GraphWidget" name="m_timeline" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<property name="margin">
<number>0</number>
</property>
- <item>
- <layout class="QGridLayout" name="gridLayout">
- <property name="spacing">
- <number>2</number>
- </property>
- <item row="0" column="0">
- <widget class="TimelineWidget" name="m_timeline" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="focusPolicy">
- <enum>Qt::WheelFocus</enum>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QScrollBar" name="m_verticalScrollBar">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QScrollBar" name="m_horizontalScrollBar">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximum">
- <number>10000</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- </layout>
- </item>
</layout>
</widget>
<widget class="QWidget" name="graphContainer" native="true">
<number>0</number>
</property>
<item>
- <widget class="GraphWidget" name="m_gpuGraph" native="true">
+ <widget class="CallDurationGraph" name="m_gpuGraph" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
</widget>
</item>
<item>
- <widget class="GraphWidget" name="m_cpuGraph" native="true">
+ <widget class="CallDurationGraph" name="m_cpuGraph" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
</widget>
<customwidgets>
<customwidget>
- <class>TimelineWidget</class>
+ <class>GraphWidget</class>
<extends>QWidget</extends>
- <header>timelinewidget.h</header>
+ <header>graphing/graphwidget.h</header>
<container>1</container>
- <slots>
- <signal>horizontalScrollValueChanged(int)</signal>
- <signal>verticalScrollValueChanged(int)</signal>
- <signal>horizontalScrollMaxChanged(int)</signal>
- <signal>verticalScrollMaxChanged(int)</signal>
- <signal>jumpToCall(int)</signal>
- <signal>selectedTime(int64_t,int64_t)</signal>
- <signal>selectedProgram(unsigned)</signal>
- <signal>selectedNone()</signal>
- <slot>setHorizontalScrollValue(int)</slot>
- <slot>setVerticalScrollValue(int)</slot>
- </slots>
</customwidget>
<customwidget>
- <class>GraphWidget</class>
+ <class>CallDurationGraph</class>
<extends>QWidget</extends>
- <header>graphwidget.h</header>
+ <header>calldurationgraph.h</header>
<container>1</container>
- <slots>
- <signal>viewChanged(int,int)</signal>
- <signal>selectedTime(int64_t,int64_t)</signal>
- <signal>selectedProgram(unsigned)</signal>
- <signal>selectedNone()</signal>
- <slot>changeView(int,int)</slot>
- </slots>
</customwidget>
</customwidgets>
<resources/>
<connections>
- <connection>
- <sender>m_horizontalScrollBar</sender>
- <signal>sliderMoved(int)</signal>
- <receiver>m_timeline</receiver>
- <slot>setHorizontalScrollValue(int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>373</x>
- <y>434</y>
- </hint>
- <hint type="destinationlabel">
- <x>373</x>
- <y>213</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_verticalScrollBar</sender>
- <signal>sliderMoved(int)</signal>
- <receiver>m_timeline</receiver>
- <slot>setVerticalScrollValue(int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>754</x>
- <y>213</y>
- </hint>
- <hint type="destinationlabel">
- <x>373</x>
- <y>213</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_timeline</sender>
- <signal>horizontalScrollValueChanged(int)</signal>
- <receiver>m_horizontalScrollBar</receiver>
- <slot>setValue(int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>373</x>
- <y>213</y>
- </hint>
- <hint type="destinationlabel">
- <x>373</x>
- <y>434</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_timeline</sender>
- <signal>verticalScrollValueChanged(int)</signal>
- <receiver>m_verticalScrollBar</receiver>
- <slot>setValue(int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>373</x>
- <y>213</y>
- </hint>
- <hint type="destinationlabel">
- <x>754</x>
- <y>213</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_timeline</sender>
- <signal>horizontalScrollMaxChanged(int)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>setHorizontalScrollMax(int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>373</x>
- <y>213</y>
- </hint>
- <hint type="destinationlabel">
- <x>511</x>
- <y>383</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_timeline</sender>
- <signal>verticalScrollMaxChanged(int)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>setVerticalScrollMax(int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>373</x>
- <y>213</y>
- </hint>
- <hint type="destinationlabel">
- <x>511</x>
- <y>383</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_cpuGraph</sender>
- <signal>viewChanged(int,int)</signal>
- <receiver>m_gpuGraph</receiver>
- <slot>changeView(int,int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>511</x>
- <y>687</y>
- </hint>
- <hint type="destinationlabel">
- <x>511</x>
- <y>527</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_gpuGraph</sender>
- <signal>viewChanged(int,int)</signal>
- <receiver>m_cpuGraph</receiver>
- <slot>changeView(int,int)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>511</x>
- <y>527</y>
- </hint>
- <hint type="destinationlabel">
- <x>511</x>
- <y>687</y>
- </hint>
- </hints>
- </connection>
<connection>
<sender>m_table</sender>
<signal>doubleClicked(QModelIndex)</signal>
</hint>
</hints>
</connection>
- <connection>
- <sender>m_cpuGraph</sender>
- <signal>selectedTime(int64_t,int64_t)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectTime(int64_t,int64_t)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>552</x>
- <y>555</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_gpuGraph</sender>
- <signal>selectedTime(int64_t,int64_t)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectTime(int64_t,int64_t)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>552</x>
- <y>455</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_timeline</sender>
- <signal>selectedTime(int64_t,int64_t)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectTime(int64_t,int64_t)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>544</x>
- <y>192</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_cpuGraph</sender>
- <signal>selectedProgram(unsigned)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectProgram(unsigned)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>552</x>
- <y>555</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_gpuGraph</sender>
- <signal>selectedProgram(unsigned)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectProgram(unsigned)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>552</x>
- <y>455</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_timeline</sender>
- <signal>selectedProgram(unsigned)</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectProgram(unsigned)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>544</x>
- <y>192</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_cpuGraph</sender>
- <signal>selectedNone()</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectNone()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>552</x>
- <y>555</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_gpuGraph</sender>
- <signal>selectedNone()</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectNone()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>552</x>
- <y>455</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>m_timeline</sender>
- <signal>selectedNone()</signal>
- <receiver>ProfileDialog</receiver>
- <slot>selectNone()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>544</x>
- <y>192</y>
- </hint>
- <hint type="destinationlabel">
- <x>552</x>
- <y>401</y>
- </hint>
- </hints>
- </connection>
</connections>
<slots>
<signal>jumpToCall(int)</signal>