diff --git a/include/profiler/reader.h b/include/profiler/reader.h index 9def127..f0ee606 100644 --- a/include/profiler/reader.h +++ b/include/profiler/reader.h @@ -27,7 +27,7 @@ along with this program.If not, see . ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//#define PROFILER_COUNT_TOTAL_CHILDREN_NUMBER +#define PROFILER_COUNT_TOTAL_CHILDREN_NUMBER #define PROFILER_COUNT_DEPTH namespace profiler { diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index 55b20b8..66a8520 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -29,11 +29,13 @@ #include #include #include +#include #include #include #include "blocks_graphics_view.h" -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// const qreal SCALING_COEFFICIENT = 1.25; const qreal SCALING_COEFFICIENT_INV = 1.0 / SCALING_COEFFICIENT; @@ -41,9 +43,8 @@ const qreal SCALING_COEFFICIENT_INV = 1.0 / SCALING_COEFFICIENT; const unsigned short GRAPHICS_ROW_SIZE = 16; const unsigned short GRAPHICS_ROW_SIZE_FULL = GRAPHICS_ROW_SIZE + 2; const unsigned short ROW_SPACING = 4; -const QRgb DEFAULT_COLOR = 0x00f0e094; -const QRgb BORDERS_COLOR = 0x00a08080; +const QRgb BORDERS_COLOR = 0x00a07050; const QRgb BACKGROUND_1 = 0x00f8f8f8; const QRgb BACKGROUND_2 = 0x00ffffff;// 0x00e0e0e0; @@ -71,16 +72,7 @@ QRgb BG2() return BACKGROUND_2; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -QRgb toRgb(unsigned int _red, unsigned int _green, unsigned int _blue) -{ - if (_red == 0 && _green == 0 && _blue == 0) - return DEFAULT_COLOR; - return (_red << 16) + (_green << 8) + _blue; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// void ProfBlockItem::setRect(qreal _x, float _y, float _w, float _h) { x = _x; @@ -119,10 +111,16 @@ ProfGraphicsItem::ProfGraphicsItem() : ProfGraphicsItem(false) { } -ProfGraphicsItem::ProfGraphicsItem(bool _test) : QGraphicsItem(nullptr), m_bTest(_test), m_backgroundColor(0x00ffffff) +ProfGraphicsItem::ProfGraphicsItem(bool _test) : QGraphicsItem(nullptr), m_bTest(_test), m_backgroundColor(0x00ffffff), m_thread_id(0), m_pRoot(nullptr) { } +ProfGraphicsItem::ProfGraphicsItem(::profiler::thread_id_t _thread_id, const BlocksTree* _root) : ProfGraphicsItem(false) +{ + m_thread_id = _thread_id; + m_pRoot = _root; +} + ProfGraphicsItem::~ProfGraphicsItem() { } @@ -132,17 +130,16 @@ const ProfGraphicsView* ProfGraphicsItem::view() const return static_cast(scene()->parent()); } +////////////////////////////////////////////////////////////////////////// + QRectF ProfGraphicsItem::boundingRect() const { //const auto sceneView = view(); - //return QRectF(m_rect.left() - sceneView->offset() / sceneView->scale(), m_rect.top(), m_rect.width() * sceneView->scale(), m_rect.height()); - return m_rect; + //return QRectF(m_boundingRect.left() - sceneView->offset() / sceneView->scale(), m_boundingRect.top(), m_boundingRect.width() * sceneView->scale(), m_boundingRect.height()); + return m_boundingRect; } -QRgb ProfGraphicsItem::backgroundColor() const -{ - return m_backgroundColor; -} +////////////////////////////////////////////////////////////////////////// void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) { @@ -152,8 +149,8 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } const auto sceneView = view(); - const auto currentScale = sceneView->scale(); // Current GraphicsView scale const auto visibleSceneRect = sceneView->visibleSceneRect(); // Current visible scene rect + const auto currentScale = sceneView->scale(); // Current GraphicsView scale const auto offset = sceneView->offset(); const auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale; @@ -176,7 +173,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* // Search for first visible top item auto& level0 = m_levels[0]; - auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ProfBlockItem& _item, double _value) + auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ProfBlockItem& _item, qreal _value) { return _item.left() < _value; }); @@ -197,7 +194,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* brush.setColor(m_backgroundColor); _painter->setBrush(brush); _painter->setPen(Qt::NoPen); - rect.setRect(0, m_rect.top() - (ROW_SPACING >> 1), visibleSceneRect.width(), m_rect.height() + ROW_SPACING); + rect.setRect(0, m_boundingRect.top() - (ROW_SPACING >> 1), visibleSceneRect.width(), m_boundingRect.height() + ROW_SPACING); _painter->drawRect(rect); // END. Draw background. ~~~~~~~~~~~~~~~~~~~~ @@ -230,7 +227,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* state = item.state; } - if (item.right() < sceneLeft || state == -1) + if (item.right() < sceneLeft || state == -1 || (l == 0 && (item.top() > visibleSceneRect.bottom() || (item.top() + item.totalHeight) < visibleSceneRect.top()))) { ++m_levelsIndexes[l]; continue; @@ -360,21 +357,73 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _painter->restore(); } +////////////////////////////////////////////////////////////////////////// + +void ProfGraphicsItem::getBlocks(qreal _left, qreal _right, TreeBlocks& _blocks) const +{ + //if (m_bTest) + //{ + // return; + //} + + // Search for first top item + auto& level0 = m_levels[0]; + auto first = ::std::lower_bound(level0.begin(), level0.end(), _left, [](const ProfBlockItem& _item, qreal _value) + { + return _item.left() < _value; + }); + + size_t itemIndex = 0; + if (first != level0.end()) + { + itemIndex = first - level0.begin(); + if (itemIndex > 0) + itemIndex -= 1; + } + else + { + itemIndex = level0.size() - 1; + } + + for (size_t i = itemIndex, end = level0.size(); i < end; ++i) + { + const auto& item = level0[i]; + + if (item.left() > _right) + { + break; + } + + if (item.right() < _left) + { + continue; + } + + _blocks.emplace_back(m_thread_id, m_pRoot, item.block); + } +} + +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsItem::setBackgroundColor(QRgb _color) { m_backgroundColor = _color; } +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) { - m_rect.setRect(x, y, w, h); + m_boundingRect.setRect(x, y, w, h); } void ProfGraphicsItem::setBoundingRect(const QRectF& _rect) { - m_rect = _rect; + m_boundingRect = _rect; } +////////////////////////////////////////////////////////////////////////// + unsigned short ProfGraphicsItem::levels() const { return static_cast(m_levels.size()); @@ -391,6 +440,8 @@ void ProfGraphicsItem::reserve(unsigned short _level, size_t _items) m_levels[_level].reserve(_items); } +////////////////////////////////////////////////////////////////////////// + const ProfGraphicsItem::Children& ProfGraphicsItem::items(unsigned short _level) const { return m_levels[_level]; @@ -422,24 +473,173 @@ size_t ProfGraphicsItem::addItem(unsigned short _level, ProfBlockItem&& _item) { m_levels[_level].emplace_back(::std::forward(_item)); return m_levels[_level].size() - 1; -} +} -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// -ProfGraphicsScene::ProfGraphicsScene(QGraphicsView* _parent, bool _test) : QGraphicsScene(_parent), m_beginTime(-1) +ProfChronometerItem::ProfChronometerItem() : QGraphicsItem(), m_left(0), m_right(0), m_font(QFont("CourierNew", 16, 2)) { +} + +ProfChronometerItem::~ProfChronometerItem() +{ +} + +QRectF ProfChronometerItem::boundingRect() const +{ + return m_boundingRect; +} + +void ProfChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) +{ + const auto sceneView = view(); + const auto currentScale = sceneView->scale(); + const auto offset = sceneView->offset(); + const auto visibleSceneRect = sceneView->visibleSceneRect(); + const auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale; + + if (m_left > sceneRight || m_right < sceneLeft) + { + return; + } + + const auto time = width(); + QRectF rect(m_left * currentScale, visibleSceneRect.top(), ::std::max(time * currentScale, 1.0), visibleSceneRect.height()); + + QString text; + if (time < 1e-3) + { + text = ::std::move(QString("%1 ns").arg(time * 1e6, 0, 'f', 1)); + } + else if (time < 1) + { + text = ::std::move(QString("%1 us").arg(time * 1e3, 0, 'f', 1)); + } + else if (time < 1e3) + { + text = ::std::move(QString("%1 ms").arg(time, 0, 'f', 1)); + } + else + { + text = ::std::move(QString("%1 sec").arg(time * 1e-3, 0, 'f', 1)); + } + + const auto textRect = QFontMetrics(m_font).boundingRect(text); + + _painter->save(); + + _painter->setTransform(QTransform::fromTranslate(-x() - offset * currentScale, -y()), true); + _painter->setBrush(QColor(64, 64, 64, 64)); + _painter->setPen(Qt::NoPen); + _painter->drawRect(rect); + + _painter->setPen(0xff302010); + _painter->setFont(m_font); + + //if (m_left > sceneLeft && m_right < sceneRight) + { + if (textRect.width() < rect.width()) + { + _painter->drawText(rect, Qt::AlignCenter, text); + } + else + { + _painter->drawText(QPointF(rect.right(), rect.top() + rect.height() * 0.5 + textRect.height() * 0.33), text); + } + } + //else if (m_left > sceneLeft) + //{ + // if (textRect.width() < rect.width()) + // { + // _painter->drawText(rect, Qt::AlignCenter, text); + // } + // else + // { + // _painter->drawText(QPointF(rect.right(), rect.top() + rect.height() * 0.5 + textRect.height() * 0.33), text); + // } + //} + //else + //{ + //} + + _painter->restore(); +} + +void ProfChronometerItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) +{ + m_boundingRect.setRect(x, y, w, h); +} + +void ProfChronometerItem::setBoundingRect(const QRectF& _rect) +{ + m_boundingRect = _rect; +} + +void ProfChronometerItem::setLeftRight(qreal _left, qreal _right) +{ + if (_left < _right) + { + m_left = _left; + m_right = _right; + } + else + { + m_left = _right; + m_right = _left; + } +} + +const ProfGraphicsView* ProfChronometerItem::view() const +{ + return static_cast(scene()->parent()); +} + +////////////////////////////////////////////////////////////////////////// + +ProfGraphicsView::ProfGraphicsView(bool _test) + : QGraphicsView() + , m_beginTime(-1) + , m_scale(1) + , m_offset(0) + , m_mouseButtons(Qt::NoButton) + , m_pScrollbar(nullptr) + , m_chronometerItem(nullptr) + , m_flickerSpeed(0) + , m_bUpdatingRect(false) + , m_bTest(_test) + , m_bEmpty(true) +{ + initMode(); + setScene(new QGraphicsScene(this)); + if (_test) { test(18000, 40000000, 5); } + + updateVisibleSceneRect(); } -ProfGraphicsScene::ProfGraphicsScene(const thread_blocks_tree_t& _blocksTree, QGraphicsView* _parent) : QGraphicsScene(_parent), m_beginTime(-1) +ProfGraphicsView::ProfGraphicsView(const thread_blocks_tree_t& _blocksTree) + : QGraphicsView() + , m_beginTime(-1) + , m_scale(1) + , m_offset(0) + , m_mouseButtons(Qt::NoButton) + , m_pScrollbar(nullptr) + , m_chronometerItem(nullptr) + , m_flickerSpeed(0) + , m_bUpdatingRect(false) + , m_bTest(false) + , m_bEmpty(true) { + initMode(); + setScene(new QGraphicsScene(this)); setTree(_blocksTree); + updateVisibleSceneRect(); } -ProfGraphicsScene::~ProfGraphicsScene() +ProfGraphicsView::~ProfGraphicsView() { } @@ -480,7 +680,7 @@ void fillChildren(ProfGraphicsItem* _item, int _level, qreal _x, qreal _y, size_ } } -void ProfGraphicsScene::test(size_t _frames_number, size_t _total_items_number_estimate, int _depth) +void ProfGraphicsView::test(size_t _frames_number, size_t _total_items_number_estimate, int _depth) { static const qreal X_BEGIN = 50; static const qreal Y_BEGIN = 0; @@ -527,35 +727,76 @@ void ProfGraphicsScene::test(size_t _frames_number, size_t _total_items_number_e const auto h = item->getItem(0, 0).totalHeight; item->setBoundingRect(0, 0, x, h); - addItem(item); - setSceneRect(0, 0, x, Y_BEGIN + h); + clearSilent(); + m_items.push_back(item); + scene()->addItem(item); + scene()->setSceneRect(0, 0, x, Y_BEGIN + h); + + m_offset = 0; + updateVisibleSceneRect(); + setScrollbar(m_pScrollbar); + + m_chronometerItem = new ProfChronometerItem(); + m_chronometerItem->setZValue(10); + m_chronometerItem->setBoundingRect(scene()->sceneRect()); + m_chronometerItem->hide(); + scene()->addItem(m_chronometerItem); + + m_bTest = true; + m_bEmpty = false; } ////////////////////////////////////////////////////////////////////////// -void ProfGraphicsScene::clearSilent() +void ProfGraphicsView::clearSilent() { - const QSignalBlocker b(this); // block all scene signals (otherwise clear() would be extremely slow!) - clear(); - m_beginTime = -1; + const QSignalBlocker blocker(this), sceneBlocker(scene()); // block all scene signals (otherwise clear() would be extremely slow!) + m_flickerTimer.stop(); + scene()->clear(); + m_items.clear(); + m_selectedBlocks.clear(); + m_beginTime = -1; // reset begin time + m_scale = 1; // scale back to initial 100% scale + m_bTest = false; + m_bEmpty = true; + emit treeblocksChanged(m_selectedBlocks, m_beginTime); } -void ProfGraphicsScene::setTree(const thread_blocks_tree_t& _blocksTree) +void ProfGraphicsView::setTree(const thread_blocks_tree_t& _blocksTree) { + // clear scene + clearSilent(); + + // set new blocks tree // calculate scene size and fill it with items + + // Calculating start and end time ::profiler::timestamp_t finish = 0; - qreal y = 0; for (const auto& threadTree : _blocksTree) { const auto timestart = threadTree.second.children.front().node->block()->getBegin(); const auto timefinish = threadTree.second.children.back().node->block()->getEnd(); - if (m_beginTime > timestart) m_beginTime = timestart; - if (finish < timefinish) finish = timefinish; + if (m_beginTime > timestart) + { + m_beginTime = timestart; + } + + if (finish < timefinish) + { + finish = timefinish; + } + } + + // Filling scene with items + m_items.reserve(_blocksTree.size()); + qreal y = 0; + for (const auto& threadTree : _blocksTree) + { // fill scene with new items - qreal h = 0, x = time2position(timestart); - auto item = new ProfGraphicsItem(); + qreal h = 0, x = time2position(threadTree.second.children.front().node->block()->getBegin()); + auto item = new ProfGraphicsItem(threadTree.first, &threadTree.second); item->setLevels(threadTree.second.depth); item->setPos(0, y); @@ -563,22 +804,40 @@ void ProfGraphicsScene::setTree(const thread_blocks_tree_t& _blocksTree) item->setBoundingRect(0, 0, children_duration + x, h); item->setBackgroundColor(GetBackgroundColor()); - addItem(item); + m_items.push_back(item); + scene()->addItem(item); y += h + ROW_SPACING; } + // Calculating scene rect const qreal endX = time2position(finish + 1000000); - setSceneRect(0, 0, endX, y); + scene()->setSceneRect(0, 0, endX, y); - for (auto item : items()) + // Painting stub... + for (auto item : m_items) { - auto profilerItem = static_cast(item); - profilerItem->setBoundingRect(0, 0, endX, profilerItem->boundingRect().height()); + item->setBoundingRect(0, 0, endX, item->boundingRect().height()); } + + // Center view on the beginning of the scene + m_offset = 0; + updateVisibleSceneRect(); + setScrollbar(m_pScrollbar); + + // Adding invisible chronometer item (it will be shown on mouse right button click) + m_chronometerItem = new ProfChronometerItem(); + m_chronometerItem->setZValue(10); + m_chronometerItem->setBoundingRect(scene()->sceneRect()); + m_chronometerItem->hide(); + scene()->addItem(m_chronometerItem); + + // Setting flags + m_bTest = false; + m_bEmpty = false; } -qreal ProfGraphicsScene::setTree(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y, unsigned short _level) +qreal ProfGraphicsView::setTree(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y, unsigned short _level) { if (_children.empty()) { @@ -651,39 +910,7 @@ qreal ProfGraphicsScene::setTree(ProfGraphicsItem* _item, const BlocksTree::chil return total_duration; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -ProfGraphicsView::ProfGraphicsView(bool _test) - : QGraphicsView() - , m_scale(1) - , m_offset(0) - , m_mouseButtons(Qt::NoButton) - , m_pScrollbar(nullptr) - , m_flickerSpeed(0) - , m_bUpdatingRect(false) -{ - initMode(); - setScene(new ProfGraphicsScene(this, _test)); - updateVisibleSceneRect(); -} - -ProfGraphicsView::ProfGraphicsView(const thread_blocks_tree_t& _blocksTree) - : QGraphicsView() - , m_scale(1) - , m_offset(0) - , m_mouseButtons(Qt::NoButton) - , m_pScrollbar(nullptr) - , m_flickerSpeed(0) - , m_bUpdatingRect(false) -{ - initMode(); - setScene(new ProfGraphicsScene(_blocksTree, this)); - updateVisibleSceneRect(); -} - -ProfGraphicsView::~ProfGraphicsView() -{ -} +////////////////////////////////////////////////////////////////////////// void ProfGraphicsView::setScrollbar(GraphicsHorizontalScrollbar* _scrollbar) { @@ -699,6 +926,8 @@ void ProfGraphicsView::setScrollbar(GraphicsHorizontalScrollbar* _scrollbar) connect(m_pScrollbar, &GraphicsHorizontalScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange); } +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsView::updateVisibleSceneRect() { m_visibleSceneRect = mapToScene(rect()).boundingRect(); @@ -709,8 +938,16 @@ void ProfGraphicsView::updateScene() scene()->update(m_visibleSceneRect); } +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsView::wheelEvent(QWheelEvent* _event) { + if (m_bEmpty) + { + _event->accept(); + return; + } + const decltype(m_scale) scaleCoeff = _event->delta() > 0 ? SCALING_COEFFICIENT : SCALING_COEFFICIENT_INV; // Remember current mouse position @@ -739,8 +976,16 @@ void ProfGraphicsView::wheelEvent(QWheelEvent* _event) _event->accept(); } +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsView::mousePressEvent(QMouseEvent* _event) { + if (m_bEmpty) + { + _event->accept(); + return; + } + m_mouseButtons = _event->buttons(); if (m_mouseButtons & Qt::LeftButton) @@ -748,17 +993,97 @@ void ProfGraphicsView::mousePressEvent(QMouseEvent* _event) m_mousePressPos = _event->globalPos(); } + if (m_mouseButtons & Qt::RightButton) + { + const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale; + m_chronometerItem->setLeftRight(mouseX, mouseX); + m_chronometerItem->hide(); + } + _event->accept(); } +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsView::mouseReleaseEvent(QMouseEvent* _event) { + if (m_bEmpty) + { + _event->accept(); + return; + } + + bool changedSelection = false; + if (m_mouseButtons & Qt::RightButton) + { + if (m_chronometerItem->isVisible() && m_chronometerItem->width() < 1e-6) + { + m_chronometerItem->hide(); + } + + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + m_selectedBlocks.clear(); + } + + if (!m_bTest && m_chronometerItem->isVisible()) + { + //printf("INTERVAL: {%lf, %lf} ms\n", m_chronometerItem->left(), m_chronometerItem->right()); + + for (auto item : m_items) + { + item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks); + } + + if (!m_selectedBlocks.empty()) + { + changedSelection = true; + } + } + } + m_mouseButtons = _event->buttons(); _event->accept(); + + if (changedSelection) + { + emit treeblocksChanged(m_selectedBlocks, m_beginTime); + } } +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsView::mouseMoveEvent(QMouseEvent* _event) { + if (m_bEmpty) + { + _event->accept(); + return; + } + + bool needUpdate = false; + + if (m_mouseButtons & Qt::RightButton) + { + const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale; + if (mouseX > m_chronometerItem->left()) + { + m_chronometerItem->setLeftRight(m_chronometerItem->left(), mouseX); + } + else + { + m_chronometerItem->setLeftRight(mouseX, m_chronometerItem->right()); + } + + if (!m_chronometerItem->isVisible() && m_chronometerItem->width() > 1e-6) + { + m_chronometerItem->show(); + } + + needUpdate = true; + } + if (m_mouseButtons & Qt::LeftButton) { const auto pos = _event->globalPos(); @@ -783,12 +1108,27 @@ void ProfGraphicsView::mouseMoveEvent(QMouseEvent* _event) m_flickerTimer.start(20); } + needUpdate = true; + } + + if (needUpdate) + { updateScene(); // repaint scene } _event->accept(); } +////////////////////////////////////////////////////////////////////////// + +void ProfGraphicsView::resizeEvent(QResizeEvent* _event) +{ + updateVisibleSceneRect(); // Update scene visible rect only once + updateScene(); // repaint scene +} + +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsView::initMode() { // TODO: find mode with least number of bugs :) @@ -804,56 +1144,29 @@ void ProfGraphicsView::initMode() connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout); } -void ProfGraphicsView::setTree(const thread_blocks_tree_t& _blocksTree) -{ - // clear scene - clearSilent(); - - // set new blocks tree - static_cast(scene())->setTree(_blocksTree); - - // center view on the beginning of the scene - m_offset = 0; - updateVisibleSceneRect(); - setScrollbar(m_pScrollbar); -} - -void ProfGraphicsView::clearSilent() -{ - // block all signals for faster scene recreation - const QSignalBlocker b(this); - - // clear scene - static_cast(scene())->clearSilent(); - - // scale back to initial 100% scale - m_scale = 1; -} - -void ProfGraphicsView::test(size_t _frames_number, size_t _total_items_number_estimate, int _depth) -{ - static_cast(scene())->test(_frames_number, _total_items_number_estimate, _depth); - m_offset = 0; - updateVisibleSceneRect(); - setScrollbar(m_pScrollbar); -} +////////////////////////////////////////////////////////////////////////// void ProfGraphicsView::onScrollbarValueChange(int) { - if (!m_bUpdatingRect) + if (!m_bUpdatingRect && !m_bEmpty) updateVisibleSceneRect(); } void ProfGraphicsView::onGraphicsScrollbarValueChange(qreal _value) { - m_offset = _value; - if (!m_bUpdatingRect) + if (!m_bEmpty) { - updateVisibleSceneRect(); - updateScene(); + m_offset = _value; + if (!m_bUpdatingRect) + { + updateVisibleSceneRect(); + updateScene(); + } } } +////////////////////////////////////////////////////////////////////////// + void ProfGraphicsView::onFlickerTimeout() { if (m_mouseButtons & Qt::LeftButton) @@ -883,7 +1196,7 @@ void ProfGraphicsView::onFlickerTimeout() } } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// ProfGraphicsViewWidget::ProfGraphicsViewWidget(bool _test) : QWidget(nullptr) @@ -919,4 +1232,5 @@ ProfGraphicsView* ProfGraphicsViewWidget::view() return m_view; } -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/blocks_graphics_view.h b/profiler_gui/blocks_graphics_view.h index 4f8f7e5..10b44c8 100644 --- a/profiler_gui/blocks_graphics_view.h +++ b/profiler_gui/blocks_graphics_view.h @@ -26,16 +26,18 @@ #include #include -#include -#include +#include +#include #include #include #include #include #include "graphics_scrollbar.h" #include "profiler/reader.h" +#include "common_types.h" -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// #pragma pack(push, 1) struct ProfBlockItem @@ -60,7 +62,7 @@ struct ProfBlockItem }; #pragma pack(pop) -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// class ProfGraphicsView; @@ -73,20 +75,23 @@ class ProfGraphicsItem : public QGraphicsItem DrawIndexes m_levelsIndexes; Sublevels m_levels; - QRectF m_rect; - QRgb m_backgroundColor; - const bool m_bTest; + QRectF m_boundingRect; + const BlocksTree* m_pRoot; + ::profiler::thread_id_t m_thread_id; + QRgb m_backgroundColor; + const bool m_bTest; public: ProfGraphicsItem(); ProfGraphicsItem(bool _test); + ProfGraphicsItem(::profiler::thread_id_t _thread_id, const BlocksTree* _root); virtual ~ProfGraphicsItem(); QRectF boundingRect() const override; void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; - QRgb backgroundColor() const; +public: void setBoundingRect(qreal x, qreal y, qreal w, qreal h); void setBoundingRect(const QRectF& _rect); @@ -103,49 +108,58 @@ public: size_t addItem(unsigned short _level, const ProfBlockItem& _item); size_t addItem(unsigned short _level, ProfBlockItem&& _item); + void getBlocks(qreal _left, qreal _right, TreeBlocks& _blocks) const; + private: const ProfGraphicsView* view() const; }; // END of class ProfGraphicsItem. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// -class ProfGraphicsScene : public QGraphicsScene +class ProfChronometerItem : public QGraphicsItem { - friend class ProfGraphicsView; - - Q_OBJECT - -private: - - typedef ProfGraphicsScene This; - - ::profiler::timestamp_t m_beginTime; + QFont m_font; + QRectF m_boundingRect; + qreal m_left, m_right; public: - ProfGraphicsScene(QGraphicsView* _parent, bool _test = false); - ProfGraphicsScene(const thread_blocks_tree_t& _blocksTree, QGraphicsView* _parent); - virtual ~ProfGraphicsScene(); + ProfChronometerItem(); + virtual ~ProfChronometerItem(); + + QRectF boundingRect() const override; + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + +public: + + void setBoundingRect(qreal x, qreal y, qreal w, qreal h); + void setBoundingRect(const QRectF& _rect); + void setLeftRight(qreal _left, qreal _right); + + inline qreal left() const + { + return m_left; + } + + inline qreal right() const + { + return m_right; + } + + inline qreal width() const + { + return m_right - m_left; + } private: - void test(size_t _frames_number, size_t _total_items_number_estimate, int _depth); + const ProfGraphicsView* view() const; - void clearSilent(); +}; // END of class ProfChronometerItem. - void setTree(const thread_blocks_tree_t& _blocksTree); - qreal setTree(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y, unsigned short _level); - - inline qreal time2position(const profiler::timestamp_t& _time) const - { - return qreal(_time - m_beginTime) * 1e-6; - } - -}; // END of class ProfGraphicsScene. - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// class ProfGraphicsView : public QGraphicsView { @@ -154,21 +168,26 @@ class ProfGraphicsView : public QGraphicsView private: typedef ProfGraphicsView This; + typedef ::std::vector Items; + Items m_items; + TreeBlocks m_selectedBlocks; QTimer m_flickerTimer; QRectF m_visibleSceneRect; + ::profiler::timestamp_t m_beginTime; qreal m_scale; qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :( QPoint m_mousePressPos; Qt::MouseButtons m_mouseButtons; GraphicsHorizontalScrollbar* m_pScrollbar; + ProfChronometerItem* m_chronometerItem; int m_flickerSpeed; bool m_bUpdatingRect; + bool m_bTest; + bool m_bEmpty; public: - using QGraphicsView::scale; - ProfGraphicsView(bool _test = false); ProfGraphicsView(const thread_blocks_tree_t& _blocksTree); virtual ~ProfGraphicsView(); @@ -177,6 +196,7 @@ public: void mousePressEvent(QMouseEvent* _event) override; void mouseReleaseEvent(QMouseEvent* _event) override; void mouseMoveEvent(QMouseEvent* _event) override; + void resizeEvent(QResizeEvent* _event) override; inline qreal scale() const { @@ -193,17 +213,27 @@ public: return m_visibleSceneRect; } + inline qreal time2position(const profiler::timestamp_t& _time) const + { + return qreal(_time - m_beginTime) * 1e-6; + } + void setScrollbar(GraphicsHorizontalScrollbar* _scrollbar); - void setTree(const thread_blocks_tree_t& _blocksTree); void clearSilent(); void test(size_t _frames_number, size_t _total_items_number_estimate, int _depth); + void setTree(const thread_blocks_tree_t& _blocksTree); + +signals: + + void treeblocksChanged(const TreeBlocks& _blocks, ::profiler::timestamp_t _begin_time); private: void initMode(); void updateVisibleSceneRect(); void updateScene(); + qreal setTree(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y, unsigned short _level); private slots: @@ -213,7 +243,7 @@ private slots: }; // END of class ProfGraphicsView. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// class ProfGraphicsViewWidget : public QWidget { @@ -236,6 +266,7 @@ private: }; // END of class ProfGraphicsViewWidget. -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// #endif // MY____GRAPHICS___VIEW_H diff --git a/profiler_gui/blocks_tree_widget.cpp b/profiler_gui/blocks_tree_widget.cpp index 84a238a..25a5396 100644 --- a/profiler_gui/blocks_tree_widget.cpp +++ b/profiler_gui/blocks_tree_widget.cpp @@ -36,8 +36,8 @@ enum ColumnsIndexes COL_UNKNOWN = -1, COL_NAME = 0, - COL_DURATION, COL_BEGIN, + COL_DURATION, COL_END, COL_MIN_TOTAL, COL_MAX_TOTAL, @@ -70,11 +70,13 @@ bool ProfTreeWidgetItem::operator < (const Parent& _other) const switch (col) { // case COL_UNKNOWN: -// case COL_NAME: -// { -// // column 0 - Name -// return Parent::operator < (_other); -// } + case COL_NAME: + { + // column 0 - Name + if (parent() == nullptr) + return false; // Do not sort topLevelItems + return Parent::operator < (_other); + } case COL_NCALLS_TOTAL: case COL_NCALLS: @@ -160,9 +162,29 @@ void ProfTreeWidgetItem::colorize(bool _colorize) } } +void ProfTreeWidgetItem::collapseAll() +{ + for (int i = 0, childrenNumber = childCount(); i < childrenNumber; ++i) + { + static_cast(child(i))->collapseAll(); + } + + setExpanded(false); +} + +void ProfTreeWidgetItem::expandAll() +{ + for (int i = 0, childrenNumber = childCount(); i < childrenNumber; ++i) + { + static_cast(child(i))->expandAll(); + } + + setExpanded(true); +} + ////////////////////////////////////////////////////////////////////////// -ProfTreeWidget::ProfTreeWidget(QWidget* _parent) : Parent(_parent), m_beginTime(-1), m_bColorRows(false) +ProfTreeWidget::ProfTreeWidget(QWidget* _parent) : Parent(_parent), m_beginTime(-1), m_bColorRows(true) { setAutoFillBackground(false); setAlternatingRowColors(true); @@ -201,6 +223,8 @@ ProfTreeWidget::~ProfTreeWidget() { } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ProfTreeWidget::setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree) { clearSilent(); @@ -213,6 +237,21 @@ void ProfTreeWidget::setTree(const unsigned int _blocksNumber, const thread_bloc connect(this, &Parent::itemExpanded, this, &This::onItemExpand); } +void ProfTreeWidget::setTreeBlocks(const TreeBlocks& _blocks, ::profiler::timestamp_t _begin_time) +{ + clearSilent(); + + m_beginTime = _begin_time; + setTreeInternal(_blocks); + + setSortingEnabled(true); + sortByColumn(COL_BEGIN, Qt::AscendingOrder); + + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ProfTreeWidget::clearSilent() { m_beginTime = -1; @@ -227,6 +266,8 @@ void ProfTreeWidget::clearSilent() clear(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree) { m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks @@ -245,9 +286,109 @@ void ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thr { auto item = new ProfTreeWidgetItem(&threadTree.second); item->setText(COL_NAME, QString("Thread %1").arg(threadTree.first)); + m_items.push_back(item); + setTreeInternal(threadTree.second.children, item); + addTopLevelItem(item); + + if (m_bColorRows) + { + item->colorize(m_bColorRows); + } + } +} + +void ProfTreeWidget::setTreeInternal(const TreeBlocks& _blocks) +{ + if (_blocks.empty()) + { + return; + } + + size_t blocksNumber = 0; + for (const auto& block : _blocks) + { + blocksNumber += block.tree->total_children_number; + } + + m_items.reserve(blocksNumber + _blocks.size()); // blocksNumber does not include root blocks + + typedef ::std::unordered_map<::profiler::thread_id_t, ProfTreeWidgetItem*, ::btw::do_no_hash<::profiler::thread_id_t>::hasher_t> ThreadsMap; + ThreadsMap threadsMap; + + const QSignalBlocker b(this); + for (const auto& block : _blocks) + { + ProfTreeWidgetItem* thread_item = nullptr; + auto thread_item_it = threadsMap.find(block.thread_id); + if (thread_item_it != threadsMap.end()) + { + thread_item = thread_item_it->second; + } + else + { + thread_item = new ProfTreeWidgetItem(block.thread_tree); + thread_item->setText(COL_NAME, QString("Thread %1").arg(block.thread_id)); + m_items.push_back(thread_item); + threadsMap.insert(::std::make_pair(block.thread_id, thread_item)); + } + + auto item = new ProfTreeWidgetItem(block.tree, thread_item); + item->setText(COL_NAME, block.tree->node->getBlockName()); + item->setTimeSmart(COL_DURATION, block.tree->node->block()->duration()); + item->setTimeMs(COL_BEGIN, block.tree->node->block()->getBegin() - m_beginTime); + item->setTimeMs(COL_END, block.tree->node->block()->getEnd() - m_beginTime); + + if (block.tree->total_statistics) + { + item->setTimeSmart(COL_MIN_TOTAL, block.tree->total_statistics->min_duration); + item->setTimeSmart(COL_MAX_TOTAL, block.tree->total_statistics->max_duration); + item->setTimeSmart(COL_AVERAGE_TOTAL, block.tree->total_statistics->average_duration()); + + item->setData(COL_NCALLS_TOTAL, Qt::UserRole, block.tree->total_statistics->calls_number); + item->setText(COL_NCALLS_TOTAL, QString::number(block.tree->total_statistics->calls_number)); + } + + if (block.tree->frame_statistics) + { + item->setTimeSmart(COL_MIN, block.tree->frame_statistics->min_duration); + item->setTimeSmart(COL_MAX, block.tree->frame_statistics->max_duration); + item->setTimeSmart(COL_AVERAGE, block.tree->frame_statistics->average_duration()); + + item->setData(COL_NCALLS, Qt::UserRole, block.tree->frame_statistics->calls_number); + item->setText(COL_NCALLS, QString::number(block.tree->frame_statistics->calls_number)); + } + + const auto color = block.tree->node->block()->getColor(); + const auto bgColor = QColor(toRgb(profiler::colors::get_red(color), profiler::colors::get_green(color), profiler::colors::get_blue(color))); + const auto fgColor = QColor(QRgb(0x00ffffff - bgColor.rgb())); + item->setBackgroundColor(bgColor); + item->setTextColor(fgColor); + + m_items.push_back(item); + m_itemblocks[block.tree->node] = item; + + if (!block.tree->children.empty()) + { + setTreeInternal(block.tree->children, item); + } + + if (m_bColorRows) + { + item->colorize(m_bColorRows); + } + } + + for (auto it : threadsMap) + { + addTopLevelItem(it.second); + + if (m_bColorRows) + { + it.second->colorize(m_bColorRows); + } } } @@ -282,7 +423,7 @@ void ProfTreeWidget::setTreeInternal(const BlocksTree::children_t& _children, Pr } const auto color = child.node->block()->getColor(); - const auto bgColor = QColor(profiler::colors::get_red(color), profiler::colors::get_green(color), profiler::colors::get_blue(color)); + const auto bgColor = QColor(toRgb(profiler::colors::get_red(color), profiler::colors::get_green(color), profiler::colors::get_blue(color))); const auto fgColor = QColor(QRgb(0x00ffffff - bgColor.rgb())); item->setBackgroundColor(bgColor); item->setTextColor(fgColor); @@ -294,9 +435,16 @@ void ProfTreeWidget::setTreeInternal(const BlocksTree::children_t& _children, Pr { setTreeInternal(child.children, item); } + + if (m_bColorRows) + { + item->colorize(m_bColorRows); + } } } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event) { const auto col = currentColumn(); @@ -319,6 +467,16 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event) menu.addSeparator(); + action = new QAction("Expand all children", nullptr); + connect(action, &QAction::triggered, this, &This::onExpandAllChildrenClicked); + menu.addAction(action); + + action = new QAction("Collapse all children", nullptr); + connect(action, &QAction::triggered, this, &This::onCollapseAllChildrenClicked); + menu.addAction(action); + + menu.addSeparator(); + action = new QAction("Color rows", nullptr); action->setCheckable(true); action->setChecked(m_bColorRows); @@ -353,6 +511,8 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event) _event->accept(); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ProfTreeWidget::onJumpToMinItemClicked(ProfTreeWidgetItem* _item) { auto it = m_itemblocks.find(_item->block()->total_statistics->min_duration_block); @@ -386,6 +546,27 @@ void ProfTreeWidget::onExpandAllClicked(bool) connect(this, &Parent::itemExpanded, this, &This::onItemExpand); } +void ProfTreeWidget::onCollapseAllChildrenClicked(bool) +{ + auto current = static_cast(currentItem()); + if (current != nullptr) + { + current->collapseAll(); + } +} + +void ProfTreeWidget::onExpandAllChildrenClicked(bool) +{ + auto current = static_cast(currentItem()); + if (current != nullptr) + { + disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand); + current->expandAll(); + resizeColumnToContents(COL_NAME); + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); + } +} + void ProfTreeWidget::onItemExpand(QTreeWidgetItem*) { resizeColumnToContents(COL_NAME); diff --git a/profiler_gui/blocks_tree_widget.h b/profiler_gui/blocks_tree_widget.h index 8f0d9ec..2e74def 100644 --- a/profiler_gui/blocks_tree_widget.h +++ b/profiler_gui/blocks_tree_widget.h @@ -31,6 +31,7 @@ #include #include #include "profiler/reader.h" +#include "common_types.h" ////////////////////////////////////////////////////////////////////////// @@ -109,6 +110,10 @@ public: void colorize(bool _colorize); + void collapseAll(); + + void expandAll(); + }; // END of class ProfTreeWidgetItem. ////////////////////////////////////////////////////////////////////////// @@ -169,13 +174,20 @@ public: ProfTreeWidget(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree, QWidget* _parent = nullptr); virtual ~ProfTreeWidget(); - void setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree); void clearSilent(); +public slots: + + void setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree); + + void setTreeBlocks(const TreeBlocks& _blocks, ::profiler::timestamp_t _begin_time); + protected: void setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree); + void setTreeInternal(const TreeBlocks& _blocks); + void setTreeInternal(const BlocksTree::children_t& _children, ProfTreeWidgetItem* _parent); void contextMenuEvent(QContextMenuEvent* _event) override; @@ -190,6 +202,10 @@ private slots: void onExpandAllClicked(bool); + void onCollapseAllChildrenClicked(bool); + + void onExpandAllChildrenClicked(bool); + void onItemExpand(QTreeWidgetItem*); void onColorizeRowsTriggered(bool _colorize); diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h new file mode 100644 index 0000000..2deb8ed --- /dev/null +++ b/profiler_gui/common_types.h @@ -0,0 +1,61 @@ +/************************************************************************ +* file name : common_types.h +* ----------------- : +* creation time : 2016/07/31 +* copyright : (c) 2016 Victor Zarubkin +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains declaration of common types for both GraphicsView +* : and TreeWidget. +* ----------------- : +* change log : * 2016/07/31 Victor Zarubkin: initial commit. +* : +* : * +* ----------------- : +* license : TODO: add license text +************************************************************************/ + +#ifndef EASY_PROFILER__GUI_COMMON_TYPES_H +#define EASY_PROFILER__GUI_COMMON_TYPES_H + +#include +#include +#include +#include "profiler/reader.h" + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +const QRgb DEFAULT_COLOR = 0x00f0e094; + +inline QRgb toRgb(unsigned int _red, unsigned int _green, unsigned int _blue) +{ + if (_red == 0 && _green == 0 && _blue == 0) + return DEFAULT_COLOR; + return (_red << 16) + (_green << 8) + _blue; +} + +////////////////////////////////////////////////////////////////////////// + +struct ProfBlock +{ + const BlocksTree* thread_tree; + const BlocksTree* tree; + ::profiler::thread_id_t thread_id; + + ProfBlock() : thread_tree(nullptr), tree(nullptr), thread_id(0) + { + } + + ProfBlock(::profiler::thread_id_t _id, const BlocksTree* _thread_tree, const BlocksTree* _tree) : thread_tree(_thread_tree), tree(_tree), thread_id(_id) + { + } +}; + +typedef ::std::vector TreeBlocks; + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__GUI_COMMON_TYPES_H diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index c1af83c..6a78fca 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -82,6 +82,9 @@ ProfMainWindow::ProfMainWindow() : QMainWindow(), m_treeWidget(nullptr), m_graph menuBar()->addMenu(menu); + connect(graphicsView->view(), &ProfGraphicsView::treeblocksChanged, treeWidget, &ProfTreeWidget::setTreeBlocks); + + if(QCoreApplication::arguments().size() > 1) { auto opened_filename = QCoreApplication::arguments().at(1).toStdString(); @@ -112,7 +115,6 @@ void ProfMainWindow::loadFile(const std::string& stdfilename) { m_lastFile = stdfilename; m_currentProf.swap(prof_blocks); - //static_cast(m_treeWidget->widget())->setTree(nblocks, m_currentProf); static_cast(m_graphicsView->widget())->view()->setTree(m_currentProf); } } @@ -132,7 +134,6 @@ void ProfMainWindow::onReloadFileClicked(bool) if (nblocks != 0) { m_currentProf.swap(prof_blocks); - //static_cast(m_treeWidget->widget())->setTree(nblocks, m_currentProf); static_cast(m_graphicsView->widget())->view()->setTree(m_currentProf); } }