From 694497b5ca53fa0ab4ff7a9ddda41cbf11cbc55f Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Sat, 20 Jan 2018 15:23:28 +0300 Subject: [PATCH] Release 2.0 further work. Intermediate commit. --- easy_profiler_core/CMakeLists.txt | 1 + easy_profiler_core/event_trace_win.cpp | 14 +- easy_profiler_core/include/easy/utility.h | 8 +- easy_profiler_core/reader.cpp | 6 +- profiler_gui/CMakeLists.txt | 4 + profiler_gui/blocks_graphics_view.cpp | 10 +- profiler_gui/blocks_tree_widget.h | 15 +- profiler_gui/easy_frame_rate_viewer.h | 17 +- profiler_gui/easy_graphics_scrollbar.cpp | 1369 +++++---------------- profiler_gui/easy_graphics_scrollbar.h | 271 +--- profiler_gui/easy_qtimer.cpp | 52 +- profiler_gui/easy_qtimer.h | 17 +- profiler_gui/graphics_image_item.cpp | 331 +++++ profiler_gui/graphics_image_item.h | 96 ++ profiler_gui/graphics_slider_area.cpp | 521 ++++++++ profiler_gui/graphics_slider_area.h | 173 +++ 16 files changed, 1570 insertions(+), 1335 deletions(-) create mode 100644 profiler_gui/graphics_image_item.cpp create mode 100644 profiler_gui/graphics_image_item.h create mode 100644 profiler_gui/graphics_slider_area.cpp create mode 100644 profiler_gui/graphics_slider_area.h diff --git a/easy_profiler_core/CMakeLists.txt b/easy_profiler_core/CMakeLists.txt index 17012f6..1d1b08e 100644 --- a/easy_profiler_core/CMakeLists.txt +++ b/easy_profiler_core/CMakeLists.txt @@ -141,6 +141,7 @@ set(INCLUDE_FILES ${EASY_INCLUDE_DIR}/easy_socket.h ${EASY_INCLUDE_DIR}/profiler.h ${EASY_INCLUDE_DIR}/reader.h + ${EASY_INCLUDE_DIR}/utility.h ${EASY_INCLUDE_DIR}/serialized_block.h ${EASY_INCLUDE_DIR}/details/arbitrary_value_aux.h ${EASY_INCLUDE_DIR}/details/arbitrary_value_public_types.h diff --git a/easy_profiler_core/event_trace_win.cpp b/easy_profiler_core/event_trace_win.cpp index 78cf0ac..0927f63 100644 --- a/easy_profiler_core/event_trace_win.cpp +++ b/easy_profiler_core/event_trace_win.cpp @@ -126,13 +126,13 @@ //extern ProfileManager& MANAGER; #define MANAGER ProfileManager::instance() -extern const ::profiler::color_t EASY_COLOR_INTERNAL_EVENT; +extern const profiler::color_t EASY_COLOR_INTERNAL_EVENT; #ifdef __MINGW32__ -::std::atomic TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL); +std::atomic TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL); char KERNEL_LOGGER[] = KERNEL_LOGGER_NAME; #else -::std::atomic_uint64_t TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL); +std::atomic_uint64_t TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL); #endif /** @@ -171,7 +171,7 @@ namespace profiler { ////////////////////////////////////////////////////////////////////////// struct ProcessInfo { - std::string name; + ::std::string name; processid_t id = 0; int8_t valid = 0; }; @@ -205,12 +205,12 @@ namespace profiler { } }; - typedef ::std::unordered_map thread_process_info_map; - typedef ::std::unordered_map process_info_map; + using thread_process_info_map = ::std::unordered_map; + using process_info_map = ::std::unordered_map; // Using static is safe because processTraceEvent() is called from one thread process_info_map PROCESS_INFO_TABLE; - thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })(); + thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([] { thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })(); ////////////////////////////////////////////////////////////////////////// diff --git a/easy_profiler_core/include/easy/utility.h b/easy_profiler_core/include/easy/utility.h index 167e17d..2730b63 100644 --- a/easy_profiler_core/include/easy/utility.h +++ b/easy_profiler_core/include/easy/utility.h @@ -23,7 +23,13 @@ namespace estd { } template struct hash EASY_FINAL : public ::estd::detail::hasher sizeof(void*))> { - using ::estd::detail::hasher sizeof(void*))>::operator(); + using Parent = ::estd::detail::hasher sizeof(void*))>; +#if defined(_MSC_VER) && _MSC_VER >= 1910 + // TODO: Try to compile "using Parent::operator();" in MSVC 2017 + size_t operator () (typename Parent::type value) const { return Parent::operator () (value); } +#else + using Parent::operator(); +#endif }; template struct hash EASY_FINAL { diff --git a/easy_profiler_core/reader.cpp b/easy_profiler_core/reader.cpp index 7dc3e06..9fc6009 100644 --- a/easy_profiler_core/reader.cpp +++ b/easy_profiler_core/reader.cpp @@ -484,8 +484,8 @@ extern "C" { int64_t file_cpu_frequency = 0LL; inFile.read((char*)&file_cpu_frequency, sizeof(int64_t)); - uint64_t cpu_frequency = file_cpu_frequency; - const double conversion_factor = static_cast(TIME_FACTOR) / static_cast(cpu_frequency); + const uint64_t cpu_frequency = file_cpu_frequency; + const double conversion_factor = (cpu_frequency != 0 ? static_cast(TIME_FACTOR) / static_cast(cpu_frequency) : 1.); ::profiler::timestamp_t begin_time = 0ULL; ::profiler::timestamp_t end_time = 0ULL; @@ -749,7 +749,7 @@ extern "C" { /**/ EASY_BLOCK("Find children", ::profiler::colors::Blue); auto rlower1 = ++root.children.rbegin(); - for (; rlower1 != root.children.rend() && !(mt0 > blocks[*rlower1].node->begin()); ++rlower1); + for (; rlower1 != root.children.rend() && mt0 <= blocks[*rlower1].node->begin(); ++rlower1); auto lower = rlower1.base(); ::std::move(lower, root.children.end(), ::std::back_inserter(tree.children)); diff --git a/profiler_gui/CMakeLists.txt b/profiler_gui/CMakeLists.txt index e78c964..0d76064 100644 --- a/profiler_gui/CMakeLists.txt +++ b/profiler_gui/CMakeLists.txt @@ -36,6 +36,10 @@ if (Qt5Widgets_FOUND) globals.h globals.cpp globals_qobjects.cpp + graphics_image_item.h + graphics_image_item.cpp + graphics_slider_area.h + graphics_slider_area.cpp main_window.h main_window.cpp tree_widget_item.h diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index 1639bac..be5b90c 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -881,7 +881,7 @@ void EasyGraphicsView::mousePressEvent(QMouseEvent* _event) const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale; m_chronometerItem->setLeftRight(mouseX, mouseX); m_chronometerItem->hide(); - m_pScrollbar->hideChrono(); + m_pScrollbar->hideSelectionIndicator(); } } @@ -932,7 +932,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) if (m_chronometerItem->isVisible() && m_chronometerItem->width() < 1e-6) { m_chronometerItem->hide(); - m_pScrollbar->hideChrono(); + m_pScrollbar->hideSelectionIndicator(); } if (!m_selectedBlocks.empty()) @@ -1144,11 +1144,11 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) if (m_mouseButtons & Qt::RightButton) { bool showItem = moveChrono(m_chronometerItem, x); - m_pScrollbar->setChronoPos(m_chronometerItem->left(), m_chronometerItem->right()); + m_pScrollbar->setSelectionPos(m_chronometerItem->left(), m_chronometerItem->right()); if (showItem) { - m_pScrollbar->showChrono(); + m_pScrollbar->showSelectionIndicator(); } needUpdate = true; @@ -2006,7 +2006,7 @@ void EasyGraphicsView::onRefreshRequired() EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent) : QWidget(_parent) - , m_scrollbar(new EasyGraphicsScrollbar(this)) + , m_scrollbar(new EasyGraphicsScrollbar(true, 85 + (QFontMetrics(font()).height() << 1), this)) , m_view(new EasyGraphicsView(this)) , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, m_scrollbar->height(), this)) { diff --git a/profiler_gui/blocks_tree_widget.h b/profiler_gui/blocks_tree_widget.h index f5d484e..c3ca6a1 100644 --- a/profiler_gui/blocks_tree_widget.h +++ b/profiler_gui/blocks_tree_widget.h @@ -77,8 +77,8 @@ class EasyTreeWidget : public QTreeWidget { Q_OBJECT - typedef QTreeWidget Parent; - typedef EasyTreeWidget This; + using Parent = QTreeWidget; + using This = EasyTreeWidget; protected: @@ -100,9 +100,10 @@ protected: public: explicit EasyTreeWidget(QWidget* _parent = nullptr); - virtual ~EasyTreeWidget(); + ~EasyTreeWidget() override; void contextMenuEvent(QContextMenuEvent* _event) override; + void dragEnterEvent(QDragEnterEvent*) override {} void clearSilent(bool _global = false); int findNext(const QString& _str, Qt::MatchFlags _flags); @@ -167,8 +168,8 @@ class EasyHierarchyWidget : public QWidget { Q_OBJECT - typedef QWidget Parent; - typedef EasyHierarchyWidget This; + using Parent = QWidget; + using This = EasyHierarchyWidget; private: @@ -183,9 +184,11 @@ public: // Public virtual methods explicit EasyHierarchyWidget(QWidget* _parent = nullptr); - virtual ~EasyHierarchyWidget(); + ~EasyHierarchyWidget() override; + void keyPressEvent(QKeyEvent* _event) override; void contextMenuEvent(QContextMenuEvent* _event) override; + void dragEnterEvent(QDragEnterEvent*) override {} public: diff --git a/profiler_gui/easy_frame_rate_viewer.h b/profiler_gui/easy_frame_rate_viewer.h index ca07303..78be499 100644 --- a/profiler_gui/easy_frame_rate_viewer.h +++ b/profiler_gui/easy_frame_rate_viewer.h @@ -67,9 +67,9 @@ class EasyFPSGraphicsItem : public QGraphicsItem { - typedef QGraphicsItem Parent; - typedef EasyFPSGraphicsItem This; - typedef std::deque > FrameTimes; + using Parent = QGraphicsItem; + using This = EasyFPSGraphicsItem; + using FrameTimes = std::deque >; std::vector m_points1, m_points2; FrameTimes m_frames; @@ -78,10 +78,10 @@ class EasyFPSGraphicsItem : public QGraphicsItem public: explicit EasyFPSGraphicsItem(); - virtual ~EasyFPSGraphicsItem(); + ~EasyFPSGraphicsItem() override; QRectF boundingRect() const override; - void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) override; void setBoundingRect(const QRectF& _boundingRect); void setBoundingRect(qreal x, qreal y, qreal w, qreal h); @@ -99,20 +99,21 @@ class EasyFrameRateViewer : public QGraphicsView private: - typedef QGraphicsView Parent; - typedef EasyFrameRateViewer This; + using Parent = QGraphicsView; + using This = EasyFrameRateViewer; EasyFPSGraphicsItem* m_fpsItem; public: explicit EasyFrameRateViewer(QWidget* _parent = nullptr); - virtual ~EasyFrameRateViewer(); + ~EasyFrameRateViewer() override; void resizeEvent(QResizeEvent* _event) override; void hideEvent(QHideEvent* _event) override; void showEvent(QShowEvent* _event) override; void contextMenuEvent(QContextMenuEvent* _event) override; + void dragEnterEvent(QDragEnterEvent*) override {} public slots: diff --git a/profiler_gui/easy_graphics_scrollbar.cpp b/profiler_gui/easy_graphics_scrollbar.cpp index 207d826..60bcd9f 100644 --- a/profiler_gui/easy_graphics_scrollbar.cpp +++ b/profiler_gui/easy_graphics_scrollbar.cpp @@ -57,58 +57,16 @@ #include #include #include -#include -#include #include #include "easy_graphics_scrollbar.h" #include "globals.h" - -// TODO: use profiler_core/spin_lock.h - -#if defined(_WIN32) && defined(EASY_GUI_USE_CRITICAL_SECTION) -# include -# ifdef min -# undef min -# endif -# ifdef max -# undef max -# endif - -namespace profiler_gui { - void spin_lock::lock() { - EnterCriticalSection((CRITICAL_SECTION*)m_lock); - } - - void spin_lock::unlock() { - LeaveCriticalSection((CRITICAL_SECTION*)m_lock); - } - - spin_lock::spin_lock() : m_lock(new CRITICAL_SECTION) { - InitializeCriticalSection((CRITICAL_SECTION*)m_lock); - } - - spin_lock::~spin_lock() { - DeleteCriticalSection((CRITICAL_SECTION*)m_lock); - delete ((CRITICAL_SECTION*)m_lock); - } -} -#endif - ////////////////////////////////////////////////////////////////////////// -EASY_CONSTEXPR int DEFAULT_TOP = -40; -EASY_CONSTEXPR int DEFAULT_HEIGHT = 80; -EASY_CONSTEXPR int INDICATOR_SIZE = 6; -EASY_CONSTEXPR int INDICATOR_SIZE_x2 = INDICATOR_SIZE << 1; EASY_CONSTEXPR int HIST_COLUMN_MIN_HEIGHT = 2; -EASY_CONSTEXPR int WORKER_THREAD_CHECK_INTERVAL = 40; -EASY_CONSTEXPR int BOUNDARY_TIMER_INTERVAL = 100; ////////////////////////////////////////////////////////////////////////// -using estd::sqr; - inline qreal calculate_color1(qreal h, qreal, qreal k) { return std::min(h * k, 0.9999999); @@ -116,166 +74,37 @@ inline qreal calculate_color1(qreal h, qreal, qreal k) inline qreal calculate_color2(qreal, qreal duration, qreal k) { + using estd::sqr; return std::min(sqr(sqr(duration)) * k, 0.9999999); } ////////////////////////////////////////////////////////////////////////// -EasyGraphicsSliderItem::EasyGraphicsSliderItem(bool _main) : Parent(), m_halfwidth(0), m_bMain(_main) -{ - m_indicator.reserve(3); - - if (_main) - { - m_indicator.push_back(QPointF(0, DEFAULT_TOP + INDICATOR_SIZE)); - m_indicator.push_back(QPointF(-INDICATOR_SIZE, DEFAULT_TOP)); - m_indicator.push_back(QPointF(INDICATOR_SIZE, DEFAULT_TOP)); - } - else - { - m_indicator.push_back(QPointF(0, DEFAULT_TOP + DEFAULT_HEIGHT - INDICATOR_SIZE)); - m_indicator.push_back(QPointF(-INDICATOR_SIZE, DEFAULT_TOP + DEFAULT_HEIGHT)); - m_indicator.push_back(QPointF(INDICATOR_SIZE, DEFAULT_TOP + DEFAULT_HEIGHT)); - } - - setWidth(1); - setBrush(Qt::SolidPattern); -} - -EasyGraphicsSliderItem::~EasyGraphicsSliderItem() -{ - -} - -void EasyGraphicsSliderItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* /* _option */, QWidget* /* _widget */) -{ - if (static_cast(scene()->parent())->bindMode()) - { - return; - } - - const auto currentScale = static_cast(scene()->parent())->getWindowScale(); - const auto br = rect(); - - qreal w = width() * currentScale; - QRectF r(br.left() * currentScale, br.top() + INDICATOR_SIZE, w, br.height() - INDICATOR_SIZE_x2); - const auto r_right = r.right(); - const auto r_bottom = r.bottom(); - auto b = brush(); - - _painter->save(); - _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true); - _painter->setBrush(b); - - if (w > 1) - { - _painter->setPen(Qt::NoPen); - _painter->drawRect(r); - - // Draw left and right borders - auto cmode = _painter->compositionMode(); - if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion); - _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb())); - _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom)); - _painter->drawLine(QPointF(r_right, r.top()), QPointF(r_right, r_bottom)); - if (!m_bMain) _painter->setCompositionMode(cmode); - } - else - { - _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb())); - _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom)); - if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion); - } - - // Draw triangle indicators for small slider - _painter->setTransform(QTransform::fromTranslate(r.left() + w * 0.5, 0), true); - _painter->setPen(b.color().rgb()); - _painter->drawPolygon(m_indicator); - - _painter->restore(); -} - -qreal EasyGraphicsSliderItem::width() const -{ - return m_halfwidth * 2.0; -} - -qreal EasyGraphicsSliderItem::halfwidth() const -{ - return m_halfwidth; -} - -void EasyGraphicsSliderItem::setWidth(qreal _width) -{ - m_halfwidth = _width * 0.5; - setRect(-m_halfwidth, DEFAULT_TOP, _width, DEFAULT_HEIGHT); -} - -void EasyGraphicsSliderItem::setHalfwidth(qreal _halfwidth) -{ - m_halfwidth = _halfwidth; - setRect(-m_halfwidth, DEFAULT_TOP, m_halfwidth * 2.0, DEFAULT_HEIGHT); -} - -void EasyGraphicsSliderItem::setColor(QRgb _color) -{ - setColor(QColor::fromRgba(_color)); -} - -void EasyGraphicsSliderItem::setColor(const QColor& _color) -{ - auto b = brush(); - b.setColor(_color); - setBrush(b); -} - -////////////////////////////////////////////////////////////////////////// - -EasyHistogramItem::EasyHistogramItem() : Parent(nullptr) +GraphicsHistogramItem::GraphicsHistogramItem() : Parent() , m_threadDuration(0) , m_threadProfiledTime(0) , m_threadWaitTime(0) , m_pSource(nullptr) - , m_workerImage(nullptr) - , m_topDuration(0) - , m_maxDuration(0) - , m_minDuration(0) - , m_imageOrigin(0) - , m_imageScale(1) - , m_workerImageOrigin(0) - , m_workerImageScale(1) , m_workerTopDuration(0) , m_workerBottomDuration(0) , m_blockTotalDuraion(0) - , m_timer(::std::bind(&This::onTimeout, this)) - , m_boundaryTimer([this](){ updateImage(); }, true) , m_pProfilerThread(nullptr) , m_threadId(0) - , m_blockId(::profiler_gui::numeric_max()) - , m_timeouts(0) - , m_timeUnits(::profiler_gui::TimeUnits_auto) + , m_blockId(profiler_gui::numeric_max()) + , m_timeUnits(profiler_gui::TimeUnits_auto) , m_regime(Hist_Pointer) - , m_bPermitImageUpdate(false) { - m_bReady = ATOMIC_VAR_INIT(false); + } -EasyHistogramItem::~EasyHistogramItem() +GraphicsHistogramItem::~GraphicsHistogramItem() { - m_bReady.store(true, ::std::memory_order_release); - if (m_workerThread.joinable()) - m_workerThread.join(); - delete m_workerImage; + } -QRectF EasyHistogramItem::boundingRect() const +void GraphicsHistogramItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* /* _option */, QWidget* /* _widget */) { - return m_boundingRect; -} - -void EasyHistogramItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* /* _option */, QWidget* /* _widget */) -{ - if (!m_bPermitImageUpdate || (m_regime == Hist_Pointer && m_pSource == nullptr) || (m_regime == Hist_Id && (m_threadId == 0 || ::profiler_gui::is_max(m_blockId)))) + if (!isImageUpdatePermitted() || (m_regime == Hist_Pointer && m_pSource == nullptr) || (m_regime == Hist_Id && (m_threadId == 0 || profiler_gui::is_max(m_blockId)))) return; if (m_regime == Hist_Pointer) @@ -284,19 +113,7 @@ void EasyHistogramItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem paintById(_painter); } -void EasyHistogramItem::paintBusyIndicator(QPainter* _painter, qreal _current_scale) -{ - const auto width = m_boundingRect.width() * _current_scale; - const auto h = _painter->fontMetrics().height(); - - _painter->setPen(::profiler_gui::TEXT_COLOR); - _painter->drawText(QRectF(0, m_boundingRect.top(), width, m_boundingRect.height() - h), - Qt::AlignCenter, "Generating image"); - _painter->drawText(QRectF(0, m_boundingRect.top() + h, width, m_boundingRect.height() - h), - Qt::AlignCenter, QString(m_timeouts, QChar('.'))); -} - -void EasyHistogramItem::paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, qreal _top_width, qreal _mouse_y, qreal _delta_time, int _font_h) +void GraphicsHistogramItem::paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, qreal _top_width, qreal _mouse_y, qreal _delta_time, int _font_h) { if (_font_h != 0 && _top < _mouse_y && _mouse_y < _bottom) { @@ -304,13 +121,13 @@ void EasyHistogramItem::paintMouseIndicator(QPainter* _painter, qreal _top, qrea _painter->setPen(Qt::blue); - const auto mouseStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration + _delta_time * (_bottom - _mouse_y) / _height, 3); + const auto mouseStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue + _delta_time * (_bottom - _mouse_y) / _height, 3); qreal mouseIndicatorRight = _width; if (_mouse_y < _top + half_font_h) mouseIndicatorRight = _top_width; qreal mouseIndicatorLeft = 0; - const QRectF rect(0, _mouse_y - _font_h, _width, _font_h << 1); + const QRectF rect(0, _mouse_y - _font_h - 2, _width, 4 + (_font_h << 1)); if (_mouse_y > _bottom - half_font_h) { _painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, mouseStr); @@ -329,16 +146,15 @@ void EasyHistogramItem::paintMouseIndicator(QPainter* _painter, qreal _top, qrea } } -void EasyHistogramItem::paintByPtr(QPainter* _painter) +void GraphicsHistogramItem::paintByPtr(QPainter* _painter) { const auto widget = static_cast(scene()->parent()); const bool bindMode = widget->bindMode(); const auto currentScale = widget->getWindowScale(); const auto bottom = m_boundingRect.bottom(); const auto width = m_boundingRect.width() * currentScale; - const auto dtime = m_topDuration - m_bottomDuration; - const auto maxColumnHeight = m_boundingRect.height(); - const auto coeff = (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + const auto dtime = m_topValue - m_bottomValue; + const auto coeff = m_boundingRect.height() / (dtime > 1e-3 ? dtime : 1.); QRectF rect; QBrush brush(Qt::SolidPattern); @@ -349,132 +165,36 @@ void EasyHistogramItem::paintByPtr(QPainter* _painter) if (!m_pSource->empty()) { - _painter->setPen(Qt::NoPen); - if (!bindMode) - _painter->drawImage(0, m_boundingRect.top(), m_mainImage); + paintImage(_painter); else - { - const auto range = widget->sliderWidth(); - const auto minimum = widget->value(); - const auto slider_k = widget->range() / range; - - /*if (false)//slider_k < 8) - { - _painter->setTransform(QTransform::fromScale(slider_k, 1), true); - _painter->drawImage((widget->minimum() - minimum) * currentScale, m_boundingRect.top(), m_mainImage); - _painter->setTransform(QTransform::fromScale(1. / slider_k, 1), true); - } - else*/ - { - const auto deltaScale = slider_k / m_imageScale; - _painter->setTransform(QTransform::fromScale(deltaScale, 1), true); - _painter->drawImage((widget->minimum() + m_imageOrigin - minimum) * currentScale * m_imageScale, m_boundingRect.top(), m_mainImage); - _painter->setTransform(QTransform::fromScale(1. / deltaScale, 1), true); - } - - /*if (false) - { - const bool gotFrame = EASY_GLOBALS.frame_time > 1e-6f; - qreal frameCoeff = 1; - if (gotFrame) - { - if (EASY_GLOBALS.frame_time <= m_bottomDuration) - frameCoeff = m_boundingRect.height(); - else - frameCoeff = 0.9 / EASY_GLOBALS.frame_time; - } - - auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1; - auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / m_boundingRect.height(); - - const auto& items = *m_pSource; - const auto maximum = minimum + range; - const auto realScale = currentScale * slider_k; - const auto offset = minimum * realScale; - - auto first = ::std::lower_bound(items.begin(), items.end(), minimum, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) - { - return _item.left() < _value; - }); - - if (first != items.end()) - { - if (first != items.begin()) - --first; - } - else - { - first = items.begin() + items.size() - 1; - } - - qreal previous_x = -1e30, previous_h = -1e30; - for (auto it = first, end = items.end(); it != end; ++it) - { - // Draw rectangle - - if (it->left() > maximum) - break; - - if (it->right() < minimum) - continue; - - const qreal item_x = it->left() * realScale - offset; - const qreal item_w = ::std::max(it->width() * realScale, 1.0); - const qreal item_r = item_x + item_w; - const qreal h = it->width() <= m_bottomDuration ? HIST_COLUMN_MIN_HEIGHT : - (it->width() > m_topDuration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (it->width() - m_bottomDuration) * coeff)); - - if (h < previous_h && item_r < previous_x) - continue; - - const auto col = calculate_color(h, it->width(), k); - const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb(); - - if (previousColor != color) - { - // Set background color brush for rectangle - previousColor = color; - brush.setColor(QColor::fromRgba(0xc0000000 | color)); - _painter->setBrush(brush); - } - - rect.setRect(item_x, bottom - h, item_w, h); - _painter->drawRect(rect); - - previous_x = item_r; - previous_h = h; - } - }*/ - } + paintImage(_painter, currentScale, widget->minimum(), widget->maximum(), widget->value(), widget->sliderWidth()); } - //if (!m_bReady.load(::std::memory_order_acquire)) - // paintBusyIndicator(_painter, currentScale); - qreal top_width = width, bottom_width = width; - int font_h = 0; + const auto font_h = widget->fontHeight(); + rect.setRect(0, m_boundingRect.top() - widget->margin(), width - 3, m_boundingRect.height() + widget->margins()); + + _painter->setPen(profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, bindMode ? " Mode: Zoom" : " Mode: Overview"); + if (!m_topDurationStr.isEmpty()) { - rect.setRect(0, m_boundingRect.top() - INDICATOR_SIZE, width - 3, m_boundingRect.height() + INDICATOR_SIZE_x2); - if (m_timeUnits != EASY_GLOBALS.time_units) { m_timeUnits = EASY_GLOBALS.time_units; - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_topValue, 3); + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue, 3); } - auto fm = _painter->fontMetrics(); - font_h = fm.height(); - //bottom_width -= fm.width(m_bottomDurationStr) + 7; - top_width -= fm.width(m_topDurationStr) + 7; + //auto fm = _painter->fontMetrics(); + //top_width -= fm.width(m_topDurationStr) + 7; - _painter->setPen(m_topDuration < m_maxDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->setPen(m_topValue < m_maxValue ? QColor(Qt::darkRed) : profiler_gui::TEXT_COLOR); _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_topDurationStr); rect.setRect(0, bottom, width - 3, font_h); - _painter->setPen(m_bottomDuration > m_minDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->setPen(m_bottomValue > m_minValue ? QColor(Qt::darkRed) : profiler_gui::TEXT_COLOR); _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_bottomDurationStr); } @@ -482,16 +202,16 @@ void EasyHistogramItem::paintByPtr(QPainter* _painter) _painter->drawLine(QLineF(0, bottom, bottom_width, bottom)); _painter->drawLine(QLineF(0, m_boundingRect.top(), top_width, m_boundingRect.top())); - paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, maxColumnHeight - HIST_COLUMN_MIN_HEIGHT, top_width, m_mouseY, dtime, font_h); + paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, m_boundingRect.height(), top_width, m_mousePos.y(), dtime, font_h); - if (m_bottomDuration < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topDuration) + if (m_bottomValue < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topValue) { // Draw marker displaying expected frame_time step - const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomDuration) * coeff; + const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomValue) * coeff; _painter->setPen(Qt::DashLine); auto w = width; - const auto boundary = INDICATOR_SIZE - font_h; + const auto boundary = widget->margin() - font_h; if (h < (m_boundingRect.top() - boundary)) w = top_width; else if (h > (bottom + boundary)) @@ -500,35 +220,32 @@ void EasyHistogramItem::paintByPtr(QPainter* _painter) _painter->drawLine(QLineF(0, h, w, h)); } - _painter->setPen(::profiler_gui::TEXT_COLOR); - rect.setRect(0, bottom + 2, width, widget->defaultFontHeight()); + _painter->setPen(profiler_gui::TEXT_COLOR); + rect.setRect(0, bottom + 2, width, font_h); const auto eventsSize = m_pProfilerThread->events.size(); - _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | duration: %2 | profiled: %3 (%4%) | wait: %5 (%6%) | %7 frames | %8 blocks | %9 markers") + _painter->drawText(rect, Qt::AlignCenter | Qt::TextDontClip, QString("%1 | duration: %2 | profiled: %3 (%4%) | wait: %5 (%6%) | %7 frames | %8 blocks | %9 markers") .arg(m_threadName) - .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadDuration)) - .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadProfiledTime)) + .arg(profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadDuration)) + .arg(profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadProfiledTime)) .arg(m_threadDuration ? QString::number(100. * (double)m_threadProfiledTime / (double)m_threadDuration, 'f', 2) : QString("0")) - .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadWaitTime)) + .arg(profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadWaitTime)) .arg(m_threadDuration ? QString::number(100. * (double)m_threadWaitTime / (double)m_threadDuration, 'f', 2) : QString("0")) .arg(m_pProfilerThread->frames_number) .arg(m_pProfilerThread->blocks_number - eventsSize) .arg(eventsSize)); - _painter->drawText(rect, Qt::AlignLeft, bindMode ? " MODE: zoom" : " MODE: overview"); - _painter->restore(); } -void EasyHistogramItem::paintById(QPainter* _painter) +void GraphicsHistogramItem::paintById(QPainter* _painter) { const auto widget = static_cast(scene()->parent()); const bool bindMode = widget->bindMode(); const auto currentScale = widget->getWindowScale(); const auto bottom = m_boundingRect.bottom(); const auto width = m_boundingRect.width() * currentScale; - const auto dtime = m_topDuration - m_bottomDuration; - const auto maxColumnHeight = m_boundingRect.height(); - const auto coeff = (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + const auto dtime = m_topValue - m_bottomValue; + const auto coeff = m_boundingRect.height() / (dtime > 1e-3 ? dtime : 1.); QRectF rect; QBrush brush(Qt::SolidPattern); @@ -540,180 +257,36 @@ void EasyHistogramItem::paintById(QPainter* _painter) const auto& items = m_selectedBlocks; if (!items.empty()) { - _painter->setPen(Qt::NoPen); - if (!bindMode) - _painter->drawImage(0, m_boundingRect.top(), m_mainImage); + paintImage(_painter); else - { - const auto range = widget->sliderWidth(); - auto minimum = widget->value(); - const auto slider_k = widget->range() / range; - - /*if (false)//slider_k < 8) - { - _painter->setTransform(QTransform::fromScale(slider_k, 1), true); - _painter->drawImage((widget->minimum() - minimum) * currentScale, m_boundingRect.top(), m_mainImage); - _painter->setTransform(QTransform::fromScale(1. / slider_k, 1), true); - } - else*/ - { - const auto deltaScale = slider_k / m_imageScale; - _painter->setTransform(QTransform::fromScale(deltaScale, 1), true); - _painter->drawImage((widget->minimum() + m_imageOrigin - minimum) * currentScale * m_imageScale, m_boundingRect.top(), m_mainImage); - _painter->setTransform(QTransform::fromScale(1. / deltaScale, 1), true); - } - - /*if (false) - { - minimum *= 1e3; - const auto maximum = minimum + range * 1e3; - const auto realScale = currentScale * slider_k; - const auto offset = minimum * realScale; - - auto first = ::std::lower_bound(items.begin(), items.end(), minimum + EASY_GLOBALS.begin_time, [](::profiler::block_index_t _item, qreal _value) - { - return easyBlock(_item).tree.node->begin() < _value; - }); - - if (first != items.end()) - { - if (first != items.begin()) - --first; - } - else - { - first = items.begin() + (items.size() - 1); - } - - auto last = ::std::upper_bound(first, items.end(), maximum + EASY_GLOBALS.begin_time, [](qreal _value, ::profiler::block_index_t _item) - { - return _value < easyBlock(_item).tree.node->begin(); - }); - - const auto n = static_cast(::std::distance(first, last)); - - if (n > 0) - { - const bool gotFrame = EASY_GLOBALS.frame_time > 1e-6f; - qreal frameCoeff = 1; - if (gotFrame) - { - if (EASY_GLOBALS.frame_time <= m_bottomDuration) - frameCoeff = m_boundingRect.height(); - else - frameCoeff = 0.9 / EASY_GLOBALS.frame_time; - } - - auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1; - auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / m_boundingRect.height(); - - const auto draw = [this, &previousColor, &brush, &_painter](qreal x, qreal y, qreal w, qreal h, QRgb color) - { - m_spin.lock(); - - if (previousColor != color) - { - // Set background color brush for rectangle - previousColor = color; - brush.setColor(QColor::fromRgba(0xc0000000 | color)); - _painter->setBrush(brush); - } - - _painter->drawRect(QRectF(x, y, w, h)); - - m_spin.unlock(); - }; - - ::std::vector<::std::thread> threads; - const auto n_threads = ::std::min(n, ::std::thread::hardware_concurrency()); - threads.reserve(n_threads); - const auto n_items = n / n_threads; - for (uint32_t i = 0; i < n_threads; ++i) - { - auto begin = first + i * n_items; - threads.emplace_back([this, &draw, &maximum, &minimum, &realScale, &offset, &coeff, &calculate_color, &k, &bottom, &maxColumnHeight](decltype(begin) it, decltype(begin) end) - { - qreal previous_x = -1e30, previous_h = -1e30; - - //for (auto it = first, end = items.end(); it != end; ++it) - for (; it != end; ++it) - { - // Draw rectangle - const auto item = easyBlock(*it).tree.node; - - const auto beginTime = item->begin() - EASY_GLOBALS.begin_time; - if (beginTime > maximum) - break; - - const auto endTime = item->end() - EASY_GLOBALS.begin_time; - if (endTime < minimum) - continue; - - const qreal duration = item->duration() * 1e-3; - const qreal item_x = (beginTime * realScale - offset) * 1e-3; - const qreal item_w = ::std::max(duration * realScale, 1.0); - const qreal item_r = item_x + item_w; - const qreal h = duration <= m_bottomDuration ? HIST_COLUMN_MIN_HEIGHT : - (duration > m_topDuration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (duration - m_bottomDuration) * coeff)); - - if (h < previous_h && item_r < previous_x) - continue; - - const auto col = calculate_color(h, duration, k); - const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb(); - - draw(item_x, bottom - h, item_w, h, color); - //if (previousColor != color) - //{ - // // Set background color brush for rectangle - // previousColor = color; - // brush.setColor(QColor::fromRgba(0xc0000000 | color)); - // _painter->setBrush(brush); - //} - - //rect.setRect(item_x, bottom - h, item_w, h); - //_painter->drawRect(rect); - - previous_x = item_r; - previous_h = h; - } - }, begin, i == (n_threads - 1) ? items.end() : begin + n_items); - } - - for (auto& t : threads) - t.join(); - } - }*/ - } + paintImage(_painter, currentScale, widget->minimum(), widget->maximum(), widget->value(), widget->sliderWidth()); } - //if (!m_bReady.load(::std::memory_order_acquire)) - // paintBusyIndicator(_painter, currentScale); - qreal top_width = width, bottom_width = width; - int font_h = 0; + const auto font_h = widget->fontHeight(); + rect.setRect(0, m_boundingRect.top() - widget->margin(), width - 3, m_boundingRect.height() + widget->margins()); + + _painter->setPen(profiler_gui::TEXT_COLOR); + _painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, bindMode ? " Mode: Zoom" : " Mode: Overview"); + if (!m_topDurationStr.isEmpty()) { - rect.setRect(0, m_boundingRect.top() - INDICATOR_SIZE, width - 3, m_boundingRect.height() + INDICATOR_SIZE_x2); - if (m_timeUnits != EASY_GLOBALS.time_units) { m_timeUnits = EASY_GLOBALS.time_units; - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_topValue, 3); + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue, 3); } - auto fm = _painter->fontMetrics(); - font_h = fm.height(); - //bottom_width -= fm.width(m_bottomDurationStr) + 7; - top_width -= fm.width(m_topDurationStr) + 7; + //auto fm = _painter->fontMetrics(); + //top_width -= fm.width(m_topDurationStr) + 7; - _painter->setPen(m_topDuration < m_maxDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->setPen(m_topValue < m_maxValue ? QColor(Qt::darkRed) : profiler_gui::TEXT_COLOR); _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_topDurationStr); rect.setRect(0, bottom, width - 3, font_h); - _painter->setPen(m_bottomDuration > m_minDuration ? QColor(Qt::darkRed) : ::profiler_gui::TEXT_COLOR); + _painter->setPen(m_bottomValue > m_minValue ? QColor(Qt::darkRed) : profiler_gui::TEXT_COLOR); _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_bottomDurationStr); } @@ -721,16 +294,16 @@ void EasyHistogramItem::paintById(QPainter* _painter) _painter->drawLine(QLineF(0, bottom, bottom_width, bottom)); _painter->drawLine(QLineF(0, m_boundingRect.top(), top_width, m_boundingRect.top())); - paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, maxColumnHeight - HIST_COLUMN_MIN_HEIGHT, top_width, m_mouseY, dtime, font_h); + paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, m_boundingRect.height(), top_width, m_mousePos.y(), dtime, font_h); - if (m_bottomDuration < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topDuration) + if (m_bottomValue < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topValue) { // Draw marker displaying required frame_time step - const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomDuration) * coeff; + const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomValue) * coeff; _painter->setPen(Qt::DashLine); auto w = width; - const auto boundary = INDICATOR_SIZE - font_h; + const auto boundary = widget->margin() - font_h; if (h < (m_boundingRect.top() - boundary)) w = top_width; else if (h >(bottom + boundary)) @@ -739,47 +312,45 @@ void EasyHistogramItem::paintById(QPainter* _painter) _painter->drawLine(QLineF(0, h, w, h)); } - _painter->setPen(::profiler_gui::TEXT_COLOR); - rect.setRect(0, bottom + 2, width, widget->defaultFontHeight()); + _painter->setPen(profiler_gui::TEXT_COLOR); + rect.setRect(0, bottom + 2, width, font_h); if (!m_selectedBlocks.empty()) { - _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | %2 | %3 calls | %4% of thread profiled time") - .arg(m_threadName).arg(m_blockName).arg(m_selectedBlocks.size()) - .arg(m_threadProfiledTime ? QString::number(100. * (double)m_blockTotalDuraion / (double)m_threadProfiledTime, 'f', 2) : QString("100"))); + if (m_threadProfiledTime != 0) + { + _painter->drawText(rect, Qt::AlignCenter | Qt::TextDontClip, + QString("%1 | %2 | %3 calls | %4% of thread profiled time") + .arg(m_threadName).arg(m_blockName).arg(m_selectedBlocks.size()) + .arg(QString::number(100. * (double)m_blockTotalDuraion / (double)m_threadProfiledTime, 'f', 2))); + } + else + { + _painter->drawText(rect, Qt::AlignCenter | Qt::TextDontClip, + QString("%1 | %2 | %3 calls | 100% of thread profiled time") + .arg(m_threadName).arg(m_blockName).arg(m_selectedBlocks.size())); + } } else { - _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | %2 | 0 calls").arg(m_threadName).arg(m_blockName)); + _painter->drawText(rect, Qt::AlignCenter | Qt::TextDontClip, QString("%1 | %2 | 0 calls").arg(m_threadName).arg(m_blockName)); } - _painter->drawText(rect, Qt::AlignLeft, bindMode ? " MODE: zoom" : " MODE: overview"); - _painter->restore(); } -::profiler::thread_id_t EasyHistogramItem::threadId() const +profiler::thread_id_t GraphicsHistogramItem::threadId() const { return m_threadId; } -void EasyHistogramItem::setBoundingRect(const QRectF& _rect) -{ - m_boundingRect = _rect; -} - -void EasyHistogramItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) -{ - m_boundingRect.setRect(x, y, w, h); -} - -void EasyHistogramItem::rebuildSource(HistRegime _regime) +void GraphicsHistogramItem::rebuildSource(HistRegime _regime) { if (m_regime == _regime) rebuildSource(); } -void EasyHistogramItem::rebuildSource() +void GraphicsHistogramItem::rebuildSource() { if (m_regime == Hist_Id) { @@ -793,34 +364,29 @@ void EasyHistogramItem::rebuildSource() } } -void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items) +void GraphicsHistogramItem::setSource(profiler::thread_id_t _thread_id, const profiler_gui::EasyItems* _items) { if (m_regime == Hist_Pointer && m_threadId == _thread_id && m_pSource == _items) return; - m_timer.stop(); - m_boundaryTimer.stop(); + cancelAnyJob(); - m_bReady.store(true, ::std::memory_order_release); - if (m_workerThread.joinable()) - m_workerThread.join(); + m_boundaryTimer.stop(); m_blockName.clear(); m_blockTotalDuraion = 0; - delete m_workerImage; - m_workerImage = nullptr; m_imageOriginUpdate = m_imageOrigin = 0; m_imageScaleUpdate = m_imageScale = 1; m_selectedBlocks.clear(); - { ::profiler::BlocksTree::children_t().swap(m_selectedBlocks); } + { profiler::BlocksTree::children_t().swap(m_selectedBlocks); } - m_bPermitImageUpdate = false; + setImageUpdatePermitted(false); m_regime = Hist_Pointer; m_pSource = _items; m_threadId = _thread_id; - ::profiler_gui::set_max(m_blockId); + profiler_gui::set_max(m_blockId); if (m_pSource != nullptr) { @@ -831,7 +397,7 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, const ::pr else { const auto& root = EASY_GLOBALS.profiler_blocks[_thread_id]; - m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id); + m_threadName = profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id); if (root.children.empty()) m_threadDuration = 0; @@ -843,52 +409,52 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, const ::pr m_pProfilerThread = &root; m_timeUnits = EASY_GLOBALS.time_units; - m_bReady.store(false, ::std::memory_order_release); - m_workerThread = ::std::thread([this](const ::profiler_gui::EasyItems* _source) + setReady(false); + m_workerThread = std::thread([this](const profiler_gui::EasyItems* _source) { - m_maxDuration = 0; - m_minDuration = 1e30; + m_maxValue = 0; + m_minValue = 1e30; bool empty = true; for (const auto& item : *_source) { - if (m_bReady.load(::std::memory_order_acquire)) + if (isReady()) return; - if (easyDescriptor(easyBlock(item.block).tree.node->id()).type() == ::profiler::BlockType::Event) + if (easyDescriptor(easyBlock(item.block).tree.node->id()).type() == profiler::BlockType::Event) continue; const auto w = item.width(); - if (w > m_maxDuration) - m_maxDuration = w; + if (w > m_maxValue) + m_maxValue = w; - if (w < m_minDuration) - m_minDuration = w; + if (w < m_minValue) + m_minValue = w; empty = false; } - if ((m_maxDuration - m_minDuration) < 1e-3) + if ((m_maxValue - m_minValue) < 1e-3) { - if (m_minDuration > 0.1) + if (m_minValue > 0.1) { - m_minDuration -= 0.1; + m_minValue -= 0.1; } else { - m_maxDuration = 0.1; - m_minDuration = 0; + m_maxValue = 0.1; + m_minValue = 0; } } - m_topDuration = m_maxDuration; - m_bottomDuration = m_minDuration; + m_topValue = m_maxValue; + m_bottomValue = m_minValue; if (!empty) { - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_topValue, 3); + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue, 3); } else { @@ -896,12 +462,11 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, const ::pr m_bottomDurationStr.clear(); } - m_bReady.store(true, ::std::memory_order_release); + setReady(true); }, m_pSource); - m_timeouts = 3; - m_timer.start(WORKER_THREAD_CHECK_INTERVAL); + startTimer(); show(); } } @@ -916,44 +481,39 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, const ::pr } } -void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id) +void GraphicsHistogramItem::setSource(profiler::thread_id_t _thread_id, profiler::block_id_t _block_id) { if (m_regime == Hist_Id && m_threadId == _thread_id && m_blockId == _block_id) return; - m_bPermitImageUpdate = false; // Set to false because m_workerThread have to parse input data first. This will be set to true when m_workerThread finish - see onTimeout() + cancelAnyJob(); + + setImageUpdatePermitted(false); // Set to false because m_workerThread have to parse input data first. This will be set to true when m_workerThread finish - see onTimeout() m_regime = Hist_Id; - m_timer.stop(); m_boundaryTimer.stop(); - m_bReady.store(true, ::std::memory_order_release); - if (m_workerThread.joinable()) - m_workerThread.join(); - m_pSource = nullptr; m_topDurationStr.clear(); m_bottomDurationStr.clear(); m_blockName.clear(); m_blockTotalDuraion = 0; - delete m_workerImage; - m_workerImage = nullptr; m_imageOriginUpdate = m_imageOrigin = 0; m_imageScaleUpdate = m_imageScale = 1; m_selectedBlocks.clear(); - { ::profiler::BlocksTree::children_t().swap(m_selectedBlocks); } + { profiler::BlocksTree::children_t().swap(m_selectedBlocks); } m_threadId = _thread_id; m_blockId = _block_id; - if (m_threadId != 0 && !::profiler_gui::is_max(m_blockId)) + if (m_threadId != 0 && !profiler_gui::is_max(m_blockId)) { - m_blockName = ::profiler_gui::toUnicode(easyDescriptor(m_blockId).name()); + m_blockName = profiler_gui::toUnicode(easyDescriptor(m_blockId).name()); const auto& root = EASY_GLOBALS.profiler_blocks[_thread_id]; - m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id); + m_threadName = profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id); m_pProfilerThread = &root; m_timeUnits = EASY_GLOBALS.time_units; @@ -963,12 +523,11 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler m_threadProfiledTime = 0; m_threadWaitTime = 0; - m_topDuration = m_maxDuration = 0; - m_bottomDuration = m_minDuration = 1e30; + m_topValue = m_maxValue = 0; + m_bottomValue = m_minValue = 1e30; - m_bPermitImageUpdate = true; - - m_bReady.store(true, ::std::memory_order_release); + setImageUpdatePermitted(true); + setReady(true); } else { @@ -976,18 +535,18 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler m_threadProfiledTime = root.profiled_time; m_threadWaitTime = root.wait_time; - m_bReady.store(false, ::std::memory_order_release); - m_workerThread = ::std::thread([this](decltype(root) profiler_thread, ::profiler::block_index_t selected_block, bool _showOnlyTopLevelBlocks) + setReady(false); + m_workerThread = std::thread([this](decltype(root) profiler_thread, profiler::block_index_t selected_block, bool _showOnlyTopLevelBlocks) { - typedef ::std::vector<::std::pair<::profiler::block_index_t, ::profiler::block_index_t> > Stack; + using Stack = std::vector >; - m_maxDuration = 0; - m_minDuration = 1e30; + m_maxValue = 0; + m_minValue = 1e30; //const auto& profiler_thread = EASY_GLOBALS.profiler_blocks[m_threadId]; Stack stack; stack.reserve(profiler_thread.depth); - const bool has_selected_block = !::profiler_gui::is_max(selected_block); + const bool has_selected_block = !profiler_gui::is_max(selected_block); for (auto frame : profiler_thread.children) { @@ -997,11 +556,11 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler m_selectedBlocks.push_back(frame); const auto w = frame_block.node->duration(); - if (w > m_maxDuration) - m_maxDuration = w; + if (w > m_maxValue) + m_maxValue = w; - if (w < m_minDuration) - m_minDuration = w; + if (w < m_minValue) + m_minValue = w; m_blockTotalDuraion += w; } @@ -1009,10 +568,10 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler if (_showOnlyTopLevelBlocks) continue; - stack.push_back(::std::make_pair(frame, 0U)); + stack.emplace_back(frame, 0U); while (!stack.empty()) { - if (m_bReady.load(::std::memory_order_acquire)) + if (isReady()) return; auto& top = stack.back(); @@ -1020,7 +579,7 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler const auto stack_size = stack.size(); for (auto end = top_children.size(); top.second < end; ++top.second) { - if (m_bReady.load(::std::memory_order_acquire)) + if (isReady()) return; const auto child_index = top_children[top.second]; @@ -1030,11 +589,11 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler m_selectedBlocks.push_back(child_index); const auto w = child.node->duration(); - if (w > m_maxDuration) - m_maxDuration = w; + if (w > m_maxValue) + m_maxValue = w; - if (w < m_minDuration) - m_minDuration = w; + if (w < m_minValue) + m_minValue = w; m_blockTotalDuraion += w; } @@ -1042,7 +601,7 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler if (!child.children.empty()) { ++top.second; - stack.push_back(::std::make_pair(child_index, 0U)); + stack.emplace_back(child_index, 0U); break; } } @@ -1065,40 +624,37 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler { const auto& item = easyBlock(selected_block).tree; if (*item.node->name() != 0) - m_blockName = ::profiler_gui::toUnicode(item.node->name()); + m_blockName = profiler_gui::toUnicode(item.node->name()); } - m_maxDuration *= 1e-3; - m_minDuration *= 1e-3; + m_maxValue *= 1e-3; + m_minValue *= 1e-3; - if ((m_maxDuration - m_minDuration) < 1e-3) + if ((m_maxValue - m_minValue) < 1e-3) { - if (m_minDuration > 0.1) + if (m_minValue > 0.1) { - m_minDuration -= 0.1; + m_minValue -= 0.1; } else { - m_maxDuration = 0.1; - m_minDuration = 0; + m_maxValue = 0.1; + m_minValue = 0; } } - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_maxDuration, 3); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_minDuration, 3); + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_maxValue, 3); + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_minValue, 3); } + m_topValue = m_maxValue; + m_bottomValue = m_minValue; - - m_topDuration = m_maxDuration; - m_bottomDuration = m_minDuration; - - m_bReady.store(true, ::std::memory_order_release); + setReady(true); }, std::ref(root), EASY_GLOBALS.selected_block, EASY_GLOBALS.display_only_frames_on_histogram); - m_timeouts = 3; - m_timer.start(WORKER_THREAD_CHECK_INTERVAL); + startTimer(); } show(); @@ -1113,185 +669,110 @@ void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler ////////////////////////////////////////////////////////////////////////// -void EasyHistogramItem::validateName() +void GraphicsHistogramItem::validateName() { if (m_threadName.isEmpty()) return; - m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.profiler_blocks[m_threadId], EASY_GLOBALS.hex_thread_id); + m_threadName = profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.profiler_blocks[m_threadId], EASY_GLOBALS.hex_thread_id); } ////////////////////////////////////////////////////////////////////////// -void EasyHistogramItem::onTimeout() +bool GraphicsHistogramItem::pickTopValue() { - if (!isVisible()) + const bool result = Parent::pickTopValue(); + + if (result && !m_topDurationStr.isEmpty()) { - m_timer.stop(); - return; + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_topValue, 3); + scene()->update(); // to update top-boundary text right now } - if (++m_timeouts > 8) - m_timeouts = 3; + return result; +} - if (m_bReady.load(::std::memory_order_acquire)) +bool GraphicsHistogramItem::increaseTopValue() +{ + const bool result = Parent::increaseTopValue(); + + if (result && !m_topDurationStr.isEmpty()) { - m_timer.stop(); - if (!m_bPermitImageUpdate) - { - // Worker thread have finished parsing input data (when setSource(_block_id) was called) - m_bPermitImageUpdate = true; // From now we can update an image - updateImage(); - } - else - { - // Image updated - - if (m_workerThread.joinable()) - m_workerThread.join(); - - m_workerImage->swap(m_mainImage); - delete m_workerImage; - m_workerImage = nullptr; - - m_imageOriginUpdate = m_imageOrigin = m_workerImageOrigin; - m_imageScaleUpdate = m_imageScale = m_workerImageScale; - - if (EASY_GLOBALS.auto_adjust_histogram_height && !m_topDurationStr.isEmpty()) - { - m_topDuration = m_workerTopDuration; - m_bottomDuration = m_workerBottomDuration; - - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); - } - } + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_topValue, 3); + scene()->update(); // to update top-boundary text right now } - scene()->update(); + return result; +} + +bool GraphicsHistogramItem::decreaseTopValue() +{ + const bool result = Parent::decreaseTopValue(); + + if (result && !m_topDurationStr.isEmpty()) + { + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_topValue, 3); + scene()->update(); // to update top-boundary text right now + } + + return result; } ////////////////////////////////////////////////////////////////////////// -void EasyHistogramItem::pickTopBoundary(qreal _y) +bool GraphicsHistogramItem::pickBottomValue() { - if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty()) + const bool result = Parent::pickBottomValue(); + + if (result && !m_bottomDurationStr.isEmpty()) { - m_topDuration = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT); - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); - m_boundaryTimer.stop(); - updateImage(); + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue, 3); scene()->update(); // to update top-boundary text right now } + + return result; } -void EasyHistogramItem::increaseTopBoundary() +bool GraphicsHistogramItem::increaseBottomValue() { - if (m_bPermitImageUpdate && m_topDuration < m_maxDuration && !m_topDurationStr.isEmpty()) - { - auto step = 0.05 * (m_maxDuration - m_bottomDuration); - if (m_topDuration < (m_bottomDuration + 1.25 * step)) - step = 0.1 * (m_topDuration - m_bottomDuration); + const bool result = Parent::increaseBottomValue(); - m_topDuration = std::min(m_maxDuration, m_topDuration + step); - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); - updateImage(); + if (result && !m_bottomDurationStr.isEmpty()) + { + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue, 3); scene()->update(); // to update top-boundary text right now - - m_boundaryTimer.stop(); - m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); } + + return result; } -void EasyHistogramItem::decreaseTopBoundary() +bool GraphicsHistogramItem::decreaseBottomValue() { - if (m_bPermitImageUpdate && m_topDuration > m_bottomDuration && !m_topDurationStr.isEmpty()) + const bool result = Parent::decreaseBottomValue(); + + if (result && !m_bottomDurationStr.isEmpty()) { - auto step = 0.05 * (m_maxDuration - m_bottomDuration); - if (m_topDuration < (m_bottomDuration + 1.25 * step)) - step = std::max(0.1 * (m_topDuration - m_bottomDuration), 0.3); - - if (m_topDuration > (m_bottomDuration + 1.25 * step)) - { - m_topDuration = std::max(m_bottomDuration + step, m_topDuration - step); - m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3); - scene()->update(); // to update top-boundary text right now - - m_boundaryTimer.stop(); - m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); - } + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue, 3); + scene()->update(); // to update top-boundary text right now } + + return result; } ////////////////////////////////////////////////////////////////////////// -void EasyHistogramItem::pickBottomBoundary(qreal _y) +void GraphicsHistogramItem::pickFrameTime(qreal _y) const { - if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_bottomDurationStr.isEmpty()) + if (isImageUpdatePermitted() && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty()) { - m_bottomDuration = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); - m_boundaryTimer.stop(); - updateImage(); - scene()->update(); // to update top-boundary text right now - } -} - -void EasyHistogramItem::increaseBottomBoundary() -{ - if (m_bPermitImageUpdate && m_bottomDuration < m_topDuration && !m_bottomDurationStr.isEmpty()) - { - auto step = 0.05 * (m_topDuration - m_minDuration); - if (m_bottomDuration > (m_topDuration - 1.25 * step)) - step = 0.1 * (m_topDuration - m_bottomDuration); - - if (m_bottomDuration < (m_topDuration - 1.25 * step)) - { - m_bottomDuration = std::min(m_topDuration - step, m_bottomDuration + step); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); - scene()->update(); // to update bottom-boundary text right now - - m_boundaryTimer.stop(); - m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); - } - } -} - -void EasyHistogramItem::decreaseBottomBoundary() -{ - if (m_bPermitImageUpdate && m_bottomDuration > m_minDuration && !m_bottomDurationStr.isEmpty()) - { - auto step = 0.05 * (m_topDuration - m_minDuration); - if (m_bottomDuration > (m_topDuration - 1.25 * step)) - step = std::max(0.1 * (m_topDuration - m_bottomDuration), 0.3); - - m_bottomDuration = std::max(m_minDuration, m_bottomDuration - step); - m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3); - scene()->update(); // to update top-boundary text right now - - m_boundaryTimer.stop(); - m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); - } -} - -////////////////////////////////////////////////////////////////////////// - -void EasyHistogramItem::setMouseY(qreal _mouseY) -{ - m_mouseY = _mouseY; -} - -void EasyHistogramItem::pickFrameTime(qreal _y) const -{ - if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty()) - { - EASY_GLOBALS.frame_time = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT); + const auto frame_time = m_bottomValue + (m_topValue - m_bottomValue) * (m_boundingRect.bottom() - _y) / m_boundingRect.height(); + EASY_GLOBALS.frame_time = static_cast(frame_time); emit EASY_GLOBALS.events.expectedFrameTimeChanged(); } } ////////////////////////////////////////////////////////////////////////// -void EasyHistogramItem::onValueChanged() +void GraphicsHistogramItem::onValueChanged() { const auto widget = static_cast(scene()->parent()); @@ -1304,33 +785,35 @@ void EasyHistogramItem::onValueChanged() const auto k = widget->range() * sliderWidth_inv; const auto deltaScale = m_imageScaleUpdate < k ? (k / m_imageScaleUpdate) : (m_imageScaleUpdate / k); - if (deltaScale > 4) { + if (deltaScale > 4) + { updateImage(); return; } const auto deltaOffset = (widget->value() - m_imageOriginUpdate) * sliderWidth_inv; - if (deltaOffset < 1.5 || deltaOffset > 4.5) { + if (deltaOffset < 1.5 || deltaOffset > 4.5) + { updateImage(); return; } - m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL); + m_boundaryTimer.start(); } ////////////////////////////////////////////////////////////////////////// -void EasyHistogramItem::onModeChanged() +void GraphicsHistogramItem::onModeChanged() { - if (!m_bPermitImageUpdate) + if (!isImageUpdatePermitted()) return; const auto widget = static_cast(scene()->parent()); if (!widget->bindMode() && EASY_GLOBALS.auto_adjust_histogram_height) { - m_topDuration = m_maxDuration; - m_bottomDuration = m_minDuration; + m_topValue = m_maxValue; + m_bottomValue = m_minValue; } m_boundaryTimer.stop(); @@ -1339,61 +822,40 @@ void EasyHistogramItem::onModeChanged() ////////////////////////////////////////////////////////////////////////// -void EasyHistogramItem::cancelImageUpdate() +bool GraphicsHistogramItem::updateImage() { - if (!m_bPermitImageUpdate) - return; - - m_bReady.store(true, ::std::memory_order_release); - if (m_workerThread.joinable()) - m_workerThread.join(); - m_bReady.store(false, ::std::memory_order_release); - - delete m_workerImage; - m_workerImage = nullptr; - - m_imageOriginUpdate = m_imageOrigin; - m_imageScaleUpdate = m_imageScale; - - m_timer.stop(); -} - -void EasyHistogramItem::updateImage() -{ - if (!m_bPermitImageUpdate) - return; + if (!Parent::updateImage()) + return false; const auto widget = static_cast(scene()->parent()); - m_bReady.store(true, ::std::memory_order_release); - if (m_workerThread.joinable()) - m_workerThread.join(); - m_bReady.store(false, ::std::memory_order_release); - - delete m_workerImage; - m_workerImage = nullptr; - m_imageScaleUpdate = widget->range() / widget->sliderWidth(); m_imageOriginUpdate = widget->bindMode() ? (widget->value() - widget->sliderWidth() * 3) : widget->minimum(); - m_workerThread = ::std::thread([this](QRectF _boundingRect, HistRegime _regime, qreal _current_scale, - qreal _minimum, qreal _maximum, qreal _range, qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, - bool _bindMode, float _frame_time, ::profiler::timestamp_t _begin_time, qreal _origin, bool _autoAdjustHist) - { - updateImage(_boundingRect, _regime, _current_scale, _minimum, _maximum, _range, _value, _width, _top_duration, _bottom_duration, _bindMode, _frame_time, _begin_time, _origin, _autoAdjustHist); - m_bReady.store(true, ::std::memory_order_release); - }, m_boundingRect, m_regime, widget->getWindowScale(), widget->minimum(), widget->maximum(), widget->range(), widget->value(), widget->sliderWidth(), - m_topDuration, m_bottomDuration, widget->bindMode(), EASY_GLOBALS.frame_time, EASY_GLOBALS.begin_time, m_imageOriginUpdate, EASY_GLOBALS.auto_adjust_histogram_height); + m_workerThread = std::thread(&This::updateImageAsync, this, m_boundingRect, m_regime, widget->getWindowScale(), + widget->minimum(), widget->maximum(), widget->range(), widget->value(), widget->sliderWidth(), + m_topValue, m_bottomValue, widget->bindMode(), EASY_GLOBALS.frame_time, EASY_GLOBALS.begin_time, + EASY_GLOBALS.auto_adjust_histogram_height); - m_timeouts = 3; - m_timer.start(WORKER_THREAD_CHECK_INTERVAL); + return true; } -void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qreal _current_scale, - qreal _minimum, qreal _maximum, qreal _range, - qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, - bool _bindMode, float _frame_time, ::profiler::timestamp_t _begin_time, - qreal /* _origin */, bool _autoAdjustHist) +void GraphicsHistogramItem::onImageUpdated() +{ + if (EASY_GLOBALS.auto_adjust_histogram_height && !m_topDurationStr.isEmpty()) + { + m_topValue = m_workerTopDuration; + m_bottomValue = m_workerBottomDuration; + + m_topDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_topValue, 3); + m_bottomDurationStr = profiler_gui::timeStringReal(m_timeUnits, m_bottomValue, 3); + } +} + +void GraphicsHistogramItem::updateImageAsync(QRectF _boundingRect, HistRegime _regime, qreal _current_scale, + qreal _minimum, qreal _maximum, qreal _range, + qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, + bool _bindMode, float _frame_time, profiler::timestamp_t _begin_time, bool _autoAdjustHist) { const auto bottom = _boundingRect.height();//_boundingRect.bottom(); const auto screenWidth = _boundingRect.width() * _current_scale; @@ -1434,6 +896,7 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr frameCoeff = 0.9 / _frame_time; } + using estd::sqr; auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1; auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / _boundingRect.height(); @@ -1452,7 +915,7 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr realScale *= viewScale; offset = _minimum * realScale; - first = ::std::lower_bound(items.begin(), items.end(), _minimum, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) + first = std::lower_bound(items.begin(), items.end(), _minimum, [](const profiler_gui::EasyBlockItem& _item, qreal _value) { return _item.left() < _value; }); @@ -1491,7 +954,7 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr ++iterations; } - if (iterations) + if (iterations != 0) { _top_duration = maxDuration; _bottom_duration = minDuration; @@ -1513,7 +976,7 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr } const auto dtime = _top_duration - _bottom_duration; - const auto coeff = (_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + const auto coeff = _boundingRect.height() / (dtime > 1e-3 ? dtime : 1.); for (auto it = first, end = items.end(); it != end; ++it) { @@ -1525,10 +988,10 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr continue; const qreal item_x = it->left() * realScale - offset; - const qreal item_w = ::std::max(it->width() * realScale, 1.0); + const qreal item_w = std::max(it->width() * realScale, 1.0); const qreal item_r = item_x + item_w; const qreal h = it->width() <= _bottom_duration ? HIST_COLUMN_MIN_HEIGHT : - (it->width() > _top_duration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (it->width() - _bottom_duration) * coeff)); + (it->width() > _top_duration ? maxColumnHeight : (it->width() - _bottom_duration) * coeff); if (h < previous_h && item_r < previous_x) continue; @@ -1562,7 +1025,7 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr realScale *= viewScale; offset = _minimum * 1e3 * realScale; - first = ::std::lower_bound(m_selectedBlocks.begin(), m_selectedBlocks.end(), _minimum * 1e3 + _begin_time, [](::profiler::block_index_t _item, qreal _value) + first = std::lower_bound(m_selectedBlocks.begin(), m_selectedBlocks.end(), _minimum * 1e3 + _begin_time, [](profiler::block_index_t _item, qreal _value) { return easyBlock(_item).tree.node->begin() < _value; }); @@ -1609,7 +1072,7 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr ++iterations; } - if (iterations) + if (iterations != 0) { _top_duration = maxDuration; _bottom_duration = minDuration; @@ -1636,7 +1099,7 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr } const auto dtime = _top_duration - _bottom_duration; - const auto coeff = (_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.); + const auto coeff = _boundingRect.height() / (dtime > 1e-3 ? dtime : 1.); for (auto it = first, end = m_selectedBlocks.end(); it != end; ++it) { @@ -1653,10 +1116,10 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr const qreal duration = item->duration() * 1e-3; const qreal item_x = (beginTime * realScale - offset) * 1e-3; - const qreal item_w = ::std::max(duration * realScale, 1.0); + const qreal item_w = std::max(duration * realScale, 1.0); const qreal item_r = item_x + item_w; const auto h = duration <= _bottom_duration ? HIST_COLUMN_MIN_HEIGHT : - (duration > _top_duration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (duration - _bottom_duration) * coeff)); + (duration > _top_duration ? maxColumnHeight : (duration - _bottom_duration) * coeff); if (h < previous_h && item_r < previous_x) continue; @@ -1682,11 +1145,13 @@ void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qr m_workerTopDuration = _top_duration; m_workerBottomDuration = _bottom_duration; + + setReady(true); } ////////////////////////////////////////////////////////////////////////// -EasyGraphicsScrollbar::EasyGraphicsScrollbar(QWidget* _parent) +EasyGraphicsScrollbar::EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWidget* _parent) : Parent(_parent) , m_minimumValue(0) , m_maximumValue(500) @@ -1694,9 +1159,9 @@ EasyGraphicsScrollbar::EasyGraphicsScrollbar(QWidget* _parent) , m_windowScale(1) , m_mouseButtons(Qt::NoButton) , m_slider(nullptr) - , m_chronometerIndicator(nullptr) + , m_selectionIndicator(nullptr) , m_histogramItem(nullptr) - , m_defaultFontHeight(0) + , m_fontHeight(0) , m_bScrolling(false) , m_bBindMode(false) , m_bLocked(false) @@ -1712,32 +1177,37 @@ EasyGraphicsScrollbar::EasyGraphicsScrollbar(QWidget* _parent) setContentsMargins(0, 0, 0, 0); - auto selfScene = new QGraphicsScene(this); - m_defaultFontHeight = QFontMetrics(selfScene->font()).height(); - selfScene->setSceneRect(0, DEFAULT_TOP, 500, DEFAULT_HEIGHT + m_defaultFontHeight + 2); - setFixedHeight(DEFAULT_HEIGHT + m_defaultFontHeight + 2); + setScene(new QGraphicsScene(this)); - setScene(selfScene); + m_fontHeight = QFontMetrics(font()).height(); + + const int sceneHeight = _height - 2; + scene()->setSceneRect(0, -(sceneHeight >> 1), 500, sceneHeight); + if (_fixedHeight) + setFixedHeight(_height); + + m_histogramItem = new GraphicsHistogramItem(); + scene()->addItem(m_histogramItem); - m_histogramItem = new EasyHistogramItem(); m_histogramItem->setPos(0, 0); - m_histogramItem->setBoundingRect(0, DEFAULT_TOP + INDICATOR_SIZE, scene()->width(), DEFAULT_HEIGHT - INDICATOR_SIZE_x2); - selfScene->addItem(m_histogramItem); + m_histogramItem->setBoundingRect(0, scene()->sceneRect().top() + margin(), scene()->width(), sceneHeight - margins() - 1); m_histogramItem->hide(); - m_chronometerIndicator = new EasyGraphicsSliderItem(false); - m_chronometerIndicator->setPos(0, 0); - m_chronometerIndicator->setColor(0x40000000 | ::profiler_gui::CHRONOMETER_COLOR.rgba()); - selfScene->addItem(m_chronometerIndicator); - m_chronometerIndicator->hide(); + m_selectionIndicator = new GraphicsSliderItem(6, false); + scene()->addItem(m_selectionIndicator); + + m_selectionIndicator->setPos(0, 0); + m_selectionIndicator->setColor(0x40000000 | profiler_gui::CHRONOMETER_COLOR.rgba()); + m_selectionIndicator->hide(); + + m_slider = new GraphicsSliderItem(6, true); + scene()->addItem(m_slider); - m_slider = new EasyGraphicsSliderItem(true); m_slider->setPos(0, 0); m_slider->setColor(0x40c0c0c0); - selfScene->addItem(m_slider); m_slider->hide(); - connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, [this]() + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, [this]() { if (m_histogramItem->isVisible()) { @@ -1746,20 +1216,20 @@ EasyGraphicsScrollbar::EasyGraphicsScrollbar(QWidget* _parent) } }); - connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::autoAdjustHistogramChanged, [this]() + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::autoAdjustHistogramChanged, [this]() { if (m_histogramItem->isVisible()) m_histogramItem->onModeChanged(); }); - connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::displayOnlyFramesOnHistogramChanged, [this]() + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::displayOnlyFramesOnHistogramChanged, [this]() { if (m_histogramItem->isVisible()) - m_histogramItem->rebuildSource(EasyHistogramItem::Hist_Id); + m_histogramItem->rebuildSource(GraphicsHistogramItem::Hist_Id); }); - connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged); - connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged); + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged); + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged); centerOn(0, 0); } @@ -1785,294 +1255,37 @@ void EasyGraphicsScrollbar::onThreadViewChanged() void EasyGraphicsScrollbar::clear() { setHistogramSource(0, nullptr); - hideChrono(); - setRange(0, 100); - setSliderWidth(2); - setValue(0); + Parent::clear(); } -////////////////////////////////////////////////////////////////////////// - -bool EasyGraphicsScrollbar::bindMode() const -{ - return m_bBindMode; -} - -qreal EasyGraphicsScrollbar::getWindowScale() const -{ - return m_windowScale; -} - -::profiler::thread_id_t EasyGraphicsScrollbar::hystThread() const +profiler::thread_id_t EasyGraphicsScrollbar::hystThread() const { return m_histogramItem->threadId(); } -qreal EasyGraphicsScrollbar::minimum() const -{ - return m_minimumValue; -} - -qreal EasyGraphicsScrollbar::maximum() const -{ - return m_maximumValue; -} - -qreal EasyGraphicsScrollbar::range() const -{ - return m_maximumValue - m_minimumValue; -} - -qreal EasyGraphicsScrollbar::value() const -{ - return m_value; -} - -qreal EasyGraphicsScrollbar::sliderWidth() const -{ - return m_slider->width(); -} - -qreal EasyGraphicsScrollbar::sliderHalfWidth() const -{ - return m_slider->halfwidth(); -} - -int EasyGraphicsScrollbar::defaultFontHeight() const -{ - return m_defaultFontHeight; -} - -////////////////////////////////////////////////////////////////////////// - -void EasyGraphicsScrollbar::setValue(qreal _value) -{ - using estd::clamp; - m_value = clamp(m_minimumValue, _value, ::std::max(m_minimumValue, m_maximumValue - m_slider->width())); - m_slider->setX(m_value + m_slider->halfwidth()); - emit valueChanged(m_value); - - if (m_histogramItem->isVisible()) - m_histogramItem->onValueChanged(); -} - -void EasyGraphicsScrollbar::setRange(qreal _minValue, qreal _maxValue) -{ - const auto oldRange = range(); - const auto oldValue = oldRange < 1e-3 ? 0.0 : m_value / oldRange; - - m_minimumValue = _minValue; - m_maximumValue = _maxValue; - const auto range = this->range(); - scene()->setSceneRect(_minValue, DEFAULT_TOP, range, DEFAULT_HEIGHT + m_defaultFontHeight + 4); - - m_histogramItem->cancelImageUpdate(); - m_histogramItem->setBoundingRect(_minValue, DEFAULT_TOP + INDICATOR_SIZE, range, DEFAULT_HEIGHT - INDICATOR_SIZE_x2); - - emit rangeChanged(); - - setValue(_minValue + oldValue * range); - - onWindowWidthChange(width()); - - if (m_histogramItem->isVisible()) - m_histogramItem->updateImage(); -} - -void EasyGraphicsScrollbar::setSliderWidth(qreal _width) -{ - m_slider->setWidth(_width); - setValue(m_value); -} - -////////////////////////////////////////////////////////////////////////// - -void EasyGraphicsScrollbar::setChronoPos(qreal _left, qreal _right) -{ - m_chronometerIndicator->setWidth(_right - _left); - m_chronometerIndicator->setX(_left + m_chronometerIndicator->halfwidth()); -} - -void EasyGraphicsScrollbar::showChrono() -{ - m_chronometerIndicator->show(); -} - -void EasyGraphicsScrollbar::hideChrono() -{ - m_chronometerIndicator->hide(); -} - -////////////////////////////////////////////////////////////////////////// - -void EasyGraphicsScrollbar::setHistogramSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items) +void EasyGraphicsScrollbar::setHistogramSource(profiler::thread_id_t _thread_id, const profiler_gui::EasyItems* _items) { if (m_bLocked) return; m_histogramItem->setSource(_thread_id, _items); - m_slider->setVisible(m_histogramItem->isVisible()); scene()->update(); } -void EasyGraphicsScrollbar::setHistogramSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id) +void EasyGraphicsScrollbar::setHistogramSource(profiler::thread_id_t _thread_id, profiler::block_id_t _block_id) { if (m_bLocked) return; m_histogramItem->setSource(_thread_id, _block_id); - m_slider->setVisible(m_histogramItem->isVisible()); scene()->update(); } -////////////////////////////////////////////////////////////////////////// - void EasyGraphicsScrollbar::mousePressEvent(QMouseEvent* _event) { - _event->accept(); - - m_mouseButtons = _event->buttons(); - - if (m_mouseButtons & Qt::LeftButton) - { - if (_event->modifiers() & Qt::ControlModifier) - { - m_histogramItem->pickBottomBoundary(mapToScene(_event->pos()).y()); - } - else if (_event->modifiers() & Qt::ShiftModifier) - { - m_histogramItem->pickTopBoundary(mapToScene(_event->pos()).y()); - } - else - { - m_bScrolling = true; - m_mousePressPos = _event->pos(); - if (!m_bBindMode) - setValue(mapToScene(m_mousePressPos).x() - m_minimumValue - m_slider->halfwidth()); - } - } - - if (m_mouseButtons & Qt::RightButton) - { - if (_event->modifiers()) - { - m_histogramItem->pickFrameTime(mapToScene(_event->pos()).y()); - } - else - { - m_bBindMode = !m_bBindMode; - if (m_histogramItem->isVisible()) - m_histogramItem->onModeChanged(); - } - } - - //QGraphicsView::mousePressEvent(_event); -} - -void EasyGraphicsScrollbar::mouseReleaseEvent(QMouseEvent* _event) -{ - m_mouseButtons = _event->buttons(); - m_bScrolling = false; - _event->accept(); - //QGraphicsView::mouseReleaseEvent(_event); -} - -void EasyGraphicsScrollbar::mouseMoveEvent(QMouseEvent* _event) -{ - const auto pos = _event->pos(); - - if (m_mouseButtons & Qt::LeftButton) - { - const auto delta = pos - m_mousePressPos; - m_mousePressPos = pos; - - if (m_bScrolling) - { - auto realScale = m_windowScale; - if (m_bBindMode) - realScale *= -range() / sliderWidth(); - setValue(m_value + delta.x() / realScale); - } - } - - if (m_histogramItem->isVisible()) - { - m_histogramItem->setMouseY(mapToScene(pos).y()); - scene()->update(); - } -} - -void EasyGraphicsScrollbar::wheelEvent(QWheelEvent* _event) -{ - _event->accept(); - - if (_event->modifiers() & Qt::ShiftModifier) - { - // Shift + mouse wheel will change histogram top boundary - - if (m_histogramItem->isVisible()) - { - if (_event->delta() > 0) - m_histogramItem->increaseTopBoundary(); - else - m_histogramItem->decreaseTopBoundary(); - } - - return; - } - - if (_event->modifiers() & Qt::ControlModifier) - { - // Ctrl + mouse wheel will change histogram bottom boundary - - if (m_histogramItem->isVisible()) - { - if (_event->delta() > 0) - m_histogramItem->increaseBottomBoundary(); - else - m_histogramItem->decreaseBottomBoundary(); - } - - return; - } - - if (!m_bBindMode) - { - const auto w = m_slider->halfwidth() * (_event->delta() < 0 ? ::profiler_gui::SCALING_COEFFICIENT : ::profiler_gui::SCALING_COEFFICIENT_INV); - setValue(mapToScene(_event->pos()).x() - m_minimumValue - w); - emit wheeled(w * m_windowScale, _event->delta()); - } - else - { - const auto x = (mapToScene(_event->pos()).x() - m_minimumValue) * m_windowScale; - emit wheeled(x, _event->delta()); - } -} - -void EasyGraphicsScrollbar::resizeEvent(QResizeEvent* _event) -{ - onWindowWidthChange(_event->size().width()); - if (m_histogramItem->isVisible()) - m_histogramItem->updateImage(); -} - -////////////////////////////////////////////////////////////////////////// - -void EasyGraphicsScrollbar::onWindowWidthChange(qreal _width) -{ - const auto oldScale = m_windowScale; - const auto scrollingRange = range(); - - if (scrollingRange < 1e-3) - { - m_windowScale = 1; - } - else - { - m_windowScale = _width / scrollingRange; - } - - scale(m_windowScale / oldScale, 1); + Parent::mousePressEvent(_event); + if ((m_mouseButtons & Qt::RightButton) && _event->modifiers()) + m_histogramItem->pickFrameTime(mapToScene(_event->pos()).y()); } ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/easy_graphics_scrollbar.h b/profiler_gui/easy_graphics_scrollbar.h index 06a8ec7..9bd98b3 100644 --- a/profiler_gui/easy_graphics_scrollbar.h +++ b/profiler_gui/easy_graphics_scrollbar.h @@ -58,88 +58,16 @@ #include #include #include -#include -#include -#include -#include #include #include "easy_qtimer.h" -#include "common_types.h" +#include "graphics_slider_area.h" ////////////////////////////////////////////////////////////////////////// -// TODO: use profiler_core/spin_lock.h - -#define EASY_GUI_USE_CRITICAL_SECTION // Use CRITICAL_SECTION instead of std::atomic_flag -#if defined(_WIN32) && defined(EASY_GUI_USE_CRITICAL_SECTION) -namespace profiler_gui { - // std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag... - // By the way, Windows critical sections are slower than std::atomic_flag on Unix. - class spin_lock { void* m_lock; public: - void lock(); - void unlock(); - spin_lock(); - ~spin_lock(); - }; -#else -namespace profiler_gui { - // std::atomic_flag on Unix works fine and very fast (almost instant!) - class spin_lock { - ::std::atomic_flag m_lock; public: - - void lock() { - while (m_lock.test_and_set(::std::memory_order_acquire)); - } - - void unlock() { - m_lock.clear(::std::memory_order_release); - } - - spin_lock() { - m_lock.clear(); - } - }; -#endif - -} // END of namespace profiler_gui. - -////////////////////////////////////////////////////////////////////////// - -class EasyGraphicsSliderItem : public QGraphicsRectItem +class GraphicsHistogramItem : public GraphicsImageItem { - typedef QGraphicsRectItem Parent; - typedef EasyGraphicsSliderItem This; - -private: - - QPolygonF m_indicator; - qreal m_halfwidth; - bool m_bMain; - -public: - - explicit EasyGraphicsSliderItem(bool _main); - virtual ~EasyGraphicsSliderItem(); - - void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; - - qreal width() const; - qreal halfwidth() const; - - void setWidth(qreal _width); - void setHalfwidth(qreal _halfwidth); - - void setColor(QRgb _color); - void setColor(const QColor& _color); - -}; // END of class EasyGraphicsSliderItem. - -////////////////////////////////////////////////////////////////////////// - -class EasyHistogramItem : public QGraphicsItem -{ - typedef QGraphicsItem Parent; - typedef EasyHistogramItem This; + using Parent = GraphicsImageItem; + using This = GraphicsHistogramItem; public: @@ -147,193 +75,108 @@ public: private: - QRectF m_boundingRect; - qreal m_topDuration; - qreal m_bottomDuration; - qreal m_maxDuration; - qreal m_minDuration; - qreal m_mouseY; - qreal m_imageOrigin; - qreal m_imageScale; - qreal m_imageOriginUpdate; - qreal m_imageScaleUpdate; - qreal m_workerImageOrigin; - qreal m_workerImageScale; - qreal m_workerTopDuration; - qreal m_workerBottomDuration; - ::profiler::timestamp_t m_blockTotalDuraion; - QString m_topDurationStr; - QString m_bottomDurationStr; - QString m_threadName; - QString m_blockName; - ::profiler::BlocksTree::children_t m_selectedBlocks; - QImage m_mainImage; - EasyQTimer m_timer; - EasyQTimer m_boundaryTimer; - ::std::thread m_workerThread; - ::profiler::timestamp_t m_threadDuration; - ::profiler::timestamp_t m_threadProfiledTime; - ::profiler::timestamp_t m_threadWaitTime; - const ::profiler_gui::EasyItems* m_pSource; - QImage* m_workerImage; - const ::profiler::BlocksTreeRoot* m_pProfilerThread; - ::profiler::thread_id_t m_threadId; - ::profiler::block_index_t m_blockId; - int m_timeouts; - ::profiler_gui::TimeUnits m_timeUnits; - HistRegime m_regime; - bool m_bPermitImageUpdate; ///< Is false when m_workerThread is parsing input dataset (when setSource(_block_id) is called) - ::profiler_gui::spin_lock m_spin; - ::std::atomic_bool m_bReady; + qreal m_workerTopDuration; + qreal m_workerBottomDuration; + profiler::timestamp_t m_blockTotalDuraion; + QString m_topDurationStr; + QString m_bottomDurationStr; + QString m_threadName; + QString m_blockName; + profiler::BlocksTree::children_t m_selectedBlocks; + profiler::timestamp_t m_threadDuration; + profiler::timestamp_t m_threadProfiledTime; + profiler::timestamp_t m_threadWaitTime; + const profiler_gui::EasyItems* m_pSource; + const profiler::BlocksTreeRoot* m_pProfilerThread; + profiler::thread_id_t m_threadId; + profiler::block_index_t m_blockId; + profiler_gui::TimeUnits m_timeUnits; + HistRegime m_regime; public: - explicit EasyHistogramItem(); - virtual ~EasyHistogramItem(); + explicit GraphicsHistogramItem(); + ~GraphicsHistogramItem() override; - // Public virtual methods + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) override; - QRectF boundingRect() const override; - void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override; + bool updateImage() override; + void onImageUpdated() override; + + bool pickTopValue() override; + bool increaseTopValue() override; + bool decreaseTopValue() override; + + bool pickBottomValue() override; + bool increaseBottomValue() override; + bool decreaseBottomValue() override; + + void onModeChanged() override; + void onValueChanged() override; public: // Public non-virtual methods - ::profiler::thread_id_t threadId() const; + profiler::thread_id_t threadId() const; - void setBoundingRect(const QRectF& _rect); - void setBoundingRect(qreal x, qreal y, qreal w, qreal h); - - void setSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items); - void setSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id); + void setSource(profiler::thread_id_t _thread_id, const profiler_gui::EasyItems* _items); + void setSource(profiler::thread_id_t _thread_id, profiler::block_id_t _block_id); void rebuildSource(HistRegime _regime); void rebuildSource(); void validateName(); - void updateImage(); - void cancelImageUpdate(); - void pickTopBoundary(qreal _y); - void increaseTopBoundary(); - void decreaseTopBoundary(); - - void pickBottomBoundary(qreal _y); - void increaseBottomBoundary(); - void decreaseBottomBoundary(); - - void setMouseY(qreal _mouseY); void pickFrameTime(qreal _y) const; - void onValueChanged(); - void onModeChanged(); - private: - void paintBusyIndicator(QPainter* _painter, qreal _current_scale); void paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, qreal _top_width, qreal _mouse_y, qreal _delta_time, int _font_h); void paintByPtr(QPainter* _painter); void paintById(QPainter* _painter); - void onTimeout(); - void updateImage(QRectF _boundingRect, HistRegime _regime, qreal _current_scale, - qreal _minimum, qreal _maximum, qreal _range, - qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, bool _bindMode, - float _frame_time, ::profiler::timestamp_t _begin_time, qreal _origin, bool _autoAdjustHist); -}; // END of class EasyHistogramItem. + void updateImageAsync(QRectF _boundingRect, HistRegime _regime, qreal _current_scale, + qreal _minimum, qreal _maximum, qreal _range, + qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration, bool _bindMode, + float _frame_time, profiler::timestamp_t _begin_time, bool _autoAdjustHist); + +}; // END of class GraphicsHistogramItem. ////////////////////////////////////////////////////////////////////////// -class EasyGraphicsScrollbar : public QGraphicsView +class EasyGraphicsScrollbar : public GraphicsSliderArea { Q_OBJECT private: - typedef QGraphicsView Parent; - typedef EasyGraphicsScrollbar This; + using Parent = GraphicsSliderArea; + using This = EasyGraphicsScrollbar; - qreal m_minimumValue; - qreal m_maximumValue; - qreal m_value; - qreal m_windowScale; - QPoint m_mousePressPos; - Qt::MouseButtons m_mouseButtons; - EasyGraphicsSliderItem* m_slider; - EasyGraphicsSliderItem* m_chronometerIndicator; - EasyHistogramItem* m_histogramItem; - int m_defaultFontHeight; - bool m_bScrolling; - bool m_bBindMode; - bool m_bLocked; + GraphicsHistogramItem* m_histogramItem = nullptr; public: - explicit EasyGraphicsScrollbar(QWidget* _parent = nullptr); - virtual ~EasyGraphicsScrollbar(); - - // Public virtual methods + explicit EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWidget* _parent = nullptr); + ~EasyGraphicsScrollbar() override; + void clear() override; void mousePressEvent(QMouseEvent* _event) override; - void mouseReleaseEvent(QMouseEvent* _event) override; - void mouseMoveEvent(QMouseEvent* _event) override; - void wheelEvent(QWheelEvent* _event) override; - void resizeEvent(QResizeEvent* _event) override; - - void dragEnterEvent(QDragEnterEvent*) override {} public: // Public non-virtual methods - void clear(); + profiler::thread_id_t hystThread() const; - bool bindMode() const; - qreal getWindowScale() const; - ::profiler::thread_id_t hystThread() const; - - qreal minimum() const; - qreal maximum() const; - qreal range() const; - qreal value() const; - qreal sliderWidth() const; - qreal sliderHalfWidth() const; - int defaultFontHeight() const; - - void setValue(qreal _value); - void setRange(qreal _minValue, qreal _maxValue); - void setSliderWidth(qreal _width); - void setChronoPos(qreal _left, qreal _right); - void showChrono(); - void hideChrono(); - - void setHistogramSource(::profiler::thread_id_t _thread_id, const::profiler_gui::EasyItems* _items); - void setHistogramSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id); - - inline void setHistogramSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems& _items) - { + void setHistogramSource(profiler::thread_id_t _thread_id, const profiler_gui::EasyItems* _items); + void setHistogramSource(profiler::thread_id_t _thread_id, profiler::block_id_t _block_id); + void setHistogramSource(profiler::thread_id_t _thread_id, const profiler_gui::EasyItems& _items) { setHistogramSource(_thread_id, &_items); } - inline void lock() - { - m_bLocked = true; - } - - inline void unlock() - { - m_bLocked = false; - } - -signals: - - void rangeChanged(); - void valueChanged(qreal _value); - void wheeled(qreal _mouseX, int _wheelDelta); - private slots: void onThreadViewChanged(); - void onWindowWidthChange(qreal _width); }; // END of class EasyGraphicsScrollbar. diff --git a/profiler_gui/easy_qtimer.cpp b/profiler_gui/easy_qtimer.cpp index 4eb1740..9428f9e 100644 --- a/profiler_gui/easy_qtimer.cpp +++ b/profiler_gui/easy_qtimer.cpp @@ -60,15 +60,15 @@ EasyQTimer::EasyQTimer() : QObject() { - connect(&m_timer, &QTimer::timeout, [this](){ m_handler(); }); + connect(&m_timer, &QTimer::timeout, [this] { m_handler(); }); } -EasyQTimer::EasyQTimer(::std::function&& _handler, bool _isSignleShot) +EasyQTimer::EasyQTimer(::std::function&& handler, bool signleShot) : QObject() - , m_handler(::std::forward<::std::function&&>(_handler)) + , m_handler(::std::forward<::std::function&&>(handler)) { - m_timer.setSingleShot(_isSignleShot); - connect(&m_timer, &QTimer::timeout, [this](){ m_handler(); }); + m_timer.setSingleShot(signleShot); + connect(&m_timer, &QTimer::timeout, [this] { m_handler(); }); } EasyQTimer::~EasyQTimer() @@ -76,9 +76,47 @@ EasyQTimer::~EasyQTimer() } -void EasyQTimer::setHandler(::std::function&& _handler) +void EasyQTimer::setHandler(::std::function&& handler) { - m_handler = _handler; + m_handler = handler; +} + +void EasyQTimer::setSignleShot(bool singleShot) +{ + m_timer.setSingleShot(singleShot); +} + +bool EasyQTimer::isSingleShot() const +{ + return m_timer.isSingleShot(); +} + +void EasyQTimer::setInterval(int msec) +{ + m_timer.setInterval(msec); +} + +void EasyQTimer::start(int msec) +{ + stop(); + m_timer.start(msec); +} + +void EasyQTimer::start() +{ + stop(); + m_timer.start(); +} + +void EasyQTimer::stop() +{ + if (m_timer.isActive()) + m_timer.stop(); +} + +bool EasyQTimer::isActive() const +{ + return m_timer.isActive(); } ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/easy_qtimer.h b/profiler_gui/easy_qtimer.h index 818e628..598d2bf 100644 --- a/profiler_gui/easy_qtimer.h +++ b/profiler_gui/easy_qtimer.h @@ -72,15 +72,20 @@ private: public: - EasyQTimer(); - EasyQTimer(::std::function&& _handler, bool _isSignleShot = false); - virtual ~EasyQTimer(); + explicit EasyQTimer(); + explicit EasyQTimer(::std::function&& handler, bool signleShot = false); + ~EasyQTimer() override; void setHandler(::std::function&& _handler); - inline void start(int msec) { m_timer.start(msec); } - inline void stop() { if (m_timer.isActive()) m_timer.stop(); } - inline bool isActive() const { return m_timer.isActive(); } + void setSignleShot(bool singleShot); + bool isSingleShot() const; + + void setInterval(int msec); + void start(int msec); + void start(); + void stop(); + bool isActive() const; }; // END of class EasyQTimer. diff --git a/profiler_gui/graphics_image_item.cpp b/profiler_gui/graphics_image_item.cpp new file mode 100644 index 0000000..44764f1 --- /dev/null +++ b/profiler_gui/graphics_image_item.cpp @@ -0,0 +1,331 @@ + +#include +#include +#include "graphics_image_item.h" +#include "graphics_slider_area.h" +#include "globals.h" + +EASY_CONSTEXPR int TimerInterval = 40; +EASY_CONSTEXPR int BoundaryTimerInterval = 100; + +GraphicsImageItem::GraphicsImageItem() : Parent(nullptr) + , m_boundaryTimer([this] { updateImage(); }, true) + , m_workerImage(nullptr) + , m_imageOrigin(0) + , m_imageScale(1) + , m_imageOriginUpdate(0) + , m_imageScaleUpdate(1) + , m_workerImageOrigin(0) + , m_workerImageScale(1) + , m_topValue(0) + , m_bottomValue(0) + , m_maxValue(0) + , m_minValue(0) + , m_timer(::std::bind(&This::onTimeout, this)) + , m_bPermitImageUpdate(true) +{ + m_bReady = ATOMIC_VAR_INIT(false); + m_boundaryTimer.setInterval(BoundaryTimerInterval); + m_timer.setInterval(TimerInterval); +} + +GraphicsImageItem::~GraphicsImageItem() +{ + cancelAnyJob(); +} + +QRectF GraphicsImageItem::boundingRect() const +{ + return m_boundingRect; +} + +void GraphicsImageItem::setBoundingRect(const QRectF& _rect) +{ + m_boundingRect = _rect; +} + +void GraphicsImageItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) +{ + m_boundingRect.setRect(x, y, w, h); +} + +void GraphicsImageItem::setMousePos(const QPointF& pos) +{ + m_mousePos = pos; +} + +void GraphicsImageItem::setMousePos(qreal x, qreal y) +{ + m_mousePos.setX(x); + m_mousePos.setY(y); +} + +bool GraphicsImageItem::updateImage() +{ + if (!cancelImageUpdate()) + return false; + + setReady(false); + startTimer(); + + return true; +} + + +void GraphicsImageItem::onValueChanged() +{ + const auto widget = qobject_cast(scene()->parent()); + if (widget == nullptr) + return; + + if (!widget->bindMode()) + return; + + m_boundaryTimer.stop(); + + const auto sliderWidth_inv = 1.0 / widget->sliderWidth(); + const auto k = widget->range() * sliderWidth_inv; + + const auto deltaScale = m_imageScaleUpdate < k ? (k / m_imageScaleUpdate) : (m_imageScaleUpdate / k); + if (deltaScale > 4) + { + updateImage(); + return; + } + + const auto deltaOffset = (widget->value() - m_imageOriginUpdate) * sliderWidth_inv; + if (deltaOffset < 1.5 || deltaOffset > 4.5) + { + updateImage(); + return; + } + + m_boundaryTimer.start(); +} + +void GraphicsImageItem::onModeChanged() +{ + if (!isImageUpdatePermitted()) + return; + + m_boundaryTimer.stop(); + updateImage(); +} + +void GraphicsImageItem::onImageUpdated() +{ + +} + +bool GraphicsImageItem::cancelImageUpdate() +{ + if (!isImageUpdatePermitted()) + return false; + + cancelAnyJob(); + + return true; +} + +bool GraphicsImageItem::pickTopValue() +{ + const auto y = m_mousePos.y(); + if (isImageUpdatePermitted() && m_boundingRect.top() < y && y < m_boundingRect.bottom()) + { + m_topValue = m_bottomValue + (m_topValue - m_bottomValue) * (m_boundingRect.bottom() - y) / m_boundingRect.height(); + m_boundaryTimer.stop(); + updateImage(); + return true; + } + + return false; +} + +bool GraphicsImageItem::increaseTopValue() +{ + if (isImageUpdatePermitted() && m_topValue < m_maxValue) + { + auto step = 0.05 * (m_maxValue - m_bottomValue); + if (m_topValue < (m_bottomValue + 1.25 * step)) + step = 0.1 * (m_topValue - m_bottomValue); + m_topValue = std::min(m_maxValue, m_topValue + step); + m_boundaryTimer.start(); + return true; + } + + return false; +} + +bool GraphicsImageItem::decreaseTopValue() +{ + if (isImageUpdatePermitted() && m_topValue > m_bottomValue) + { + auto step = 0.05 * (m_maxValue - m_bottomValue); + if (m_topValue < (m_bottomValue + 1.25 * step)) + step = std::max(0.1 * (m_topValue - m_bottomValue), 0.3); + + if (m_topValue > (m_bottomValue + 1.25 * step)) + { + m_topValue = std::max(m_bottomValue + step, m_topValue - step); + m_boundaryTimer.start(); + return true; + } + } + + return false; +} + +bool GraphicsImageItem::pickBottomValue() +{ + const auto y = m_mousePos.y(); + if (isImageUpdatePermitted() && m_boundingRect.top() < y && y < m_boundingRect.bottom()) + { + m_bottomValue = m_bottomValue + (m_topValue - m_bottomValue) * (m_boundingRect.bottom() - y) / m_boundingRect.height(); + m_boundaryTimer.stop(); + updateImage(); + return true; + } + + return false; +} + +bool GraphicsImageItem::increaseBottomValue() +{ + if (isImageUpdatePermitted() && m_bottomValue < m_topValue) + { + auto step = 0.05 * (m_topValue - m_minValue); + if (m_bottomValue > (m_topValue - 1.25 * step)) + step = 0.1 * (m_topValue - m_bottomValue); + + if (m_bottomValue < (m_topValue - 1.25 * step)) + { + m_bottomValue = std::min(m_topValue - step, m_bottomValue + step); + m_boundaryTimer.start(); + return true; + } + } + + return false; +} + +bool GraphicsImageItem::decreaseBottomValue() +{ + if (isImageUpdatePermitted() && m_bottomValue > m_minValue) + { + auto step = 0.05 * (m_topValue - m_minValue); + if (m_bottomValue > (m_topValue - 1.25 * step)) + step = std::max(0.1 * (m_topValue - m_bottomValue), 0.3); + m_bottomValue = std::max(m_minValue, m_bottomValue - step); + m_boundaryTimer.start(); + return true; + } + + return false; +} + +void GraphicsImageItem::paintImage(QPainter* _painter) +{ + _painter->setPen(Qt::NoPen); + _painter->drawImage(0, m_boundingRect.top(), m_image); +} + +void GraphicsImageItem::paintImage(QPainter* _painter, qreal _scale, qreal _sceneLeft, qreal _sceneRight, + qreal _visibleRegionLeft, qreal _visibleRegionWidth) +{ + const auto dscale = (_sceneRight - _sceneLeft) / (_visibleRegionWidth * m_imageScale); + + _painter->setPen(Qt::NoPen); + _painter->setTransform(QTransform::fromScale(dscale, 1), true); + _painter->drawImage(QPointF {(_sceneLeft + m_imageOrigin - _visibleRegionLeft) * _scale * m_imageScale, m_boundingRect.top()}, m_image); + _painter->setTransform(QTransform::fromScale(1. / dscale, 1), true); +} + +void GraphicsImageItem::onTimeout() +{ + if (!isVisible()) + { + stopTimer(); + return; + } + + if (isReady()) + { + stopTimer(); + + if (!isImageUpdatePermitted()) + { + // Worker thread have finished parsing input data (when setSource(_block_id) was called) + setImageUpdatePermitted(true); // From now we can update an image + updateImage(); + } + else + { + // Image updated + + if (m_workerThread.joinable()) + m_workerThread.join(); + + m_workerImage->swap(m_image); + delete m_workerImage; + m_workerImage = nullptr; + + m_imageOriginUpdate = m_imageOrigin = m_workerImageOrigin; + m_imageScaleUpdate = m_imageScale = m_workerImageScale; + + onImageUpdated(); + scene()->update(); + } + } +} + +void GraphicsImageItem::setImageUpdatePermitted(bool _permit) +{ + m_bPermitImageUpdate = _permit; +} + +bool GraphicsImageItem::isImageUpdatePermitted() const +{ + return m_bPermitImageUpdate; +} + +void GraphicsImageItem::cancelAnyJob() +{ + setReady(true); + if (m_workerThread.joinable()) + m_workerThread.join(); + //setReady(false); + + delete m_workerImage; + m_workerImage = nullptr; + + m_imageOriginUpdate = m_imageOrigin; + m_imageScaleUpdate = m_imageScale; + + stopTimer(); +} + +void GraphicsImageItem::resetTopBottomValues() +{ + m_topValue = m_maxValue; + m_bottomValue = m_minValue; +} + +bool GraphicsImageItem::isReady() const +{ + return m_bReady.load(std::memory_order_acquire); +} + +void GraphicsImageItem::setReady(bool _ready) +{ + m_bReady.store(_ready, std::memory_order_release); +} + +void GraphicsImageItem::startTimer() +{ + m_timer.start(); +} + +void GraphicsImageItem::stopTimer() +{ + m_timer.stop(); +} diff --git a/profiler_gui/graphics_image_item.h b/profiler_gui/graphics_image_item.h new file mode 100644 index 0000000..f94b89e --- /dev/null +++ b/profiler_gui/graphics_image_item.h @@ -0,0 +1,96 @@ + + +#ifndef EASY_PROFILER_GRAPHICS_IMAGE_ITEM_H +#define EASY_PROFILER_GRAPHICS_IMAGE_ITEM_H + +#include +#include +#include +#include "easy_qtimer.h" + +class GraphicsImageItem : public QGraphicsItem +{ +protected: + + using Parent = QGraphicsItem; + using This = GraphicsImageItem; + + QRectF m_boundingRect; + QImage m_image; + EasyQTimer m_boundaryTimer; + std::thread m_workerThread; + QPointF m_mousePos; + QImage* m_workerImage; + qreal m_imageOrigin; + qreal m_imageScale; + qreal m_imageOriginUpdate; + qreal m_imageScaleUpdate; + qreal m_workerImageOrigin; + qreal m_workerImageScale; + qreal m_topValue; + qreal m_bottomValue; + qreal m_maxValue; + qreal m_minValue; + +private: + + EasyQTimer m_timer; + std::atomic_bool m_bReady; + bool m_bPermitImageUpdate; ///< Is false when m_workerThread is parsing input dataset (when setSource(_block_id) is called) + +public: + + explicit GraphicsImageItem(); + ~GraphicsImageItem() override; + + QRectF boundingRect() const override; + + virtual bool pickTopValue(); + virtual bool increaseTopValue(); + virtual bool decreaseTopValue(); + + virtual bool pickBottomValue(); + virtual bool increaseBottomValue(); + virtual bool decreaseBottomValue(); + + virtual bool updateImage(); + virtual void onModeChanged(); + virtual void onValueChanged(); + +protected: + + virtual void onImageUpdated(); + +public: + + void setMousePos(const QPointF& pos); + void setMousePos(qreal x, qreal y); + void setBoundingRect(const QRectF& _rect); + void setBoundingRect(qreal x, qreal y, qreal w, qreal h); + bool cancelImageUpdate(); + +protected: + + void paintImage(QPainter* _painter); + void paintImage(QPainter* _painter, qreal _scale, qreal _sceneLeft, qreal _sceneRight, + qreal _visibleRegionLeft, qreal _visibleRegionWidth); + + void setImageUpdatePermitted(bool _permit); + bool isImageUpdatePermitted() const; + + void cancelAnyJob(); + void resetTopBottomValues(); + + bool isReady() const; + void setReady(bool _ready); + + void startTimer(); + void stopTimer(); + +private: + + void onTimeout(); + +}; // end of class GraphicsImageItem. + +#endif //EASY_PROFILER_GRAPHICS_IMAGE_ITEM_H diff --git a/profiler_gui/graphics_slider_area.cpp b/profiler_gui/graphics_slider_area.cpp new file mode 100644 index 0000000..423b336 --- /dev/null +++ b/profiler_gui/graphics_slider_area.cpp @@ -0,0 +1,521 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#include +#include +#include +#include +#include +#include +#include "graphics_slider_area.h" +#include "globals.h" + +////////////////////////////////////////////////////////////////////////// + +GraphicsSliderItem::GraphicsSliderItem(int _size, bool _main) + : Parent() + , m_halfwidth(0.5) + , m_bMain(_main) +{ + const int sizeHalf = 1 + (_size >> 1); + + if (_main) + _size = -_size; + + m_indicator.reserve(3); + m_indicator.push_back(QPointF(0, 0)); + m_indicator.push_back(QPointF(-sizeHalf, _size)); + m_indicator.push_back(QPointF(sizeHalf, _size)); + + setBrush(Qt::SolidPattern); +} + +GraphicsSliderItem::~GraphicsSliderItem() +{ + +} + +void GraphicsSliderItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* /* _option */, QWidget* /* _widget */) +{ + const auto view = static_cast(scene()->parent()); + if (view->bindMode()) + return; + + const auto currentScale = view->getWindowScale(); + const qreal w = width() * currentScale; + + const auto br = rect(); + const QRectF r(br.left() * currentScale, br.top() + view->margin(), w, br.height() - view->margins() - 1); + const auto r_right = r.right(); + const auto r_bottom = r.bottom(); + const auto b = brush(); + + _painter->save(); + _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true); + _painter->setBrush(b); + + if (w > 1) + { + _painter->setPen(Qt::NoPen); + _painter->drawRect(r); + + // Draw left and right borders + if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion); + _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb())); + _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom)); + _painter->drawLine(QPointF(r_right, r.top()), QPointF(r_right, r_bottom)); + } + else + { + if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion); + _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb())); + _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom)); + } + + // Draw triangle indicators for small slider + if (m_bMain) + _painter->setTransform(QTransform::fromTranslate(r.left() + w * 0.5, br.top() + view->margin()), true); + else + _painter->setTransform(QTransform::fromTranslate(r.left() + w * 0.5, br.bottom() - view->margin() - 1), true); + + _painter->setPen(b.color().rgb()); + _painter->drawPolygon(m_indicator); + + _painter->restore(); +} + +qreal GraphicsSliderItem::width() const +{ + return m_halfwidth * 2.0; +} + +qreal GraphicsSliderItem::halfwidth() const +{ + return m_halfwidth; +} + +void GraphicsSliderItem::setWidth(qreal _width) +{ + const auto sceneRect = scene()->sceneRect(); + + m_halfwidth = _width * 0.5; + setRect(-m_halfwidth, sceneRect.top(), _width, sceneRect.height()); +} + +void GraphicsSliderItem::setHalfwidth(qreal _halfwidth) +{ + const auto sceneRect = scene()->sceneRect(); + + m_halfwidth = _halfwidth; + setRect(-m_halfwidth, sceneRect.top(), width(), sceneRect.height()); +} + +void GraphicsSliderItem::setColor(QRgb _color) +{ + setColor(QColor::fromRgba(_color)); +} + +void GraphicsSliderItem::setColor(const QColor& _color) +{ + auto b = brush(); + b.setColor(_color); + setBrush(b); +} + +////////////////////////////////////////////////////////////////////////// + +GraphicsSliderArea::GraphicsSliderArea(QWidget* _parent) + : Parent(_parent) + , m_minimumValue(0) + , m_maximumValue(500) + , m_value(10) + , m_windowScale(1) + , m_mouseButtons(Qt::NoButton) + , m_slider(nullptr) + , m_selectionIndicator(nullptr) + , m_histogramItem(nullptr) + , m_fontHeight(0) + , m_bScrolling(false) + , m_bBindMode(false) + , m_bLocked(false) +{ + setCacheMode(QGraphicsView::CacheNone); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + //setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + setOptimizationFlag(QGraphicsView::DontSavePainterState, true); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setContentsMargins(0, 0, 0, 0); + + setScene(new QGraphicsScene(this)); + + m_fontHeight = QFontMetrics(font()).height(); + + EASY_CONSTEXPR int SceneHeight = 500; + scene()->setSceneRect(0, -(SceneHeight >> 1), 500, SceneHeight); + + m_histogramItem = new GraphicsHistogramItem(); + scene()->addItem(m_histogramItem); + + m_histogramItem->setPos(0, 0); + m_histogramItem->setBoundingRect(0, scene()->sceneRect().top() + margin(), scene()->width(), SceneHeight - margins() - 1); + m_histogramItem->hide(); + + m_selectionIndicator = new GraphicsSliderItem(6, false); + scene()->addItem(m_selectionIndicator); + + m_selectionIndicator->setPos(0, 0); + m_selectionIndicator->setColor(0x40000000 | profiler_gui::CHRONOMETER_COLOR.rgba()); + m_selectionIndicator->hide(); + + m_slider = new GraphicsSliderItem(6, true); + scene()->addItem(m_slider); + + m_slider->setPos(0, 0); + m_slider->setColor(0x40c0c0c0); + m_slider->hide(); + + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged); + connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged); + + centerOn(0, 0); +} + +GraphicsSliderArea::~GraphicsSliderArea() +{ + +} + +////////////////////////////////////////////////////////////////////////// + +void GraphicsSliderArea::clear() +{ + m_selectionIndicator->hide(); + setRange(0, 100); + setSliderWidth(2); + setValue(0); +} + +////////////////////////////////////////////////////////////////////////// + +bool GraphicsSliderArea::bindMode() const +{ + return m_bBindMode; +} + +qreal GraphicsSliderArea::getWindowScale() const +{ + return m_windowScale; +} + +qreal GraphicsSliderArea::minimum() const +{ + return m_minimumValue; +} + +qreal GraphicsSliderArea::maximum() const +{ + return m_maximumValue; +} + +qreal GraphicsSliderArea::range() const +{ + return m_maximumValue - m_minimumValue; +} + +qreal GraphicsSliderArea::value() const +{ + return m_value; +} + +qreal GraphicsSliderArea::sliderWidth() const +{ + return m_slider->width(); +} + +qreal GraphicsSliderArea::sliderHalfWidth() const +{ + return m_slider->halfwidth(); +} + +int GraphicsSliderArea::fontHeight() const +{ + return m_fontHeight; +} + +int GraphicsSliderArea::margin() const +{ + return m_fontHeight + 2; +} + +int GraphicsSliderArea::margins() const +{ + return margin() << 1; +} + +////////////////////////////////////////////////////////////////////////// + +void GraphicsSliderArea::setValue(qreal _value) +{ + using estd::clamp; + m_value = clamp(m_minimumValue, _value, std::max(m_minimumValue, m_maximumValue - m_slider->width())); + m_slider->setX(m_value + m_slider->halfwidth()); + emit valueChanged(m_value); + + if (m_imageItem->isVisible()) + m_imageItem->onValueChanged(); +} + +void GraphicsSliderArea::setRange(qreal _minValue, qreal _maxValue) +{ + const auto oldRange = range(); + const auto oldValue = oldRange < 1e-3 ? 0.0 : m_value / oldRange; + + m_minimumValue = _minValue; + m_maximumValue = _maxValue; + const auto range = this->range(); + + const auto sceneRect = scene()->sceneRect(); + scene()->setSceneRect(_minValue, sceneRect.top(), range, sceneRect.height()); + + const auto histogramRect = m_imageItem->boundingRect(); + m_imageItem->cancelImageUpdate(); + m_imageItem->setBoundingRect(_minValue, histogramRect.top(), range, histogramRect.height()); + + emit rangeChanged(); + + setValue(_minValue + oldValue * range); + + onWindowWidthChange(width()); + + if (m_imageItem->isVisible()) + m_imageItem->updateImage(); +} + +void GraphicsSliderArea::setSliderWidth(qreal _width) +{ + m_slider->setWidth(_width); + setValue(m_value); +} + +////////////////////////////////////////////////////////////////////////// + +void GraphicsSliderArea::setSelectionPos(qreal _left, qreal _right) +{ + m_selectionIndicator->setWidth(_right - _left); + m_selectionIndicator->setX(_left + m_selectionIndicator->halfwidth()); +} + +void GraphicsSliderArea::showSelectionIndicator() +{ + if (!m_selectionIndicator->isVisible()) + { + m_selectionIndicator->show(); + scene()->update(); + } +} + +void GraphicsSliderArea::hideSelectionIndicator() +{ + if (m_selectionIndicator->isVisible()) + { + m_selectionIndicator->hide(); + scene()->update(); + } +} + +////////////////////////////////////////////////////////////////////////// + +void GraphicsSliderArea::mousePressEvent(QMouseEvent* _event) +{ + _event->accept(); + + m_mouseButtons = _event->buttons(); + + if (m_mouseButtons & Qt::LeftButton) + { + if (_event->modifiers() & Qt::ControlModifier) + { + m_imageItem->pickBottomValue(); + } + else if (_event->modifiers() & Qt::ShiftModifier) + { + m_imageItem->pickTopValue(); + } + else + { + m_bScrolling = true; + m_mousePressPos = _event->pos(); + if (!m_bBindMode) + setValue(mapToScene(m_mousePressPos).x() - m_minimumValue - m_slider->halfwidth()); + } + } + + if (m_mouseButtons & Qt::RightButton) + { + if (!_event->modifiers()) + { + m_bBindMode = !m_bBindMode; + if (m_imageItem->isVisible()) + m_imageItem->onModeChanged(); + } + } + + //Parent::mousePressEvent(_event); +} + +void GraphicsSliderArea::mouseReleaseEvent(QMouseEvent* _event) +{ + m_mouseButtons = _event->buttons(); + m_bScrolling = false; + _event->accept(); + //Parent::mouseReleaseEvent(_event); +} + +void GraphicsSliderArea::mouseMoveEvent(QMouseEvent* _event) +{ + const auto pos = _event->pos(); + + if (m_mouseButtons & Qt::LeftButton) + { + const auto delta = pos - m_mousePressPos; + m_mousePressPos = pos; + + if (m_bScrolling) + { + auto realScale = m_windowScale; + if (bindMode()) + realScale *= -range() / sliderWidth(); + setValue(m_value + delta.x() / realScale); + } + } + + m_imageItem->setMousePos(mapToScene(pos)); + if (m_imageItem->isVisible()) + scene()->update(); +} + +void GraphicsSliderArea::wheelEvent(QWheelEvent* _event) +{ + _event->accept(); + + if (_event->modifiers() & Qt::ShiftModifier) + { + // Shift + mouse wheel will change histogram top boundary + + if (m_imageItem->isVisible()) + { + if (_event->delta() > 0) + m_imageItem->increaseTopValue(); + else + m_imageItem->decreaseTopValue(); + } + + return; + } + + if (_event->modifiers() & Qt::ControlModifier) + { + // Ctrl + mouse wheel will change histogram bottom boundary + + if (m_imageItem->isVisible()) + { + if (_event->delta() > 0) + m_imageItem->increaseBottomValue(); + else + m_imageItem->decreaseBottomValue(); + } + + return; + } + + if (!bindMode()) + { + const auto w = m_slider->halfwidth() * (_event->delta() < 0 ? profiler_gui::SCALING_COEFFICIENT : profiler_gui::SCALING_COEFFICIENT_INV); + setValue(mapToScene(_event->pos()).x() - m_minimumValue - w); + emit wheeled(w * m_windowScale, _event->delta()); + } + else + { + const auto x = (mapToScene(_event->pos()).x() - m_minimumValue) * m_windowScale; + emit wheeled(x, _event->delta()); + } +} + +void GraphicsSliderArea::resizeEvent(QResizeEvent* _event) +{ + const int h = _event->size().height(); + if (_event->oldSize().height() != h) + { + const int sceneHeight = h - 2; + scene()->setSceneRect(0, -(sceneHeight >> 1), 500, sceneHeight); + + const auto br = m_imageItem->boundingRect(); + m_imageItem->setBoundingRect(br.left(), scene()->sceneRect().top() + margin(), br.width(), sceneHeight - margins() - 1); + } + + onWindowWidthChange(_event->size().width()); + if (m_imageItem->isVisible()) + m_imageItem->updateImage(); +} + +////////////////////////////////////////////////////////////////////////// + +void GraphicsSliderArea::onWindowWidthChange(qreal _width) +{ + const auto oldScale = m_windowScale; + const auto scrollingRange = range(); + + if (scrollingRange < 1e-3) + { + m_windowScale = 1; + } + else + { + m_windowScale = _width / scrollingRange; + } + + scale(m_windowScale / oldScale, 1); +} diff --git a/profiler_gui/graphics_slider_area.h b/profiler_gui/graphics_slider_area.h new file mode 100644 index 0000000..cbb8590 --- /dev/null +++ b/profiler_gui/graphics_slider_area.h @@ -0,0 +1,173 @@ +/** +Lightweight profiler library for c++ +Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin + +Licensed under either of + * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) + * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) +at your option. + +The MIT License + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + +The Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +**/ + +#ifndef EASY_PROFILER_GRAPHICS_SLIDER_AREA_H +#define EASY_PROFILER_GRAPHICS_SLIDER_AREA_H + +#include +#include +#include +#include +#include +#include "common_types.h" +#include "graphics_image_item.h" + +////////////////////////////////////////////////////////////////////////// + +class GraphicsSliderItem : public QGraphicsRectItem +{ + using Parent = QGraphicsRectItem; + using This = GraphicsSliderItem; + +private: + + QPolygonF m_indicator; + qreal m_halfwidth; + bool m_bMain; + +public: + + explicit GraphicsSliderItem(int _size, bool _main); + ~GraphicsSliderItem() override; + + void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) override; + + qreal width() const; + qreal halfwidth() const; + + void setWidth(qreal _width); + void setHalfwidth(qreal _halfwidth); + + void setColor(QRgb _color); + void setColor(const QColor& _color); + +}; // END of class GraphicsSliderItem. + +////////////////////////////////////////////////////////////////////////// + +class GraphicsSliderArea : public QGraphicsView +{ + Q_OBJECT + + using Parent = QGraphicsView; + using This = GraphicsSliderArea; + +protected: + + qreal m_minimumValue; + qreal m_maximumValue; + qreal m_value; + qreal m_windowScale; + QPoint m_mousePressPos; + Qt::MouseButtons m_mouseButtons; + GraphicsSliderItem* m_slider; + GraphicsSliderItem* m_selectionIndicator; + GraphicsHistogramItem* m_histogramItem; + GraphicsImageItem* m_imageItem; + int m_fontHeight; + bool m_bScrolling; + bool m_bBindMode; + bool m_bLocked; + +public: + + explicit GraphicsSliderArea(QWidget* _parent = nullptr); + ~GraphicsSliderArea() override; + + // Public virtual methods + + void mousePressEvent(QMouseEvent* _event) override; + void mouseReleaseEvent(QMouseEvent* _event) override; + void mouseMoveEvent(QMouseEvent* _event) override; + void wheelEvent(QWheelEvent* _event) override; + void resizeEvent(QResizeEvent* _event) override; + void dragEnterEvent(QDragEnterEvent*) override {} + + virtual void clear(); + +public: + + // Public non-virtual methods + + bool bindMode() const; + qreal getWindowScale() const; + + qreal minimum() const; + qreal maximum() const; + qreal range() const; + qreal value() const; + qreal sliderWidth() const; + qreal sliderHalfWidth() const; + int fontHeight() const; + int margin() const; + int margins() const; + + void setValue(qreal _value); + void setRange(qreal _minValue, qreal _maxValue); + void setSliderWidth(qreal _width); + void setSelectionPos(qreal _left, qreal _right); + void showSelectionIndicator(); + void hideSelectionIndicator(); + + inline void lock() { + m_bLocked = true; + } + + inline void unlock() { + m_bLocked = false; + } + +signals: + + void rangeChanged(); + void valueChanged(qreal _value); + void wheeled(qreal _mouseX, int _wheelDelta); + +protected slots: + + void onWindowWidthChange(qreal _width); + +}; // END of class GraphicsSliderArea. + +////////////////////////////////////////////////////////////////////////// + +#endif //EASY_PROFILER_GRAPHICS_SLIDER_AREA_H