mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-26 16:11:02 +08:00
Highly optimized painting algorithm and memory consumption for Profiler GraphicsView
This commit is contained in:
parent
4b287b31fa
commit
5ca4158abf
@ -11,9 +11,13 @@
|
||||
* ----------------- :
|
||||
* change log : * 2016/06/26 Victor Zarubkin: Moved sources from graphics_view.h
|
||||
* : and renamed classes from My* to Prof*.
|
||||
* :
|
||||
* : * 2016/06/27 Victor Zarubkin: Added text shifting relatively to it's parent item.
|
||||
* : Disabled border lines painting because of vertical lines painting bug.
|
||||
* : Changed height of blocks. Variable thread-block height.
|
||||
* :
|
||||
* : * 2016/06/29 Victor Zarubkin: Highly optimized painting performance and memory consumption.
|
||||
* :
|
||||
* : *
|
||||
* ----------------- :
|
||||
* license : TODO: add license text
|
||||
@ -23,225 +27,458 @@
|
||||
#include <QMouseEvent>
|
||||
#include <QSignalBlocker>
|
||||
#include <QScrollBar>
|
||||
#include <math.h>
|
||||
#include "blocks_graphics_view.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const qreal BASE_TEXT_SHIFT = 5; ///< Text position relatively to parent polygon item
|
||||
const qreal GRAPHICS_ROW_SIZE = 15;
|
||||
const qreal GRAPHICS_ROW_SIZE = 16;
|
||||
const qreal GRAPHICS_ROW_SIZE_FULL = GRAPHICS_ROW_SIZE + 1;
|
||||
const qreal ROW_SPACING = 5;
|
||||
const qreal ROW_SPACING = 10;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProfGraphicsPolygonItem::ProfGraphicsPolygonItem(bool _drawBorders, QGraphicsItem* _parent)
|
||||
: QGraphicsPolygonItem(_parent)
|
||||
, m_bDrawBorders(_drawBorders)
|
||||
ProfGraphicsItem::ProfGraphicsItem() : QGraphicsItem(nullptr), m_bTest(false)
|
||||
{
|
||||
if (!_drawBorders)
|
||||
}
|
||||
|
||||
ProfGraphicsItem::ProfGraphicsItem(bool _test) : QGraphicsItem(nullptr), m_bTest(_test)
|
||||
{
|
||||
}
|
||||
|
||||
ProfGraphicsItem::~ProfGraphicsItem()
|
||||
{
|
||||
}
|
||||
|
||||
QRectF ProfGraphicsItem::boundingRect() const
|
||||
{
|
||||
return m_rect;
|
||||
}
|
||||
|
||||
void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget)
|
||||
{
|
||||
const auto nitems = m_items.size();
|
||||
if (nitems == 0)
|
||||
{
|
||||
setPen(QPen(Qt::NoPen));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProfGraphicsPolygonItem::~ProfGraphicsPolygonItem()
|
||||
{
|
||||
}
|
||||
|
||||
//void ProfGraphicsPolygonItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
||||
//{
|
||||
// if (m_bDrawBorders)
|
||||
// {
|
||||
// const auto currentScale = static_cast<ProfGraphicsView*>(scene()->parent())->currentScale();
|
||||
//// if (boundingRect().width() * currentScale < 5)
|
||||
//// {
|
||||
//// auto linePen = pen();
|
||||
//// if (linePen.style() != Qt::NoPen)
|
||||
//// {
|
||||
//// linePen.setStyle(Qt::NoPen);
|
||||
//// setPen(linePen);
|
||||
//// }
|
||||
//// }
|
||||
//// else
|
||||
//// {
|
||||
//// auto linePen = pen();
|
||||
//// //linePen.setWidthF(1.25 / currentScale);
|
||||
////
|
||||
//// if (linePen.style() != Qt::SolidLine)
|
||||
//// {
|
||||
//// linePen.setStyle(Qt::SolidLine);
|
||||
//// setPen(linePen);
|
||||
//// }
|
||||
//// }
|
||||
// }
|
||||
//
|
||||
// QGraphicsPolygonItem::paint(painter, option, widget);
|
||||
//}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProfGraphicsTextItem::ProfGraphicsTextItem(const char* _text, QGraphicsItem* _parent)
|
||||
: QGraphicsSimpleTextItem(_text, _parent)
|
||||
{
|
||||
}
|
||||
|
||||
ProfGraphicsTextItem::~ProfGraphicsTextItem()
|
||||
{
|
||||
}
|
||||
|
||||
void ProfGraphicsTextItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
||||
{
|
||||
const auto currentScale = static_cast<ProfGraphicsView*>(parentItem()->scene()->parent())->currentScale();
|
||||
const auto scaleRevert = 1. / currentScale;
|
||||
const auto dx = BASE_TEXT_SHIFT * scaleRevert;
|
||||
if ((boundingRect().width() + dx) < parentItem()->boundingRect().width() * currentScale)
|
||||
const auto view = static_cast<ProfGraphicsView*>(scene()->parent());
|
||||
const auto visibleSceneRect = view->visibleSceneRect(); // Current visible scene rect
|
||||
const auto sceneLeft = visibleSceneRect.left() - x(), sceneRight = visibleSceneRect.right() - x();
|
||||
if (m_rect.left() > sceneRight || m_rect.right() < sceneLeft)
|
||||
{
|
||||
painter->setTransform(QTransform::fromScale(scaleRevert, 1), true);
|
||||
//setScale(scaleRevert);
|
||||
setX(dx);
|
||||
QGraphicsSimpleTextItem::paint(painter, option, widget);
|
||||
// this frame is not visible at all
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentScale = view->currentScale(); // Current GraphicsView scale
|
||||
const auto scaleRevert = 1.0 / currentScale; // Multiplier for reverting current GraphicsView scale
|
||||
const auto screenWidth = m_rect.width() * currentScale; // Screen width of the top block
|
||||
|
||||
QRectF rect;
|
||||
QBrush brush;
|
||||
QPen pen = _painter->pen();
|
||||
brush.setStyle(Qt::SolidPattern);
|
||||
|
||||
_painter->save();
|
||||
|
||||
_painter->setPen(Qt::NoPen);
|
||||
if (screenWidth > 20)
|
||||
{
|
||||
// TODO: Test performance for drawText inside one loop with drawRect, but with _painter->save() and restore()
|
||||
// and drawText in separate loop.
|
||||
|
||||
QRgb previousColor = 0;
|
||||
for (auto& item : m_items)
|
||||
{
|
||||
item.draw = !(item.rect.left() > sceneRight || item.rect.right() < sceneLeft);
|
||||
if (!item.draw)
|
||||
{
|
||||
// this item is fully out of scene visible rect
|
||||
continue;
|
||||
}
|
||||
|
||||
if (previousColor != item.color)
|
||||
{
|
||||
previousColor = item.color;
|
||||
brush.setColor(previousColor);
|
||||
_painter->setBrush(brush);
|
||||
}
|
||||
|
||||
_painter->drawRect(item.rect);
|
||||
}
|
||||
|
||||
_painter->setPen(Qt::SolidLine);
|
||||
//_painter->setBrush(Qt::NoBrush);
|
||||
_painter->setTransform(QTransform::fromScale(scaleRevert, 1), true);
|
||||
if (m_bTest)
|
||||
{
|
||||
for (auto& item : m_items)
|
||||
{
|
||||
if (!item.draw)
|
||||
continue;
|
||||
|
||||
auto w = item.rect.width() * currentScale;
|
||||
if (w < 20)
|
||||
continue;
|
||||
|
||||
rect.setRect(item.rect.left() * currentScale, item.rect.top(), w, item.rect.height());
|
||||
|
||||
_painter->drawText(rect, 0, "NOT VERY LONG TEST TEXT");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
previousColor = 0;
|
||||
for (auto& item : m_items)
|
||||
{
|
||||
if (!item.draw)
|
||||
continue;
|
||||
|
||||
auto w = item.rect.width() * currentScale;
|
||||
if (w < 20)
|
||||
continue;
|
||||
|
||||
rect.setRect(item.rect.left() * currentScale, item.rect.top(), w, item.rect.height());
|
||||
|
||||
if (previousColor != item.color)
|
||||
{
|
||||
previousColor = item.color;
|
||||
pen.setColor(0x00ffffff - previousColor);
|
||||
_painter->setPen(pen);
|
||||
}
|
||||
|
||||
//_painter->drawRect(rect);
|
||||
_painter->drawText(rect, 0, item.block->node->getBlockName());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& item = m_items.front();
|
||||
brush.setColor(item.color);
|
||||
_painter->setBrush(brush);
|
||||
_painter->drawRect(m_rect);
|
||||
|
||||
//if (screenWidth > 10)
|
||||
//{
|
||||
// _painter->setTransform(QTransform::fromScale(scaleRevert, 1), true);
|
||||
// //_painter->setBrush(Qt::NoBrush);
|
||||
// rect.setRect(m_rect.left() * currentScale, m_rect.top(), screenWidth, m_rect.height());
|
||||
// if (m_bTest)
|
||||
// {
|
||||
// _painter->setPen(Qt::SolidLine);
|
||||
// _painter->drawText(rect, 0, "NOT VERY LONG TEST TEXT");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// pen.setColor(0x00ffffff - item.color);
|
||||
// _painter->setPen(pen);
|
||||
// //_painter->drawRect(rect);
|
||||
// _painter->drawText(rect, 0, item.block->node->getBlockName());
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
_painter->restore();
|
||||
}
|
||||
|
||||
void ProfGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h)
|
||||
{
|
||||
m_rect.setRect(x, y, w, h);
|
||||
}
|
||||
|
||||
void ProfGraphicsItem::setBoundingRect(const QRectF& _rect)
|
||||
{
|
||||
m_rect = _rect;
|
||||
}
|
||||
|
||||
void ProfGraphicsItem::reserve(unsigned int _items)
|
||||
{
|
||||
m_items.reserve(_items);
|
||||
}
|
||||
|
||||
const ProfGraphicsItem::Children& ProfGraphicsItem::items() const
|
||||
{
|
||||
return m_items;
|
||||
}
|
||||
|
||||
const ProfBlockItem& ProfGraphicsItem::getItem(size_t _index) const
|
||||
{
|
||||
return m_items[_index];
|
||||
}
|
||||
|
||||
ProfBlockItem& ProfGraphicsItem::getItem(size_t _index)
|
||||
{
|
||||
return m_items[_index];
|
||||
}
|
||||
|
||||
size_t ProfGraphicsItem::addItem()
|
||||
{
|
||||
m_items.emplace_back();
|
||||
return m_items.size() - 1;
|
||||
}
|
||||
|
||||
size_t ProfGraphicsItem::addItem(const ProfBlockItem& _item)
|
||||
{
|
||||
m_items.emplace_back(_item);
|
||||
return m_items.size() - 1;
|
||||
}
|
||||
|
||||
size_t ProfGraphicsItem::addItem(ProfBlockItem&& _item)
|
||||
{
|
||||
m_items.emplace_back(::std::forward<ProfBlockItem&&>(_item));
|
||||
return m_items.size() - 1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const qreal LEFT = -2500000;
|
||||
const qreal WIDTH = 5000000;
|
||||
const int N_ITEMS = 200000;
|
||||
|
||||
ProfGraphicsScene::ProfGraphicsScene(QGraphicsView* _parent, bool _test) : QGraphicsScene(_parent), m_start(-1)
|
||||
ProfGraphicsScene::ProfGraphicsScene(QGraphicsView* _parent, bool _test) : QGraphicsScene(_parent), m_beginTime(-1)
|
||||
{
|
||||
if (_test)
|
||||
{
|
||||
setSceneRect(QRectF(LEFT, -200, WIDTH, 220));
|
||||
test();
|
||||
test(10000, 20000000, 5);
|
||||
}
|
||||
}
|
||||
|
||||
ProfGraphicsScene::ProfGraphicsScene(const thread_blocks_tree_t& _blocksTree, QGraphicsView* _parent) : QGraphicsScene(_parent), m_start(-1)
|
||||
ProfGraphicsScene::ProfGraphicsScene(const thread_blocks_tree_t& _blocksTree, QGraphicsView* _parent) : QGraphicsScene(_parent), m_beginTime(-1)
|
||||
{
|
||||
setTreeInternal(_blocksTree);
|
||||
setTree(_blocksTree);
|
||||
}
|
||||
|
||||
ProfGraphicsScene::~ProfGraphicsScene()
|
||||
{
|
||||
}
|
||||
|
||||
void ProfGraphicsScene::test()
|
||||
void fillChildren(ProfGraphicsItem* _item, qreal _x, qreal _y, size_t _childrenNumber, size_t& _total_items)
|
||||
{
|
||||
QPolygonF poly(QRectF(-5, -180, 10, 180));
|
||||
for (int i = 0; i < N_ITEMS; ++i)
|
||||
size_t nchildren = _childrenNumber;
|
||||
_childrenNumber >>= 1;
|
||||
|
||||
for (size_t i = 0; i < nchildren; ++i)
|
||||
{
|
||||
ProfGraphicsPolygonItem* item = new ProfGraphicsPolygonItem(true);
|
||||
int h = 50 + rand() % 131;
|
||||
item->setPolygon(QRectF(-5, -h, 10, h));
|
||||
item->setPos(LEFT + i * 10, 0);
|
||||
item->setBrush(QBrush(QColor(30 + rand() % 225, 30 + rand() % 225, 30 + rand() % 225)));
|
||||
size_t j = _item->addItem();
|
||||
|
||||
if (_childrenNumber > 0)
|
||||
{
|
||||
fillChildren(_item, _x, _y + 18, _childrenNumber, _total_items);
|
||||
|
||||
auto& last = _item->items().back();
|
||||
auto& b = _item->getItem(j);
|
||||
b.color = ((30 + rand() % 225) << 16) + ((30 + rand() % 225) << 8) + (30 + rand() % 225);
|
||||
b.rect.setRect(_x, _y, last.rect.right() - _x, 15);
|
||||
b.totalHeight = last.rect.bottom() - _y;
|
||||
_x = b.rect.right();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& b = _item->getItem(j);
|
||||
b.color = ((30 + rand() % 225) << 16) + ((30 + rand() % 225) << 8) + (30 + rand() % 225);
|
||||
b.rect.setRect(_x, _y, 10 + rand() % 40, 15);
|
||||
b.totalHeight = 15;
|
||||
_x = b.rect.right();
|
||||
}
|
||||
|
||||
++_total_items;
|
||||
}
|
||||
}
|
||||
|
||||
void ProfGraphicsScene::test(size_t _frames_number, size_t _total_items_number_estimate, int _depth)
|
||||
{
|
||||
const auto children_per_frame = _total_items_number_estimate / _frames_number;
|
||||
const size_t first_level_children_count = sqrt(pow(2, _depth - 1)) * pow(children_per_frame, 1.0 / double(_depth)) + 0.5;
|
||||
|
||||
size_t total_items = 0;
|
||||
qreal x = 0, y = 0, h = 0;
|
||||
for (unsigned int i = 0; i < _frames_number; ++i)
|
||||
{
|
||||
auto item = new ProfGraphicsItem(true);
|
||||
item->setPos(x, 0);
|
||||
|
||||
size_t j = item->addItem();
|
||||
fillChildren(item, 0, y + 18, first_level_children_count, total_items);
|
||||
|
||||
auto& last = item->items().back();
|
||||
auto& b = item->getItem(j);
|
||||
b.color = ((30 + rand() % 225) << 16) + ((30 + rand() % 225) << 8) + (30 + rand() % 225);
|
||||
b.rect.setRect(0, 0, last.rect.right(), 15);
|
||||
b.totalHeight = last.rect.bottom() - y;
|
||||
item->setBoundingRect(0, 0, b.rect.width(), b.totalHeight);
|
||||
x += b.rect.width() + 500;
|
||||
|
||||
if (last.rect.bottom() > h)
|
||||
{
|
||||
h = last.rect.bottom();
|
||||
}
|
||||
|
||||
item->addItem(b);
|
||||
++total_items;
|
||||
|
||||
addItem(item);
|
||||
}
|
||||
|
||||
setSceneRect(0, 0, x, h);
|
||||
}
|
||||
|
||||
void ProfGraphicsScene::clearSilent()
|
||||
{
|
||||
const QSignalBlocker b(this); // block all scene signals (otherwise clear() would be extremely slow!)
|
||||
clear(); // clear would be VERY SLOW if signals would not be blocked!
|
||||
m_beginTime = -1;
|
||||
}
|
||||
|
||||
void ProfGraphicsScene::setTree(const thread_blocks_tree_t& _blocksTree)
|
||||
{
|
||||
// delete all items created for previous session
|
||||
clearSilent();
|
||||
|
||||
// fill scene with new items
|
||||
m_start = -1;
|
||||
setTreeInternal(_blocksTree);
|
||||
}
|
||||
|
||||
void ProfGraphicsScene::setTreeInternal(const thread_blocks_tree_t& _blocksTree)
|
||||
{
|
||||
// calculate scene size
|
||||
// calculate scene size and fill it with items
|
||||
::profiler::timestamp_t finish = 0;
|
||||
qreal y = ROW_SPACING;
|
||||
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_start > timestart) m_start = timestart;
|
||||
if (m_beginTime > timestart) m_beginTime = timestart;
|
||||
if (finish < timefinish) finish = timefinish;
|
||||
|
||||
unsigned short depth = 0;
|
||||
setTreeInternal(threadTree.second.children, depth, y, 0);
|
||||
y += static_cast<qreal>(depth) * GRAPHICS_ROW_SIZE_FULL + ROW_SPACING;
|
||||
// fill scene with new items
|
||||
qreal h = 0;
|
||||
setTreeInternal(threadTree.second.children, h, y);
|
||||
y += h + ROW_SPACING;
|
||||
}
|
||||
|
||||
const qreal endX = time2position(finish + 1000000);
|
||||
setSceneRect(QRectF(0, 0, endX, y));
|
||||
setSceneRect(QRectF(0, 0, endX, y + ROW_SPACING));
|
||||
}
|
||||
|
||||
qreal ProfGraphicsScene::setTreeInternal(const BlocksTree::children_t& _children, unsigned short& _depth, qreal _y, unsigned short _level)
|
||||
qreal ProfGraphicsScene::setTreeInternal(const BlocksTree::children_t& _children, qreal& _height, qreal _y)
|
||||
{
|
||||
if (_depth < _level)
|
||||
{
|
||||
_depth = _level;
|
||||
}
|
||||
|
||||
qreal total_duration = 0;
|
||||
qreal total_duration = 0, prev_end = 0, maxh = 0;
|
||||
for (const auto& child : _children)
|
||||
{
|
||||
const qreal xbegin = time2position(child.node->block()->getBegin());
|
||||
qreal xbegin = time2position(child.node->block()->getBegin());
|
||||
qreal duration = time2position(child.node->block()->getEnd()) - xbegin;
|
||||
|
||||
const bool drawBorders = duration > 1;
|
||||
if (!drawBorders)
|
||||
const qreal dt = xbegin - prev_end;
|
||||
if (dt < 0)
|
||||
{
|
||||
duration = 1;
|
||||
duration += dt;
|
||||
xbegin -= dt;
|
||||
}
|
||||
|
||||
ProfGraphicsPolygonItem* item = new ProfGraphicsPolygonItem(drawBorders);
|
||||
if (duration < 0.1)
|
||||
{
|
||||
duration = 0.1;
|
||||
}
|
||||
|
||||
auto item = new ProfGraphicsItem();
|
||||
item->setPos(xbegin, _y);
|
||||
item->setZValue(_level);
|
||||
item->reserve(child.total_children_number + 1);
|
||||
auto i = item->addItem();
|
||||
|
||||
const auto color = child.node->block()->getColor();
|
||||
const auto itemBrush = QBrush(QColor(profiler::colors::get_red(color), profiler::colors::get_green(color), profiler::colors::get_blue(color)));
|
||||
item->setBrush(itemBrush);
|
||||
item->setPen(QPen(Qt::NoPen)); // Don't paint lines! There are display bugs if bounding lines are painted: vertical lines are very thick.
|
||||
|
||||
ProfGraphicsTextItem* text = new ProfGraphicsTextItem(child.node->getBlockName(), item);
|
||||
text->setPos(BASE_TEXT_SHIFT, 1);
|
||||
|
||||
auto textBrush = text->brush();
|
||||
textBrush.setColor(QRgb(0x00ffffff - itemBrush.color().rgb()));
|
||||
text->setBrush(textBrush);
|
||||
|
||||
const auto children_duration = setTreeInternal(child.children, _depth, _y + GRAPHICS_ROW_SIZE_FULL, _level + 1);
|
||||
qreal h = 0;
|
||||
const auto children_duration = setTreeInternal(item, child.children, h, _y + GRAPHICS_ROW_SIZE_FULL);
|
||||
if (duration < children_duration)
|
||||
{
|
||||
duration = children_duration;
|
||||
}
|
||||
|
||||
item->setPolygon(QRectF(0, 0, duration, GRAPHICS_ROW_SIZE));
|
||||
if (h > maxh)
|
||||
{
|
||||
maxh = h;
|
||||
}
|
||||
|
||||
const auto color = child.node->block()->getColor();
|
||||
auto& b = item->getItem(i);
|
||||
b.block = &child;
|
||||
b.color = (::profiler::colors::get_red(color) << 16) + (::profiler::colors::get_green(color) << 8) + ::profiler::colors::get_blue(color);
|
||||
b.rect.setRect(0, 0, duration, GRAPHICS_ROW_SIZE);
|
||||
b.totalHeight = GRAPHICS_ROW_SIZE + h;
|
||||
|
||||
item->setBoundingRect(0, 0, duration, b.totalHeight);
|
||||
addItem(item);
|
||||
|
||||
total_duration += duration;
|
||||
|
||||
prev_end = xbegin + duration;
|
||||
}
|
||||
|
||||
_height += GRAPHICS_ROW_SIZE + maxh;
|
||||
|
||||
return total_duration;
|
||||
}
|
||||
|
||||
qreal ProfGraphicsScene::setTreeInternal(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y)
|
||||
{
|
||||
if (_children.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
qreal total_duration = 0, prev_end = 0, maxh = 0;
|
||||
for (const auto& child : _children)
|
||||
{
|
||||
qreal xbegin = time2position(child.node->block()->getBegin());
|
||||
qreal duration = time2position(child.node->block()->getEnd()) - xbegin;
|
||||
|
||||
const qreal dt = xbegin - prev_end;
|
||||
if (dt < 0)
|
||||
{
|
||||
duration += dt;
|
||||
xbegin -= dt;
|
||||
}
|
||||
|
||||
if (duration < 0.1)
|
||||
{
|
||||
duration = 0.1;
|
||||
}
|
||||
|
||||
auto i = _item->addItem();
|
||||
|
||||
qreal h = 0;
|
||||
const auto children_duration = setTreeInternal(_item, child.children, h, _y + GRAPHICS_ROW_SIZE_FULL);
|
||||
if (duration < children_duration)
|
||||
{
|
||||
duration = children_duration;
|
||||
}
|
||||
|
||||
if (h > maxh)
|
||||
{
|
||||
maxh = h;
|
||||
}
|
||||
|
||||
const auto color = child.node->block()->getColor();
|
||||
auto& b = _item->getItem(i);
|
||||
b.block = &child;
|
||||
b.color = (::profiler::colors::get_red(color) << 16) + (::profiler::colors::get_green(color) << 8) + ::profiler::colors::get_blue(color);
|
||||
b.rect.setRect(xbegin - _item->x(), _y - _item->y(), duration, GRAPHICS_ROW_SIZE);
|
||||
b.totalHeight = GRAPHICS_ROW_SIZE + h;
|
||||
|
||||
total_duration += duration;
|
||||
|
||||
prev_end = xbegin + duration;
|
||||
}
|
||||
|
||||
_height += GRAPHICS_ROW_SIZE_FULL + maxh;
|
||||
|
||||
return total_duration;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProfGraphicsView::ProfGraphicsView(bool _test) : QGraphicsView(), m_scale(1), m_scaleCoeff(1.25), m_mouseButtons(Qt::NoButton)
|
||||
ProfGraphicsView::ProfGraphicsView(bool _test) : QGraphicsView(), m_scale(1), m_scaleCoeff(1.25), m_mouseButtons(Qt::NoButton), m_bUpdatingRect(false)
|
||||
{
|
||||
initMode();
|
||||
setScene(new ProfGraphicsScene(this, _test));
|
||||
centerOn(0, 0);
|
||||
updateVisibleSceneRect();
|
||||
}
|
||||
|
||||
ProfGraphicsView::ProfGraphicsView(const thread_blocks_tree_t& _blocksTree) : QGraphicsView(), m_scale(1), m_scaleCoeff(1.25), m_mouseButtons(Qt::NoButton)
|
||||
ProfGraphicsView::ProfGraphicsView(const thread_blocks_tree_t& _blocksTree) : QGraphicsView(), m_scale(1), m_scaleCoeff(1.25), m_mouseButtons(Qt::NoButton), m_bUpdatingRect(false)
|
||||
{
|
||||
initMode();
|
||||
setScene(new ProfGraphicsScene(_blocksTree, this));
|
||||
centerOn(0, 0);
|
||||
updateVisibleSceneRect();
|
||||
}
|
||||
|
||||
ProfGraphicsView::~ProfGraphicsView()
|
||||
@ -260,10 +497,12 @@ void ProfGraphicsView::wheelEvent(QWheelEvent* _event)
|
||||
scaleCoeff = 1. / m_scaleCoeff;
|
||||
}
|
||||
|
||||
scale(scaleCoeff, 1);// scaleCoeff);
|
||||
m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
|
||||
scale(scaleCoeff, 1); // Scale scene
|
||||
m_scale *= scaleCoeff;
|
||||
m_bUpdatingRect = false;
|
||||
|
||||
scene()->update();
|
||||
updateVisibleSceneRect(); // Update scene rect
|
||||
|
||||
_event->accept();
|
||||
//QGraphicsView::wheelEvent(_event);
|
||||
@ -296,7 +535,17 @@ void ProfGraphicsView::mouseMoveEvent(QMouseEvent* _event)
|
||||
const auto pos = _event->globalPos();
|
||||
const auto delta = pos - m_mousePressPos;
|
||||
m_mousePressPos = pos;
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->value() - delta.y());
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - delta.x());
|
||||
|
||||
auto vbar = verticalScrollBar();
|
||||
auto hbar = horizontalScrollBar();
|
||||
|
||||
m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once
|
||||
vbar->setValue(vbar->value() - delta.y());
hbar->setValue(hbar->value() - delta.x());
|
||||
m_bUpdatingRect = false;
|
||||
// Seems like an ugly stub, but QSignalBlocker is also a bad decision
|
||||
// because if scrollbar does not emit valueChanged signal then viewport does not move
|
||||
|
||||
updateVisibleSceneRect(); // Update scene visible rect only once
|
||||
}
|
||||
|
||||
//_event->accept();
|
||||
@ -308,29 +557,55 @@ void ProfGraphicsView::initMode()
|
||||
// TODO: find mode with least number of bugs :)
|
||||
// There are always some display bugs...
|
||||
|
||||
//setCacheMode(QGraphicsView::CacheBackground);
|
||||
setCacheMode(QGraphicsView::CacheNone);
|
||||
|
||||
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
||||
setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
|
||||
|
||||
setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
|
||||
//setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
||||
connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange);
|
||||
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange);
|
||||
}
|
||||
|
||||
void ProfGraphicsView::updateVisibleSceneRect()
|
||||
{
|
||||
m_visibleSceneRect = mapToScene(rect()).boundingRect();
|
||||
}
|
||||
|
||||
void ProfGraphicsView::setTree(const thread_blocks_tree_t& _blocksTree)
|
||||
{
|
||||
// block all signals for faster scene recreation
|
||||
const QSignalBlocker b(this);
|
||||
|
||||
// scale back to initial 100% scale
|
||||
scale(1. / m_scale, 1);
|
||||
m_scale = 1;
|
||||
// clear scene
|
||||
clearSilent();
|
||||
|
||||
// set new blocks tree
|
||||
static_cast<ProfGraphicsScene*>(scene())->setTree(_blocksTree);
|
||||
|
||||
// center view on the beginning of the scene
|
||||
centerOn(0, 0);
|
||||
updateVisibleSceneRect();
|
||||
}
|
||||
|
||||
void ProfGraphicsView::clearSilent()
|
||||
{
|
||||
// block all signals for faster scene recreation
|
||||
const QSignalBlocker b(this);
|
||||
|
||||
// clear scene
|
||||
static_cast<ProfGraphicsScene*>(scene())->clearSilent();
|
||||
|
||||
// scale back to initial 100% scale
|
||||
scale(1. / m_scale, 1);
|
||||
m_scale = 1;
|
||||
}
|
||||
|
||||
void ProfGraphicsView::test(size_t _frames_number, size_t _total_items_number_estimate, int _depth)
|
||||
{
|
||||
static_cast<ProfGraphicsScene*>(scene())->test(_frames_number, _total_items_number_estimate, _depth);
|
||||
updateVisibleSceneRect();
|
||||
}
|
||||
|
||||
void ProfGraphicsView::onScrollbarValueChange(int)
|
||||
{
|
||||
if (!m_bUpdatingRect)
|
||||
updateVisibleSceneRect();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -11,6 +11,9 @@
|
||||
* ----------------- :
|
||||
* change log : * 2016/06/26 Victor Zarubkin: moved sources from graphics_view.h
|
||||
* : and renamed classes from My* to Prof*.
|
||||
* :
|
||||
* : * 2016/06/29 Victor Zarubkin: Highly optimized painting performance and memory consumption.
|
||||
* :
|
||||
* : *
|
||||
* ----------------- :
|
||||
* license : TODO: add license text
|
||||
@ -25,35 +28,56 @@
|
||||
#include <QGraphicsSimpleTextItem>
|
||||
#include <QPoint>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include "profiler/reader.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProfGraphicsPolygonItem : public QGraphicsPolygonItem
|
||||
#pragma pack(push, 1)
|
||||
struct ProfBlockItem
|
||||
{
|
||||
bool m_bDrawBorders;
|
||||
QRectF rect;
|
||||
qreal totalHeight;
|
||||
const BlocksTree* block;
|
||||
QRgb color;
|
||||
bool draw;
|
||||
|
||||
ProfBlockItem() : totalHeight(0), block(nullptr), color(0), draw(true)
|
||||
{
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class ProfGraphicsItem : public QGraphicsItem
|
||||
{
|
||||
typedef ::std::vector<ProfBlockItem> Children;
|
||||
Children m_items;
|
||||
QRectF m_rect;
|
||||
const bool m_bTest;
|
||||
|
||||
public:
|
||||
|
||||
ProfGraphicsPolygonItem(bool _drawBorders, QGraphicsItem* _parent = nullptr);
|
||||
virtual ~ProfGraphicsPolygonItem();
|
||||
ProfGraphicsItem();
|
||||
ProfGraphicsItem(bool _test);
|
||||
virtual ~ProfGraphicsItem();
|
||||
|
||||
//void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = Q_NULLPTR) override;
|
||||
QRectF boundingRect() const override;
|
||||
void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override;
|
||||
|
||||
}; // END of class ProfGraphicsPolygonItem.
|
||||
void setBoundingRect(qreal x, qreal y, qreal w, qreal h);
|
||||
void setBoundingRect(const QRectF& _rect);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void reserve(unsigned int _items);
|
||||
|
||||
class ProfGraphicsTextItem : public QGraphicsSimpleTextItem
|
||||
{
|
||||
public:
|
||||
const Children& items() const;
|
||||
const ProfBlockItem& getItem(size_t _index) const;
|
||||
ProfBlockItem& getItem(size_t _index);
|
||||
|
||||
ProfGraphicsTextItem(const char* _text, QGraphicsItem* _parent = nullptr);
|
||||
virtual ~ProfGraphicsTextItem();
|
||||
size_t addItem();
|
||||
size_t addItem(const ProfBlockItem& _item);
|
||||
size_t addItem(ProfBlockItem&& _item);
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = Q_NULLPTR) override;
|
||||
|
||||
}; // END of class ProfGraphicsTextItem.
|
||||
}; // END of class ProfGraphicsItem.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -65,7 +89,9 @@ class ProfGraphicsScene : public QGraphicsScene
|
||||
|
||||
private:
|
||||
|
||||
profiler::timestamp_t m_start;
|
||||
typedef ProfGraphicsScene This;
|
||||
|
||||
::profiler::timestamp_t m_beginTime;
|
||||
|
||||
public:
|
||||
|
||||
@ -75,18 +101,18 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void test();
|
||||
void test(size_t _frames_number, size_t _total_items_number_estimate, int _depth);
|
||||
|
||||
void clearSilent();
|
||||
|
||||
void setTree(const thread_blocks_tree_t& _blocksTree);
|
||||
|
||||
void setTreeInternal(const thread_blocks_tree_t& _blocksTree);
|
||||
qreal setTreeInternal(const BlocksTree::children_t& _children, unsigned short& _depth, qreal _y, unsigned short _level);
|
||||
qreal setTreeInternal(const BlocksTree::children_t& _children, qreal& _height, qreal _y);
|
||||
qreal setTreeInternal(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y);
|
||||
|
||||
inline qreal time2position(const profiler::timestamp_t& _time) const
|
||||
{
|
||||
return qreal(_time - m_start) * 1e-6;
|
||||
return qreal(_time - m_beginTime) * 1e-6;
|
||||
}
|
||||
|
||||
}; // END of class ProfGraphicsScene.
|
||||
@ -99,10 +125,14 @@ class ProfGraphicsView : public QGraphicsView
|
||||
|
||||
private:
|
||||
|
||||
typedef ProfGraphicsView This;
|
||||
|
||||
QRectF m_visibleSceneRect;
|
||||
qreal m_scale;
|
||||
qreal m_scaleCoeff;
|
||||
QPoint m_mousePressPos;
|
||||
Qt::MouseButtons m_mouseButtons;
|
||||
bool m_bUpdatingRect;
|
||||
|
||||
public:
|
||||
|
||||
@ -120,9 +150,24 @@ public:
|
||||
return m_scale;
|
||||
}
|
||||
|
||||
inline const QRectF& visibleSceneRect() const
|
||||
{
|
||||
return m_visibleSceneRect;
|
||||
}
|
||||
|
||||
void setTree(const thread_blocks_tree_t& _blocksTree);
|
||||
void clearSilent();
|
||||
|
||||
void test(size_t _frames_number, size_t _total_items_number_estimate, int _depth);
|
||||
|
||||
private:
|
||||
|
||||
void initMode();
|
||||
void updateVisibleSceneRect();
|
||||
|
||||
private slots:
|
||||
|
||||
void onScrollbarValueChange(int);
|
||||
|
||||
}; // END of class ProfGraphicsView.
|
||||
|
||||
|
@ -11,10 +11,14 @@
|
||||
* ----------------- :
|
||||
* change log : * 2016/06/26 Victor Zarubkin: Moved sources from tree_view.h
|
||||
* : and renamed classes from My* to Prof*.
|
||||
* :
|
||||
* : * 2016/06/27 Victor Zarubkin: Added possibility to colorize rows
|
||||
* : with profiler blocks' colors.
|
||||
* : Also added displaying frame statistics for blocks.
|
||||
* : Disabled sorting by name to save order of threads displayed on graphics view.
|
||||
* :
|
||||
* : * 2016/06/29 Victor Zarubkin: Added clearSilent() method.
|
||||
* :
|
||||
* : *
|
||||
* ----------------- :
|
||||
* license : TODO: add license text
|
||||
@ -188,7 +192,6 @@ ProfTreeWidget::ProfTreeWidget(const unsigned int _blocksNumber, const thread_bl
|
||||
setTreeInternal(_blocksNumber, _blocksTree);
|
||||
|
||||
setSortingEnabled(true);
|
||||
//sortByColumn(COL_NAME, Qt::AscendingOrder);
|
||||
sortByColumn(COL_BEGIN, Qt::AscendingOrder);
|
||||
|
||||
connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
|
||||
@ -200,24 +203,28 @@ ProfTreeWidget::~ProfTreeWidget()
|
||||
|
||||
void ProfTreeWidget::setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree)
|
||||
{
|
||||
clearSilent();
|
||||
|
||||
setTreeInternal(_blocksNumber, _blocksTree);
|
||||
|
||||
setSortingEnabled(true);
|
||||
sortByColumn(COL_BEGIN, Qt::AscendingOrder);
|
||||
|
||||
connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
|
||||
}
|
||||
|
||||
void ProfTreeWidget::clearSilent()
|
||||
{
|
||||
m_beginTime = -1;
|
||||
|
||||
setSortingEnabled(false);
|
||||
disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand);
|
||||
|
||||
m_items.clear();
|
||||
m_itemblocks.clear();
|
||||
|
||||
QSignalBlocker b(this);
|
||||
const QSignalBlocker b(this);
|
||||
clear();
|
||||
b.unblock();
|
||||
|
||||
m_beginTime = -1;
|
||||
setTreeInternal(_blocksNumber, _blocksTree);
|
||||
|
||||
setSortingEnabled(true);
|
||||
//sortByColumn(COL_NAME, Qt::AscendingOrder);
|
||||
sortByColumn(COL_BEGIN, Qt::AscendingOrder);
|
||||
|
||||
connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
|
||||
}
|
||||
|
||||
void ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree)
|
||||
@ -233,6 +240,7 @@ void ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thr
|
||||
}
|
||||
}
|
||||
|
||||
const QSignalBlocker b(this);
|
||||
for (const auto& threadTree : _blocksTree)
|
||||
{
|
||||
auto item = new ProfTreeWidgetItem(&threadTree.second);
|
||||
|
@ -11,8 +11,12 @@
|
||||
* ----------------- :
|
||||
* change log : * 2016/06/26 Victor Zarubkin: moved sources from tree_view.h
|
||||
* : and renamed classes from My* to Prof*.
|
||||
* :
|
||||
* : * 2016/06/27 Victor Zarubkin: Added possibility to colorize rows
|
||||
* : with profiler blocks' colors.
|
||||
* :
|
||||
* : * 2016/06/29 Victor Zarubkin: Added clearSilent() method.
|
||||
* :
|
||||
* : *
|
||||
* ----------------- :
|
||||
* license : TODO: add license text
|
||||
@ -166,6 +170,7 @@ public:
|
||||
virtual ~ProfTreeWidget();
|
||||
|
||||
void setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree);
|
||||
void clearSilent();
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -9,7 +9,11 @@
|
||||
* description : The file contains implementation of MainWindow for easy_profiler GUI.
|
||||
* ----------------- :
|
||||
* change log : * 2016/06/26 Victor Zarubkin: Initial commit.
|
||||
* :
|
||||
* : * 2016/06/27 Victor Zarubkin: Passing blocks number to ProfTreeWidget::setTree().
|
||||
* :
|
||||
* : * 2016/06/29 Victor Zarubkin: Added menu with tests.
|
||||
* :
|
||||
* : *
|
||||
* ----------------- :
|
||||
* license : TODO: add license text
|
||||
@ -27,18 +31,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void graphicsViewDeleterThread(ProfGraphicsView* _widget)
|
||||
{
|
||||
delete _widget;
|
||||
}
|
||||
|
||||
void treeDeleterThread(ProfTreeWidget* _widget)
|
||||
{
|
||||
delete _widget;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ProfMainWindow::ProfMainWindow() : QMainWindow(), m_treeWidget(nullptr), m_graphicsView(nullptr)
|
||||
{
|
||||
setObjectName("ProfilerGUI_MainWindow");
|
||||
@ -48,7 +40,7 @@ ProfMainWindow::ProfMainWindow() : QMainWindow(), m_treeWidget(nullptr), m_graph
|
||||
|
||||
setStatusBar(new QStatusBar());
|
||||
|
||||
auto graphicsView = new ProfGraphicsView();
|
||||
auto graphicsView = new ProfGraphicsView(false);
|
||||
m_graphicsView = new QDockWidget("Blocks diagram");
|
||||
m_graphicsView->setMinimumHeight(50);
|
||||
m_graphicsView->setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||
@ -64,21 +56,27 @@ ProfMainWindow::ProfMainWindow() : QMainWindow(), m_treeWidget(nullptr), m_graph
|
||||
addDockWidget(Qt::BottomDockWidgetArea, m_treeWidget);
|
||||
|
||||
auto actionOpen = new QAction("Open", nullptr);
|
||||
connect(actionOpen, &QAction::triggered, this, &ProfMainWindow::onOpenFileClicked);
|
||||
connect(actionOpen, &QAction::triggered, this, &This::onOpenFileClicked);
|
||||
|
||||
auto actionReload = new QAction("Reload", nullptr);
|
||||
connect(actionReload, &QAction::triggered, this, &ProfMainWindow::onReloadFileClicked);
|
||||
connect(actionReload, &QAction::triggered, this, &This::onReloadFileClicked);
|
||||
|
||||
auto actionExit = new QAction("Exit", nullptr);
|
||||
connect(actionExit, &QAction::triggered, this, &ProfMainWindow::onExitClicked);
|
||||
connect(actionExit, &QAction::triggered, this, &This::onExitClicked);
|
||||
|
||||
auto menuFile = new QMenu("File");
|
||||
menuFile->addAction(actionOpen);
|
||||
menuFile->addAction(actionReload);
|
||||
menuFile->addSeparator();
|
||||
menuFile->addAction(actionExit);
|
||||
auto actionTestView = new QAction("Test viewport", nullptr);
|
||||
connect(actionTestView, &QAction::triggered, this, &This::onTestViewportClicked);
|
||||
|
||||
menuBar()->addMenu(menuFile);
|
||||
auto menu = new QMenu("File");
|
||||
menu->addAction(actionOpen);
|
||||
menu->addAction(actionReload);
|
||||
menu->addSeparator();
|
||||
menu->addAction(actionExit);
|
||||
menuBar()->addMenu(menu);
|
||||
|
||||
menu = new QMenu("Tests");
|
||||
menu->addAction(actionTestView);
|
||||
menuBar()->addMenu(menu);
|
||||
}
|
||||
|
||||
ProfMainWindow::~ProfMainWindow()
|
||||
@ -133,6 +131,20 @@ void ProfMainWindow::onExitClicked(bool)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProfMainWindow::onTestViewportClicked(bool)
|
||||
{
|
||||
static_cast<ProfTreeWidget*>(m_treeWidget->widget())->clearSilent();
|
||||
|
||||
auto view = static_cast<ProfGraphicsView*>(m_graphicsView->widget());
|
||||
view->clearSilent();
|
||||
m_currentProf.clear();
|
||||
|
||||
view->test(18000, 40000000, 5);
|
||||
//view->test(3, 300, 4);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -33,6 +33,8 @@ class ProfMainWindow : public QMainWindow
|
||||
|
||||
protected:
|
||||
|
||||
typedef ProfMainWindow This;
|
||||
|
||||
::std::string m_lastFile;
|
||||
thread_blocks_tree_t m_currentProf;
|
||||
QDockWidget* m_treeWidget;
|
||||
@ -48,6 +50,7 @@ protected slots:
|
||||
void onOpenFileClicked(bool);
|
||||
void onReloadFileClicked(bool);
|
||||
void onExitClicked(bool);
|
||||
void onTestViewportClicked(bool);
|
||||
|
||||
}; // END of class ProfMainWindow.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user