X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=gui%2Ftimelinewidget.cpp;h=0f30225d268c9bf72515ea91fea5fa2562d8354f;hb=b70a86af10057c5b7fcf79b674cfe5abbeaadebb;hp=92c971f9aa83037d2ffa022597af586244c2d0c7;hpb=50f9686913b8fc9f46662b299a9810dee08b0a9c;p=apitrace diff --git a/gui/timelinewidget.cpp b/gui/timelinewidget.cpp index 92c971f..0f30225 100644 --- a/gui/timelinewidget.cpp +++ b/gui/timelinewidget.cpp @@ -1,8 +1,10 @@ #include "timelinewidget.h" +#include "profiledialog.h" #include "trace_profiler.hpp" -#include +#include #include +#include #include #include #include @@ -11,28 +13,35 @@ 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_timeSelectionStart(0), - m_timeSelectionEnd(0), m_rowHeight(20), m_axisWidth(50), - m_axisHeight(20), + m_axisHeight(30), + m_axisLine(QColor(240, 240, 240)), m_axisBorder(Qt::black), - m_axisBackground(Qt::lightGray), + m_axisForeground(Qt::black), + m_axisBackground(QColor(210, 210, 210)), m_itemBorder(Qt::red), - m_itemForeground(Qt::cyan), - m_itemBackground(Qt::red), - m_selectionBorder(QColor(50, 50, 255)), - m_selectionBackground(QColor(245, 245, 255)), - m_zoomBorder(Qt::green), - m_zoomBackground(QColor(100, 255, 100, 80)) + 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; } @@ -69,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::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::setSelection(int64_t start, int64_t end, bool notify) +void TimelineWidget::selectTime(int64_t start, int64_t end, bool notify) { - m_timeSelectionStart = start; - m_timeSelectionEnd = end; + m_selection.timeStart = start; + m_selection.timeEnd = end; + m_selection.type = SelectTime; if (notify) { - emit selectionChanged(m_timeSelectionStart, m_timeSelectionEnd); + emit selectedTime(start, end); } update(); @@ -111,18 +152,94 @@ int64_t TimelineWidget::positionToTime(int pos) /** - * Return the item at position + * Binary Search for a time in start+durations */ -const VisibleItem* TimelineWidget::itemAtPosition(const QPoint& pos) +template +typename std::vector::const_iterator binarySearchTimespan( + typename std::vector::const_iterator begin, + typename std::vector::const_iterator end, + int64_t time) { - foreach (const VisibleItem& item, m_visibleItems) { - if (pos.x() < item.rect.left() || pos.y() < item.rect.top()) - continue; + int lower = 0; + int upper = end - begin; + int pos = (lower + upper) / 2; + typename std::vector::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; + } - if (pos.x() > item.rect.right() || pos.y() > item.rect.bottom()) - continue; + pos = (lower + upper) / 2; + itr = begin + pos; + } - return &item; + if (lower <= upper) { + return itr; + } else { + return end; + } +} + + +/** + * Binary Search for a time in start+durations on an array of indices + */ +std::vector::const_iterator binarySearchTimespanIndexed( + const std::vector& calls, + std::vector::const_iterator begin, + std::vector::const_iterator end, + int64_t time) +{ + int lower = 0; + int upper = end - begin - 1; + int pos = (lower + upper) / 2; + std::vector::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::const_iterator res + = binarySearchTimespan( + m_profile->frames.begin(), + m_profile->frames.end(), + time); + + if (res != m_profile->frames.end()) { + return &*res; } return NULL; @@ -130,45 +247,102 @@ const VisibleItem* TimelineWidget::itemAtPosition(const QPoint& pos) /** - * Calculate the row order by total gpu time per shader + * Find the CPU call at time */ -void TimelineWidget::calculateRows() +const Call* TimelineWidget::cpuCallAtTime(int64_t time) { - typedef QPair HeatProgram; - QList heats; - int idx; + if (!m_profile) { + return NULL; + } - m_programRowMap.clear(); - m_rowCount = 0; + std::vector::const_iterator res + = binarySearchTimespan( + m_profile->calls.begin(), + m_profile->calls.end(), + time); - for (Frame::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) { - const Frame& frame = *itr; + if (res != m_profile->calls.end()) { + return &*res; + } + + return NULL; +} - for (Call::const_iterator jtr = frame.calls.begin(); jtr != frame.calls.end(); ++jtr) { - const Call& call = *jtr; - while (call.program >= heats.size()) { - heats.append(HeatProgram(0, heats.size())); - m_programRowMap.append(m_programRowMap.size()); - } +/** + * Find the draw call at time + */ +const Call* TimelineWidget::drawCallAtTime(int64_t time) +{ + if (!m_profile) { + return NULL; + } - heats[call.program].first += call.gpuDuration; + for (int i = 0; i < m_rowPrograms.size(); ++i) { + const Call* call = drawCallAtTime(time, m_rowPrograms[i]); + + if (call) { + return call; } } - qSort(heats); - idx = heats.size() - 1; + return NULL; +} - for (QList::iterator itr = heats.begin(); itr != heats.end(); ++itr, --idx) { - HeatProgram& pair = *itr; - if (pair.first == 0) { - m_programRowMap[pair.second] = -1; - } else { - m_programRowMap[pair.second] = idx; - m_rowCount++; +/** + * 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::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 Pair; + std::vector gpu; + + /* Map shader to visible row */ + for (std::vector::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::const_reverse_iterator itr = gpu.rbegin(); itr != gpu.rend(); ++itr) { + m_rowPrograms.push_back(itr->second); + } + + m_rowCount = m_rowPrograms.size(); } @@ -183,8 +357,8 @@ void TimelineWidget::setProfile(trace::Profile* profile) m_profile = profile; calculateRows(); - m_timeMin = m_profile->frames.front().gpuStart; - m_timeMax = m_profile->frames.back().gpuStart + m_profile->frames.back().gpuDuration; + 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; @@ -197,7 +371,7 @@ void TimelineWidget::setProfile(trace::Profile* profile) setTimeScroll(m_time); setRowScroll(0); - + selectNone(); update(); } @@ -253,8 +427,8 @@ void TimelineWidget::setRowScroll(int position, bool notify) void TimelineWidget::resizeEvent(QResizeEvent *e) { /* Update viewport size */ - m_viewWidth = width() - m_axisWidth; - m_viewHeight = height() - m_axisHeight; + m_viewWidth = qMax(0, width() - m_axisWidth); + m_viewHeight = qMax(0, height() - m_axisHeight - m_rowHeight * 2); /* Update vertical scroll bar */ if (m_profile) { @@ -267,33 +441,78 @@ void TimelineWidget::resizeEvent(QResizeEvent *e) 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) { - const VisibleItem* item = itemAtPosition(e->pos()); - - if (item) { - const trace::Profile::Call* call = item->call; - - QString text; - text = QString::fromStdString(call->name); - text += QString("\nCall: %1").arg(call->no); - text += QString("\nGPU Time: %1").arg(call->gpuDuration); - text += QString("\nCPU Time: %1").arg(call->cpuDuration); - text += QString("\nPixels Drawn: %1").arg(call->pixels); + 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; - QToolTip::showText(e->globalPos(), text); - } - } + if (y < m_rowHeight * 2) { + call = drawCallAtTime(time); + } else { + int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight; - m_mousePosition = e->pos(); + if (row < m_rowPrograms.size()) { + call = drawCallAtTime(time, m_rowPrograms[row]); + } + } - if (e->buttons().testFlag(Qt::LeftButton)) { - QToolTip::hideText(); + 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; @@ -305,37 +524,40 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *e) 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(e->pos().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(); } + + if (!tooltip) { + QToolTip::hideText(); + } } void TimelineWidget::mousePressEvent(QMouseEvent *e) { if (e->buttons() & Qt::LeftButton) { - if (e->pos().y() < m_axisHeight) { + if (e->pos().y() < m_axisHeight && e->pos().x() >= m_axisWidth) { if (QApplication::keyboardModifiers() & Qt::ControlModifier) { 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(); } @@ -349,8 +571,10 @@ 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(e->pos().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); @@ -361,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); + } } } @@ -371,27 +610,41 @@ void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *e) { int64_t time = positionToTime(e->pos().x() - m_axisWidth); - if (e->pos().y() < m_axisHeight) { - int64_t time = positionToTime(e->pos().x() - m_axisWidth); + if (e->pos().x() > m_axisWidth) { + int row = (e->pos().y() - m_axisHeight) / m_rowHeight; - for (Frame::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) { - const Frame& frame = *itr; + if (e->pos().y() < m_axisHeight) { + /* Horizontal axis */ + const Frame* frame = frameAtTime(time); - if (frame.gpuStart + frame.gpuDuration < time) - continue; + if (frame) { + selectTime(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true); + return; + } + } else if (row == 0) { + /* CPU Calls */ + const Call* call = cpuCallAtTime(time); - if (frame.gpuStart > time) - break; + if (call) { + emit jumpToCall(call->no); + return; + } + } else if (row > 0) { + /* Draw Calls */ + const Call* call = drawCallAtTime(time, 0); - setSelection(frame.gpuStart, frame.gpuStart + frame.gpuDuration, true); - return; + 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 (const VisibleItem* item = itemAtPosition(e->pos())) { - emit jumpToCall(item->call->no); - } else if (time < m_timeSelectionStart || time > m_timeSelectionEnd) { - setSelection(0, 0, true); + if (row >= 0 && row < m_rowPrograms.size()) { + selectProgram(m_rowPrograms[row], true); + } } } @@ -402,8 +655,13 @@ void TimelineWidget::wheelEvent(QWheelEvent *e) 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; } @@ -432,27 +690,139 @@ void TimelineWidget::wheelEvent(QWheelEvent *e) /** * Paints a single pixel column of the heat map */ -void TimelineWidget::paintHeatmapColumn(int x, QPainter& painter, QVector& rows) +void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool gpu, bool selected) { - double timePerPixel = m_timeWidth; - timePerPixel /= m_viewWidth; + 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; - for(int i = 0, y = 0; i < rows.size(); ++i, y += m_rowHeight) { - if (rows[i] == 0) - continue; + /* Gamma correction */ + colour = qPow(colour, 1.0 / 2.0); - if (y > m_viewHeight) - continue; + 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); +} - double heat = rows[i] / timePerPixel; - heat = qBound(0.0, heat, 1.0); - heat *= 255.0; - painter.setPen(QColor(255, 255 - heat, 255 - heat)); - painter.drawLine(x, y, x, y + m_rowHeight); +/** + * 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; - rows[i] = 0; + 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; } @@ -464,230 +834,244 @@ void TimelineWidget::paintEvent(QPaintEvent *e) if (!m_profile) return; - QVector heatMap(m_programRowMap.size(), 0); QPainter painter(this); - int64_t timeEnd; - int selectionRight; - int selectionLeft; - int rowEnd; - int lastX; + 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 the active selection background - */ - if (m_timeSelectionStart != m_timeSelectionEnd) { - selectionLeft = timeToPosition(m_timeSelectionStart) + m_axisWidth; - selectionRight = (timeToPosition(m_timeSelectionEnd) + 0.5) + m_axisWidth; - selectionLeft = qBound(-1, selectionLeft, width() + 1); - selectionRight = qBound(-1, selectionRight, width() + 1); + /* 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::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.setPen(Qt::NoPen); - painter.setBrush(m_selectionBackground); - painter.drawRect(selectionLeft, m_axisHeight, selectionRight - selectionLeft, m_viewHeight); + painter.translate(0, m_rowHeight); } - /* - * Draw profile heatmap - */ - rowEnd = m_row + (m_viewHeight / m_rowHeight) + 1; - timeEnd = m_time + m_timeWidth; - m_visibleItems.clear(); - lastX = 0; + /* Draw CPU/GPU rows */ + painter.resetTransform(); + painter.translate(m_axisWidth, m_axisHeight); + painter.fillRect(0, 0, m_viewWidth, m_rowHeight * 2, Qt::white); - painter.translate(m_axisWidth + 1, m_axisHeight + 1 - (m_scrollY % m_rowHeight)); - painter.setBrush(m_itemBackground); - painter.setPen(m_itemBorder); + lastCpuX = lastGpuX = 0; + heatCPU = heatGPU = 0; - for (Frame::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) { - const Frame& frame = *itr; + for (std::vector::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) { + const Call& call = *itr; - if (frame.gpuStart > timeEnd) - break; + /* Draw gpu row */ + if (call.pixels >= 0) { + painter.translate(0, m_rowHeight); + drawCall(painter, call, lastGpuX, heatGPU, true); + painter.translate(0, -m_rowHeight); + } - if (frame.gpuStart + frame.gpuDuration < m_time) - continue; + /* Draw cpu row */ + if (!drawCall(painter, call, lastCpuX, heatCPU, false)) { + break; + } + } - for (Call::const_iterator jtr = frame.calls.begin(); jtr != frame.calls.end(); ++jtr) { - const Call& call = *jtr; - int row = m_programRowMap[call.program]; - if (call.gpuStart + call.gpuDuration < m_time || call.gpuStart > timeEnd) - continue; + /* Draw axis */ + painter.resetTransform(); + painter.setPen(m_axisBorder); - if (row < m_row || row > rowEnd) - continue; + /* Top Rect */ + painter.fillRect(m_axisWidth - 1, 0, widgetWidth, m_axisHeight - 1, m_axisBackground); + painter.drawLine(0, m_axisHeight - 1, widgetWidth, m_axisHeight - 1); - double left = qMax(0.0, timeToPosition(call.gpuStart)); - double right = timeToPosition(call.gpuStart + call.gpuDuration); + /* Left Rect */ + painter.fillRect(0, m_axisHeight - 1, m_axisWidth - 1, widgetHeight, m_axisBackground); + painter.drawLine(m_axisWidth - 1, 0, m_axisWidth - 1, widgetHeight); - int leftX = left; - int rightX = right; - if (lastX != leftX) { - paintHeatmapColumn(lastX, painter, heatMap); - lastX = leftX; - } + /* Draw the program numbers */ + painter.translate(0, m_axisHeight + m_rowHeight * 2); - row -= m_row; + for (int row = m_row; row < rowEnd; ++row) { + int y = (row - m_row) * m_rowHeight - (m_scrollY % m_rowHeight); - if (rightX <= lastX + 1) { - if (lastX == rightX) { - /* Fully contained in this X */ - heatMap[row] += call.gpuDuration; - } else { - /* Split call time between the two pixels it occupies */ - int64_t time = positionToTime(rightX); + painter.setPen(m_axisForeground); + painter.drawText(0, y, m_axisWidth, m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, QString("%1").arg(m_rowPrograms[row])); - heatMap[row] += time - call.gpuStart; - paintHeatmapColumn(lastX, painter, heatMap); + 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); - heatMap[row] += (call.gpuDuration + call.gpuStart) - time; - lastX = rightX; - } - } else { - leftX = (left + 0.5); - rightX = (right + 0.5); - - QRect rect; - rect.setLeft(leftX); - rect.setWidth(rightX - leftX); - rect.setTop(row * m_rowHeight); - rect.setHeight(m_rowHeight); - - VisibleItem vis; - vis.rect = painter.transform().mapRect(rect); - vis.frame = &frame; - vis.call = &call; - m_visibleItems.push_back(vis); - - painter.drawRect(rect); - - if (rect.width() > 6) { - rect.adjust(1, 0, -1, 0); - painter.setPen(m_itemForeground); - painter.drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, QString::fromStdString(call.name)); - painter.setPen(m_itemBorder); - } - } + painter.setPen(m_axisLine); + painter.drawLine(m_axisWidth, y + m_rowHeight - 1, widgetWidth, y + m_rowHeight - 1); } } - /* Paint the last column if needed */ - paintHeatmapColumn(lastX, painter, heatMap); - - /* - * Draw the axis border and background - */ + /* Draw the "CPU" axis label */ painter.resetTransform(); - painter.setPen(Qt::NoPen); + painter.translate(0, m_axisHeight); + + painter.setPen(m_axisBorder); painter.setBrush(m_axisBackground); - painter.drawRect(0, 0, m_viewWidth + m_axisWidth, m_axisHeight); - painter.drawRect(0, m_axisHeight, m_axisWidth, m_viewHeight); + 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(0, m_axisHeight, m_axisWidth + m_viewWidth, m_axisHeight); - painter.drawLine(m_axisWidth, 0, m_axisWidth, m_viewHeight + m_axisHeight); + painter.drawLine(m_axisWidth, m_rowHeight - 1, widgetWidth, m_rowHeight - 1); - /* - * Draw horizontal axis - */ - for (Frame::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) { - const Frame& frame = *itr; - int left, right, width; - bool drawText = true; - QString text; + /* Draw the "GPU" axis label */ + painter.translate(0, m_rowHeight); - if (frame.gpuStart > timeEnd) - break; + 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"); - if (frame.gpuStart + frame.gpuDuration < m_time) - continue; + painter.setPen(m_axisBorder); + painter.drawLine(m_axisWidth, m_rowHeight - 1, widgetWidth, m_rowHeight - 1); - left = timeToPosition(frame.gpuStart); - right = timeToPosition(frame.gpuStart + frame.gpuDuration) + 0.5; - left = qBound(0, left, m_viewWidth) + m_axisWidth; - right = qBound(0, right, m_viewWidth) + m_axisWidth; + /* Draw the frame numbers */ + painter.resetTransform(); - width = right - left; + painter.setPen(m_axisForeground); + painter.translate(m_axisWidth, 0); - text = QString("Frame %1").arg(frame.no); + int lastLabel = -999; /* Ensure first label gets drawn */ - if (painter.fontMetrics().width(text) > width) { - text = QString("%1").arg(frame.no); + double scroll = m_time; + scroll /= m_timeWidth; + scroll *= m_viewWidth; - if (painter.fontMetrics().width(text) > width) { - drawText = false; - } - } + for (std::vector::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 (drawText) { - painter.drawText(left, 0, width, m_axisHeight, Qt::AlignHCenter | Qt::AlignVCenter, text); + if (frame.cpuStart > m_timeEnd) { + break; } - if (width > 5) { - painter.drawLine(left, 0, left, m_axisHeight); - painter.drawLine(right, 0, right, m_axisHeight); + 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; - /* - * Draw vertical axis - */ - painter.translate(0, -(m_scrollY % m_rowHeight)); + QString text = QString("%1").arg(frame.no); - for (int i = 0; i < m_programRowMap.size(); ++i) { - int y = (m_programRowMap[i] - m_row) * m_rowHeight; + width = painter.fontMetrics().width(text) + padding * 2; - if (m_programRowMap[i] < 0 || y < -m_rowHeight || y > m_viewHeight) - continue; + if (left + width > scroll) + draw = true; - y += m_axisHeight + 1; - painter.drawText(0, y, m_axisWidth, m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, QString("%1").arg(i)); - painter.drawLine(0, y + m_rowHeight, m_axisWidth, y + m_rowHeight); + /* 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 the top left square again to cover up any hanging over text */ + + /* Draw "Frame" axis label */ painter.resetTransform(); - painter.setPen(Qt::NoPen); + + painter.setPen(m_axisBorder); painter.setBrush(m_axisBackground); - painter.drawRect(0, 0, m_axisWidth, m_axisHeight); + 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_timeSelectionStart != m_timeSelectionEnd) { + + /* Draw the active selection border */ + if (m_selection.type == SelectTime) { painter.setPen(m_selectionBorder); - painter.drawLine(selectionLeft, 0, selectionLeft, m_viewHeight + m_axisHeight); - painter.drawLine(selectionRight, 0, selectionRight, m_viewHeight + m_axisHeight); - painter.drawLine(selectionLeft, m_axisHeight, selectionRight, m_axisHeight); + + 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 - */ + /* Draw the ruler zoom */ if (m_mousePressMode == RulerZoom) { int x1 = m_mousePressPosition.x(); - int x2 = m_mousePosition.x(); - int y1 = m_axisHeight; - int y2 = height(); + int x2 = qMax(m_mousePosition.x(), m_axisWidth); painter.setPen(m_zoomBorder); - painter.drawLine(x1, 0, x1, y2); - painter.drawLine(x2, 0, x2, y2); - painter.drawLine(x1, y1, x2, y1); - - painter.setPen(Qt::NoPen); - painter.setBrush(m_zoomBackground); - painter.drawRect(x1, y1, x2 - x1, y2); + 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); } }