2016-07-10 01:29:09 +03:00
/************************************************************************
2018-05-07 21:58:37 +03:00
* file name : graphics_scrollbar . cpp
2016-07-10 01:29:09 +03:00
* - - - - - - - - - - - - - - - - - :
* creation time : 2016 / 07 / 04
* author : Victor Zarubkin
* email : v . s . zarubkin @ gmail . com
* - - - - - - - - - - - - - - - - - :
* description : .
* - - - - - - - - - - - - - - - - - :
* change log : * 2016 / 07 / 04 Victor Zarubkin : Initial commit .
* :
* : *
* - - - - - - - - - - - - - - - - - :
2016-09-06 21:49:32 +03:00
* license : Lightweight profiler library for c + +
2019-10-20 16:12:37 +03:00
* : Copyright ( C ) 2016 - 2019 Sergey Yagovtsev , Victor Zarubkin
2016-09-06 21:49:32 +03:00
* :
2017-03-30 06:55:15 +03:00
* : 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 .
2016-11-13 16:39:59 +03:00
* :
2017-03-30 06:55:15 +03:00
* : 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 .
2016-11-13 16:39:59 +03:00
* : 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 .
2016-07-10 01:29:09 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2016-07-31 13:13:48 +03:00
# include <algorithm>
2016-08-18 23:26:41 +03:00
# include <QGraphicsScene>
2016-07-31 13:13:48 +03:00
# include <QWheelEvent>
# include <QMouseEvent>
# include <QResizeEvent>
2017-12-28 00:23:14 +03:00
# include <easy/utility.h>
2018-05-07 21:58:37 +03:00
# include "graphics_scrollbar.h"
2016-08-08 22:45:57 +03:00
# include "globals.h"
2016-07-10 01:29:09 +03:00
2019-10-26 01:04:21 +03:00
namespace {
2016-07-10 01:29:09 +03:00
//////////////////////////////////////////////////////////////////////////
2017-11-23 22:01:44 +03:00
EASY_CONSTEXPR int HIST_COLUMN_MIN_HEIGHT = 2 ;
2016-07-31 13:13:48 +03:00
//////////////////////////////////////////////////////////////////////////
2016-12-07 22:29:16 +03:00
inline qreal calculate_color1 ( qreal h , qreal , qreal k )
2016-11-16 22:17:39 +03:00
{
2017-12-28 00:23:14 +03:00
return std : : min ( h * k , 0.9999999 ) ;
2016-11-16 22:17:39 +03:00
}
2016-12-07 22:29:16 +03:00
inline qreal calculate_color2 ( qreal , qreal duration , qreal k )
2016-11-16 22:17:39 +03:00
{
2018-01-20 15:23:28 +03:00
using estd : : sqr ;
2017-12-28 00:23:14 +03:00
return std : : min ( sqr ( sqr ( duration ) ) * k , 0.9999999 ) ;
2016-11-16 22:17:39 +03:00
}
2019-10-26 01:04:21 +03:00
} // end of namespace <noname>.
2016-07-10 01:29:09 +03:00
//////////////////////////////////////////////////////////////////////////
2018-01-20 15:23:28 +03:00
GraphicsHistogramItem : : GraphicsHistogramItem ( ) : Parent ( )
2019-10-26 01:04:21 +03:00
, m_workerTopDuration ( 0 )
, m_workerBottomDuration ( 0 )
, m_blockTotalDuraion ( 0 )
2016-12-04 16:51:27 +03:00
, m_threadDuration ( 0 )
2016-12-14 22:16:14 +03:00
, m_threadProfiledTime ( 0 )
, m_threadWaitTime ( 0 )
2019-10-26 01:04:21 +03:00
, m_medianDuration ( 0 )
, m_avgDuration ( 0 )
, m_medianDurationFull ( 0 )
, m_avgDurationFull ( 0 )
, m_workerMedianDuration ( 0 )
, m_workerAvgDuration ( 0 )
2016-12-04 16:51:27 +03:00
, m_pSource ( nullptr )
2016-12-12 22:28:54 +03:00
, m_pProfilerThread ( nullptr )
2016-12-04 16:51:27 +03:00
, m_threadId ( 0 )
2018-01-20 15:23:28 +03:00
, m_blockId ( profiler_gui : : numeric_max < decltype ( m_blockId ) > ( ) )
, m_timeUnits ( profiler_gui : : TimeUnits_auto )
2017-03-07 19:59:57 +03:00
, m_regime ( Hist_Pointer )
2016-08-03 00:06:36 +03:00
{
}
2018-01-20 15:23:28 +03:00
GraphicsHistogramItem : : ~ GraphicsHistogramItem ( )
2016-08-03 00:06:36 +03:00
{
2018-01-20 15:23:28 +03:00
2016-08-03 00:06:36 +03:00
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : paint ( QPainter * _painter , const QStyleOptionGraphicsItem * /* _option */ , QWidget * /* _widget */ )
2016-08-03 00:06:36 +03:00
{
2018-01-20 15:23:28 +03:00
if ( ! isImageUpdatePermitted ( ) | | ( m_regime = = Hist_Pointer & & m_pSource = = nullptr ) | | ( m_regime = = Hist_Id & & ( m_threadId = = 0 | | profiler_gui : : is_max ( m_blockId ) ) ) )
2016-12-04 16:51:27 +03:00
return ;
2017-03-07 19:59:57 +03:00
if ( m_regime = = Hist_Pointer )
2017-03-07 00:29:34 +03:00
paintByPtr ( _painter ) ;
else
paintById ( _painter ) ;
}
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
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 )
2017-03-07 00:29:34 +03:00
{
2019-10-20 16:12:37 +03:00
if ( isEmpty ( ) )
{
return ;
}
2017-03-07 00:29:34 +03:00
if ( _font_h ! = 0 & & _top < _mouse_y & & _mouse_y < _bottom )
{
const int half_font_h = _font_h > > 1 ;
2016-12-04 16:51:27 +03:00
2017-03-07 00:29:34 +03:00
_painter - > setPen ( Qt : : blue ) ;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
const auto mouseStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue + _delta_time * ( _bottom - _mouse_y ) / _height , 3 ) ;
2017-03-07 00:29:34 +03:00
qreal mouseIndicatorRight = _width ;
if ( _mouse_y < _top + half_font_h )
mouseIndicatorRight = _top_width ;
2016-12-04 16:51:27 +03:00
2017-03-07 00:29:34 +03:00
qreal mouseIndicatorLeft = 0 ;
2018-01-20 15:23:28 +03:00
const QRectF rect ( 0 , _mouse_y - _font_h - 2 , _width , 4 + ( _font_h < < 1 ) ) ;
2017-03-07 00:29:34 +03:00
if ( _mouse_y > _bottom - half_font_h )
{
_painter - > drawText ( rect , Qt : : AlignLeft | Qt : : AlignTop , mouseStr ) ;
}
else if ( _mouse_y < _top + half_font_h )
{
_painter - > drawText ( rect , Qt : : AlignLeft | Qt : : AlignBottom , mouseStr ) ;
}
else
{
_painter - > drawText ( rect , Qt : : AlignLeft | Qt : : AlignVCenter , mouseStr ) ;
2024-05-13 04:09:30 -04:00
mouseIndicatorLeft = _painter - > fontMetrics ( ) . boundingRect ( mouseStr ) . width ( ) + 3 ;
2017-03-07 00:29:34 +03:00
}
2016-08-03 00:06:36 +03:00
2017-03-07 00:29:34 +03:00
_painter - > drawLine ( QLineF ( mouseIndicatorLeft , _mouse_y , mouseIndicatorRight , _mouse_y ) ) ;
}
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : paintByPtr ( QPainter * _painter )
2016-12-04 16:51:27 +03:00
{
2018-05-07 21:58:37 +03:00
const auto widget = static_cast < const GraphicsScrollbar * > ( scene ( ) - > parent ( ) ) ;
2016-11-30 22:37:11 +03:00
const bool bindMode = widget - > bindMode ( ) ;
const auto currentScale = widget - > getWindowScale ( ) ;
2016-08-03 00:06:36 +03:00
const auto bottom = m_boundingRect . bottom ( ) ;
2016-12-01 21:55:11 +03:00
const auto width = m_boundingRect . width ( ) * currentScale ;
2018-01-20 15:23:28 +03:00
const auto dtime = m_topValue - m_bottomValue ;
const auto coeff = m_boundingRect . height ( ) / ( dtime > 1e-3 ? dtime : 1. ) ;
2016-08-03 00:06:36 +03:00
QBrush brush ( Qt : : SolidPattern ) ;
2017-04-10 22:04:09 +03:00
//QRgb previousColor = 0;
2016-08-03 00:06:36 +03:00
2016-12-04 16:51:27 +03:00
_painter - > save ( ) ;
_painter - > setTransform ( QTransform : : fromScale ( 1.0 / currentScale , 1 ) , true ) ;
2017-03-13 00:43:15 +03:00
if ( ! m_pSource - > empty ( ) )
2016-12-04 16:51:27 +03:00
{
if ( ! bindMode )
2018-01-20 15:23:28 +03:00
paintImage ( _painter ) ;
2016-12-04 16:51:27 +03:00
else
2018-01-20 15:23:28 +03:00
paintImage ( _painter , currentScale , widget - > minimum ( ) , widget - > maximum ( ) , widget - > value ( ) , widget - > sliderWidth ( ) ) ;
2016-12-04 16:51:27 +03:00
}
qreal top_width = width , bottom_width = width ;
2018-01-20 15:23:28 +03:00
const auto font_h = widget - > fontHeight ( ) ;
2019-10-26 01:04:21 +03:00
QRectF bottomRect ( 3 , bottom + 2 , width - 3 , font_h ) ;
QRectF topRect ( 3 , m_boundingRect . top ( ) - widget - > margin ( ) , width - 3 , font_h ) ;
QRectF textBounds ;
// MODE
{
_painter - > setPen ( Qt : : blue ) ;
_painter - > drawText (
topRect ,
Qt : : AlignLeft | Qt : : AlignVCenter | Qt : : TextDontClip | Qt : : TextIncludeTrailingSpaces ,
QStringLiteral ( " MODE: " ) ,
& textBounds
) ;
topRect . adjust ( textBounds . width ( ) , 0 , 0 , 0 ) ;
_painter - > setPen ( profiler_gui : : TEXT_COLOR ) ;
_painter - > drawText (
topRect ,
Qt : : AlignLeft | Qt : : AlignVCenter | Qt : : TextDontClip ,
bindMode ? " Zoom " : " Overview " ,
& textBounds
) ;
topRect . adjust ( textBounds . width ( ) + 3 , 0 , 0 , 0 ) ;
}
2018-01-20 15:23:28 +03:00
2019-10-26 01:04:21 +03:00
// TOP & BOTTOM duration
2019-10-20 16:12:37 +03:00
if ( ! isEmpty ( ) & & ! m_topDurationStr . isEmpty ( ) )
2016-12-04 16:51:27 +03:00
{
if ( m_timeUnits ! = EASY_GLOBALS . time_units )
{
m_timeUnits = EASY_GLOBALS . time_units ;
2018-01-20 15:23:28 +03:00
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_topValue , 3 ) ;
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue , 3 ) ;
2019-10-26 01:04:21 +03:00
if ( m_avgDuration ! = 0 | | m_medianDuration ! = 0 )
{
m_medianDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_medianDuration , 3 ) ;
m_avgDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_avgDuration , 3 ) ;
}
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
//auto fm = _painter->fontMetrics();
//top_width -= fm.width(m_topDurationStr) + 7;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
_painter - > setPen ( m_topValue < m_maxValue ? QColor ( Qt : : darkRed ) : profiler_gui : : TEXT_COLOR ) ;
2019-10-26 01:04:21 +03:00
_painter - > drawText ( topRect , Qt : : AlignRight | Qt : : AlignVCenter | Qt : : TextDontClip , m_topDurationStr , & textBounds ) ;
topRect . adjust ( 0 , 0 , - textBounds . width ( ) - 3 , 0 ) ;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
_painter - > setPen ( m_bottomValue > m_minValue ? QColor ( Qt : : darkRed ) : profiler_gui : : TEXT_COLOR ) ;
2019-10-26 01:04:21 +03:00
_painter - > drawText ( bottomRect , Qt : : AlignRight | Qt : : AlignVCenter | Qt : : TextDontClip , m_bottomDurationStr , & textBounds ) ;
bottomRect . adjust ( 0 , 0 , - textBounds . width ( ) - 3 , 0 ) ;
2016-12-04 16:51:27 +03:00
}
_painter - > setPen ( Qt : : darkGray ) ;
_painter - > drawLine ( QLineF ( 0 , bottom , bottom_width , bottom ) ) ;
_painter - > drawLine ( QLineF ( 0 , m_boundingRect . top ( ) , top_width , m_boundingRect . top ( ) ) ) ;
2018-01-20 15:23:28 +03:00
paintMouseIndicator ( _painter , m_boundingRect . top ( ) , bottom , width , m_boundingRect . height ( ) , top_width , m_mousePos . y ( ) , dtime , font_h ) ;
2017-03-07 00:29:34 +03:00
2019-10-26 01:04:21 +03:00
// MEDIAN & EXPECTED FRAME TIME
if ( ! isEmpty ( ) )
2016-12-04 16:51:27 +03:00
{
2019-10-26 01:04:21 +03:00
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_bottomValue ) * coeff ;
_painter - > setPen ( Qt : : DashLine ) ;
auto w = width ;
const auto boundary = widget - > margin ( ) - font_h ;
if ( h < ( m_boundingRect . top ( ) - boundary ) )
w = top_width ;
else if ( h > ( bottom + boundary ) )
w = bottom_width ;
_painter - > drawLine ( QLineF ( 0 , h , w , h ) ) ;
}
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
_painter - > setPen ( profiler_gui : : TEXT_COLOR ) ;
2019-11-28 10:34:53 +03:00
const auto eventsCount = static_cast < uint64_t > ( m_pProfilerThread - > events . size ( ) ) ;
2019-10-26 01:04:21 +03:00
const auto blocksCount = m_pProfilerThread - > blocks_number - eventsCount ;
QString durationsStr ;
if ( ! m_medianDurationStr . isEmpty ( ) | | ! m_avgDurationStr . isEmpty ( ) )
{
durationsStr = QString ( " avg: %1 | mdn: %2 | " ) . arg ( m_avgDurationStr ) . arg ( m_medianDurationStr ) ;
}
_painter - > drawText ( topRect , Qt : : AlignCenter , m_threadName ) ;
_painter - > drawText ( bottomRect , Qt : : AlignCenter ,
QStringLiteral ( " time: %1 | profiled: %2 (%3%) | wait: %4 (%5%) | %6%7 frames | %8 blocks | %9 markers " )
. 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 ( m_threadDuration ? QString : : number ( 100. * ( double ) m_threadWaitTime / ( double ) m_threadDuration , ' f ' , 2 )
: QString ( " 0 " ) )
. arg ( durationsStr )
. arg ( profiler_gui : : shortenCountString ( m_pProfilerThread - > frames_number ) )
. arg ( profiler_gui : : shortenCountString ( blocksCount ) )
. arg ( profiler_gui : : shortenCountString ( eventsCount ) )
) ;
2016-12-04 16:51:27 +03:00
_painter - > restore ( ) ;
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : paintById ( QPainter * _painter )
2016-12-04 16:51:27 +03:00
{
2018-05-07 21:58:37 +03:00
const auto widget = static_cast < const GraphicsScrollbar * > ( scene ( ) - > parent ( ) ) ;
2016-12-04 16:51:27 +03:00
const bool bindMode = widget - > bindMode ( ) ;
const auto currentScale = widget - > getWindowScale ( ) ;
const auto bottom = m_boundingRect . bottom ( ) ;
const auto width = m_boundingRect . width ( ) * currentScale ;
2018-01-20 15:23:28 +03:00
const auto dtime = m_topValue - m_bottomValue ;
const auto coeff = m_boundingRect . height ( ) / ( dtime > 1e-3 ? dtime : 1. ) ;
2016-12-04 16:51:27 +03:00
QBrush brush ( Qt : : SolidPattern ) ;
2017-04-10 22:04:09 +03:00
//QRgb previousColor = 0;
2016-08-03 00:06:36 +03:00
_painter - > save ( ) ;
_painter - > setTransform ( QTransform : : fromScale ( 1.0 / currentScale , 1 ) , true ) ;
2017-03-13 00:43:15 +03:00
const auto & items = m_selectedBlocks ;
if ( ! items . empty ( ) )
2016-08-03 00:06:36 +03:00
{
2017-03-13 00:43:15 +03:00
if ( ! bindMode )
2018-01-20 15:23:28 +03:00
paintImage ( _painter ) ;
2017-03-13 00:43:15 +03:00
else
2018-01-20 15:23:28 +03:00
paintImage ( _painter , currentScale , widget - > minimum ( ) , widget - > maximum ( ) , widget - > value ( ) , widget - > sliderWidth ( ) ) ;
2016-08-03 00:06:36 +03:00
}
2016-12-01 21:55:11 +03:00
qreal top_width = width , bottom_width = width ;
2018-01-20 15:23:28 +03:00
const auto font_h = widget - > fontHeight ( ) ;
2019-10-26 01:04:21 +03:00
QRectF bottomRect ( 3 , bottom + 2 , width - 3 , font_h ) ;
QRectF topRect ( 3 , m_boundingRect . top ( ) - widget - > margin ( ) , width - 3 , font_h ) ;
QRectF textBounds ;
// MODE
{
_painter - > setPen ( Qt : : blue ) ;
_painter - > drawText (
topRect ,
Qt : : AlignLeft | Qt : : AlignVCenter | Qt : : TextDontClip | Qt : : TextIncludeTrailingSpaces ,
QStringLiteral ( " MODE: " ) ,
& textBounds
) ;
topRect . adjust ( textBounds . width ( ) , 0 , 0 , 0 ) ;
_painter - > setPen ( profiler_gui : : TEXT_COLOR ) ;
_painter - > drawText (
topRect ,
Qt : : AlignLeft | Qt : : AlignVCenter | Qt : : TextDontClip ,
bindMode ? " Zoom " : " Overview " ,
& textBounds
) ;
topRect . adjust ( textBounds . width ( ) + 3 , 0 , 0 , 0 ) ;
}
2018-01-20 15:23:28 +03:00
2019-10-26 01:04:21 +03:00
// TOP & BOTTOM duration
2019-10-20 16:12:37 +03:00
if ( ! isEmpty ( ) & & ! m_topDurationStr . isEmpty ( ) )
2016-12-01 21:55:11 +03:00
{
if ( m_timeUnits ! = EASY_GLOBALS . time_units )
{
m_timeUnits = EASY_GLOBALS . time_units ;
2018-01-20 15:23:28 +03:00
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_topValue , 3 ) ;
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue , 3 ) ;
2019-10-26 01:04:21 +03:00
if ( m_avgDuration ! = 0 | | m_medianDuration ! = 0 )
{
m_medianDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_medianDuration , 3 ) ;
m_avgDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_avgDuration , 3 ) ;
}
2016-12-01 21:55:11 +03:00
}
2018-01-20 15:23:28 +03:00
//auto fm = _painter->fontMetrics();
//top_width -= fm.width(m_topDurationStr) + 7;
2016-12-01 21:55:11 +03:00
2018-01-20 15:23:28 +03:00
_painter - > setPen ( m_topValue < m_maxValue ? QColor ( Qt : : darkRed ) : profiler_gui : : TEXT_COLOR ) ;
2019-10-26 01:04:21 +03:00
_painter - > drawText ( topRect , Qt : : AlignRight | Qt : : AlignVCenter | Qt : : TextDontClip , m_topDurationStr , & textBounds ) ;
topRect . adjust ( 0 , 0 , - textBounds . width ( ) - 3 , 0 ) ;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
_painter - > setPen ( m_bottomValue > m_minValue ? QColor ( Qt : : darkRed ) : profiler_gui : : TEXT_COLOR ) ;
2019-10-26 01:04:21 +03:00
_painter - > drawText ( bottomRect , Qt : : AlignRight | Qt : : AlignVCenter | Qt : : TextDontClip , m_bottomDurationStr , & textBounds ) ;
bottomRect . adjust ( 0 , 0 , - textBounds . width ( ) - 3 , 0 ) ;
2016-12-01 21:55:11 +03:00
}
2016-08-23 22:44:59 +03:00
_painter - > setPen ( Qt : : darkGray ) ;
2016-12-01 21:55:11 +03:00
_painter - > drawLine ( QLineF ( 0 , bottom , bottom_width , bottom ) ) ;
_painter - > drawLine ( QLineF ( 0 , m_boundingRect . top ( ) , top_width , m_boundingRect . top ( ) ) ) ;
2016-11-13 22:02:47 +03:00
2018-01-20 15:23:28 +03:00
paintMouseIndicator ( _painter , m_boundingRect . top ( ) , bottom , width , m_boundingRect . height ( ) , top_width , m_mousePos . y ( ) , dtime , font_h ) ;
2017-03-07 00:29:34 +03:00
2019-10-26 01:04:21 +03:00
// MEDIAN & EXPECTED FRAME TIME
if ( ! isEmpty ( ) )
2016-11-13 22:02:47 +03:00
{
2019-10-26 01:04:21 +03:00
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_bottomValue ) * coeff ;
_painter - > setPen ( Qt : : DashLine ) ;
auto w = width ;
const auto boundary = widget - > margin ( ) - font_h ;
if ( h < ( m_boundingRect . top ( ) - boundary ) )
w = top_width ;
else if ( h > ( bottom + boundary ) )
w = bottom_width ;
_painter - > drawLine ( QLineF ( 0 , h , w , h ) ) ;
}
2016-11-13 22:02:47 +03:00
}
2016-08-23 22:44:59 +03:00
2019-10-26 01:04:21 +03:00
const auto colorIndicatorSize = font_h * 2 / 3 ;
_painter - > setBrush ( QColor : : fromRgba ( easyDescriptor ( m_blockId ) . color ( ) ) ) ;
_painter - > setPen ( profiler_gui : : BLOCK_BORDER_COLOR ) ;
_painter - > drawRect ( QRectF ( bottomRect . left ( ) , bottomRect . top ( ) + 1 + font_h / 6. , colorIndicatorSize , colorIndicatorSize ) ) ;
bottomRect . adjust ( colorIndicatorSize + 3 , 0 , 0 , 0 ) ;
2018-01-20 15:23:28 +03:00
_painter - > setPen ( profiler_gui : : TEXT_COLOR ) ;
2019-10-26 01:04:21 +03:00
_painter - > drawText ( topRect , Qt : : AlignCenter , m_threadName ) ;
_painter - > drawText ( bottomRect , Qt : : AlignLeft , m_blockName , & textBounds ) ;
bottomRect . adjust ( textBounds . width ( ) + 3 , 0 , 0 , 0 ) ;
_painter - > drawText ( bottomRect , Qt : : AlignLeft , QStringLiteral ( " | %1 " ) . arg ( m_blockType ) , & textBounds ) ;
bottomRect . adjust ( textBounds . width ( ) + 3 , 0 , 0 , 0 ) ;
2016-12-04 16:51:27 +03:00
2019-10-20 16:12:37 +03:00
if ( ! items . empty ( ) )
2016-12-04 16:51:27 +03:00
{
2019-10-26 01:04:21 +03:00
QString durationsStr ;
if ( ! m_medianDurationStr . isEmpty ( ) | | ! m_avgDurationStr . isEmpty ( ) )
{
durationsStr = QString ( " avg: %1 | mdn: %2 | " ) . arg ( m_avgDurationStr ) . arg ( m_medianDurationStr ) ;
}
2018-01-20 15:23:28 +03:00
if ( m_threadProfiledTime ! = 0 )
{
2019-10-26 01:04:21 +03:00
_painter - > drawText ( bottomRect , Qt : : AlignCenter ,
QStringLiteral ( " %1 calls | %2%3% of thread profiled time " )
. arg ( items . size ( ) )
. arg ( durationsStr )
. arg ( QString : : number ( 100. * ( double ) m_blockTotalDuraion / ( double ) m_threadProfiledTime , ' f ' , 2 ) )
) ;
2018-01-20 15:23:28 +03:00
}
else
{
2019-10-26 01:04:21 +03:00
_painter - > drawText ( bottomRect , Qt : : AlignCenter ,
QStringLiteral ( " %1 calls | %2100% of thread profiled time " )
. arg ( items . size ( ) )
. arg ( durationsStr )
) ;
2018-01-20 15:23:28 +03:00
}
2016-12-04 16:51:27 +03:00
}
else
{
2019-10-26 01:04:21 +03:00
_painter - > drawText ( bottomRect , Qt : : AlignCenter , QStringLiteral ( " 0 calls " ) ) ;
2016-12-04 16:51:27 +03:00
}
2016-08-03 00:06:36 +03:00
_painter - > restore ( ) ;
}
2018-01-20 15:23:28 +03:00
profiler : : thread_id_t GraphicsHistogramItem : : threadId ( ) const
2016-08-03 23:00:04 +03:00
{
return m_threadId ;
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : rebuildSource ( HistRegime _regime )
2017-06-07 02:08:53 +03:00
{
if ( m_regime = = _regime )
rebuildSource ( ) ;
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : rebuildSource ( )
2017-06-07 02:08:53 +03:00
{
if ( m_regime = = Hist_Id )
{
m_regime = Hist_Pointer ;
setSource ( m_threadId , m_blockId ) ;
}
else
{
m_regime = Hist_Id ;
setSource ( m_threadId , m_pSource ) ;
}
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : setSource ( profiler : : thread_id_t _thread_id , const profiler_gui : : EasyItems * _items )
2016-08-03 00:06:36 +03:00
{
2017-03-07 19:59:57 +03:00
if ( m_regime = = Hist_Pointer & & m_threadId = = _thread_id & & m_pSource = = _items )
2016-12-04 16:51:27 +03:00
return ;
2018-01-20 15:23:28 +03:00
cancelAnyJob ( ) ;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
m_boundaryTimer . stop ( ) ;
2016-12-04 16:51:27 +03:00
2017-06-05 21:26:10 +03:00
m_blockName . clear ( ) ;
2019-10-26 01:04:21 +03:00
m_blockType = QStringLiteral ( " Thread " ) ;
2017-06-05 21:26:10 +03:00
m_blockTotalDuraion = 0 ;
2019-10-26 01:04:21 +03:00
m_medianDuration = 0 ;
m_avgDuration = 0 ;
m_medianDurationFull = 0 ;
m_avgDurationFull = 0 ;
2017-06-05 21:26:10 +03:00
2017-03-13 00:43:15 +03:00
m_imageOriginUpdate = m_imageOrigin = 0 ;
m_imageScaleUpdate = m_imageScale = 1 ;
2016-12-04 16:51:27 +03:00
m_selectedBlocks . clear ( ) ;
2019-10-20 16:12:37 +03:00
setEmpty ( true ) ;
2018-01-20 15:23:28 +03:00
{ profiler : : BlocksTree : : children_t ( ) . swap ( m_selectedBlocks ) ; }
2016-12-04 16:51:27 +03:00
2019-10-26 01:04:21 +03:00
m_topDurationStr . clear ( ) ;
m_bottomDurationStr . clear ( ) ;
m_medianDurationStr . clear ( ) ;
m_avgDurationStr . clear ( ) ;
2018-01-20 15:23:28 +03:00
setImageUpdatePermitted ( false ) ;
2017-03-07 19:59:57 +03:00
m_regime = Hist_Pointer ;
2016-08-03 00:06:36 +03:00
m_pSource = _items ;
2016-08-03 23:00:04 +03:00
m_threadId = _thread_id ;
2018-01-20 15:23:28 +03:00
profiler_gui : : set_max ( m_blockId ) ;
2016-08-03 00:06:36 +03:00
2016-12-01 21:55:11 +03:00
if ( m_pSource ! = nullptr )
2016-08-03 00:06:36 +03:00
{
if ( m_pSource - > empty ( ) )
{
m_pSource = nullptr ;
}
2016-08-08 22:17:56 +03:00
else
2016-08-03 00:06:36 +03:00
{
2017-03-13 00:43:15 +03:00
const auto & root = EASY_GLOBALS . profiler_blocks [ _thread_id ] ;
2018-01-20 15:23:28 +03:00
m_threadName = profiler_gui : : decoratedThreadName ( EASY_GLOBALS . use_decorated_thread_name , root , EASY_GLOBALS . hex_thread_id ) ;
2017-03-13 00:43:15 +03:00
if ( root . children . empty ( ) )
m_threadDuration = 0 ;
else
m_threadDuration = easyBlock ( root . children . back ( ) ) . tree . node - > end ( ) - easyBlock ( root . children . front ( ) ) . tree . node - > begin ( ) ;
m_threadProfiledTime = root . profiled_time ;
m_threadWaitTime = root . wait_time ;
m_pProfilerThread = & root ;
m_timeUnits = EASY_GLOBALS . time_units ;
2018-01-20 15:23:28 +03:00
setReady ( false ) ;
2018-01-28 20:52:17 +03:00
auto source = m_pSource ;
m_worker . enqueue ( [ this , source ]
2016-08-03 00:06:36 +03:00
{
2018-01-20 15:23:28 +03:00
m_maxValue = 0 ;
m_minValue = 1e30 ;
2017-03-13 00:43:15 +03:00
2019-10-26 01:04:21 +03:00
size_t totalCount = 0 ;
profiler_gui : : DurationsCountMap durations ;
2017-03-13 20:30:57 +03:00
bool empty = true ;
2018-01-28 20:52:17 +03:00
for ( const auto & item : * source )
2017-03-13 00:43:15 +03:00
{
2018-01-20 15:23:28 +03:00
if ( isReady ( ) )
2017-03-13 00:43:15 +03:00
return ;
2019-10-26 01:04:21 +03:00
auto & block = easyBlock ( item . block ) . tree ;
auto & desc = easyDescriptor ( block . node - > id ( ) ) ;
if ( desc . type ( ) ! = profiler : : BlockType : : Block )
2017-03-13 00:43:15 +03:00
continue ;
2019-10-26 01:04:21 +03:00
const auto duration = block . node - > duration ( ) ;
+ + totalCount ;
+ + durations [ duration ] . count ;
m_avgDuration + = duration ;
2017-03-13 00:43:15 +03:00
const auto w = item . width ( ) ;
2018-01-20 15:23:28 +03:00
if ( w > m_maxValue )
m_maxValue = w ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
if ( w < m_minValue )
m_minValue = w ;
2017-03-13 00:43:15 +03:00
2017-03-13 20:30:57 +03:00
empty = false ;
}
2017-03-13 00:43:15 +03:00
2019-10-26 01:04:21 +03:00
if ( ! empty )
{
m_avgDuration / = totalCount ;
m_medianDuration = profiler_gui : : calculateMedian ( durations ) ;
}
2018-01-20 15:23:28 +03:00
if ( ( m_maxValue - m_minValue ) < 1e-3 )
2017-03-13 20:30:57 +03:00
{
2018-01-20 15:23:28 +03:00
if ( m_minValue > 0.1 )
2017-03-13 20:30:57 +03:00
{
2018-01-20 15:23:28 +03:00
m_minValue - = 0.1 ;
2017-03-13 00:43:15 +03:00
}
2017-03-13 20:30:57 +03:00
else
2017-03-13 00:43:15 +03:00
{
2018-01-20 15:23:28 +03:00
m_maxValue = 0.1 ;
m_minValue = 0 ;
2017-03-13 00:43:15 +03:00
}
}
2018-01-20 15:23:28 +03:00
m_topValue = m_maxValue ;
m_bottomValue = m_minValue ;
2017-03-13 00:43:15 +03:00
2017-03-13 20:30:57 +03:00
if ( ! empty )
{
2018-01-20 15:23:28 +03:00
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_topValue , 3 ) ;
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue , 3 ) ;
2017-03-13 20:30:57 +03:00
}
2019-10-26 01:04:21 +03:00
if ( m_avgDuration ! = 0 | | m_medianDuration ! = 0 )
2017-03-13 20:30:57 +03:00
{
2019-10-26 01:04:21 +03:00
m_avgDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_avgDuration , 3 ) ;
m_medianDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_medianDuration , 3 ) ;
m_medianDurationFull = m_medianDuration ;
m_avgDurationFull = m_avgDuration ;
2017-03-13 20:30:57 +03:00
}
2017-03-13 00:43:15 +03:00
2019-10-20 16:12:37 +03:00
setEmpty ( empty ) ;
2018-01-20 15:23:28 +03:00
setReady ( true ) ;
2017-03-13 00:43:15 +03:00
2018-01-28 20:52:17 +03:00
} , m_bReady ) ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
startTimer ( ) ;
2017-03-13 00:43:15 +03:00
show ( ) ;
2016-08-03 00:06:36 +03:00
}
}
if ( m_pSource = = nullptr )
{
2016-12-12 22:28:54 +03:00
m_pProfilerThread = nullptr ;
2016-12-04 16:51:27 +03:00
m_threadName . clear ( ) ;
2016-08-03 00:06:36 +03:00
hide ( ) ;
}
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : setSource ( profiler : : thread_id_t _thread_id , profiler : : block_id_t _block_id )
2016-12-04 16:51:27 +03:00
{
2017-03-07 19:59:57 +03:00
if ( m_regime = = Hist_Id & & m_threadId = = _thread_id & & m_blockId = = _block_id )
2016-12-04 16:51:27 +03:00
return ;
2018-01-20 15:23:28 +03:00
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()
2017-03-07 19:59:57 +03:00
m_regime = Hist_Id ;
2016-12-04 16:51:27 +03:00
2017-03-13 00:43:15 +03:00
m_boundaryTimer . stop ( ) ;
2016-12-04 16:51:27 +03:00
2017-06-05 21:26:10 +03:00
m_pSource = nullptr ;
m_topDurationStr . clear ( ) ;
m_bottomDurationStr . clear ( ) ;
2019-10-26 01:04:21 +03:00
m_medianDurationStr . clear ( ) ;
m_avgDurationStr . clear ( ) ;
2017-06-05 21:26:10 +03:00
m_blockName . clear ( ) ;
2019-10-26 01:04:21 +03:00
m_blockType . clear ( ) ;
2017-06-05 21:26:10 +03:00
m_blockTotalDuraion = 0 ;
2019-10-26 01:04:21 +03:00
m_medianDuration = 0 ;
m_avgDuration = 0 ;
m_medianDurationFull = 0 ;
m_avgDurationFull = 0 ;
2017-06-05 21:26:10 +03:00
2017-03-13 00:43:15 +03:00
m_imageOriginUpdate = m_imageOrigin = 0 ;
m_imageScaleUpdate = m_imageScale = 1 ;
2016-12-04 16:51:27 +03:00
m_selectedBlocks . clear ( ) ;
2019-10-20 16:12:37 +03:00
setEmpty ( true ) ;
2018-01-20 15:23:28 +03:00
{ profiler : : BlocksTree : : children_t ( ) . swap ( m_selectedBlocks ) ; }
2016-12-04 16:51:27 +03:00
m_threadId = _thread_id ;
m_blockId = _block_id ;
2018-01-20 15:23:28 +03:00
if ( m_threadId ! = 0 & & ! profiler_gui : : is_max ( m_blockId ) )
2016-12-04 16:51:27 +03:00
{
2019-10-26 01:04:21 +03:00
auto & desc = easyDescriptor ( m_blockId ) ;
m_blockName = profiler_gui : : toUnicode ( desc . name ( ) ) ;
switch ( desc . type ( ) )
{
case profiler : : BlockType : : Block :
{
m_blockType = QStringLiteral ( " Block " ) ;
break ;
}
case profiler : : BlockType : : Event :
{
m_blockType = QStringLiteral ( " Event " ) ;
break ;
}
case profiler : : BlockType : : Value :
{
m_blockType = QStringLiteral ( " Value " ) ;
break ;
}
default : break ;
}
2017-06-05 21:26:10 +03:00
2016-12-04 16:51:27 +03:00
const auto & root = EASY_GLOBALS . profiler_blocks [ _thread_id ] ;
2018-01-20 15:23:28 +03:00
m_threadName = profiler_gui : : decoratedThreadName ( EASY_GLOBALS . use_decorated_thread_name , root , EASY_GLOBALS . hex_thread_id ) ;
2017-06-05 21:26:10 +03:00
m_pProfilerThread = & root ;
m_timeUnits = EASY_GLOBALS . time_units ;
2016-12-04 16:51:27 +03:00
if ( root . children . empty ( ) )
2017-06-05 21:26:10 +03:00
{
2016-12-04 16:51:27 +03:00
m_threadDuration = 0 ;
2017-06-05 21:26:10 +03:00
m_threadProfiledTime = 0 ;
m_threadWaitTime = 0 ;
2018-01-20 15:23:28 +03:00
m_topValue = m_maxValue = 0 ;
m_bottomValue = m_minValue = 1e30 ;
2017-06-05 21:26:10 +03:00
2018-01-20 15:23:28 +03:00
setImageUpdatePermitted ( true ) ;
setReady ( true ) ;
2017-06-05 21:26:10 +03:00
}
2016-12-04 16:51:27 +03:00
else
2017-06-05 21:26:10 +03:00
{
2016-12-04 16:51:27 +03:00
m_threadDuration = easyBlock ( root . children . back ( ) ) . tree . node - > end ( ) - easyBlock ( root . children . front ( ) ) . tree . node - > begin ( ) ;
2017-06-05 21:26:10 +03:00
m_threadProfiledTime = root . profiled_time ;
m_threadWaitTime = root . wait_time ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
setReady ( false ) ;
2018-01-28 20:52:17 +03:00
const auto selected_thread = std : : ref ( root ) ;
const auto selected_block = EASY_GLOBALS . selected_block ;
const bool showOnlyTopLevelBlocks = EASY_GLOBALS . display_only_frames_on_histogram ;
m_worker . enqueue ( [ this , selected_thread , selected_block , showOnlyTopLevelBlocks ]
2017-06-05 21:26:10 +03:00
{
2018-01-20 15:23:28 +03:00
using Stack = std : : vector < std : : pair < profiler : : block_index_t , profiler : : block_index_t > > ;
2017-03-13 00:43:15 +03:00
2018-01-28 20:52:17 +03:00
const auto & profiler_thread = selected_thread . get ( ) ;
2018-01-20 15:23:28 +03:00
m_maxValue = 0 ;
m_minValue = 1e30 ;
2017-06-05 21:26:10 +03:00
//const auto& profiler_thread = EASY_GLOBALS.profiler_blocks[m_threadId];
Stack stack ;
stack . reserve ( profiler_thread . depth ) ;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
const bool has_selected_block = ! profiler_gui : : is_max ( selected_block ) ;
2016-12-04 16:51:27 +03:00
2019-10-26 01:04:21 +03:00
size_t totalCount = 0 ;
profiler_gui : : DurationsCountMap durations ;
2017-06-05 21:26:10 +03:00
for ( auto frame : profiler_thread . children )
2016-12-04 16:51:27 +03:00
{
2017-06-05 21:26:10 +03:00
const auto & frame_block = easyBlock ( frame ) . tree ;
if ( frame_block . node - > id ( ) = = m_blockId | | ( ! has_selected_block & & m_blockId = = easyDescriptor ( frame_block . node - > id ( ) ) . id ( ) ) )
{
m_selectedBlocks . push_back ( frame ) ;
2016-12-04 16:51:27 +03:00
2017-06-05 21:26:10 +03:00
const auto w = frame_block . node - > duration ( ) ;
2018-01-20 15:23:28 +03:00
if ( w > m_maxValue )
m_maxValue = w ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
if ( w < m_minValue )
m_minValue = w ;
2016-12-04 16:51:27 +03:00
2019-10-26 01:04:21 +03:00
+ + totalCount ;
+ + durations [ w ] . count ;
2017-06-05 21:26:10 +03:00
m_blockTotalDuraion + = w ;
}
2017-03-13 00:43:15 +03:00
2018-01-28 20:52:17 +03:00
if ( showOnlyTopLevelBlocks )
2017-06-07 02:08:53 +03:00
continue ;
2018-01-20 15:23:28 +03:00
stack . emplace_back ( frame , 0U ) ;
2017-06-05 21:26:10 +03:00
while ( ! stack . empty ( ) )
2016-12-04 16:51:27 +03:00
{
2018-01-20 15:23:28 +03:00
if ( isReady ( ) )
2017-03-13 00:43:15 +03:00
return ;
2017-06-05 21:26:10 +03:00
auto & top = stack . back ( ) ;
const auto & top_children = easyBlock ( top . first ) . tree . children ;
const auto stack_size = stack . size ( ) ;
for ( auto end = top_children . size ( ) ; top . second < end ; + + top . second )
2016-12-04 16:51:27 +03:00
{
2018-01-20 15:23:28 +03:00
if ( isReady ( ) )
2017-06-05 21:26:10 +03:00
return ;
const auto child_index = top_children [ top . second ] ;
const auto & child = easyBlock ( child_index ) . tree ;
if ( child . node - > id ( ) = = m_blockId | | ( ! has_selected_block & & m_blockId = = easyDescriptor ( child . node - > id ( ) ) . id ( ) ) )
{
m_selectedBlocks . push_back ( child_index ) ;
const auto w = child . node - > duration ( ) ;
2018-01-20 15:23:28 +03:00
if ( w > m_maxValue )
m_maxValue = w ;
2017-06-05 21:26:10 +03:00
2018-01-20 15:23:28 +03:00
if ( w < m_minValue )
m_minValue = w ;
2017-06-05 21:26:10 +03:00
2019-10-26 01:04:21 +03:00
+ + totalCount ;
+ + durations [ w ] . count ;
2017-06-05 21:26:10 +03:00
m_blockTotalDuraion + = w ;
}
2016-12-04 16:51:27 +03:00
2017-06-05 21:26:10 +03:00
if ( ! child . children . empty ( ) )
{
+ + top . second ;
2018-01-20 15:23:28 +03:00
stack . emplace_back ( child_index , 0U ) ;
2017-06-05 21:26:10 +03:00
break ;
}
2016-12-04 16:51:27 +03:00
}
2017-06-05 21:26:10 +03:00
if ( stack_size = = stack . size ( ) )
2016-12-04 16:51:27 +03:00
{
2017-06-05 21:26:10 +03:00
stack . pop_back ( ) ;
2016-12-04 16:51:27 +03:00
}
}
}
2017-03-13 20:30:57 +03:00
2017-06-05 21:26:10 +03:00
if ( m_selectedBlocks . empty ( ) )
2017-03-13 20:30:57 +03:00
{
2017-06-05 21:26:10 +03:00
m_topDurationStr . clear ( ) ;
m_bottomDurationStr . clear ( ) ;
2019-10-26 01:04:21 +03:00
m_medianDurationStr . clear ( ) ;
m_avgDurationStr . clear ( ) ;
2017-06-05 21:26:10 +03:00
}
else
{
if ( has_selected_block )
2017-03-13 20:30:57 +03:00
{
2017-06-05 21:26:10 +03:00
const auto & item = easyBlock ( selected_block ) . tree ;
if ( * item . node - > name ( ) ! = 0 )
2018-01-20 15:23:28 +03:00
m_blockName = profiler_gui : : toUnicode ( item . node - > name ( ) ) ;
2017-03-13 20:30:57 +03:00
}
2017-06-05 21:26:10 +03:00
2018-01-20 15:23:28 +03:00
m_maxValue * = 1e-3 ;
m_minValue * = 1e-3 ;
2017-06-05 21:26:10 +03:00
2018-01-20 15:23:28 +03:00
if ( ( m_maxValue - m_minValue ) < 1e-3 )
2017-03-13 20:30:57 +03:00
{
2018-01-20 15:23:28 +03:00
if ( m_minValue > 0.1 )
2017-06-05 21:26:10 +03:00
{
2018-01-20 15:23:28 +03:00
m_minValue - = 0.1 ;
2017-06-05 21:26:10 +03:00
}
else
{
2018-01-20 15:23:28 +03:00
m_maxValue = 0.1 ;
m_minValue = 0 ;
2017-06-05 21:26:10 +03:00
}
2017-03-13 20:30:57 +03:00
}
2017-06-05 21:26:10 +03:00
2019-10-29 23:48:17 +03:00
if ( totalCount ! = 0 )
{
m_avgDuration = m_blockTotalDuraion / totalCount ;
m_medianDuration = profiler_gui : : calculateMedian ( durations ) ;
}
2019-10-26 01:04:21 +03:00
m_medianDurationFull = m_medianDuration ;
m_avgDurationFull = m_avgDuration ;
2018-01-20 15:23:28 +03:00
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_maxValue , 3 ) ;
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_minValue , 3 ) ;
2019-10-26 01:04:21 +03:00
if ( m_avgDuration ! = 0 | | m_medianDuration ! = 0 )
{
m_avgDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_avgDuration , 3 ) ;
m_medianDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_medianDuration , 3 ) ;
}
2017-03-13 20:30:57 +03:00
}
2018-01-20 15:23:28 +03:00
m_topValue = m_maxValue ;
m_bottomValue = m_minValue ;
2016-12-04 16:51:27 +03:00
2019-10-20 16:12:37 +03:00
setEmpty ( m_selectedBlocks . empty ( ) ) ;
2018-01-20 15:23:28 +03:00
setReady ( true ) ;
2017-03-07 00:29:34 +03:00
2018-01-28 20:52:17 +03:00
} , m_bReady ) ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
startTimer ( ) ;
2017-06-05 21:26:10 +03:00
}
2016-12-04 16:51:27 +03:00
2017-03-13 00:43:15 +03:00
show ( ) ;
2016-12-04 16:51:27 +03:00
}
else
{
2016-12-12 22:28:54 +03:00
m_pProfilerThread = nullptr ;
2016-12-04 16:51:27 +03:00
m_threadName . clear ( ) ;
hide ( ) ;
}
}
//////////////////////////////////////////////////////////////////////////
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : validateName ( )
2016-12-14 21:47:33 +03:00
{
if ( m_threadName . isEmpty ( ) )
return ;
2018-01-20 15:23:28 +03:00
m_threadName = profiler_gui : : decoratedThreadName ( EASY_GLOBALS . use_decorated_thread_name , EASY_GLOBALS . profiler_blocks [ m_threadId ] , EASY_GLOBALS . hex_thread_id ) ;
2016-12-14 21:47:33 +03:00
}
//////////////////////////////////////////////////////////////////////////
2018-01-20 15:23:28 +03:00
bool GraphicsHistogramItem : : pickTopValue ( )
2016-12-04 16:51:27 +03:00
{
2018-01-20 15:23:28 +03:00
const bool result = Parent : : pickTopValue ( ) ;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
if ( result & & ! m_topDurationStr . isEmpty ( ) )
2016-12-04 16:51:27 +03:00
{
2018-01-20 15:23:28 +03:00
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_topValue , 3 ) ;
scene ( ) - > update ( ) ; // to update top-boundary text right now
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
return result ;
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
bool GraphicsHistogramItem : : increaseTopValue ( )
2017-03-07 00:29:34 +03:00
{
2018-01-20 15:23:28 +03:00
const bool result = Parent : : increaseTopValue ( ) ;
if ( result & & ! m_topDurationStr . isEmpty ( ) )
2017-03-13 00:43:15 +03:00
{
2018-01-20 15:23:28 +03:00
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_topValue , 3 ) ;
2017-03-13 00:43:15 +03:00
scene ( ) - > update ( ) ; // to update top-boundary text right now
}
2018-01-20 15:23:28 +03:00
return result ;
2017-03-13 00:43:15 +03:00
}
2017-03-07 00:29:34 +03:00
2018-01-20 15:23:28 +03:00
bool GraphicsHistogramItem : : decreaseTopValue ( )
2017-03-13 00:43:15 +03:00
{
2018-01-20 15:23:28 +03:00
const bool result = Parent : : decreaseTopValue ( ) ;
2017-03-07 00:29:34 +03:00
2018-01-20 15:23:28 +03:00
if ( result & & ! m_topDurationStr . isEmpty ( ) )
{
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_topValue , 3 ) ;
2017-03-07 00:29:34 +03:00
scene ( ) - > update ( ) ; // to update top-boundary text right now
}
2018-01-20 15:23:28 +03:00
return result ;
2017-03-07 00:29:34 +03:00
}
//////////////////////////////////////////////////////////////////////////
2018-01-20 15:23:28 +03:00
bool GraphicsHistogramItem : : pickBottomValue ( )
2017-03-07 00:29:34 +03:00
{
2018-01-20 15:23:28 +03:00
const bool result = Parent : : pickBottomValue ( ) ;
if ( result & & ! m_bottomDurationStr . isEmpty ( ) )
2017-03-13 00:43:15 +03:00
{
2018-01-20 15:23:28 +03:00
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue , 3 ) ;
2017-03-13 00:43:15 +03:00
scene ( ) - > update ( ) ; // to update top-boundary text right now
}
2018-01-20 15:23:28 +03:00
return result ;
2017-03-13 00:43:15 +03:00
}
2017-03-07 00:29:34 +03:00
2018-01-20 15:23:28 +03:00
bool GraphicsHistogramItem : : increaseBottomValue ( )
2017-03-13 00:43:15 +03:00
{
2018-01-20 15:23:28 +03:00
const bool result = Parent : : increaseBottomValue ( ) ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
if ( result & & ! m_bottomDurationStr . isEmpty ( ) )
{
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue , 3 ) ;
scene ( ) - > update ( ) ; // to update top-boundary text right now
2017-03-07 00:29:34 +03:00
}
2018-01-20 15:23:28 +03:00
return result ;
2017-03-07 00:29:34 +03:00
}
2018-01-20 15:23:28 +03:00
bool GraphicsHistogramItem : : decreaseBottomValue ( )
2017-03-07 00:29:34 +03:00
{
2018-01-20 15:23:28 +03:00
const bool result = Parent : : decreaseBottomValue ( ) ;
2017-03-07 00:29:34 +03:00
2018-01-20 15:23:28 +03:00
if ( result & & ! m_bottomDurationStr . isEmpty ( ) )
{
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue , 3 ) ;
2017-03-07 00:29:34 +03:00
scene ( ) - > update ( ) ; // to update top-boundary text right now
}
2018-01-20 15:23:28 +03:00
return result ;
2017-03-07 00:29:34 +03:00
}
//////////////////////////////////////////////////////////////////////////
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : pickFrameTime ( qreal _y ) const
2017-03-07 00:29:34 +03:00
{
2019-10-20 16:12:37 +03:00
if ( ! isEmpty ( ) & & isImageUpdatePermitted ( ) & & m_boundingRect . top ( ) < _y & & _y < m_boundingRect . bottom ( ) & & ! m_topDurationStr . isEmpty ( ) )
2017-03-07 01:09:27 +03:00
{
2018-01-20 15:23:28 +03:00
const auto frame_time = m_bottomValue + ( m_topValue - m_bottomValue ) * ( m_boundingRect . bottom ( ) - _y ) / m_boundingRect . height ( ) ;
EASY_GLOBALS . frame_time = static_cast < decltype ( EASY_GLOBALS . frame_time ) > ( frame_time ) ;
2017-03-13 20:30:57 +03:00
emit EASY_GLOBALS . events . expectedFrameTimeChanged ( ) ;
2017-03-07 01:09:27 +03:00
}
}
2017-03-07 00:29:34 +03:00
//////////////////////////////////////////////////////////////////////////
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : onModeChanged ( )
2017-03-13 20:30:57 +03:00
{
2018-01-20 15:23:28 +03:00
if ( ! isImageUpdatePermitted ( ) )
2017-03-13 20:30:57 +03:00
return ;
2018-05-07 21:58:37 +03:00
const auto widget = static_cast < const GraphicsScrollbar * > ( scene ( ) - > parent ( ) ) ;
2017-03-13 20:30:57 +03:00
if ( ! widget - > bindMode ( ) & & EASY_GLOBALS . auto_adjust_histogram_height )
{
2018-01-20 15:23:28 +03:00
m_topValue = m_maxValue ;
m_bottomValue = m_minValue ;
2017-03-13 20:30:57 +03:00
}
m_boundaryTimer . stop ( ) ;
updateImage ( ) ;
}
//////////////////////////////////////////////////////////////////////////
2018-01-20 15:23:28 +03:00
bool GraphicsHistogramItem : : updateImage ( )
2017-03-13 00:43:15 +03:00
{
2018-01-20 15:23:28 +03:00
if ( ! Parent : : updateImage ( ) )
return false ;
2017-03-13 00:43:15 +03:00
2018-01-21 19:37:44 +03:00
const auto widget = static_cast < const GraphicsSliderArea * > ( scene ( ) - > parent ( ) ) ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
m_imageScaleUpdate = widget - > range ( ) / widget - > sliderWidth ( ) ;
m_imageOriginUpdate = widget - > bindMode ( ) ? ( widget - > value ( ) - widget - > sliderWidth ( ) * 3 ) : widget - > minimum ( ) ;
2017-03-13 00:43:15 +03:00
2018-01-28 20:52:17 +03:00
// Ugly, but doesn't use exceeded count of threads
const auto rect = m_boundingRect ;
const auto regime = m_regime ;
const auto scale = widget - > getWindowScale ( ) ;
const auto left = widget - > minimum ( ) ;
const auto right = widget - > maximum ( ) ;
const auto value = widget - > value ( ) ;
const auto window = widget - > sliderWidth ( ) ;
const auto top = m_topValue ;
const auto bottom = m_bottomValue ;
const auto bindMode = widget - > bindMode ( ) ;
const auto frameTime = EASY_GLOBALS . frame_time ;
const auto beginTime = EASY_GLOBALS . begin_time ;
const auto autoHeight = EASY_GLOBALS . auto_adjust_histogram_height ;
2019-10-26 01:04:21 +03:00
const auto drawBorders = EASY_GLOBALS . draw_histogram_borders ;
2019-10-27 14:27:53 +03:00
const auto minColumnWidth = EASY_GLOBALS . histogram_column_width_min ;
2018-01-29 23:42:18 +03:00
m_worker . enqueue ( [ = ] {
2019-10-27 14:27:53 +03:00
updateImageAsync ( rect , regime , scale , left , right , right - left , value , window , top , bottom ,
minColumnWidth , bindMode , frameTime , beginTime , autoHeight , drawBorders ) ;
2018-01-28 20:52:17 +03:00
} , m_bReady ) ;
2017-03-13 00:43:15 +03:00
2018-01-20 15:23:28 +03:00
return true ;
2017-03-13 00:43:15 +03:00
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : onImageUpdated ( )
2017-03-07 00:29:34 +03:00
{
2018-01-20 15:23:28 +03:00
if ( EASY_GLOBALS . auto_adjust_histogram_height & & ! m_topDurationStr . isEmpty ( ) )
2016-12-04 16:51:27 +03:00
{
2018-01-20 15:23:28 +03:00
m_topValue = m_workerTopDuration ;
m_bottomValue = m_workerBottomDuration ;
2016-12-04 16:51:27 +03:00
2018-01-20 15:23:28 +03:00
m_topDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_topValue , 3 ) ;
m_bottomDurationStr = profiler_gui : : timeStringReal ( m_timeUnits , m_bottomValue , 3 ) ;
}
2019-10-26 01:04:21 +03:00
if ( static_cast < const GraphicsSliderArea * > ( scene ( ) - > parent ( ) ) - > bindMode ( ) )
{
m_medianDuration = m_workerMedianDuration ;
m_avgDuration = m_workerAvgDuration ;
}
else
{
m_medianDuration = m_medianDurationFull ;
m_avgDuration = m_avgDurationFull ;
}
if ( m_avgDuration ! = 0 | | m_medianDuration ! = 0 )
{
m_medianDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_medianDuration , 3 ) ;
m_avgDurationStr = profiler_gui : : timeStringRealNs ( m_timeUnits , m_avgDuration , 3 ) ;
}
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
void GraphicsHistogramItem : : updateImageAsync ( QRectF _boundingRect , HistRegime _regime , qreal _current_scale ,
2019-10-27 14:27:53 +03:00
qreal _minimum , qreal _maximum , qreal _range , qreal _value , qreal _width , qreal _top_duration , qreal _bottom_duration ,
int _min_column_width , bool _bindMode , float _frame_time , profiler : : timestamp_t _begin_time , bool _autoAdjustHist , bool _drawBorders )
2017-03-13 00:43:15 +03:00
{
const auto bottom = _boundingRect . height ( ) ; //_boundingRect.bottom();
const auto screenWidth = _boundingRect . width ( ) * _current_scale ;
const auto maxColumnHeight = _boundingRect . height ( ) ;
const auto viewScale = _range / _width ;
if ( _bindMode )
{
2017-03-13 20:30:57 +03:00
m_workerImageScale = viewScale ;
m_workerImageOrigin = _value - _width * 3 ;
m_workerImage = new QImage ( screenWidth * 7 + 0.5 , _boundingRect . height ( ) , QImage : : Format_ARGB32 ) ;
2017-03-13 00:43:15 +03:00
}
else
{
2017-03-13 20:30:57 +03:00
m_workerImageScale = 1 ;
m_workerImageOrigin = _minimum ;
m_workerImage = new QImage ( screenWidth + 0.5 , _boundingRect . height ( ) , QImage : : Format_ARGB32 ) ;
2017-03-13 00:43:15 +03:00
}
2017-03-13 20:30:57 +03:00
m_workerImage - > fill ( 0 ) ;
QPainter p ( m_workerImage ) ;
2016-12-04 16:51:27 +03:00
p . setPen ( Qt : : NoPen ) ;
QRectF rect ;
QBrush brush ( Qt : : SolidPattern ) ;
QRgb previousColor = 0 ;
qreal previous_x = - 1e30 , previous_h = - 1e30 , offset = 0. ;
auto realScale = _current_scale ;
const bool gotFrame = _frame_time > 1e-6 f ;
qreal frameCoeff = 1 ;
if ( gotFrame )
{
2017-03-13 00:43:15 +03:00
if ( _frame_time < = _bottom_duration )
frameCoeff = _boundingRect . height ( ) ;
2016-12-04 16:51:27 +03:00
else
2016-12-07 22:29:16 +03:00
frameCoeff = 0.9 / _frame_time ;
2016-12-04 16:51:27 +03:00
}
2018-01-20 15:23:28 +03:00
using estd : : sqr ;
2016-12-04 16:51:27 +03:00
auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1 ;
2017-03-13 00:43:15 +03:00
auto const k = gotFrame ? sqr ( sqr ( frameCoeff ) ) : 1.0 / _boundingRect . height ( ) ;
2016-12-04 16:51:27 +03:00
2019-10-26 01:04:21 +03:00
size_t totalCount = 0 ;
m_workerMedianDuration = 0 ;
m_workerAvgDuration = 0 ;
profiler_gui : : DurationsCountMap durations ;
2017-03-07 19:59:57 +03:00
if ( _regime = = Hist_Pointer )
2016-12-04 16:51:27 +03:00
{
const auto & items = * m_pSource ;
if ( items . empty ( ) )
return ;
2017-03-07 19:59:57 +03:00
auto first = items . begin ( ) ;
2017-03-13 00:43:15 +03:00
if ( _bindMode )
{
2017-03-13 20:30:57 +03:00
_minimum = m_workerImageOrigin ;
_maximum = m_workerImageOrigin + _width * 7 ;
2017-03-13 00:43:15 +03:00
realScale * = viewScale ;
offset = _minimum * realScale ;
2019-10-26 01:04:21 +03:00
first = std : : lower_bound ( items . begin ( ) , items . end ( ) , _minimum , [ ] ( const profiler_gui : : EasyBlockItem & _item , qreal _value )
2017-03-13 00:43:15 +03:00
{
return _item . left ( ) < _value ;
} ) ;
if ( first ! = items . end ( ) )
{
if ( first ! = items . begin ( ) )
- - first ;
}
else
{
first = items . begin ( ) + items . size ( ) - 1 ;
}
2017-03-13 20:30:57 +03:00
if ( _autoAdjustHist )
{
const auto maxVal = _value + _width ;
decltype ( _top_duration ) maxDuration = 0 ;
decltype ( _bottom_duration ) minDuration = 1e30 ;
size_t iterations = 0 ;
for ( auto it = first , end = items . end ( ) ; it ! = end ; + + it )
{
// Draw rectangle
if ( it - > left ( ) > maxVal )
break ;
if ( it - > right ( ) < _value )
continue ;
if ( maxDuration < it - > width ( ) )
maxDuration = it - > width ( ) ;
if ( minDuration > it - > width ( ) )
minDuration = it - > width ( ) ;
+ + iterations ;
}
2018-01-20 15:23:28 +03:00
if ( iterations ! = 0 )
2017-03-13 20:30:57 +03:00
{
_top_duration = maxDuration ;
_bottom_duration = minDuration ;
if ( ( _top_duration - _bottom_duration ) < 1e-3 )
{
if ( _bottom_duration > 0.1 )
{
_bottom_duration - = 0.1 ;
}
else
{
_top_duration = 0.1 ;
_bottom_duration = 0 ;
}
}
}
}
2017-03-13 00:43:15 +03:00
}
2016-12-04 16:51:27 +03:00
2017-03-13 20:30:57 +03:00
const auto dtime = _top_duration - _bottom_duration ;
2018-01-20 15:23:28 +03:00
const auto coeff = _boundingRect . height ( ) / ( dtime > 1e-3 ? dtime : 1. ) ;
2017-03-13 20:30:57 +03:00
2019-10-27 14:27:53 +03:00
const qreal minWidth = _drawBorders ? _min_column_width : 1 ;
2019-10-26 01:04:21 +03:00
if ( _drawBorders )
p . setPen ( profiler_gui : : BLOCK_BORDER_COLOR ) ;
2017-03-07 19:59:57 +03:00
for ( auto it = first , end = items . end ( ) ; it ! = end ; + + it )
2016-12-04 16:51:27 +03:00
{
// Draw rectangle
2017-03-07 19:59:57 +03:00
if ( it - > left ( ) > _maximum )
2016-12-04 16:51:27 +03:00
break ;
2017-03-07 19:59:57 +03:00
if ( it - > right ( ) < _minimum )
2016-12-04 16:51:27 +03:00
continue ;
2019-10-26 01:04:21 +03:00
if ( _bindMode )
{
// calculate avg and median
const auto duration = easyBlock ( it - > block ) . tree . node - > duration ( ) ;
m_workerAvgDuration + = duration ;
+ + totalCount ;
+ + durations [ duration ] . count ;
}
auto maxItemWidth = it - > width ( ) ;
// calculate column width and height
qreal item_x = it - > left ( ) * realScale - offset ;
qreal item_w = it - > width ( ) * realScale ;
qreal item_r = item_x + item_w ;
const auto width = it - > width ( ) ;
qreal h = width < = _bottom_duration ? HIST_COLUMN_MIN_HEIGHT :
( width > _top_duration ? maxColumnHeight : ( width - _bottom_duration ) * coeff ) ;
if ( _drawBorders )
{
if ( item_r < previous_x )
{
item_w - = previous_x - item_r ;
item_x = previous_x ;
item_r = item_x + item_w ;
}
// if column width < minWidth then try to merge several columns together
auto jt = it ;
while ( item_w < minWidth & & jt ! = end )
{
if ( jt - > left ( ) > _maximum )
break ;
const qreal jx = jt - > left ( ) * realScale - offset ;
auto dx = jx - item_r ;
if ( dx > std : : max ( item_w , minWidth ) )
{
item_w = minWidth ;
break ;
}
const qreal jw = jt - > width ( ) * realScale ;
if ( jw > ( minWidth + item_w ) )
{
item_w = minWidth ;
break ;
}
const auto jwidth = jt - > width ( ) ;
const qreal jh = jwidth < = _bottom_duration ? HIST_COLUMN_MIN_HEIGHT :
( jwidth > _top_duration ? maxColumnHeight : ( jwidth - _bottom_duration ) * coeff ) ;
item_w + = jw ;
h = std : : max ( h , jh ) ;
maxItemWidth = std : : max ( maxItemWidth , jwidth ) ;
+ + jt ;
}
item_r = item_x + item_w ;
if ( _bindMode )
{
// if merged several columns then avg and median should be calculated for these columns too
for ( auto it2 = it ; it2 ! = jt ; + + it2 )
{
const auto duration = easyBlock ( it2 - > block ) . tree . node - > duration ( ) ;
m_workerAvgDuration + = duration ;
+ + totalCount ;
+ + durations [ duration ] . count ;
}
}
if ( jt ! = it )
{
// bypass merged columns
it = jt ;
- - it ;
}
}
else if ( item_w < 1 )
{
item_w = 1 ;
item_r = item_x + 1 ;
}
2016-12-04 16:51:27 +03:00
if ( h < previous_h & & item_r < previous_x )
continue ;
2019-10-26 01:04:21 +03:00
const auto col = calculate_color ( h , maxItemWidth , k ) ;
2016-12-04 16:51:27 +03:00
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 ) ) ;
p . setBrush ( brush ) ;
}
rect . setRect ( item_x , bottom - h , item_w , h ) ;
p . drawRect ( rect ) ;
previous_x = item_r ;
previous_h = h ;
}
}
else
{
2017-03-13 00:43:15 +03:00
auto first = m_selectedBlocks . begin ( ) ;
if ( _bindMode )
{
2017-03-13 20:30:57 +03:00
_minimum = m_workerImageOrigin ;
_maximum = m_workerImageOrigin + _width * 7 ;
2017-03-13 00:43:15 +03:00
realScale * = viewScale ;
offset = _minimum * 1e3 * realScale ;
2018-01-20 15:23:28 +03:00
first = std : : lower_bound ( m_selectedBlocks . begin ( ) , m_selectedBlocks . end ( ) , _minimum * 1e3 + _begin_time , [ ] ( profiler : : block_index_t _item , qreal _value )
2017-03-13 00:43:15 +03:00
{
return easyBlock ( _item ) . tree . node - > begin ( ) < _value ;
} ) ;
if ( first ! = m_selectedBlocks . end ( ) )
{
if ( first ! = m_selectedBlocks . begin ( ) )
- - first ;
}
else
{
first = m_selectedBlocks . begin ( ) + m_selectedBlocks . size ( ) - 1 ;
}
2017-03-13 20:30:57 +03:00
_minimum * = 1e3 ;
_maximum * = 1e3 ;
if ( _autoAdjustHist )
{
const auto minVal = _value * 1e3 , maxVal = ( _value + _width ) * 1e3 ;
decltype ( _top_duration ) maxDuration = 0 ;
decltype ( _bottom_duration ) minDuration = 1e30 ;
size_t iterations = 0 ;
for ( auto it = first , end = m_selectedBlocks . end ( ) ; it ! = end ; + + it )
{
const auto item = easyBlock ( * it ) . tree . node ;
const auto beginTime = item - > begin ( ) - _begin_time ;
if ( beginTime > maxVal )
break ;
const auto endTime = item - > end ( ) - _begin_time ;
if ( endTime < minVal )
continue ;
const qreal duration = item - > duration ( ) * 1e-3 ;
if ( maxDuration < duration )
maxDuration = duration ;
if ( minDuration > duration )
minDuration = duration ;
+ + iterations ;
}
2018-01-20 15:23:28 +03:00
if ( iterations ! = 0 )
2017-03-13 20:30:57 +03:00
{
_top_duration = maxDuration ;
_bottom_duration = minDuration ;
if ( ( _top_duration - _bottom_duration ) < 1e-3 )
{
if ( _bottom_duration > 0.1 )
{
_bottom_duration - = 0.1 ;
}
else
{
_top_duration = 0.1 ;
_bottom_duration = 0 ;
}
}
}
}
}
else
{
_minimum * = 1e3 ;
_maximum * = 1e3 ;
2017-03-13 00:43:15 +03:00
}
2017-03-13 20:30:57 +03:00
const auto dtime = _top_duration - _bottom_duration ;
2018-01-20 15:23:28 +03:00
const auto coeff = _boundingRect . height ( ) / ( dtime > 1e-3 ? dtime : 1. ) ;
2016-12-04 16:51:27 +03:00
2019-10-27 14:27:53 +03:00
const qreal minWidth = _drawBorders ? _min_column_width : 1 ;
2019-10-26 01:04:21 +03:00
if ( _drawBorders )
p . setPen ( profiler_gui : : BLOCK_BORDER_COLOR ) ;
2017-03-13 00:43:15 +03:00
for ( auto it = first , end = m_selectedBlocks . end ( ) ; it ! = end ; + + it )
2016-12-04 16:51:27 +03:00
{
// Draw rectangle
const auto item = easyBlock ( * it ) . tree . node ;
const auto beginTime = item - > begin ( ) - _begin_time ;
2017-03-07 19:59:57 +03:00
if ( beginTime > _maximum )
2016-12-04 16:51:27 +03:00
break ;
const auto endTime = item - > end ( ) - _begin_time ;
2017-03-07 19:59:57 +03:00
if ( endTime < _minimum )
2016-12-04 16:51:27 +03:00
continue ;
2019-10-26 01:04:21 +03:00
if ( _bindMode )
{
// calculate avg and median
const auto duration = item - > duration ( ) ;
m_workerAvgDuration + = duration ;
+ + totalCount ;
+ + durations [ duration ] . count ;
}
2016-12-04 16:51:27 +03:00
const qreal duration = item - > duration ( ) * 1e-3 ;
2019-10-26 01:04:21 +03:00
auto maxItemDuration = duration ;
// calculate column width and height
qreal item_x = ( beginTime * realScale - offset ) * 1e-3 ;
qreal item_w = duration * realScale ;
qreal item_r = item_x + item_w ;
auto h = duration < = _bottom_duration ? HIST_COLUMN_MIN_HEIGHT :
2018-01-20 15:23:28 +03:00
( duration > _top_duration ? maxColumnHeight : ( duration - _bottom_duration ) * coeff ) ;
2016-12-04 16:51:27 +03:00
2019-10-26 01:04:21 +03:00
if ( _drawBorders )
{
if ( item_r < previous_x )
{
item_w - = previous_x - item_r ;
item_x = previous_x ;
item_r = item_x + item_w ;
}
// if column width < minWidth then try to merge several columns together
auto jt = it ;
while ( item_w < minWidth & & jt ! = end )
{
const auto jtem = easyBlock ( * jt ) . tree . node ;
const auto jbeginTime = jtem - > begin ( ) - _begin_time ;
if ( jbeginTime > _maximum )
break ;
const qreal jduration = jtem - > duration ( ) * 1e-3 ;
const qreal jx = ( ( jtem - > begin ( ) - _begin_time ) * realScale - offset ) * 1e-3 ;
auto dx = jx - item_r ;
if ( dx > std : : max ( item_w , minWidth ) )
{
item_w = minWidth ;
break ;
}
const qreal jw = jduration * realScale ;
if ( jw > ( minWidth + item_w ) )
{
item_w = minWidth ;
break ;
}
const qreal jh = jduration < = _bottom_duration ? HIST_COLUMN_MIN_HEIGHT :
( jduration > _top_duration ? maxColumnHeight : ( jduration - _bottom_duration ) * coeff ) ;
item_w + = jw ;
h = std : : max ( h , jh ) ;
maxItemDuration = std : : max ( maxItemDuration , jduration ) ;
+ + jt ;
}
item_r = item_x + item_w ;
if ( _bindMode )
{
// if merged several columns then avg and median should be calculated for these columns too
for ( auto it2 = it ; it2 ! = jt ; + + it2 )
{
const auto duration = easyBlock ( * it2 ) . tree . node - > duration ( ) ;
m_workerAvgDuration + = duration ;
+ + totalCount ;
+ + durations [ duration ] . count ;
}
}
if ( jt ! = it )
{
// bypass merged columns
it = jt ;
- - it ;
}
}
else if ( item_w < 1 )
{
item_w = 1 ;
item_r = item_x + 1 ;
}
2016-12-04 16:51:27 +03:00
if ( h < previous_h & & item_r < previous_x )
continue ;
2019-10-26 01:04:21 +03:00
const auto col = calculate_color ( h , maxItemDuration , k ) ;
2016-12-04 16:51:27 +03:00
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 ) ) ;
p . setBrush ( brush ) ;
}
rect . setRect ( item_x , bottom - h , item_w , h ) ;
p . drawRect ( rect ) ;
previous_x = item_r ;
previous_h = h ;
}
2016-08-03 00:06:36 +03:00
}
2017-03-13 20:30:57 +03:00
m_workerTopDuration = _top_duration ;
m_workerBottomDuration = _bottom_duration ;
2018-01-20 15:23:28 +03:00
2019-10-29 23:48:17 +03:00
if ( _bindMode & & totalCount ! = 0 )
2019-10-26 01:04:21 +03:00
{
m_workerAvgDuration / = totalCount ;
m_workerMedianDuration = profiler_gui : : calculateMedian ( durations ) ;
}
2018-01-20 15:23:28 +03:00
setReady ( true ) ;
2016-08-03 00:06:36 +03:00
}
//////////////////////////////////////////////////////////////////////////
2018-05-07 21:58:37 +03:00
GraphicsScrollbar : : GraphicsScrollbar ( int _initialHeight , QWidget * _parent )
2016-07-10 01:29:09 +03:00
: Parent ( _parent )
2017-03-07 00:29:34 +03:00
, m_histogramItem ( nullptr )
2016-07-10 01:29:09 +03:00
{
2018-01-21 19:37:44 +03:00
const int sceneHeight = _initialHeight - 2 ;
2018-01-20 15:23:28 +03:00
scene ( ) - > setSceneRect ( 0 , - ( sceneHeight > > 1 ) , 500 , sceneHeight ) ;
m_histogramItem = new GraphicsHistogramItem ( ) ;
2018-01-21 19:37:44 +03:00
m_imageItem = m_histogramItem ;
2018-01-20 15:23:28 +03:00
scene ( ) - > addItem ( m_histogramItem ) ;
2016-07-10 01:29:09 +03:00
2017-03-07 00:29:34 +03:00
m_histogramItem - > setPos ( 0 , 0 ) ;
2018-01-20 15:23:28 +03:00
m_histogramItem - > setBoundingRect ( 0 , scene ( ) - > sceneRect ( ) . top ( ) + margin ( ) , scene ( ) - > width ( ) , sceneHeight - margins ( ) - 1 ) ;
2017-03-07 00:29:34 +03:00
m_histogramItem - > hide ( ) ;
2016-07-10 01:29:09 +03:00
2018-01-28 20:52:17 +03:00
connect ( & EASY_GLOBALS . events , & profiler_gui : : GlobalSignals : : expectedFrameTimeChanged ,
2019-10-26 01:04:21 +03:00
this , & This : : repaintHistogramImage ) ;
2017-03-13 20:30:57 +03:00
2018-01-28 20:52:17 +03:00
connect ( & EASY_GLOBALS . events , & profiler_gui : : GlobalSignals : : autoAdjustHistogramChanged ,
2018-01-28 02:01:49 +03:00
this , & This : : onAutoAdjustHistogramChanged ) ;
2016-11-13 22:02:47 +03:00
2018-01-28 20:52:17 +03:00
connect ( & EASY_GLOBALS . events , & profiler_gui : : GlobalSignals : : displayOnlyFramesOnHistogramChanged ,
2018-01-28 02:01:49 +03:00
this , & This : : onDisplayOnlyFramesOnHistogramChanged ) ;
2017-06-07 02:08:53 +03:00
2018-01-28 20:52:17 +03:00
connect ( & EASY_GLOBALS . events , & profiler_gui : : GlobalSignals : : threadNameDecorationChanged , this , & This : : onThreadViewChanged ) ;
connect ( & EASY_GLOBALS . events , & profiler_gui : : GlobalSignals : : hexThreadIdChanged , this , & This : : onThreadViewChanged ) ;
2018-03-13 01:28:58 +03:00
connect ( & EASY_GLOBALS . events , & profiler_gui : : GlobalSignals : : allDataGoingToBeDeleted , this , & This : : clear ) ;
2016-07-10 01:29:09 +03:00
}
2018-05-07 21:58:37 +03:00
GraphicsScrollbar : : ~ GraphicsScrollbar ( )
2016-07-10 01:29:09 +03:00
{
}
//////////////////////////////////////////////////////////////////////////
2019-10-26 01:04:21 +03:00
void GraphicsScrollbar : : repaintHistogramImage ( )
2017-06-05 21:26:10 +03:00
{
if ( m_histogramItem - > isVisible ( ) )
{
2019-10-26 01:04:21 +03:00
m_histogramItem - > updateImage ( ) ;
2017-06-05 21:26:10 +03:00
scene ( ) - > update ( ) ;
}
}
2019-10-26 01:04:21 +03:00
void GraphicsScrollbar : : onThreadViewChanged ( )
2018-01-28 02:01:49 +03:00
{
if ( m_histogramItem - > isVisible ( ) )
{
2019-10-26 01:04:21 +03:00
m_histogramItem - > validateName ( ) ;
2018-01-28 02:01:49 +03:00
scene ( ) - > update ( ) ;
}
}
2018-05-07 21:58:37 +03:00
void GraphicsScrollbar : : onAutoAdjustHistogramChanged ( )
2018-01-28 02:01:49 +03:00
{
if ( m_histogramItem - > isVisible ( ) )
m_histogramItem - > onModeChanged ( ) ;
}
2018-05-07 21:58:37 +03:00
void GraphicsScrollbar : : onDisplayOnlyFramesOnHistogramChanged ( )
2018-01-28 02:01:49 +03:00
{
if ( m_histogramItem - > isVisible ( ) )
m_histogramItem - > rebuildSource ( GraphicsHistogramItem : : Hist_Id ) ;
}
2017-06-05 21:26:10 +03:00
//////////////////////////////////////////////////////////////////////////
2018-05-07 21:58:37 +03:00
void GraphicsScrollbar : : clear ( )
2016-09-21 22:09:04 +03:00
{
2017-03-07 19:59:57 +03:00
setHistogramSource ( 0 , nullptr ) ;
2018-01-20 15:23:28 +03:00
Parent : : clear ( ) ;
2016-09-21 22:09:04 +03:00
}
2019-10-26 01:04:21 +03:00
profiler : : thread_id_t GraphicsScrollbar : : histThread ( ) const
2016-08-03 23:00:04 +03:00
{
2017-03-07 00:29:34 +03:00
return m_histogramItem - > threadId ( ) ;
2016-08-03 23:00:04 +03:00
}
2018-05-07 21:58:37 +03:00
void GraphicsScrollbar : : setHistogramSource ( profiler : : thread_id_t _thread_id , const profiler_gui : : EasyItems * _items )
2016-08-03 00:06:36 +03:00
{
2016-12-04 16:51:27 +03:00
if ( m_bLocked )
return ;
2017-03-07 00:29:34 +03:00
m_histogramItem - > setSource ( _thread_id , _items ) ;
2016-12-04 16:51:27 +03:00
scene ( ) - > update ( ) ;
}
2018-05-07 21:58:37 +03:00
void GraphicsScrollbar : : setHistogramSource ( profiler : : thread_id_t _thread_id , profiler : : block_id_t _block_id )
2016-12-04 16:51:27 +03:00
{
if ( m_bLocked )
return ;
2017-03-07 00:29:34 +03:00
m_histogramItem - > setSource ( _thread_id , _block_id ) ;
2016-08-03 23:00:04 +03:00
scene ( ) - > update ( ) ;
2016-08-03 00:06:36 +03:00
}
2018-05-07 21:58:37 +03:00
void GraphicsScrollbar : : mousePressEvent ( QMouseEvent * _event )
2016-07-31 13:13:48 +03:00
{
2018-01-20 15:23:28 +03:00
Parent : : mousePressEvent ( _event ) ;
if ( ( m_mouseButtons & Qt : : RightButton ) & & _event - > modifiers ( ) )
m_histogramItem - > pickFrameTime ( mapToScene ( _event - > pos ( ) ) . y ( ) ) ;
2016-07-31 13:13:48 +03:00
}
//////////////////////////////////////////////////////////////////////////