From b70a86af10057c5b7fcf79b674cfe5abbeaadebb Mon Sep 17 00:00:00 2001 From: James Benton Date: Tue, 28 Aug 2012 18:41:43 +0100 Subject: [PATCH] Combine timeline and histogram tabs. Synchronise time / program selections between all profile data views. --- common/trace_profiler.cpp | 9 +- common/trace_profiler.hpp | 2 + gui/graphwidget.cpp | 355 ++++++++++++++++++------- gui/graphwidget.h | 60 +++-- gui/profiledialog.cpp | 110 ++++++-- gui/profiledialog.h | 4 +- gui/profiletablemodel.cpp | 54 +++- gui/profiletablemodel.h | 7 +- gui/timelinewidget.cpp | 515 ++++++++++++++++++++++++------------- gui/timelinewidget.h | 43 +++- gui/ui/profiledialog.ui | 529 ++++++++++++++++++++++++++------------ 11 files changed, 1200 insertions(+), 488 deletions(-) diff --git a/common/trace_profiler.cpp b/common/trace_profiler.cpp index 402cdac..0f90ee2 100644 --- a/common/trace_profiler.cpp +++ b/common/trace_profiler.cpp @@ -145,7 +145,6 @@ void Profiler::parseLine(const char* in, Profile* profile) if (type.compare("call") == 0) { Profile::Call call; - unsigned programNo; line >> call.no >> call.gpuStart @@ -153,7 +152,7 @@ void Profiler::parseLine(const char* in, Profile* profile) >> call.cpuStart >> call.cpuDuration >> call.pixels - >> programNo + >> call.program >> call.name; if (lastGpuTime < call.gpuStart + call.gpuDuration) { @@ -167,11 +166,11 @@ void Profiler::parseLine(const char* in, Profile* profile) profile->calls.push_back(call); if (call.pixels >= 0) { - if (profile->programs.size() <= programNo) { - profile->programs.resize(programNo + 1); + if (profile->programs.size() <= call.program) { + profile->programs.resize(call.program + 1); } - Profile::Program& program = profile->programs[programNo]; + Profile::Program& program = profile->programs[call.program]; program.cpuTotal += call.cpuDuration; program.gpuTotal += call.gpuDuration; program.pixelTotal += call.pixels; diff --git a/common/trace_profiler.hpp b/common/trace_profiler.hpp index 8463c61..d833242 100644 --- a/common/trace_profiler.hpp +++ b/common/trace_profiler.hpp @@ -37,6 +37,8 @@ struct Profile { struct Call { unsigned no; + unsigned program; + int64_t gpuStart; int64_t gpuDuration; diff --git a/gui/graphwidget.cpp b/gui/graphwidget.cpp index 401c034..ea0f6a7 100644 --- a/gui/graphwidget.cpp +++ b/gui/graphwidget.cpp @@ -21,15 +21,20 @@ GraphWidget::GraphWidget(QWidget *parent) m_axisForeground(Qt::black), m_axisBackground(Qt::lightGray) { - setBackgroundRole(QPalette::Base); - setAutoFillBackground(true); - setMouseTracking(true); + m_selection.type = SelectNone; + + m_graphGradientGpu.setColorAt(0.9, QColor(210, 0, 0)); + m_graphGradientGpu.setColorAt(0.0, QColor(255, 130, 130)); - m_graphGradientGpu.setColorAt(0.9, QColor(255, 0, 0)); - m_graphGradientGpu.setColorAt(0.0, QColor(255, 200, 200)); + m_graphGradientCpu.setColorAt(0.9, QColor(0, 0, 210)); + m_graphGradientCpu.setColorAt(0.0, QColor(130, 130, 255)); - m_graphGradientCpu.setColorAt(0.9, QColor(0, 0, 255)); - m_graphGradientCpu.setColorAt(0.0, QColor(200, 200, 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); } @@ -40,35 +45,84 @@ void GraphWidget::setProfile(trace::Profile* profile, GraphType type) { m_type = type; m_profile = profile; - m_maxTime = 0; + m_timeMax = 0; /* Find longest call to use as y axis */ if (m_type == GraphGpu) { for (std::vector::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) { const Call& call = *itr; - if (call.gpuDuration > m_maxTime) { - m_maxTime = call.gpuDuration; + if (call.gpuDuration > m_timeMax) { + m_timeMax = call.gpuDuration; } } } else { for (std::vector::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) { const Call& call = *itr; - if (call.cpuDuration > m_maxTime) { - m_maxTime = call.cpuDuration; + if (call.cpuDuration > m_timeMax) { + m_timeMax = call.cpuDuration; } } } - m_minCall = 0; - m_maxCall = m_profile->calls.size(); + m_callMin = 0; + m_callMax = m_profile->calls.size(); - m_minCallWidth = 10; - m_maxCallWidth = m_maxCall - m_minCall; + m_callWidthMin = 10; + m_callWidthMax = m_callMax - m_callMin; - m_call = m_minCall; - m_callWidth = m_maxCallWidth; + 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(); } @@ -90,22 +144,22 @@ void GraphWidget::changeView(int call, int width) */ void GraphWidget::update() { - m_maxTime = 0; + 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_maxTime) { - m_maxTime = call.gpuDuration; + 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_maxTime) { - m_maxTime = call.cpuDuration; + if (call.cpuDuration > m_timeMax) { + m_timeMax = call.cpuDuration; } } } @@ -129,15 +183,15 @@ const Call* GraphWidget::callAtPosition(const QPoint& pos) int posX = qMax(0, pos.x() - m_axisWidth); int posY = qMax(0, pos.y() - m_axisHeight); - time = ((m_graphHeight - posY) / (double)m_graphHeight) * m_maxTime; - time -= (2 * m_maxTime) / m_graphHeight; + 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_minCall, left - size); + left = qMax(m_callMin, left - size); - right = qMin(m_maxCall - 1, left + size * 2); + right = qMin(m_callMax - 1, left + size * 2); if (m_type == GraphGpu) { const Call* longest = NULL; @@ -184,26 +238,71 @@ void GraphWidget::mousePressEvent(QMouseEvent *e) } -void GraphWidget::mouseMoveEvent(QMouseEvent *e) +void GraphWidget::mouseReleaseEvent(QMouseEvent *e) { if (!m_profile) { return; } - if (e->pos().x() < m_axisWidth || e->pos().y() < m_axisHeight) { + 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)) { - /* Horizontal scroll */ double dcdx = m_callWidth / (double)m_graphWidth; - dcdx *= m_mousePressPosition.x() - e->pos().x(); - m_call = m_mousePressCall + dcdx; - m_call = qBound(m_minCall, m_call, m_maxCall - m_callWidth); + 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); - emit viewChanged(m_call, m_callWidth); - update(); + int left = qMax(qMin(down, up), 0); + int right = qMin(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()); @@ -217,6 +316,7 @@ void GraphWidget::mouseMoveEvent(QMouseEvent *e) 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); @@ -232,7 +332,7 @@ void GraphWidget::wheelEvent(QWheelEvent *e) return; } - if (e->pos().x() < m_axisWidth || e->pos().y() < m_axisHeight) { + if (e->pos().x() < m_axisWidth) { return; } @@ -251,7 +351,7 @@ void GraphWidget::wheelEvent(QWheelEvent *e) size /= 120 * (100 / zoomPercent); m_callWidth += size; - m_callWidth = qBound(m_minCallWidth, m_callWidth, m_maxCallWidth); + m_callWidth = qBound(m_callWidthMin, m_callWidth, m_callWidthMax); /* Scroll view to zoom around mouse */ dt -= m_callWidth; @@ -259,23 +359,13 @@ void GraphWidget::wheelEvent(QWheelEvent *e) dt /= m_graphWidth; m_call = dt + m_call; - m_call = qBound(m_minCall, m_call, m_maxCall - m_callWidth); + m_call = qBound(m_callMin, m_call, m_callMax - m_callWidth); emit viewChanged(m_call, m_callWidth); update(); } -void GraphWidget::mouseDoubleClickEvent(QMouseEvent *e) -{ - const Call* call = callAtPosition(e->pos()); - - if (call) { - emit jumpToCall(call->no); - } -} - - void GraphWidget::resizeEvent(QResizeEvent *e) { m_graphWidth = qMax(0, width() - m_axisWidth); @@ -283,6 +373,7 @@ void GraphWidget::resizeEvent(QResizeEvent *e) m_graphGradientGpu.setStart(0, m_graphHeight); m_graphGradientCpu.setStart(0, m_graphHeight); + m_graphGradientDeselected.setStart(0, m_graphHeight); } @@ -294,16 +385,20 @@ void GraphWidget::paintVerticalAxis(QPainter& painter) int height = painter.fontMetrics().height(); int ticks = m_graphHeight / (height * 2); - double step = m_maxTime / (double)ticks; + 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_maxTime; tick += step) { - int y = m_graphHeight - ((tick / m_maxTime) * m_graphHeight); + 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); @@ -312,7 +407,7 @@ void GraphWidget::paintVerticalAxis(QPainter& painter) m_axisWidth - 10, height, Qt::AlignRight | Qt::AlignVCenter, - getTimeString(tick, m_maxTime)); + getTimeString(tick, m_timeMax)); } } @@ -395,7 +490,10 @@ void GraphWidget::paintEvent(QPaintEvent *e) } QPainter painter(this); + QBrush deselectBrush; + QPen deselectPen; QBrush brush; + QPen pen; /* Draw axes */ paintHorizontalAxis(painter); @@ -412,81 +510,156 @@ void GraphWidget::paintEvent(QPaintEvent *e) } /* 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(QPen(brush, 1)); + painter.setPen(pen); painter.translate(m_axisWidth, m_axisHeight); double x = 0; - double dydt = m_graphHeight / (double)m_maxTime; + 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) { - /* Less than 1 pixel per call, draw the longest call in a pixel */ - int64_t longest = 0; + /* Draw the longest call in a pixel */ + int64_t selectedLongest = 0; + int64_t pixelLongest = 0; int lastX = 0; - if (m_type == GraphGpu) { - for (int i = m_call; i < m_call + m_callWidth; ++i) { - const Call& call = m_profile->calls[i]; + for (int i = m_call; i < m_call + m_callWidth; ++i) { + const Call& call = m_profile->calls[i]; + int ix; - if (call.gpuDuration > longest) { - longest = call.gpuDuration; + if (m_type == GraphGpu) { + if (call.gpuDuration > pixelLongest) { + pixelLongest = call.gpuDuration; } - x += dxdc; + 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 (lastX != (int)x) { - painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (longest * dydt)); - longest = 0; - lastX = x; + if (m_selection.type == SelectProgram && call.program == m_selection.program) { + if (call.cpuDuration > selectedLongest) { + selectedLongest = call.cpuDuration; + } } } - } else { - for (int i = m_call; i < m_call + m_callWidth; ++i) { - const Call& call = m_profile->calls[i]; - if (call.cpuDuration > longest) { - longest = 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); + } } - x += dxdc; + painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (pixelLongest * dydt)); + pixelLongest = 0; - if (lastX != (int)x) { - painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (longest * dydt)); - longest = 0; - lastX = x; + if (selectedLongest > 0) { + painter.setPen(pen); + painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (selectedLongest * dydt)); + painter.setPen(deselectPen); + selectedLongest = 0; } + + lastX = ix; } } } else { - /* At least 1 pixel per call, draw rects */ - if (m_type == GraphGpu) { - for (int i = m_call; i < m_call + m_callWidth; ++i) { - const Call& call = m_profile->calls[i]; + /* 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 (call.pixels >= 0) { - int y = qMax(1, call.gpuDuration * dydt); - painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), brush); + if (m_type == GraphGpu) { + y = qMax(1, call.gpuDuration * dydt); + } else { + y = qMax(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; + } } + } - 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); + } } - } else { - for (int i = m_call; i < m_call + m_callWidth; ++i) { - const Call& call = m_profile->calls[i]; + } + } - int y = qMax(1, call.cpuDuration * dydt); - 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; - x += dxdc; - } + 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); } } diff --git a/gui/graphwidget.h b/gui/graphwidget.h index 266f694..7ab17c6 100644 --- a/gui/graphwidget.h +++ b/gui/graphwidget.h @@ -12,6 +12,12 @@ enum GraphType { GraphCpu }; +enum SelectType { + SelectNone, + SelectTime, + SelectProgram +}; + class GraphWidget : public QWidget { Q_OBJECT @@ -22,18 +28,28 @@ public: 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 mousePressEvent(QMouseEvent *e); virtual void paintEvent(QPaintEvent *e); - virtual void mouseMoveEvent(QMouseEvent *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); - virtual void resizeEvent(QResizeEvent *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); @@ -44,34 +60,46 @@ private: void paintHorizontalAxis(QPainter& painter); private: + /* Data */ trace::Profile* m_profile; GraphType m_type; - int64_t m_maxTime; - - int m_minCall; - int m_maxCall; - - int m_minCallWidth; - int m_maxCallWidth; + /* 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; - QPoint m_mousePressPosition; + /* Viewport */ + int m_graphWidth; + int m_graphHeight; + + /* Mouse track data */ int m_mousePressCall; + QPoint m_mousePressPosition; + /* Style */ int m_axisWidth; int m_axisHeight; - - int m_graphWidth; - int m_graphHeight; - 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 diff --git a/gui/profiledialog.cpp b/gui/profiledialog.cpp index 146e26a..f330203 100644 --- a/gui/profiledialog.cpp +++ b/gui/profiledialog.cpp @@ -9,13 +9,8 @@ ProfileDialog::ProfileDialog(QWidget *parent) setupUi(this); connect(m_timeline, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int))); - connect(m_timeline, SIGNAL(selectionChanged(int64_t,int64_t)), SLOT(selectionChanged(int64_t,int64_t))); - connect(m_gpuGraph, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int))); connect(m_cpuGraph, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int))); - - connect(m_gpuGraph, SIGNAL(viewChanged(int,int)), m_cpuGraph, SLOT(changeView(int,int))); - connect(m_cpuGraph, SIGNAL(viewChanged(int,int)), m_gpuGraph, SLOT(changeView(int,int))); } @@ -32,36 +27,113 @@ void ProfileDialog::tableDoubleClicked(const QModelIndex& index) if (call) { emit jumpToCall(call->no); + } else { + unsigned program = model->getProgram(index); + m_timeline->selectProgram(program); + m_cpuGraph->selectProgram(program); + m_gpuGraph->selectProgram(program); } } void ProfileDialog::setProfile(trace::Profile* profile) { - if (m_profile) { - delete m_profile; + delete m_profile; + + if (profile->frames.size() == 0) { + m_profile = NULL; + } else { + 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); + + 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(); + } +} + + +void ProfileDialog::selectNone() +{ + QObject* src = QObject::sender(); + + /* Update table model */ + ProfileTableModel* model = (ProfileTableModel*)m_table->model(); + model->selectNone(); + m_table->reset(); + + /* Update graphs */ + if (src != m_gpuGraph) { + m_gpuGraph->selectNone(); + } + + if (src != m_cpuGraph) { + m_cpuGraph->selectNone(); + } + + /* Update timeline */ + if (src != m_timeline) { + m_timeline->selectNone(); } +} + + +void ProfileDialog::selectProgram(unsigned program) +{ + QObject* src = QObject::sender(); - m_profile = profile; - m_timeline->setProfile(m_profile); - m_gpuGraph->setProfile(m_profile, GraphGpu); - m_cpuGraph->setProfile(m_profile, GraphCpu); + /* Update table model */ + ProfileTableModel* model = (ProfileTableModel*)m_table->model(); + model->selectNone(); + m_table->reset(); + m_table->selectRow(model->getRowIndex(program)); + + /* Update graphs */ + if (src != m_gpuGraph) { + m_gpuGraph->selectProgram(program); + } - ProfileTableModel* model = new ProfileTableModel(m_table); - model->setProfile(m_profile); + if (src != m_cpuGraph) { + m_cpuGraph->selectProgram(program); + } - delete m_table->model(); - m_table->setModel(model); - m_table->resizeColumnsToContents(); - m_table->sortByColumn(2, Qt::DescendingOrder); + /* Update timeline */ + if (src != m_timeline) { + m_timeline->selectProgram(program); + } } -void ProfileDialog::selectionChanged(int64_t start, int64_t end) +void ProfileDialog::selectTime(int64_t start, int64_t end) { + QObject* src = QObject::sender(); + + /* Update table model */ ProfileTableModel* model = (ProfileTableModel*)m_table->model(); - model->setTimeSelection(start, end); + model->selectTime(start, end); m_table->reset(); + + /* Update graphs */ + if (src != m_gpuGraph) { + m_gpuGraph->selectTime(start, end); + } + + if (src != m_cpuGraph) { + m_cpuGraph->selectTime(start, end); + } + + /* Update timeline */ + if (src != m_timeline) { + m_timeline->selectTime(start, end); + } } diff --git a/gui/profiledialog.h b/gui/profiledialog.h index 8a2f39e..22ef066 100644 --- a/gui/profiledialog.h +++ b/gui/profiledialog.h @@ -22,7 +22,9 @@ public slots: void tableDoubleClicked(const QModelIndex& index); - void selectionChanged(int64_t start, int64_t end); + void selectNone(); + void selectProgram(unsigned program); + void selectTime(int64_t start, int64_t end); signals: void jumpToCall(int call); diff --git a/gui/profiletablemodel.cpp b/gui/profiletablemodel.cpp index 5b9d484..cbe2eea 100644 --- a/gui/profiletablemodel.cpp +++ b/gui/profiletablemodel.cpp @@ -48,10 +48,35 @@ void ProfileTableModel::setProfile(trace::Profile* profile) } -void ProfileTableModel::setTimeSelection(int64_t start, int64_t end) +/** + * Set selection to nothing + */ +void ProfileTableModel::selectNone() +{ + m_timeMin = m_timeMax = 0; + updateModel(); + sort(m_sortColumn, m_sortOrder); +} + + +/** + * Set selection to program + */ +void ProfileTableModel::selectProgram(unsigned /*program*/) +{ + /* We have no program based selection for table */ + selectNone(); +} + + +/** + * Set selection to a period of time + */ +void ProfileTableModel::selectTime(int64_t start, int64_t end) { m_timeMin = start; m_timeMax = end; + updateModel(); sort(m_sortColumn, m_sortOrder); } @@ -141,6 +166,33 @@ const trace::Profile::Call *ProfileTableModel::getJumpCall(const QModelIndex & i } +/** + * Get the shader program associated with an item in the table + */ +unsigned ProfileTableModel::getProgram(const QModelIndex & index) const +{ + const ProfileTableRow& row = m_rowData[index.row()]; + return row.program; +} + + +/** + * Get the row index for a shader program + */ +int ProfileTableModel::getRowIndex(unsigned program) const +{ + for (int i = 0; i < m_rowData.size(); ++i) { + if (m_rowData[i].program == program) + return i; + } + + return -1; +} + + +/** + * Get the row data for a shader program + */ ProfileTableRow* ProfileTableModel::getRow(unsigned program) { for (QList::iterator itr = m_rowData.begin(); itr != m_rowData.end(); ++itr) { if (itr->program == program) diff --git a/gui/profiletablemodel.h b/gui/profiletablemodel.h index dc08ba0..da17550 100644 --- a/gui/profiletablemodel.h +++ b/gui/profiletablemodel.h @@ -37,8 +37,13 @@ public: ProfileTableModel(QObject *parent = NULL); void setProfile(trace::Profile* profile); - void setTimeSelection(int64_t start, int64_t end); + void selectNone(); + void selectProgram(unsigned program); + void selectTime(int64_t start, int64_t end); + + int getRowIndex(unsigned program) const; + unsigned getProgram(const QModelIndex & index) const; const trace::Profile::Call* getJumpCall(const QModelIndex & index) const; virtual int rowCount(const QModelIndex & parent) const; diff --git a/gui/timelinewidget.cpp b/gui/timelinewidget.cpp index 0375831..0f30225 100644 --- a/gui/timelinewidget.cpp +++ b/gui/timelinewidget.cpp @@ -2,7 +2,7 @@ #include "profiledialog.h" #include "trace_profiler.hpp" -#include +#include #include #include #include @@ -18,8 +18,6 @@ typedef trace::Profile::Program Program; TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent), m_profile(NULL), - m_timeSelectionStart(0), - m_timeSelectionEnd(0), m_rowHeight(20), m_axisWidth(50), m_axisHeight(30), @@ -28,8 +26,12 @@ TimelineWidget::TimelineWidget(QWidget *parent) m_axisForeground(Qt::black), m_axisBackground(QColor(210, 210, 210)), m_itemBorder(Qt::red), - m_itemForeground(Qt::cyan), - m_itemBackground(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)), @@ -38,6 +40,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) setBackgroundRole(QPalette::Base); setAutoFillBackground(true); setMouseTracking(true); + + m_selection.type = SelectNone; } @@ -74,15 +78,47 @@ void TimelineWidget::setVerticalScrollValue(int value) /** - * Update the time selection + * 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::setSelection(int64_t start, int64_t end, bool notify) +void TimelineWidget::selectProgram(unsigned program, bool notify) { - m_timeSelectionStart = start; - m_timeSelectionEnd = end; + m_selection.program = program; + m_selection.type = SelectProgram; if (notify) { - emit selectionChanged(m_timeSelectionStart, m_timeSelectionEnd); + 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(); @@ -158,7 +194,7 @@ std::vector::const_iterator binarySearchTimespanIndexed( int64_t time) { int lower = 0; - int upper = end - begin; + int upper = end - begin - 1; int pos = (lower + upper) / 2; std::vector::const_iterator itr = begin + pos; @@ -236,6 +272,27 @@ const Call* TimelineWidget::cpuCallAtTime(int64_t time) /** * 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) { @@ -314,7 +371,7 @@ void TimelineWidget::setProfile(trace::Profile* profile) setTimeScroll(m_time); setRowScroll(0); - + selectNone(); update(); } @@ -371,7 +428,7 @@ void TimelineWidget::resizeEvent(QResizeEvent *e) { /* Update viewport size */ m_viewWidth = qMax(0, width() - m_axisWidth); - m_viewHeight = qMax(0, height() - m_axisHeight - m_rowHeight); + m_viewHeight = qMax(0, height() - m_axisHeight - m_rowHeight * 2); /* Update vertical scroll bar */ if (m_profile) { @@ -411,25 +468,48 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *e) tooltip = true; } } else { - int row = (y - m_rowHeight + m_scrollY) / m_rowHeight; + const Call* call = NULL; - if (row < m_rowPrograms.size()) { - const Call* 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; + 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)) { @@ -448,7 +528,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *e) int64_t down = positionToTime(m_mousePressPosition.x() - m_axisWidth); int64_t up = positionToTime(qMax(e->pos().x() - m_axisWidth, 0)); - setSelection(qMin(down, up), qMax(down, up)); + selectTime(qMin(down, up), qMax(down, up), true); } update(); @@ -468,18 +548,16 @@ void TimelineWidget::mousePressEvent(QMouseEvent *e) m_mousePressMode = RulerZoom; } else { m_mousePressMode = RulerSelect; - - int64_t time = positionToTime(e->pos().x() - m_axisWidth); - m_timeSelectionStart = time; - m_timeSelectionEnd = time; } - } else { + } 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; + m_mousePressRow = m_scrollY; update(); } @@ -493,6 +571,8 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *e) } /* 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)); @@ -505,8 +585,23 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *e) m_mousePressMode = NoMousePress; setTimeScroll(left); - } else if (m_mousePressMode == RulerSelect) { - setSelection(m_timeSelectionStart, m_timeSelectionEnd, true); + } 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); + } } } @@ -523,7 +618,7 @@ void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *e) const Frame* frame = frameAtTime(time); if (frame) { - setSelection(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true); + selectTime(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true); return; } } else if (row == 0) { @@ -543,10 +638,13 @@ void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *e) return; } } - } + } else { + int y = e->pos().y() - m_axisHeight; + int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight; - if (time < m_timeSelectionStart || time > m_timeSelectionEnd) { - setSelection(0, 0, true); + if (row >= 0 && row < m_rowPrograms.size()) { + selectProgram(m_rowPrograms[row], true); + } } } @@ -557,6 +655,10 @@ void TimelineWidget::wheelEvent(QWheelEvent *e) return; } + if (e->pos().x() < m_axisWidth) { + return; + } + int zoomPercent = 10; /* If holding Ctrl key then zoom 2x faster */ @@ -588,22 +690,31 @@ void TimelineWidget::wheelEvent(QWheelEvent *e) /** * Paints a single pixel column of the heat map */ -void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool isCpu) +void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool gpu, bool selected) { if (heat == 0) { return; } - double timePerPixel = m_timeWidth; - timePerPixel /= m_viewWidth; + if (m_selection.type == SelectTime) { + selected = x >= m_selectionLeft && x <= m_selectionRight; + } + double timePerPixel = m_timeWidth / (double)m_viewWidth; double colour = heat / timePerPixel; - colour = qBound(0.0, colour * 255.0, 255.0); - if (isCpu) { - painter.setPen(QColor(255 - colour, 255 - colour, 255)); - } else { + /* 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); @@ -611,156 +722,176 @@ void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool isCpu /** - * Render the whole widget + * Draws a call on the heatmap */ -void TimelineWidget::paintEvent(QPaintEvent *e) +bool TimelineWidget::drawCall(QPainter& painter, const trace::Profile::Call& call, int& lastX, int64_t& heat, bool gpu) { - if (!m_profile) - return; + int64_t start, duration, end; - QPainter painter(this); + if (gpu) { + start = call.gpuStart; + duration = call.gpuDuration; + } else { + start = call.cpuStart; + duration = call.cpuDuration; + } - int rowEnd = qMin(m_row + (m_viewHeight / m_rowHeight) + 1, m_rowCount); - int64_t timeEnd = m_time + m_timeWidth; - int64_t heat = 0; - int lastX = 0; - int widgetHeight = height(); - int widgetWidth = width(); + end = start + duration; - /* Draw GPU rows */ - painter.translate(m_axisWidth, m_axisHeight + m_rowHeight - (m_scrollY % m_rowHeight)); + if (start > m_timeEnd) { + return false; + } - for (int row = m_row; row < rowEnd; ++row) { - Program& program = m_profile->programs[m_rowPrograms[row]]; - lastX = 0; - heat = 0; + if (end < m_time) { + return true; + } - for (std::vector::const_iterator itr = program.calls.begin(); itr != program.calls.end(); ++itr) { - const Call& call = m_profile->calls[*itr]; - int64_t gpuEnd = call.gpuStart + call.gpuDuration; + double left = timeToPosition(start); + double right = timeToPosition(end); - if (call.gpuStart > timeEnd) { - break; - } + int leftX = left; + int rightX = right; - if (gpuEnd < m_time) { - continue; - } + bool selected = true; - double left = timeToPosition(call.gpuStart); - double right = timeToPosition(gpuEnd); + if (m_selection.type == SelectProgram) { + selected = call.program == m_selection.program; + } - int leftX = left; - int rightX = right; + /* Draw last heat if needed */ + if (leftX != lastX) { + drawHeat(painter, lastX, heat, gpu, selected); + lastX = leftX; + heat = 0; + } - /* Draw last heat if needed */ - if (leftX != lastX) { - drawHeat(painter, lastX, heat, false); - 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; - if (rightX <= leftX + 1) { - if (rightX == lastX) { - /* Fully contained in this X */ - heat += call.gpuDuration; - } else { - /* Split call time between the two pixels it occupies */ - int64_t time = positionToTime(rightX); + drawHeat(painter, lastX, heat, gpu, selected); - heat += time - call.gpuStart; - drawHeat(painter, lastX, heat, false); + 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); + } - heat = gpuEnd - time; - lastX = rightX; - } + /* Draw background rect */ + if (selected) { + if (gpu) { + painter.fillRect(rect, m_itemGpuBackground); } else { - QRect rect; - rect.setLeft(left + 0.5); - rect.setWidth(right - left); - rect.setTop(0); - rect.setHeight(m_rowHeight); - - painter.fillRect(rect, m_itemBackground); + painter.fillRect(rect, m_itemCpuBackground); + } + } else { + painter.fillRect(rect, m_itemDeselectedBackground); + } - if (rect.width() > 6) { - rect.adjust(1, 0, -1, -2); - painter.setPen(m_itemForeground); + /* If wide enough, draw text */ + if (rect.width() > 6) { + rect.adjust(1, 0, -1, -2); - painter.drawText(rect, - Qt::AlignLeft | Qt::AlignVCenter, - painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width())); + if (selected) { + if (gpu) { + painter.setPen(m_itemGpuForeground); + } else { + painter.setPen(m_itemCpuForeground); } + } else { + painter.setPen(m_itemDeselectedForeground); } - } - painter.translate(0, m_rowHeight); + painter.drawText(rect, + Qt::AlignLeft | Qt::AlignVCenter, + painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width())); + } } - /* Draw CPU row */ - painter.resetTransform(); - painter.translate(m_axisWidth, m_axisHeight); - painter.fillRect(0, 0, m_viewWidth, m_rowHeight, Qt::white); + return true; +} - for (std::vector::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) { - const Call& call = *itr; - int64_t cpuEnd = call.cpuStart + call.cpuDuration; - if (call.cpuStart > timeEnd) { - continue; - } +/** + * Render the whole widget + */ +void TimelineWidget::paintEvent(QPaintEvent *e) +{ + if (!m_profile) + return; - if (cpuEnd < m_time) { - continue; - } + QPainter painter(this); - double left = timeToPosition(call.cpuStart); - double right = timeToPosition(cpuEnd); + 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(); - int leftX = left; - int rightX = right; + m_timeEnd = m_time + m_timeWidth; + m_selectionLeft = timeToPosition(m_selection.timeStart); + m_selectionRight = (timeToPosition(m_selection.timeEnd) + 0.5); - /* Draw last heat if needed */ - if (leftX != lastX) { - drawHeat(painter, lastX, heat, true); - lastX = leftX; - heat = 0; - } - if (rightX <= leftX + 1) { - if (rightX == lastX) { - /* Fully contained in this X */ - heat += call.cpuDuration; - } else { - /* Split call time between the two pixels it occupies */ - int64_t time = positionToTime(rightX); + /* 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; - heat += time - call.cpuStart; - drawHeat(painter, lastX, heat, true); + for (std::vector::const_iterator itr = program.calls.begin(); itr != program.calls.end(); ++itr) { + const Call& call = m_profile->calls[*itr]; - heat = cpuEnd - time; - lastX = rightX; + if (!drawCall(painter, call, lastGpuX, heatGPU, true)) { + break; } - } else { - QRect rect; - rect.setLeft(left + 0.5); - rect.setWidth(right - left); - rect.setTop(0); - rect.setHeight(m_rowHeight); + } - painter.fillRect(rect, QColor(0, 0, 255)); + painter.translate(0, m_rowHeight); + } - if (rect.width() > 6) { - rect.adjust(1, 0, -1, -2); - painter.setPen(QColor(255, 255, 0)); - painter.drawText(rect, - Qt::AlignLeft | Qt::AlignVCenter, - painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width())); - } + /* 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::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); @@ -773,8 +904,9 @@ void TimelineWidget::paintEvent(QPaintEvent *e) 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); + 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); @@ -782,13 +914,21 @@ void TimelineWidget::paintEvent(QPaintEvent *e) painter.setPen(m_axisForeground); painter.drawText(0, y, m_axisWidth, m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, QString("%1").arg(m_rowPrograms[row])); - painter.setPen(m_axisBorder); - painter.drawLine(0, y + m_rowHeight - 1, m_axisWidth - 1, y + m_rowHeight - 1); + 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); + 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); @@ -804,13 +944,27 @@ void TimelineWidget::paintEvent(QPaintEvent *e) 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 = -9999; + int lastLabel = -999; /* Ensure first label gets drawn */ double scroll = m_time; scroll /= m_timeWidth; @@ -822,7 +976,7 @@ void TimelineWidget::paintEvent(QPaintEvent *e) bool draw = true; int width; - if (frame.cpuStart > timeEnd) { + if (frame.cpuStart > m_timeEnd) { break; } @@ -873,6 +1027,7 @@ void TimelineWidget::paintEvent(QPaintEvent *e) } } + /* Draw "Frame" axis label */ painter.resetTransform(); @@ -883,28 +1038,30 @@ void TimelineWidget::paintEvent(QPaintEvent *e) 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_timeSelectionStart != m_timeSelectionEnd) { - int selectionLeft = timeToPosition(m_timeSelectionStart) + m_axisWidth; - int selectionRight = (timeToPosition(m_timeSelectionEnd) + 0.5) + m_axisWidth; + /* Draw the active selection border */ + if (m_selection.type == SelectTime) { painter.setPen(m_selectionBorder); - if (selectionLeft >= m_axisWidth && selectionLeft < widgetWidth) { - painter.drawLine(selectionLeft, 0, selectionLeft, widgetHeight); + 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 (selectionRight >= m_axisWidth && selectionRight < widgetWidth) { - painter.drawLine(selectionRight, 0, selectionRight, widgetHeight); + if (m_selectionRight >= m_axisWidth && m_selectionRight < widgetWidth) { + painter.drawLine(m_selectionRight, 0, m_selectionRight, widgetHeight); } - selectionLeft = qBound(m_axisWidth, selectionLeft, widgetWidth); - selectionRight = qBound(m_axisWidth, selectionRight, widgetWidth); + m_selectionLeft = qBound(m_axisWidth, m_selectionLeft, widgetWidth); + m_selectionRight = qBound(m_axisWidth, m_selectionRight, widgetWidth); - painter.drawLine(selectionLeft, m_axisHeight - 1, selectionRight, m_axisHeight - 1); - painter.fillRect(selectionLeft, 0, selectionRight - selectionLeft, widgetHeight, m_selectionBackground); + 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(); diff --git a/gui/timelinewidget.h b/gui/timelinewidget.h index 9d5d8a5..ba42a10 100644 --- a/gui/timelinewidget.h +++ b/gui/timelinewidget.h @@ -17,11 +17,21 @@ class TimelineWidget : public QWidget 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); @@ -45,14 +55,16 @@ signals: void jumpToCall(int call); - void selectionChanged(int64_t start, int64_t end); + void selectedNone(); + void selectedProgram(unsigned program); + void selectedTime(int64_t start, int64_t end); private: - void setSelection(int64_t start, int64_t end, bool notify = false); void setRowScroll(int position, bool notify = true); void setTimeScroll(int64_t time, bool notify = true); - void drawHeat(QPainter& painter, int x, int64_t heat, bool isCpu); + 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); @@ -61,6 +73,7 @@ private: 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: @@ -80,13 +93,15 @@ private: /* 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; - int64_t m_timeSelectionStart; - int64_t m_timeSelectionEnd; + + int m_selectionLeft; + int m_selectionRight; /* Visible Rows */ int m_row; @@ -108,12 +123,26 @@ private: QPen m_axisForeground; QBrush m_axisBackground; QPen m_itemBorder; - QPen m_itemForeground; - QBrush m_itemBackground; + 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 diff --git a/gui/ui/profiledialog.ui b/gui/ui/profiledialog.ui index 49b77bd..7a54ef7 100644 --- a/gui/ui/profiledialog.ui +++ b/gui/ui/profiledialog.ui @@ -9,181 +9,185 @@ 0 0 - 1079 - 768 + 1105 + 804 Profile Results - - - 0 - + + + + 0 - - - 1 + + + Qt::Vertical - - - Timeline - - + + false + + + + + 0 + 1 + + + + Qt::WheelFocus + + + + 0 + 0 - - - - 0 - 0 - - - - Qt::Vertical + + + 2 - - - - 0 - 1 - - - - Qt::WheelFocus - - - - 0 + + + + + 0 + 0 + - - 0 + + Qt::WheelFocus - - - - 2 - - - - - - 0 - 0 - - - - Qt::WheelFocus - - - - - - - - 0 - 0 - - - - Qt::Vertical - - - - - - - - 0 - 0 - - - - 10000 - - - Qt::Horizontal - - - - - - - - - - - 0 - 0 - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - true - - - false - - - false - - - + + + + + + + 0 + 0 + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + 10000 + + + Qt::Horizontal + + + + - - - Histogram - - + + + + 0 + 0 + + + + + 0 + 200 + + + + + 0 + 0 - - - Qt::Vertical + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::WheelFocus + + + + + + + + 0 + 0 + + + + + 0 + 0 + - - - - 0 - 0 - - - - Qt::WheelFocus - - - - - - 0 - 0 - - - + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + false + + + false + + @@ -200,6 +204,9 @@ horizontalScrollMaxChanged(int) verticalScrollMaxChanged(int) jumpToCall(int) + selectedTime(int64_t,int64_t) + selectedProgram(unsigned) + selectedNone() setHorizontalScrollValue(int) setVerticalScrollValue(int) @@ -209,6 +216,13 @@ QWidget
graphwidget.h
1 + + viewChanged(int,int) + selectedTime(int64_t,int64_t) + selectedProgram(unsigned) + selectedNone() + changeView(int,int) + @@ -220,12 +234,12 @@ setHorizontalScrollValue(int) - 402 - 195 + 373 + 434 - 402 - 93 + 373 + 213 @@ -236,12 +250,12 @@ setVerticalScrollValue(int) - 813 - 93 + 754 + 213 - 402 - 93 + 373 + 213 @@ -252,12 +266,12 @@ setValue(int) - 402 - 93 + 373 + 213 - 402 - 195 + 373 + 434 @@ -268,12 +282,12 @@ setValue(int) - 402 - 93 + 373 + 213 - 813 - 93 + 754 + 213 @@ -284,8 +298,8 @@ setHorizontalScrollMax(int) - 504 - 277 + 373 + 213 511 @@ -300,8 +314,8 @@ setVerticalScrollMax(int) - 504 - 277 + 373 + 213 511 @@ -309,6 +323,38 @@ + + m_cpuGraph + viewChanged(int,int) + m_gpuGraph + changeView(int,int) + + + 511 + 687 + + + 511 + 527 + + + + + m_gpuGraph + viewChanged(int,int) + m_cpuGraph + changeView(int,int) + + + 511 + 527 + + + 511 + 687 + + + m_table doubleClicked(QModelIndex) @@ -316,8 +362,8 @@ tableDoubleClicked(QModelIndex) - 511 - 671 + 895 + 220 511 @@ -325,11 +371,158 @@ + + m_cpuGraph + selectedTime(int64_t,int64_t) + ProfileDialog + selectTime(int64_t,int64_t) + + + 552 + 555 + + + 552 + 401 + + + + + m_gpuGraph + selectedTime(int64_t,int64_t) + ProfileDialog + selectTime(int64_t,int64_t) + + + 552 + 455 + + + 552 + 401 + + + + + m_timeline + selectedTime(int64_t,int64_t) + ProfileDialog + selectTime(int64_t,int64_t) + + + 544 + 192 + + + 552 + 401 + + + + + m_cpuGraph + selectedProgram(unsigned) + ProfileDialog + selectProgram(unsigned) + + + 552 + 555 + + + 552 + 401 + + + + + m_gpuGraph + selectedProgram(unsigned) + ProfileDialog + selectProgram(unsigned) + + + 552 + 455 + + + 552 + 401 + + + + + m_timeline + selectedProgram(unsigned) + ProfileDialog + selectProgram(unsigned) + + + 544 + 192 + + + 552 + 401 + + + + + m_cpuGraph + selectedNone() + ProfileDialog + selectNone() + + + 552 + 555 + + + 552 + 401 + + + + + m_gpuGraph + selectedNone() + ProfileDialog + selectNone() + + + 552 + 455 + + + 552 + 401 + + + + + m_timeline + selectedNone() + ProfileDialog + selectNone() + + + 544 + 192 + + + 552 + 401 + + + jumpToCall(int) setVerticalScrollMax(int) setHorizontalScrollMax(int) tableDoubleClicked(QModelIndex) + selectTime(int64_t,int64_t) + selectProgram(unsigned) + selectNone() -- 2.43.0