X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=gui%2Ftimelinewidget.cpp;h=d9f1b5809ebf63246dba4c4c7f7cb2aeb53c9bca;hb=56ad11c7849c7e6ca0ad66558cb1a99c58d4cd3d;hp=e6d2712cb5eac272bfe7492c71117d718a6c77b9;hpb=fc4f55a3193269f86c142219f5593dd8b8e9b3c8;p=apitrace diff --git a/gui/timelinewidget.cpp b/gui/timelinewidget.cpp index e6d2712..d9f1b58 100644 --- a/gui/timelinewidget.cpp +++ b/gui/timelinewidget.cpp @@ -9,8 +9,10 @@ #include #include -typedef trace::Profile::Call Call; typedef trace::Profile::Frame Frame; +typedef trace::Profile::Program Program; +typedef trace::Profile::CpuCall CpuCall; +typedef trace::Profile::DrawCall DrawCall; TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent), @@ -19,16 +21,18 @@ TimelineWidget::TimelineWidget(QWidget *parent) 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_selectionBackground(QColor(245, 245, 255)), - m_selectionBorder(QColor(50, 50, 255)), - m_zoomBorder(Qt::green), - m_zoomBackground(QColor(100, 255, 100, 80)) + 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); @@ -111,18 +115,54 @@ 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; + } + + 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); - return &item; + if (res != m_profile->frames.end()) { + const Frame& frame = *res; + return &frame; } return NULL; @@ -130,45 +170,82 @@ 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() -{ - typedef QPair HeatProgram; - QList heats; - int idx; +const CpuCall* TimelineWidget::cpuCallAtTime(int64_t time) { + if (!m_profile) { + return NULL; + } - m_programRowMap.clear(); - m_rowCount = 0; + std::vector::const_iterator res + = binarySearchTimespan( + m_profile->cpuCalls.begin(), + m_profile->cpuCalls.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->cpuCalls.end()) { + const CpuCall& call = *res; + return &call; + } - for (Call::const_iterator jtr = frame.calls.begin(); jtr != frame.calls.end(); ++jtr) { - const Call& call = *jtr; + return NULL; +} - while (call.program >= heats.size()) { - heats.append(HeatProgram(0, heats.size())); - m_programRowMap.append(m_programRowMap.size()); - } - heats[call.program].first += call.gpuDuration; - } +/** + * Find the draw call at time + */ +const DrawCall* TimelineWidget::drawCallAtTime(int program, int64_t time) { + if (!m_profile) { + return NULL; } - qSort(heats); - idx = heats.size() - 1; + std::vector& drawCalls = m_profile->programs[program].drawCalls; - for (QList::iterator itr = heats.begin(); itr != heats.end(); ++itr, --idx) { - HeatProgram& pair = *itr; + std::vector::const_iterator res + = binarySearchTimespan( + drawCalls.begin(), + drawCalls.end(), + time); - if (pair.first == 0) { - m_programRowMap[pair.second] = -1; - } else { - m_programRowMap[pair.second] = idx; - m_rowCount++; + if (res != drawCalls.end()) { + const DrawCall& call = *res; + return &call; + } + + 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 +260,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; @@ -253,8 +330,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); /* Update vertical scroll bar */ if (m_profile) { @@ -267,33 +344,56 @@ 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); - - QToolTip::showText(e->globalPos(), text); + 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 CpuCall* 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(call->cpuStart); + text += QString("\nCPU Duration: %1").arg(call->cpuDuration); + + QToolTip::showText(e->globalPos(), text); + tooltip = true; + } + } else { + int row = (y - m_rowHeight + m_scrollY) / m_rowHeight; + + if (row < m_rowPrograms.size()) { + int program = m_rowPrograms[row]; + const DrawCall* call = drawCallAtTime(program, time); + + if (call) { + QString text; + text = QString::fromStdString(call->name); + text += QString("\nCall: %1").arg(call->no); + text += QString("\nGPU Start: %1").arg(call->gpuStart); + text += QString("\nCPU Start: %1").arg(call->cpuStart); + text += QString("\nGPU Duration: %1").arg(call->gpuDuration); + text += QString("\nCPU Duration: %1").arg(call->cpuDuration); + text += QString("\nPixels Drawn: %1").arg(call->pixels); + + QToolTip::showText(e->globalPos(), text); + tooltip = true; + } + } + } } - } - - m_mousePosition = e->pos(); - - if (e->buttons().testFlag(Qt::LeftButton)) { - QToolTip::hideText(); - + } else if (e->buttons().testFlag(Qt::LeftButton)) { if (m_mousePressMode == DragView) { /* Horizontal scroll */ double dt = m_timeWidth; @@ -305,21 +405,26 @@ 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)); } 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 { @@ -350,7 +455,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *e) /* Calculate new time view based on selected area */ 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); @@ -371,26 +476,38 @@ 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); - - for (Frame::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) { - const Frame& frame = *itr; + if (e->pos().x() > m_axisWidth) { + int row = (e->pos().y() - m_axisHeight) / m_rowHeight; - if (frame.gpuStart + frame.gpuDuration < time) - continue; + if (e->pos().y() < m_axisHeight) { + /* Horizontal axis */ + const Frame* frame = frameAtTime(time); - if (frame.gpuStart > time) - break; + if (frame) { + setSelection(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true); + return; + } + } else if (row == 0) { + /* CPU Calls */ + const CpuCall* call = cpuCallAtTime(time); - setSelection(frame.gpuStart, frame.gpuStart + frame.gpuDuration, true); - return; + if (call) { + emit jumpToCall(call->no); + return; + } + } else if (row > 0) { + /* Draw Calls */ + int program = m_rowPrograms[row - 1 + m_row]; + const DrawCall* call = drawCallAtTime(program, time); + + if (call) { + emit jumpToCall(call->no); + return; + } } } - if (const VisibleItem* item = itemAtPosition(e->pos())) { - emit jumpToCall(item->call->no); - } else if (time < m_timeSelectionStart || time > m_timeSelectionEnd) { + if (time < m_timeSelectionStart || time > m_timeSelectionEnd) { setSelection(0, 0, true); } } @@ -404,6 +521,7 @@ void TimelineWidget::wheelEvent(QWheelEvent *e) int zoomPercent = 10; + /* If holding Ctrl key then zoom 2x faster */ if (QApplication::keyboardModifiers() & Qt::ControlModifier) { zoomPercent = 20; } @@ -432,27 +550,25 @@ 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 isCpu) { + if (heat == 0) { + return; + } + double timePerPixel = m_timeWidth; timePerPixel /= m_viewWidth; - for(int i = 0, y = 0; i < rows.size(); ++i, y += m_rowHeight) { - if (rows[i] == 0) - continue; - - if (y > m_viewHeight) - continue; - - double heat = rows[i] / timePerPixel; - heat = qBound(0.0, heat, 1.0); - heat *= 255.0; + double colour = heat / timePerPixel; + colour = qBound(0.0, colour * 255.0, 255.0); - painter.setPen(QColor(255, 255 - heat, 255 - heat)); - painter.drawLine(x, y, x, y + m_rowHeight); - - rows[i] = 0; + if (isCpu) { + painter.setPen(QColor(255 - colour, 255 - colour, 255)); + } else { + painter.setPen(QColor(255, 255 - colour, 255 - colour)); } + + painter.drawLine(x, 0, x, m_rowHeight - 1); } @@ -464,230 +580,303 @@ 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; - - - /* - * 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); - - painter.setPen(Qt::NoPen); - painter.setBrush(m_selectionBackground); - painter.drawRect(selectionLeft, m_axisHeight, selectionRight - selectionLeft, m_viewHeight); - } + 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(); - /* - * Draw profile heatmap - */ - rowEnd = m_row + (m_viewHeight / m_rowHeight) + 1; - timeEnd = m_time + m_timeWidth; - m_visibleItems.clear(); - lastX = 0; + /* Draw GPU rows */ + painter.translate(m_axisWidth, m_axisHeight + m_rowHeight - (m_scrollY % m_rowHeight)); - painter.translate(m_axisWidth + 1, m_axisHeight + 1 - (m_scrollY % m_rowHeight)); - painter.setBrush(m_itemBackground); - painter.setPen(m_itemBorder); + for (int row = m_row; row < rowEnd; ++row) { + Program& program = m_profile->programs[m_rowPrograms[row]]; + lastX = 0; + heat = 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 = program.drawCalls.begin(); itr != program.drawCalls.end(); ++itr) { + const DrawCall& call = *itr; + int64_t gpuEnd = call.gpuStart + call.gpuDuration; - if (frame.gpuStart > timeEnd) - break; - - if (frame.gpuStart + frame.gpuDuration < m_time) - continue; - - 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; + if (call.gpuStart > timeEnd) { + break; + } - if (row < m_row || row > rowEnd) + if (gpuEnd < m_time) { continue; + } - double left = qMax(0.0, timeToPosition(call.gpuStart)); - double right = timeToPosition(call.gpuStart + call.gpuDuration); + double left = timeToPosition(call.gpuStart); + double right = timeToPosition(gpuEnd); - int leftX = left; + int leftX = left; int rightX = right; - if (lastX != leftX) { - paintHeatmapColumn(lastX, painter, heatMap); + /* Draw last heat if needed */ + if (leftX != lastX) { + drawHeat(painter, lastX, heat, false); lastX = leftX; + heat = 0; } - row -= m_row; - - if (rightX <= lastX + 1) { - if (lastX == rightX) { + if (rightX <= leftX + 1) { + if (rightX == lastX) { /* Fully contained in this X */ - heatMap[row] += call.gpuDuration; + heat += call.gpuDuration; } else { /* Split call time between the two pixels it occupies */ int64_t time = positionToTime(rightX); - heatMap[row] += time - call.gpuStart; - paintHeatmapColumn(lastX, painter, heatMap); + heat += time - call.gpuStart; + drawHeat(painter, lastX, heat, false); - heatMap[row] += (call.gpuDuration + call.gpuStart) - time; + heat = gpuEnd - 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.setLeft(left + 0.5); + rect.setWidth(right - left); + rect.setTop(0); 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); + painter.fillRect(rect, m_itemBackground); if (rect.width() > 6) { - rect.adjust(1, 0, -1, 0); + rect.adjust(1, 0, -1, -2); painter.setPen(m_itemForeground); - painter.drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, QString::fromStdString(call.name)); - painter.setPen(m_itemBorder); + + painter.drawText(rect, + Qt::AlignLeft | Qt::AlignVCenter, + painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width())); } } } - } - - /* Paint the last column if needed */ - paintHeatmapColumn(lastX, painter, heatMap); + painter.translate(0, m_rowHeight); + } - /* - * Draw the axis border and background - */ + /* Draw CPU row */ painter.resetTransform(); - painter.setPen(Qt::NoPen); - painter.setBrush(m_axisBackground); - painter.drawRect(0, 0, m_viewWidth + m_axisWidth, m_axisHeight); - painter.drawRect(0, m_axisHeight, m_axisWidth, m_viewHeight); + painter.translate(m_axisWidth, m_axisHeight); + painter.fillRect(0, 0, m_viewWidth, m_rowHeight, Qt::white); - 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); + for (std::vector::const_iterator itr = m_profile->cpuCalls.begin(); itr != m_profile->cpuCalls.end(); ++itr) { + const CpuCall& call = *itr; + int64_t cpuEnd = call.cpuStart + call.cpuDuration; + if (call.cpuStart > timeEnd) { + continue; + } - /* - * 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; + if (cpuEnd < m_time) { + continue; + } - if (frame.gpuStart > timeEnd) - break; + double left = timeToPosition(call.cpuStart); + double right = timeToPosition(cpuEnd); - if (frame.gpuStart + frame.gpuDuration < m_time) - continue; + int leftX = left; + int rightX = right; - left = timeToPosition(frame.gpuStart); - right = timeToPosition(frame.gpuStart + frame.gpuDuration) + 0.5; + /* Draw last heat if needed */ + if (leftX != lastX) { + drawHeat(painter, lastX, heat, true); + lastX = leftX; + heat = 0; + } - left = qBound(0, left, m_viewWidth) + m_axisWidth; - right = qBound(0, right, m_viewWidth) + m_axisWidth; + 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); - width = right - left; + heat += time - call.cpuStart; + drawHeat(painter, lastX, heat, true); - text = QString("Frame %1").arg(frame.no); + heat = cpuEnd - time; + lastX = rightX; + } + } 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)); - if (painter.fontMetrics().width(text) > width) { - text = QString("%1").arg(frame.no); + if (rect.width() > 6) { + rect.adjust(1, 0, -1, -2); + painter.setPen(QColor(255, 255, 0)); - if (painter.fontMetrics().width(text) > width) { - drawText = false; + painter.drawText(rect, + Qt::AlignLeft | Qt::AlignVCenter, + painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width())); } } + } + + /* 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); + + for (int row = m_row; row < rowEnd; ++row) { + int y = (row - m_row) * m_rowHeight - (m_scrollY % m_rowHeight); - if (drawText) { - painter.drawText(left, 0, width, m_axisHeight, Qt::AlignHCenter | Qt::AlignVCenter, text); + 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); + + 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 frame numbers */ + painter.resetTransform(); + + painter.setPen(m_axisForeground); + painter.translate(m_axisWidth, 0); + + int lastLabel = -9999; + + double scroll = m_time; + scroll /= m_timeWidth; + scroll *= m_viewWidth; + + 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 (frame.cpuStart > 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; - /* - * Draw vertical axis - */ - painter.translate(0, -(m_scrollY % m_rowHeight)); + double right = frame.cpuStart + frame.cpuDuration; + right /= m_timeWidth; + right *= m_viewWidth; - for (int i = 0; i < m_programRowMap.size(); ++i) { - int y = (m_programRowMap[i] - m_row) * m_rowHeight; + QString text = QString("%1").arg(frame.no); - if (m_programRowMap[i] < 0 || y < -m_rowHeight || y > m_viewHeight) - continue; + width = painter.fontMetrics().width(text) + padding * 2; + + 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 - */ + /* 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; + 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); - } + if (selectionLeft >= m_axisWidth && selectionLeft < widgetWidth) { + painter.drawLine(selectionLeft, 0, selectionLeft, widgetHeight); + } + + if (selectionRight >= m_axisWidth && selectionRight < widgetWidth) { + painter.drawLine(selectionRight, 0, selectionRight, widgetHeight); + } + + selectionLeft = qBound(m_axisWidth, selectionLeft, widgetWidth); + selectionRight = qBound(m_axisWidth, selectionRight, widgetWidth); - /* - * Draw the ruler zoom - */ + painter.drawLine(selectionLeft, m_axisHeight - 1, selectionRight, m_axisHeight - 1); + painter.fillRect(selectionLeft, 0, selectionRight - selectionLeft, widgetHeight, m_selectionBackground); + } + + /* 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); } }