easy_graphics_scrollbar.cpp 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084
  1. /************************************************************************
  2. * file name : easy_graphics_scrollbar.cpp
  3. * ----------------- :
  4. * creation time : 2016/07/04
  5. * author : Victor Zarubkin
  6. * email : [email protected]
  7. * ----------------- :
  8. * description : .
  9. * ----------------- :
  10. * change log : * 2016/07/04 Victor Zarubkin: Initial commit.
  11. * :
  12. * : *
  13. * ----------------- :
  14. * license : Lightweight profiler library for c++
  15. * : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
  16. * :
  17. * : Licensed under either of
  18. * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
  19. * : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
  20. * : at your option.
  21. * :
  22. * : The MIT License
  23. * :
  24. * : Permission is hereby granted, free of charge, to any person obtaining a copy
  25. * : of this software and associated documentation files (the "Software"), to deal
  26. * : in the Software without restriction, including without limitation the rights
  27. * : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  28. * : of the Software, and to permit persons to whom the Software is furnished
  29. * : to do so, subject to the following conditions:
  30. * :
  31. * : The above copyright notice and this permission notice shall be included in all
  32. * : copies or substantial portions of the Software.
  33. * :
  34. * : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  35. * : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  36. * : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  37. * : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  38. * : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  39. * : USE OR OTHER DEALINGS IN THE SOFTWARE.
  40. * :
  41. * : The Apache License, Version 2.0 (the "License")
  42. * :
  43. * : You may not use this file except in compliance with the License.
  44. * : You may obtain a copy of the License at
  45. * :
  46. * : http://www.apache.org/licenses/LICENSE-2.0
  47. * :
  48. * : Unless required by applicable law or agreed to in writing, software
  49. * : distributed under the License is distributed on an "AS IS" BASIS,
  50. * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  51. * : See the License for the specific language governing permissions and
  52. * : limitations under the License.
  53. ************************************************************************/
  54. #include <algorithm>
  55. #include <QGraphicsScene>
  56. #include <QWheelEvent>
  57. #include <QMouseEvent>
  58. #include <QResizeEvent>
  59. #include <QContextMenuEvent>
  60. #include <QMenu>
  61. #include "easy_graphics_scrollbar.h"
  62. #include "globals.h"
  63. // TODO: use profiler_core/spin_lock.h
  64. #if defined(_WIN32) && defined(EASY_GUI_USE_CRITICAL_SECTION)
  65. # include <Windows.h>
  66. # ifdef min
  67. # undef min
  68. # endif
  69. # ifdef max
  70. # undef max
  71. # endif
  72. namespace profiler_gui {
  73. void spin_lock::lock() {
  74. EnterCriticalSection((CRITICAL_SECTION*)m_lock);
  75. }
  76. void spin_lock::unlock() {
  77. LeaveCriticalSection((CRITICAL_SECTION*)m_lock);
  78. }
  79. spin_lock::spin_lock() : m_lock(new CRITICAL_SECTION) {
  80. InitializeCriticalSection((CRITICAL_SECTION*)m_lock);
  81. }
  82. spin_lock::~spin_lock() {
  83. DeleteCriticalSection((CRITICAL_SECTION*)m_lock);
  84. delete ((CRITICAL_SECTION*)m_lock);
  85. }
  86. }
  87. #endif
  88. //////////////////////////////////////////////////////////////////////////
  89. const int DEFAULT_TOP = -40;
  90. const int DEFAULT_HEIGHT = 80;
  91. const int INDICATOR_SIZE = 6;
  92. const int INDICATOR_SIZE_x2 = INDICATOR_SIZE << 1;
  93. const int HIST_COLUMN_MIN_HEIGHT = 2;
  94. const int WORKER_THREAD_CHECK_INTERVAL = 40;
  95. const int BOUNDARY_TIMER_INTERVAL = 100;
  96. //////////////////////////////////////////////////////////////////////////
  97. inline qreal clamp(qreal _minValue, qreal _value, qreal _maxValue)
  98. {
  99. return (_value < _minValue ? _minValue : (_value > _maxValue ? _maxValue : _value));
  100. }
  101. inline qreal sqr(qreal _value)
  102. {
  103. return _value * _value;
  104. }
  105. inline qreal calculate_color1(qreal h, qreal, qreal k)
  106. {
  107. return ::std::min(h * k, 0.9999999);
  108. }
  109. inline qreal calculate_color2(qreal, qreal duration, qreal k)
  110. {
  111. return ::std::min(sqr(sqr(duration)) * k, 0.9999999);
  112. }
  113. //////////////////////////////////////////////////////////////////////////
  114. EasyGraphicsSliderItem::EasyGraphicsSliderItem(bool _main) : Parent(), m_halfwidth(0), m_bMain(_main)
  115. {
  116. m_indicator.reserve(3);
  117. if (_main)
  118. {
  119. m_indicator.push_back(QPointF(0, DEFAULT_TOP + INDICATOR_SIZE));
  120. m_indicator.push_back(QPointF(-INDICATOR_SIZE, DEFAULT_TOP));
  121. m_indicator.push_back(QPointF(INDICATOR_SIZE, DEFAULT_TOP));
  122. }
  123. else
  124. {
  125. m_indicator.push_back(QPointF(0, DEFAULT_TOP + DEFAULT_HEIGHT - INDICATOR_SIZE));
  126. m_indicator.push_back(QPointF(-INDICATOR_SIZE, DEFAULT_TOP + DEFAULT_HEIGHT));
  127. m_indicator.push_back(QPointF(INDICATOR_SIZE, DEFAULT_TOP + DEFAULT_HEIGHT));
  128. }
  129. setWidth(1);
  130. setBrush(Qt::SolidPattern);
  131. }
  132. EasyGraphicsSliderItem::~EasyGraphicsSliderItem()
  133. {
  134. }
  135. void EasyGraphicsSliderItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget)
  136. {
  137. if (static_cast<const EasyGraphicsScrollbar*>(scene()->parent())->bindMode())
  138. {
  139. return;
  140. }
  141. const auto currentScale = static_cast<const EasyGraphicsScrollbar*>(scene()->parent())->getWindowScale();
  142. const auto br = rect();
  143. qreal w = width() * currentScale;
  144. QRectF r(br.left() * currentScale, br.top() + INDICATOR_SIZE, w, br.height() - INDICATOR_SIZE_x2);
  145. const auto r_right = r.right();
  146. const auto r_bottom = r.bottom();
  147. auto b = brush();
  148. _painter->save();
  149. _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true);
  150. _painter->setBrush(b);
  151. if (w > 1)
  152. {
  153. _painter->setPen(Qt::NoPen);
  154. _painter->drawRect(r);
  155. // Draw left and right borders
  156. auto cmode = _painter->compositionMode();
  157. if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion);
  158. _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb()));
  159. _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom));
  160. _painter->drawLine(QPointF(r_right, r.top()), QPointF(r_right, r_bottom));
  161. if (!m_bMain) _painter->setCompositionMode(cmode);
  162. }
  163. else
  164. {
  165. _painter->setPen(QColor::fromRgba(0xe0000000 | b.color().rgb()));
  166. _painter->drawLine(QPointF(r.left(), r.top()), QPointF(r.left(), r_bottom));
  167. if (m_bMain) _painter->setCompositionMode(QPainter::CompositionMode_Exclusion);
  168. }
  169. // Draw triangle indicators for small slider
  170. _painter->setTransform(QTransform::fromTranslate(r.left() + w * 0.5, 0), true);
  171. _painter->setPen(b.color().rgb());
  172. _painter->drawPolygon(m_indicator);
  173. _painter->restore();
  174. }
  175. qreal EasyGraphicsSliderItem::width() const
  176. {
  177. return m_halfwidth * 2.0;
  178. }
  179. qreal EasyGraphicsSliderItem::halfwidth() const
  180. {
  181. return m_halfwidth;
  182. }
  183. void EasyGraphicsSliderItem::setWidth(qreal _width)
  184. {
  185. m_halfwidth = _width * 0.5;
  186. setRect(-m_halfwidth, DEFAULT_TOP, _width, DEFAULT_HEIGHT);
  187. }
  188. void EasyGraphicsSliderItem::setHalfwidth(qreal _halfwidth)
  189. {
  190. m_halfwidth = _halfwidth;
  191. setRect(-m_halfwidth, DEFAULT_TOP, m_halfwidth * 2.0, DEFAULT_HEIGHT);
  192. }
  193. void EasyGraphicsSliderItem::setColor(QRgb _color)
  194. {
  195. setColor(QColor::fromRgba(_color));
  196. }
  197. void EasyGraphicsSliderItem::setColor(const QColor& _color)
  198. {
  199. auto b = brush();
  200. b.setColor(_color);
  201. setBrush(b);
  202. }
  203. //////////////////////////////////////////////////////////////////////////
  204. EasyHistogramItem::EasyHistogramItem() : Parent(nullptr)
  205. , m_threadDuration(0)
  206. , m_threadProfiledTime(0)
  207. , m_threadWaitTime(0)
  208. , m_pSource(nullptr)
  209. , m_workerImage(nullptr)
  210. , m_topDuration(0)
  211. , m_maxDuration(0)
  212. , m_minDuration(0)
  213. , m_imageOrigin(0)
  214. , m_imageScale(1)
  215. , m_workerImageOrigin(0)
  216. , m_workerImageScale(1)
  217. , m_workerTopDuration(0)
  218. , m_workerBottomDuration(0)
  219. , m_blockTotalDuraion(0)
  220. , m_timer(::std::bind(&This::onTimeout, this))
  221. , m_boundaryTimer([this](){ updateImage(); }, true)
  222. , m_pProfilerThread(nullptr)
  223. , m_threadId(0)
  224. , m_blockId(::profiler_gui::numeric_max<decltype(m_blockId)>())
  225. , m_timeouts(0)
  226. , m_timeUnits(::profiler_gui::TimeUnits_auto)
  227. , m_regime(Hist_Pointer)
  228. , m_bPermitImageUpdate(false)
  229. {
  230. m_bReady = ATOMIC_VAR_INIT(false);
  231. }
  232. EasyHistogramItem::~EasyHistogramItem()
  233. {
  234. m_bReady.store(true, ::std::memory_order_release);
  235. if (m_workerThread.joinable())
  236. m_workerThread.join();
  237. delete m_workerImage;
  238. }
  239. QRectF EasyHistogramItem::boundingRect() const
  240. {
  241. return m_boundingRect;
  242. }
  243. void EasyHistogramItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget)
  244. {
  245. if (!m_bPermitImageUpdate || (m_regime == Hist_Pointer && m_pSource == nullptr) || (m_regime == Hist_Id && (m_threadId == 0 || ::profiler_gui::is_max(m_blockId))))
  246. return;
  247. if (m_regime == Hist_Pointer)
  248. paintByPtr(_painter);
  249. else
  250. paintById(_painter);
  251. }
  252. void EasyHistogramItem::paintBusyIndicator(QPainter* _painter, qreal _current_scale)
  253. {
  254. const auto width = m_boundingRect.width() * _current_scale;
  255. const auto h = _painter->fontMetrics().height();
  256. _painter->setPen(Qt::black);
  257. _painter->drawText(QRectF(0, m_boundingRect.top(), width, m_boundingRect.height() - h),
  258. Qt::AlignCenter, "Generating image");
  259. _painter->drawText(QRectF(0, m_boundingRect.top() + h, width, m_boundingRect.height() - h),
  260. Qt::AlignCenter, QString(m_timeouts, QChar('.')));
  261. }
  262. 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)
  263. {
  264. if (_font_h != 0 && _top < _mouse_y && _mouse_y < _bottom)
  265. {
  266. const int half_font_h = _font_h >> 1;
  267. _painter->setPen(Qt::blue);
  268. const auto mouseStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration + _delta_time * (_bottom - _mouse_y) / _height, 3);
  269. qreal mouseIndicatorRight = _width;
  270. if (_mouse_y < _top + half_font_h)
  271. mouseIndicatorRight = _top_width;
  272. qreal mouseIndicatorLeft = 0;
  273. const QRectF rect(0, _mouse_y - _font_h, _width, _font_h << 1);
  274. if (_mouse_y > _bottom - half_font_h)
  275. {
  276. _painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, mouseStr);
  277. }
  278. else if (_mouse_y < _top + half_font_h)
  279. {
  280. _painter->drawText(rect, Qt::AlignLeft | Qt::AlignBottom, mouseStr);
  281. }
  282. else
  283. {
  284. _painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, mouseStr);
  285. mouseIndicatorLeft = _painter->fontMetrics().width(mouseStr) + 3;
  286. }
  287. _painter->drawLine(QLineF(mouseIndicatorLeft, _mouse_y, mouseIndicatorRight, _mouse_y));
  288. }
  289. }
  290. void EasyHistogramItem::paintByPtr(QPainter* _painter)
  291. {
  292. const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
  293. const bool bindMode = widget->bindMode();
  294. const auto currentScale = widget->getWindowScale();
  295. const auto bottom = m_boundingRect.bottom();
  296. const auto width = m_boundingRect.width() * currentScale;
  297. const auto dtime = m_topDuration - m_bottomDuration;
  298. const auto maxColumnHeight = m_boundingRect.height();
  299. const auto coeff = (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.);
  300. QRectF rect;
  301. QBrush brush(Qt::SolidPattern);
  302. //QRgb previousColor = 0;
  303. _painter->save();
  304. _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true);
  305. if (!m_pSource->empty())
  306. {
  307. _painter->setPen(Qt::NoPen);
  308. if (!bindMode)
  309. _painter->drawImage(0, m_boundingRect.top(), m_mainImage);
  310. else
  311. {
  312. const auto range = widget->sliderWidth();
  313. const auto minimum = widget->value();
  314. const auto slider_k = widget->range() / range;
  315. /*if (false)//slider_k < 8)
  316. {
  317. _painter->setTransform(QTransform::fromScale(slider_k, 1), true);
  318. _painter->drawImage((widget->minimum() - minimum) * currentScale, m_boundingRect.top(), m_mainImage);
  319. _painter->setTransform(QTransform::fromScale(1. / slider_k, 1), true);
  320. }
  321. else*/
  322. {
  323. const auto deltaScale = slider_k / m_imageScale;
  324. _painter->setTransform(QTransform::fromScale(deltaScale, 1), true);
  325. _painter->drawImage((widget->minimum() + m_imageOrigin - minimum) * currentScale * m_imageScale, m_boundingRect.top(), m_mainImage);
  326. _painter->setTransform(QTransform::fromScale(1. / deltaScale, 1), true);
  327. }
  328. /*if (false)
  329. {
  330. const bool gotFrame = EASY_GLOBALS.frame_time > 1e-6f;
  331. qreal frameCoeff = 1;
  332. if (gotFrame)
  333. {
  334. if (EASY_GLOBALS.frame_time <= m_bottomDuration)
  335. frameCoeff = m_boundingRect.height();
  336. else
  337. frameCoeff = 0.9 / EASY_GLOBALS.frame_time;
  338. }
  339. auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1;
  340. auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / m_boundingRect.height();
  341. const auto& items = *m_pSource;
  342. const auto maximum = minimum + range;
  343. const auto realScale = currentScale * slider_k;
  344. const auto offset = minimum * realScale;
  345. auto first = ::std::lower_bound(items.begin(), items.end(), minimum, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value)
  346. {
  347. return _item.left() < _value;
  348. });
  349. if (first != items.end())
  350. {
  351. if (first != items.begin())
  352. --first;
  353. }
  354. else
  355. {
  356. first = items.begin() + items.size() - 1;
  357. }
  358. qreal previous_x = -1e30, previous_h = -1e30;
  359. for (auto it = first, end = items.end(); it != end; ++it)
  360. {
  361. // Draw rectangle
  362. if (it->left() > maximum)
  363. break;
  364. if (it->right() < minimum)
  365. continue;
  366. const qreal item_x = it->left() * realScale - offset;
  367. const qreal item_w = ::std::max(it->width() * realScale, 1.0);
  368. const qreal item_r = item_x + item_w;
  369. const qreal h = it->width() <= m_bottomDuration ? HIST_COLUMN_MIN_HEIGHT :
  370. (it->width() > m_topDuration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (it->width() - m_bottomDuration) * coeff));
  371. if (h < previous_h && item_r < previous_x)
  372. continue;
  373. const auto col = calculate_color(h, it->width(), k);
  374. const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb();
  375. if (previousColor != color)
  376. {
  377. // Set background color brush for rectangle
  378. previousColor = color;
  379. brush.setColor(QColor::fromRgba(0xc0000000 | color));
  380. _painter->setBrush(brush);
  381. }
  382. rect.setRect(item_x, bottom - h, item_w, h);
  383. _painter->drawRect(rect);
  384. previous_x = item_r;
  385. previous_h = h;
  386. }
  387. }*/
  388. }
  389. }
  390. //if (!m_bReady.load(::std::memory_order_acquire))
  391. // paintBusyIndicator(_painter, currentScale);
  392. qreal top_width = width, bottom_width = width;
  393. int font_h = 0;
  394. if (!m_topDurationStr.isEmpty())
  395. {
  396. rect.setRect(0, m_boundingRect.top() - INDICATOR_SIZE, width - 3, m_boundingRect.height() + INDICATOR_SIZE_x2);
  397. if (m_timeUnits != EASY_GLOBALS.time_units)
  398. {
  399. m_timeUnits = EASY_GLOBALS.time_units;
  400. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3);
  401. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3);
  402. }
  403. auto fm = _painter->fontMetrics();
  404. font_h = fm.height();
  405. //bottom_width -= fm.width(m_bottomDurationStr) + 7;
  406. top_width -= fm.width(m_topDurationStr) + 7;
  407. _painter->setPen(m_topDuration < m_maxDuration ? Qt::darkRed : Qt::black);
  408. _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_topDurationStr);
  409. rect.setRect(0, bottom, width - 3, font_h);
  410. _painter->setPen(m_bottomDuration > m_minDuration ? Qt::darkRed : Qt::black);
  411. _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_bottomDurationStr);
  412. }
  413. _painter->setPen(Qt::darkGray);
  414. _painter->drawLine(QLineF(0, bottom, bottom_width, bottom));
  415. _painter->drawLine(QLineF(0, m_boundingRect.top(), top_width, m_boundingRect.top()));
  416. paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, maxColumnHeight - HIST_COLUMN_MIN_HEIGHT, top_width, m_mouseY, dtime, font_h);
  417. if (m_bottomDuration < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topDuration)
  418. {
  419. // Draw marker displaying expected frame_time step
  420. const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomDuration) * coeff;
  421. _painter->setPen(Qt::DashLine);
  422. auto w = width;
  423. const auto boundary = INDICATOR_SIZE - font_h;
  424. if (h < (m_boundingRect.top() - boundary))
  425. w = top_width;
  426. else if (h > (bottom + boundary))
  427. w = bottom_width;
  428. _painter->drawLine(QLineF(0, h, w, h));
  429. }
  430. _painter->setPen(Qt::black);
  431. rect.setRect(0, bottom + 2, width, widget->defaultFontHeight());
  432. const auto eventsSize = m_pProfilerThread->events.size();
  433. _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | duration: %2 | profiled: %3 (%4%) | wait: %5 (%6%) | %7 frames | %8 blocks | %9 markers")
  434. .arg(m_threadName)
  435. .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadDuration))
  436. .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadProfiledTime))
  437. .arg(m_threadDuration ? QString::number(100. * (double)m_threadProfiledTime / (double)m_threadDuration, 'f', 2) : QString("0"))
  438. .arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, m_threadWaitTime))
  439. .arg(m_threadDuration ? QString::number(100. * (double)m_threadWaitTime / (double)m_threadDuration, 'f', 2) : QString("0"))
  440. .arg(m_pProfilerThread->frames_number)
  441. .arg(m_pProfilerThread->blocks_number - eventsSize)
  442. .arg(eventsSize));
  443. _painter->drawText(rect, Qt::AlignLeft, bindMode ? " MODE: zoom" : " MODE: overview");
  444. _painter->restore();
  445. }
  446. void EasyHistogramItem::paintById(QPainter* _painter)
  447. {
  448. const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
  449. const bool bindMode = widget->bindMode();
  450. const auto currentScale = widget->getWindowScale();
  451. const auto bottom = m_boundingRect.bottom();
  452. const auto width = m_boundingRect.width() * currentScale;
  453. const auto dtime = m_topDuration - m_bottomDuration;
  454. const auto maxColumnHeight = m_boundingRect.height();
  455. const auto coeff = (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.);
  456. QRectF rect;
  457. QBrush brush(Qt::SolidPattern);
  458. //QRgb previousColor = 0;
  459. _painter->save();
  460. _painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true);
  461. const auto& items = m_selectedBlocks;
  462. if (!items.empty())
  463. {
  464. _painter->setPen(Qt::NoPen);
  465. if (!bindMode)
  466. _painter->drawImage(0, m_boundingRect.top(), m_mainImage);
  467. else
  468. {
  469. const auto range = widget->sliderWidth();
  470. auto minimum = widget->value();
  471. const auto slider_k = widget->range() / range;
  472. /*if (false)//slider_k < 8)
  473. {
  474. _painter->setTransform(QTransform::fromScale(slider_k, 1), true);
  475. _painter->drawImage((widget->minimum() - minimum) * currentScale, m_boundingRect.top(), m_mainImage);
  476. _painter->setTransform(QTransform::fromScale(1. / slider_k, 1), true);
  477. }
  478. else*/
  479. {
  480. const auto deltaScale = slider_k / m_imageScale;
  481. _painter->setTransform(QTransform::fromScale(deltaScale, 1), true);
  482. _painter->drawImage((widget->minimum() + m_imageOrigin - minimum) * currentScale * m_imageScale, m_boundingRect.top(), m_mainImage);
  483. _painter->setTransform(QTransform::fromScale(1. / deltaScale, 1), true);
  484. }
  485. /*if (false)
  486. {
  487. minimum *= 1e3;
  488. const auto maximum = minimum + range * 1e3;
  489. const auto realScale = currentScale * slider_k;
  490. const auto offset = minimum * realScale;
  491. auto first = ::std::lower_bound(items.begin(), items.end(), minimum + EASY_GLOBALS.begin_time, [](::profiler::block_index_t _item, qreal _value)
  492. {
  493. return easyBlock(_item).tree.node->begin() < _value;
  494. });
  495. if (first != items.end())
  496. {
  497. if (first != items.begin())
  498. --first;
  499. }
  500. else
  501. {
  502. first = items.begin() + (items.size() - 1);
  503. }
  504. auto last = ::std::upper_bound(first, items.end(), maximum + EASY_GLOBALS.begin_time, [](qreal _value, ::profiler::block_index_t _item)
  505. {
  506. return _value < easyBlock(_item).tree.node->begin();
  507. });
  508. const auto n = static_cast<uint32_t>(::std::distance(first, last));
  509. if (n > 0)
  510. {
  511. const bool gotFrame = EASY_GLOBALS.frame_time > 1e-6f;
  512. qreal frameCoeff = 1;
  513. if (gotFrame)
  514. {
  515. if (EASY_GLOBALS.frame_time <= m_bottomDuration)
  516. frameCoeff = m_boundingRect.height();
  517. else
  518. frameCoeff = 0.9 / EASY_GLOBALS.frame_time;
  519. }
  520. auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1;
  521. auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / m_boundingRect.height();
  522. const auto draw = [this, &previousColor, &brush, &_painter](qreal x, qreal y, qreal w, qreal h, QRgb color)
  523. {
  524. m_spin.lock();
  525. if (previousColor != color)
  526. {
  527. // Set background color brush for rectangle
  528. previousColor = color;
  529. brush.setColor(QColor::fromRgba(0xc0000000 | color));
  530. _painter->setBrush(brush);
  531. }
  532. _painter->drawRect(QRectF(x, y, w, h));
  533. m_spin.unlock();
  534. };
  535. ::std::vector<::std::thread> threads;
  536. const auto n_threads = ::std::min(n, ::std::thread::hardware_concurrency());
  537. threads.reserve(n_threads);
  538. const auto n_items = n / n_threads;
  539. for (uint32_t i = 0; i < n_threads; ++i)
  540. {
  541. auto begin = first + i * n_items;
  542. threads.emplace_back([this, &draw, &maximum, &minimum, &realScale, &offset, &coeff, &calculate_color, &k, &bottom, &maxColumnHeight](decltype(begin) it, decltype(begin) end)
  543. {
  544. qreal previous_x = -1e30, previous_h = -1e30;
  545. //for (auto it = first, end = items.end(); it != end; ++it)
  546. for (; it != end; ++it)
  547. {
  548. // Draw rectangle
  549. const auto item = easyBlock(*it).tree.node;
  550. const auto beginTime = item->begin() - EASY_GLOBALS.begin_time;
  551. if (beginTime > maximum)
  552. break;
  553. const auto endTime = item->end() - EASY_GLOBALS.begin_time;
  554. if (endTime < minimum)
  555. continue;
  556. const qreal duration = item->duration() * 1e-3;
  557. const qreal item_x = (beginTime * realScale - offset) * 1e-3;
  558. const qreal item_w = ::std::max(duration * realScale, 1.0);
  559. const qreal item_r = item_x + item_w;
  560. const qreal h = duration <= m_bottomDuration ? HIST_COLUMN_MIN_HEIGHT :
  561. (duration > m_topDuration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (duration - m_bottomDuration) * coeff));
  562. if (h < previous_h && item_r < previous_x)
  563. continue;
  564. const auto col = calculate_color(h, duration, k);
  565. const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb();
  566. draw(item_x, bottom - h, item_w, h, color);
  567. //if (previousColor != color)
  568. //{
  569. // // Set background color brush for rectangle
  570. // previousColor = color;
  571. // brush.setColor(QColor::fromRgba(0xc0000000 | color));
  572. // _painter->setBrush(brush);
  573. //}
  574. //rect.setRect(item_x, bottom - h, item_w, h);
  575. //_painter->drawRect(rect);
  576. previous_x = item_r;
  577. previous_h = h;
  578. }
  579. }, begin, i == (n_threads - 1) ? items.end() : begin + n_items);
  580. }
  581. for (auto& t : threads)
  582. t.join();
  583. }
  584. }*/
  585. }
  586. }
  587. //if (!m_bReady.load(::std::memory_order_acquire))
  588. // paintBusyIndicator(_painter, currentScale);
  589. qreal top_width = width, bottom_width = width;
  590. int font_h = 0;
  591. if (!m_topDurationStr.isEmpty())
  592. {
  593. rect.setRect(0, m_boundingRect.top() - INDICATOR_SIZE, width - 3, m_boundingRect.height() + INDICATOR_SIZE_x2);
  594. if (m_timeUnits != EASY_GLOBALS.time_units)
  595. {
  596. m_timeUnits = EASY_GLOBALS.time_units;
  597. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3);
  598. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3);
  599. }
  600. auto fm = _painter->fontMetrics();
  601. font_h = fm.height();
  602. //bottom_width -= fm.width(m_bottomDurationStr) + 7;
  603. top_width -= fm.width(m_topDurationStr) + 7;
  604. _painter->setPen(Qt::black);
  605. _painter->setPen(m_topDuration < m_maxDuration ? Qt::darkRed : Qt::black);
  606. _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_topDurationStr);
  607. rect.setRect(0, bottom, width - 3, font_h);
  608. _painter->setPen(m_bottomDuration > m_minDuration ? Qt::darkRed : Qt::black);
  609. _painter->drawText(rect, Qt::AlignRight | Qt::AlignTop, m_bottomDurationStr);
  610. }
  611. _painter->setPen(Qt::darkGray);
  612. _painter->drawLine(QLineF(0, bottom, bottom_width, bottom));
  613. _painter->drawLine(QLineF(0, m_boundingRect.top(), top_width, m_boundingRect.top()));
  614. paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, maxColumnHeight - HIST_COLUMN_MIN_HEIGHT, top_width, m_mouseY, dtime, font_h);
  615. if (m_bottomDuration < EASY_GLOBALS.frame_time && EASY_GLOBALS.frame_time < m_topDuration)
  616. {
  617. // Draw marker displaying required frame_time step
  618. const auto h = bottom - (EASY_GLOBALS.frame_time - m_bottomDuration) * coeff;
  619. _painter->setPen(Qt::DashLine);
  620. auto w = width;
  621. const auto boundary = INDICATOR_SIZE - font_h;
  622. if (h < (m_boundingRect.top() - boundary))
  623. w = top_width;
  624. else if (h >(bottom + boundary))
  625. w = bottom_width;
  626. _painter->drawLine(QLineF(0, h, w, h));
  627. }
  628. _painter->setPen(Qt::black);
  629. rect.setRect(0, bottom + 2, width, widget->defaultFontHeight());
  630. if (!m_selectedBlocks.empty())
  631. {
  632. _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | %2 | %3 calls | %4% of thread profiled time")
  633. .arg(m_threadName).arg(m_blockName).arg(m_selectedBlocks.size())
  634. .arg(m_threadProfiledTime ? QString::number(100. * (double)m_blockTotalDuraion / (double)m_threadProfiledTime, 'f', 2) : QString("100")));
  635. }
  636. else
  637. {
  638. _painter->drawText(rect, Qt::AlignHCenter | Qt::TextDontClip, QString("%1 | %2 | 0 calls").arg(m_threadName).arg(m_blockName));
  639. }
  640. _painter->drawText(rect, Qt::AlignLeft, bindMode ? " MODE: zoom" : " MODE: overview");
  641. _painter->restore();
  642. }
  643. ::profiler::thread_id_t EasyHistogramItem::threadId() const
  644. {
  645. return m_threadId;
  646. }
  647. void EasyHistogramItem::setBoundingRect(const QRectF& _rect)
  648. {
  649. m_boundingRect = _rect;
  650. }
  651. void EasyHistogramItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h)
  652. {
  653. m_boundingRect.setRect(x, y, w, h);
  654. }
  655. void EasyHistogramItem::rebuildSource(HistRegime _regime)
  656. {
  657. if (m_regime == _regime)
  658. rebuildSource();
  659. }
  660. void EasyHistogramItem::rebuildSource()
  661. {
  662. if (m_regime == Hist_Id)
  663. {
  664. m_regime = Hist_Pointer;
  665. setSource(m_threadId, m_blockId);
  666. }
  667. else
  668. {
  669. m_regime = Hist_Id;
  670. setSource(m_threadId, m_pSource);
  671. }
  672. }
  673. void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items)
  674. {
  675. if (m_regime == Hist_Pointer && m_threadId == _thread_id && m_pSource == _items)
  676. return;
  677. m_timer.stop();
  678. m_boundaryTimer.stop();
  679. m_bReady.store(true, ::std::memory_order_release);
  680. if (m_workerThread.joinable())
  681. m_workerThread.join();
  682. m_blockName.clear();
  683. m_blockTotalDuraion = 0;
  684. delete m_workerImage;
  685. m_workerImage = nullptr;
  686. m_imageOriginUpdate = m_imageOrigin = 0;
  687. m_imageScaleUpdate = m_imageScale = 1;
  688. m_selectedBlocks.clear();
  689. { ::profiler::BlocksTree::children_t().swap(m_selectedBlocks); }
  690. m_bPermitImageUpdate = false;
  691. m_regime = Hist_Pointer;
  692. m_pSource = _items;
  693. m_threadId = _thread_id;
  694. ::profiler_gui::set_max(m_blockId);
  695. if (m_pSource != nullptr)
  696. {
  697. if (m_pSource->empty())
  698. {
  699. m_pSource = nullptr;
  700. }
  701. else
  702. {
  703. const auto& root = EASY_GLOBALS.profiler_blocks[_thread_id];
  704. m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id);
  705. if (root.children.empty())
  706. m_threadDuration = 0;
  707. else
  708. m_threadDuration = easyBlock(root.children.back()).tree.node->end() - easyBlock(root.children.front()).tree.node->begin();
  709. m_threadProfiledTime = root.profiled_time;
  710. m_threadWaitTime = root.wait_time;
  711. m_pProfilerThread = &root;
  712. m_timeUnits = EASY_GLOBALS.time_units;
  713. m_bReady.store(false, ::std::memory_order_release);
  714. m_workerThread = ::std::thread([this](const ::profiler_gui::EasyItems* _source)
  715. {
  716. m_maxDuration = 0;
  717. m_minDuration = 1e30;
  718. bool empty = true;
  719. for (const auto& item : *_source)
  720. {
  721. if (m_bReady.load(::std::memory_order_acquire))
  722. return;
  723. if (easyDescriptor(easyBlock(item.block).tree.node->id()).type() == ::profiler::BLOCK_TYPE_EVENT)
  724. continue;
  725. const auto w = item.width();
  726. if (w > m_maxDuration)
  727. m_maxDuration = w;
  728. if (w < m_minDuration)
  729. m_minDuration = w;
  730. empty = false;
  731. }
  732. if ((m_maxDuration - m_minDuration) < 1e-3)
  733. {
  734. if (m_minDuration > 0.1)
  735. {
  736. m_minDuration -= 0.1;
  737. }
  738. else
  739. {
  740. m_maxDuration = 0.1;
  741. m_minDuration = 0;
  742. }
  743. }
  744. m_topDuration = m_maxDuration;
  745. m_bottomDuration = m_minDuration;
  746. if (!empty)
  747. {
  748. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3);
  749. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3);
  750. }
  751. else
  752. {
  753. m_topDurationStr.clear();
  754. m_bottomDurationStr.clear();
  755. }
  756. m_bReady.store(true, ::std::memory_order_release);
  757. }, m_pSource);
  758. m_timeouts = 3;
  759. m_timer.start(WORKER_THREAD_CHECK_INTERVAL);
  760. show();
  761. }
  762. }
  763. if (m_pSource == nullptr)
  764. {
  765. m_pProfilerThread = nullptr;
  766. m_topDurationStr.clear();
  767. m_bottomDurationStr.clear();
  768. m_threadName.clear();
  769. hide();
  770. }
  771. }
  772. void EasyHistogramItem::setSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id)
  773. {
  774. if (m_regime == Hist_Id && m_threadId == _thread_id && m_blockId == _block_id)
  775. return;
  776. 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()
  777. m_regime = Hist_Id;
  778. m_timer.stop();
  779. m_boundaryTimer.stop();
  780. m_bReady.store(true, ::std::memory_order_release);
  781. if (m_workerThread.joinable())
  782. m_workerThread.join();
  783. m_pSource = nullptr;
  784. m_topDurationStr.clear();
  785. m_bottomDurationStr.clear();
  786. m_blockName.clear();
  787. m_blockTotalDuraion = 0;
  788. delete m_workerImage;
  789. m_workerImage = nullptr;
  790. m_imageOriginUpdate = m_imageOrigin = 0;
  791. m_imageScaleUpdate = m_imageScale = 1;
  792. m_selectedBlocks.clear();
  793. { ::profiler::BlocksTree::children_t().swap(m_selectedBlocks); }
  794. m_threadId = _thread_id;
  795. m_blockId = _block_id;
  796. if (m_threadId != 0 && !::profiler_gui::is_max(m_blockId))
  797. {
  798. m_blockName = ::profiler_gui::toUnicode(easyDescriptor(m_blockId).name());
  799. const auto& root = EASY_GLOBALS.profiler_blocks[_thread_id];
  800. m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root, EASY_GLOBALS.hex_thread_id);
  801. m_pProfilerThread = &root;
  802. m_timeUnits = EASY_GLOBALS.time_units;
  803. if (root.children.empty())
  804. {
  805. m_threadDuration = 0;
  806. m_threadProfiledTime = 0;
  807. m_threadWaitTime = 0;
  808. m_topDuration = m_maxDuration = 0;
  809. m_bottomDuration = m_minDuration = 1e30;
  810. m_bPermitImageUpdate = true;
  811. m_bReady.store(true, ::std::memory_order_release);
  812. }
  813. else
  814. {
  815. m_threadDuration = easyBlock(root.children.back()).tree.node->end() - easyBlock(root.children.front()).tree.node->begin();
  816. m_threadProfiledTime = root.profiled_time;
  817. m_threadWaitTime = root.wait_time;
  818. m_bReady.store(false, ::std::memory_order_release);
  819. m_workerThread = ::std::thread([this](decltype(root) profiler_thread, ::profiler::block_index_t selected_block, bool _showOnlyTopLevelBlocks)
  820. {
  821. typedef ::std::vector<::std::pair<::profiler::block_index_t, ::profiler::block_index_t> > Stack;
  822. m_maxDuration = 0;
  823. m_minDuration = 1e30;
  824. //const auto& profiler_thread = EASY_GLOBALS.profiler_blocks[m_threadId];
  825. Stack stack;
  826. stack.reserve(profiler_thread.depth);
  827. const bool has_selected_block = !::profiler_gui::is_max(selected_block);
  828. for (auto frame : profiler_thread.children)
  829. {
  830. const auto& frame_block = easyBlock(frame).tree;
  831. if (frame_block.node->id() == m_blockId || (!has_selected_block && m_blockId == easyDescriptor(frame_block.node->id()).id()))
  832. {
  833. m_selectedBlocks.push_back(frame);
  834. const auto w = frame_block.node->duration();
  835. if (w > m_maxDuration)
  836. m_maxDuration = w;
  837. if (w < m_minDuration)
  838. m_minDuration = w;
  839. m_blockTotalDuraion += w;
  840. }
  841. if (_showOnlyTopLevelBlocks)
  842. continue;
  843. stack.push_back(::std::make_pair(frame, 0U));
  844. while (!stack.empty())
  845. {
  846. if (m_bReady.load(::std::memory_order_acquire))
  847. return;
  848. auto& top = stack.back();
  849. const auto& top_children = easyBlock(top.first).tree.children;
  850. const auto stack_size = stack.size();
  851. for (auto end = top_children.size(); top.second < end; ++top.second)
  852. {
  853. if (m_bReady.load(::std::memory_order_acquire))
  854. return;
  855. const auto child_index = top_children[top.second];
  856. const auto& child = easyBlock(child_index).tree;
  857. if (child.node->id() == m_blockId || (!has_selected_block && m_blockId == easyDescriptor(child.node->id()).id()))
  858. {
  859. m_selectedBlocks.push_back(child_index);
  860. const auto w = child.node->duration();
  861. if (w > m_maxDuration)
  862. m_maxDuration = w;
  863. if (w < m_minDuration)
  864. m_minDuration = w;
  865. m_blockTotalDuraion += w;
  866. }
  867. if (!child.children.empty())
  868. {
  869. ++top.second;
  870. stack.push_back(::std::make_pair(child_index, 0U));
  871. break;
  872. }
  873. }
  874. if (stack_size == stack.size())
  875. {
  876. stack.pop_back();
  877. }
  878. }
  879. }
  880. if (m_selectedBlocks.empty())
  881. {
  882. m_topDurationStr.clear();
  883. m_bottomDurationStr.clear();
  884. }
  885. else
  886. {
  887. if (has_selected_block)
  888. {
  889. const auto& item = easyBlock(selected_block).tree;
  890. if (*item.node->name() != 0)
  891. m_blockName = ::profiler_gui::toUnicode(item.node->name());
  892. }
  893. m_maxDuration *= 1e-3;
  894. m_minDuration *= 1e-3;
  895. if ((m_maxDuration - m_minDuration) < 1e-3)
  896. {
  897. if (m_minDuration > 0.1)
  898. {
  899. m_minDuration -= 0.1;
  900. }
  901. else
  902. {
  903. m_maxDuration = 0.1;
  904. m_minDuration = 0;
  905. }
  906. }
  907. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_maxDuration, 3);
  908. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_minDuration, 3);
  909. }
  910. m_topDuration = m_maxDuration;
  911. m_bottomDuration = m_minDuration;
  912. m_bReady.store(true, ::std::memory_order_release);
  913. }, std::ref(root), EASY_GLOBALS.selected_block, EASY_GLOBALS.display_only_frames_on_histogram);
  914. m_timeouts = 3;
  915. m_timer.start(WORKER_THREAD_CHECK_INTERVAL);
  916. }
  917. show();
  918. }
  919. else
  920. {
  921. m_pProfilerThread = nullptr;
  922. m_threadName.clear();
  923. hide();
  924. }
  925. }
  926. //////////////////////////////////////////////////////////////////////////
  927. void EasyHistogramItem::validateName()
  928. {
  929. if (m_threadName.isEmpty())
  930. return;
  931. m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.profiler_blocks[m_threadId], EASY_GLOBALS.hex_thread_id);
  932. }
  933. //////////////////////////////////////////////////////////////////////////
  934. void EasyHistogramItem::onTimeout()
  935. {
  936. if (!isVisible())
  937. {
  938. m_timer.stop();
  939. return;
  940. }
  941. if (++m_timeouts > 8)
  942. m_timeouts = 3;
  943. if (m_bReady.load(::std::memory_order_acquire))
  944. {
  945. m_timer.stop();
  946. if (!m_bPermitImageUpdate)
  947. {
  948. // Worker thread have finished parsing input data (when setSource(_block_id) was called)
  949. m_bPermitImageUpdate = true; // From now we can update an image
  950. updateImage();
  951. }
  952. else
  953. {
  954. // Image updated
  955. if (m_workerThread.joinable())
  956. m_workerThread.join();
  957. m_workerImage->swap(m_mainImage);
  958. delete m_workerImage;
  959. m_workerImage = nullptr;
  960. m_imageOriginUpdate = m_imageOrigin = m_workerImageOrigin;
  961. m_imageScaleUpdate = m_imageScale = m_workerImageScale;
  962. if (EASY_GLOBALS.auto_adjust_histogram_height && !m_topDurationStr.isEmpty())
  963. {
  964. m_topDuration = m_workerTopDuration;
  965. m_bottomDuration = m_workerBottomDuration;
  966. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3);
  967. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3);
  968. }
  969. }
  970. }
  971. scene()->update();
  972. }
  973. //////////////////////////////////////////////////////////////////////////
  974. void EasyHistogramItem::pickTopBoundary(qreal _y)
  975. {
  976. if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty())
  977. {
  978. m_topDuration = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT);
  979. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3);
  980. m_boundaryTimer.stop();
  981. updateImage();
  982. scene()->update(); // to update top-boundary text right now
  983. }
  984. }
  985. void EasyHistogramItem::increaseTopBoundary()
  986. {
  987. if (m_bPermitImageUpdate && m_topDuration < m_maxDuration && !m_topDurationStr.isEmpty())
  988. {
  989. auto step = 0.05 * (m_maxDuration - m_bottomDuration);
  990. if (m_topDuration < (m_bottomDuration + 1.25 * step))
  991. step = 0.1 * (m_topDuration - m_bottomDuration);
  992. m_topDuration = std::min(m_maxDuration, m_topDuration + step);
  993. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3);
  994. updateImage();
  995. scene()->update(); // to update top-boundary text right now
  996. m_boundaryTimer.stop();
  997. m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL);
  998. }
  999. }
  1000. void EasyHistogramItem::decreaseTopBoundary()
  1001. {
  1002. if (m_bPermitImageUpdate && m_topDuration > m_bottomDuration && !m_topDurationStr.isEmpty())
  1003. {
  1004. auto step = 0.05 * (m_maxDuration - m_bottomDuration);
  1005. if (m_topDuration < (m_bottomDuration + 1.25 * step))
  1006. step = std::max(0.1 * (m_topDuration - m_bottomDuration), 0.3);
  1007. if (m_topDuration > (m_bottomDuration + 1.25 * step))
  1008. {
  1009. m_topDuration = std::max(m_bottomDuration + step, m_topDuration - step);
  1010. m_topDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_topDuration, 3);
  1011. scene()->update(); // to update top-boundary text right now
  1012. m_boundaryTimer.stop();
  1013. m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL);
  1014. }
  1015. }
  1016. }
  1017. //////////////////////////////////////////////////////////////////////////
  1018. void EasyHistogramItem::pickBottomBoundary(qreal _y)
  1019. {
  1020. if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_bottomDurationStr.isEmpty())
  1021. {
  1022. m_bottomDuration = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT);
  1023. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3);
  1024. m_boundaryTimer.stop();
  1025. updateImage();
  1026. scene()->update(); // to update top-boundary text right now
  1027. }
  1028. }
  1029. void EasyHistogramItem::increaseBottomBoundary()
  1030. {
  1031. if (m_bPermitImageUpdate && m_bottomDuration < m_topDuration && !m_bottomDurationStr.isEmpty())
  1032. {
  1033. auto step = 0.05 * (m_topDuration - m_minDuration);
  1034. if (m_bottomDuration > (m_topDuration - 1.25 * step))
  1035. step = 0.1 * (m_topDuration - m_bottomDuration);
  1036. if (m_bottomDuration < (m_topDuration - 1.25 * step))
  1037. {
  1038. m_bottomDuration = std::min(m_topDuration - step, m_bottomDuration + step);
  1039. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3);
  1040. scene()->update(); // to update bottom-boundary text right now
  1041. m_boundaryTimer.stop();
  1042. m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL);
  1043. }
  1044. }
  1045. }
  1046. void EasyHistogramItem::decreaseBottomBoundary()
  1047. {
  1048. if (m_bPermitImageUpdate && m_bottomDuration > m_minDuration && !m_bottomDurationStr.isEmpty())
  1049. {
  1050. auto step = 0.05 * (m_topDuration - m_minDuration);
  1051. if (m_bottomDuration > (m_topDuration - 1.25 * step))
  1052. step = std::max(0.1 * (m_topDuration - m_bottomDuration), 0.3);
  1053. m_bottomDuration = std::max(m_minDuration, m_bottomDuration - step);
  1054. m_bottomDurationStr = ::profiler_gui::timeStringReal(m_timeUnits, m_bottomDuration, 3);
  1055. scene()->update(); // to update top-boundary text right now
  1056. m_boundaryTimer.stop();
  1057. m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL);
  1058. }
  1059. }
  1060. //////////////////////////////////////////////////////////////////////////
  1061. void EasyHistogramItem::setMouseY(qreal _mouseY)
  1062. {
  1063. m_mouseY = _mouseY;
  1064. }
  1065. void EasyHistogramItem::pickFrameTime(qreal _y) const
  1066. {
  1067. if (m_bPermitImageUpdate && m_boundingRect.top() < _y && _y < m_boundingRect.bottom() && !m_topDurationStr.isEmpty())
  1068. {
  1069. EASY_GLOBALS.frame_time = m_bottomDuration + (m_topDuration - m_bottomDuration) * (m_boundingRect.bottom() - _y) / (m_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT);
  1070. emit EASY_GLOBALS.events.expectedFrameTimeChanged();
  1071. }
  1072. }
  1073. //////////////////////////////////////////////////////////////////////////
  1074. void EasyHistogramItem::onValueChanged()
  1075. {
  1076. const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
  1077. if (!widget->bindMode())
  1078. return;
  1079. m_boundaryTimer.stop();
  1080. const auto sliderWidth_inv = 1.0 / widget->sliderWidth();
  1081. const auto k = widget->range() * sliderWidth_inv;
  1082. const auto deltaScale = m_imageScaleUpdate < k ? (k / m_imageScaleUpdate) : (m_imageScaleUpdate / k);
  1083. if (deltaScale > 4) {
  1084. updateImage();
  1085. return;
  1086. }
  1087. const auto deltaOffset = (widget->value() - m_imageOriginUpdate) * sliderWidth_inv;
  1088. if (deltaOffset < 1.5 || deltaOffset > 4.5) {
  1089. updateImage();
  1090. return;
  1091. }
  1092. m_boundaryTimer.start(BOUNDARY_TIMER_INTERVAL);
  1093. }
  1094. //////////////////////////////////////////////////////////////////////////
  1095. void EasyHistogramItem::onModeChanged()
  1096. {
  1097. if (!m_bPermitImageUpdate)
  1098. return;
  1099. const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
  1100. if (!widget->bindMode() && EASY_GLOBALS.auto_adjust_histogram_height)
  1101. {
  1102. m_topDuration = m_maxDuration;
  1103. m_bottomDuration = m_minDuration;
  1104. }
  1105. m_boundaryTimer.stop();
  1106. updateImage();
  1107. }
  1108. //////////////////////////////////////////////////////////////////////////
  1109. void EasyHistogramItem::cancelImageUpdate()
  1110. {
  1111. if (!m_bPermitImageUpdate)
  1112. return;
  1113. m_bReady.store(true, ::std::memory_order_release);
  1114. if (m_workerThread.joinable())
  1115. m_workerThread.join();
  1116. m_bReady.store(false, ::std::memory_order_release);
  1117. delete m_workerImage;
  1118. m_workerImage = nullptr;
  1119. m_imageOriginUpdate = m_imageOrigin;
  1120. m_imageScaleUpdate = m_imageScale;
  1121. m_timer.stop();
  1122. }
  1123. void EasyHistogramItem::updateImage()
  1124. {
  1125. if (!m_bPermitImageUpdate)
  1126. return;
  1127. const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
  1128. m_bReady.store(true, ::std::memory_order_release);
  1129. if (m_workerThread.joinable())
  1130. m_workerThread.join();
  1131. m_bReady.store(false, ::std::memory_order_release);
  1132. delete m_workerImage;
  1133. m_workerImage = nullptr;
  1134. m_imageScaleUpdate = widget->range() / widget->sliderWidth();
  1135. m_imageOriginUpdate = widget->bindMode() ? (widget->value() - widget->sliderWidth() * 3) : widget->minimum();
  1136. m_workerThread = ::std::thread([this](QRectF _boundingRect, HistRegime _regime, qreal _current_scale,
  1137. qreal _minimum, qreal _maximum, qreal _range, qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration,
  1138. bool _bindMode, float _frame_time, ::profiler::timestamp_t _begin_time, qreal _origin, bool _autoAdjustHist)
  1139. {
  1140. updateImage(_boundingRect, _regime, _current_scale, _minimum, _maximum, _range, _value, _width, _top_duration, _bottom_duration, _bindMode, _frame_time, _begin_time, _origin, _autoAdjustHist);
  1141. m_bReady.store(true, ::std::memory_order_release);
  1142. }, m_boundingRect, m_regime, widget->getWindowScale(), widget->minimum(), widget->maximum(), widget->range(), widget->value(), widget->sliderWidth(),
  1143. m_topDuration, m_bottomDuration, widget->bindMode(), EASY_GLOBALS.frame_time, EASY_GLOBALS.begin_time, m_imageOriginUpdate, EASY_GLOBALS.auto_adjust_histogram_height);
  1144. m_timeouts = 3;
  1145. m_timer.start(WORKER_THREAD_CHECK_INTERVAL);
  1146. }
  1147. void EasyHistogramItem::updateImage(QRectF _boundingRect, HistRegime _regime, qreal _current_scale,
  1148. qreal _minimum, qreal _maximum, qreal _range,
  1149. qreal _value, qreal _width, qreal _top_duration, qreal _bottom_duration,
  1150. bool _bindMode, float _frame_time, ::profiler::timestamp_t _begin_time,
  1151. qreal _origin, bool _autoAdjustHist)
  1152. {
  1153. const auto bottom = _boundingRect.height();//_boundingRect.bottom();
  1154. const auto screenWidth = _boundingRect.width() * _current_scale;
  1155. const auto maxColumnHeight = _boundingRect.height();
  1156. const auto viewScale = _range / _width;
  1157. if (_bindMode)
  1158. {
  1159. m_workerImageScale = viewScale;
  1160. m_workerImageOrigin = _value - _width * 3;
  1161. m_workerImage = new QImage(screenWidth * 7 + 0.5, _boundingRect.height(), QImage::Format_ARGB32);
  1162. }
  1163. else
  1164. {
  1165. m_workerImageScale = 1;
  1166. m_workerImageOrigin = _minimum;
  1167. m_workerImage = new QImage(screenWidth + 0.5, _boundingRect.height(), QImage::Format_ARGB32);
  1168. }
  1169. m_workerImage->fill(0);
  1170. QPainter p(m_workerImage);
  1171. p.setPen(Qt::NoPen);
  1172. QRectF rect;
  1173. QBrush brush(Qt::SolidPattern);
  1174. QRgb previousColor = 0;
  1175. qreal previous_x = -1e30, previous_h = -1e30, offset = 0.;
  1176. auto realScale = _current_scale;
  1177. const bool gotFrame = _frame_time > 1e-6f;
  1178. qreal frameCoeff = 1;
  1179. if (gotFrame)
  1180. {
  1181. if (_frame_time <= _bottom_duration)
  1182. frameCoeff = _boundingRect.height();
  1183. else
  1184. frameCoeff = 0.9 / _frame_time;
  1185. }
  1186. auto const calculate_color = gotFrame ? calculate_color2 : calculate_color1;
  1187. auto const k = gotFrame ? sqr(sqr(frameCoeff)) : 1.0 / _boundingRect.height();
  1188. if (_regime == Hist_Pointer)
  1189. {
  1190. const auto& items = *m_pSource;
  1191. if (items.empty())
  1192. return;
  1193. auto first = items.begin();
  1194. if (_bindMode)
  1195. {
  1196. _minimum = m_workerImageOrigin;
  1197. _maximum = m_workerImageOrigin + _width * 7;
  1198. realScale *= viewScale;
  1199. offset = _minimum * realScale;
  1200. first = ::std::lower_bound(items.begin(), items.end(), _minimum, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value)
  1201. {
  1202. return _item.left() < _value;
  1203. });
  1204. if (first != items.end())
  1205. {
  1206. if (first != items.begin())
  1207. --first;
  1208. }
  1209. else
  1210. {
  1211. first = items.begin() + items.size() - 1;
  1212. }
  1213. if (_autoAdjustHist)
  1214. {
  1215. const auto maxVal = _value + _width;
  1216. decltype(_top_duration) maxDuration = 0;
  1217. decltype(_bottom_duration) minDuration = 1e30;
  1218. size_t iterations = 0;
  1219. for (auto it = first, end = items.end(); it != end; ++it)
  1220. {
  1221. // Draw rectangle
  1222. if (it->left() > maxVal)
  1223. break;
  1224. if (it->right() < _value)
  1225. continue;
  1226. if (maxDuration < it->width())
  1227. maxDuration = it->width();
  1228. if (minDuration > it->width())
  1229. minDuration = it->width();
  1230. ++iterations;
  1231. }
  1232. if (iterations)
  1233. {
  1234. _top_duration = maxDuration;
  1235. _bottom_duration = minDuration;
  1236. if ((_top_duration - _bottom_duration) < 1e-3)
  1237. {
  1238. if (_bottom_duration > 0.1)
  1239. {
  1240. _bottom_duration -= 0.1;
  1241. }
  1242. else
  1243. {
  1244. _top_duration = 0.1;
  1245. _bottom_duration = 0;
  1246. }
  1247. }
  1248. }
  1249. }
  1250. }
  1251. const auto dtime = _top_duration - _bottom_duration;
  1252. const auto coeff = (_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.);
  1253. for (auto it = first, end = items.end(); it != end; ++it)
  1254. {
  1255. // Draw rectangle
  1256. if (it->left() > _maximum)
  1257. break;
  1258. if (it->right() < _minimum)
  1259. continue;
  1260. const qreal item_x = it->left() * realScale - offset;
  1261. const qreal item_w = ::std::max(it->width() * realScale, 1.0);
  1262. const qreal item_r = item_x + item_w;
  1263. const qreal h = it->width() <= _bottom_duration ? HIST_COLUMN_MIN_HEIGHT :
  1264. (it->width() > _top_duration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (it->width() - _bottom_duration) * coeff));
  1265. if (h < previous_h && item_r < previous_x)
  1266. continue;
  1267. const auto col = calculate_color(h, it->width(), k);
  1268. const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb();
  1269. if (previousColor != color)
  1270. {
  1271. // Set background color brush for rectangle
  1272. previousColor = color;
  1273. brush.setColor(QColor::fromRgba(0xc0000000 | color));
  1274. p.setBrush(brush);
  1275. }
  1276. rect.setRect(item_x, bottom - h, item_w, h);
  1277. p.drawRect(rect);
  1278. previous_x = item_r;
  1279. previous_h = h;
  1280. }
  1281. }
  1282. else
  1283. {
  1284. auto first = m_selectedBlocks.begin();
  1285. if (_bindMode)
  1286. {
  1287. _minimum = m_workerImageOrigin;
  1288. _maximum = m_workerImageOrigin + _width * 7;
  1289. realScale *= viewScale;
  1290. offset = _minimum * 1e3 * realScale;
  1291. first = ::std::lower_bound(m_selectedBlocks.begin(), m_selectedBlocks.end(), _minimum * 1e3 + _begin_time, [](::profiler::block_index_t _item, qreal _value)
  1292. {
  1293. return easyBlock(_item).tree.node->begin() < _value;
  1294. });
  1295. if (first != m_selectedBlocks.end())
  1296. {
  1297. if (first != m_selectedBlocks.begin())
  1298. --first;
  1299. }
  1300. else
  1301. {
  1302. first = m_selectedBlocks.begin() + m_selectedBlocks.size() - 1;
  1303. }
  1304. _minimum *= 1e3;
  1305. _maximum *= 1e3;
  1306. if (_autoAdjustHist)
  1307. {
  1308. const auto minVal = _value * 1e3, maxVal = (_value + _width) * 1e3;
  1309. decltype(_top_duration) maxDuration = 0;
  1310. decltype(_bottom_duration) minDuration = 1e30;
  1311. size_t iterations = 0;
  1312. for (auto it = first, end = m_selectedBlocks.end(); it != end; ++it)
  1313. {
  1314. const auto item = easyBlock(*it).tree.node;
  1315. const auto beginTime = item->begin() - _begin_time;
  1316. if (beginTime > maxVal)
  1317. break;
  1318. const auto endTime = item->end() - _begin_time;
  1319. if (endTime < minVal)
  1320. continue;
  1321. const qreal duration = item->duration() * 1e-3;
  1322. if (maxDuration < duration)
  1323. maxDuration = duration;
  1324. if (minDuration > duration)
  1325. minDuration = duration;
  1326. ++iterations;
  1327. }
  1328. if (iterations)
  1329. {
  1330. _top_duration = maxDuration;
  1331. _bottom_duration = minDuration;
  1332. if ((_top_duration - _bottom_duration) < 1e-3)
  1333. {
  1334. if (_bottom_duration > 0.1)
  1335. {
  1336. _bottom_duration -= 0.1;
  1337. }
  1338. else
  1339. {
  1340. _top_duration = 0.1;
  1341. _bottom_duration = 0;
  1342. }
  1343. }
  1344. }
  1345. }
  1346. }
  1347. else
  1348. {
  1349. _minimum *= 1e3;
  1350. _maximum *= 1e3;
  1351. }
  1352. const auto dtime = _top_duration - _bottom_duration;
  1353. const auto coeff = (_boundingRect.height() - HIST_COLUMN_MIN_HEIGHT) / (dtime > 1e-3 ? dtime : 1.);
  1354. for (auto it = first, end = m_selectedBlocks.end(); it != end; ++it)
  1355. {
  1356. // Draw rectangle
  1357. const auto item = easyBlock(*it).tree.node;
  1358. const auto beginTime = item->begin() - _begin_time;
  1359. if (beginTime > _maximum)
  1360. break;
  1361. const auto endTime = item->end() - _begin_time;
  1362. if (endTime < _minimum)
  1363. continue;
  1364. const qreal duration = item->duration() * 1e-3;
  1365. const qreal item_x = (beginTime * realScale - offset) * 1e-3;
  1366. const qreal item_w = ::std::max(duration * realScale, 1.0);
  1367. const qreal item_r = item_x + item_w;
  1368. const auto h = duration <= _bottom_duration ? HIST_COLUMN_MIN_HEIGHT :
  1369. (duration > _top_duration ? maxColumnHeight : (HIST_COLUMN_MIN_HEIGHT + (duration - _bottom_duration) * coeff));
  1370. if (h < previous_h && item_r < previous_x)
  1371. continue;
  1372. const auto col = calculate_color(h, duration, k);
  1373. const auto color = 0x00ffffff & QColor::fromHsvF((1.0 - col) * 0.375, 0.85, 0.85).rgb();
  1374. if (previousColor != color)
  1375. {
  1376. // Set background color brush for rectangle
  1377. previousColor = color;
  1378. brush.setColor(QColor::fromRgba(0xc0000000 | color));
  1379. p.setBrush(brush);
  1380. }
  1381. rect.setRect(item_x, bottom - h, item_w, h);
  1382. p.drawRect(rect);
  1383. previous_x = item_r;
  1384. previous_h = h;
  1385. }
  1386. }
  1387. m_workerTopDuration = _top_duration;
  1388. m_workerBottomDuration = _bottom_duration;
  1389. }
  1390. //////////////////////////////////////////////////////////////////////////
  1391. EasyGraphicsScrollbar::EasyGraphicsScrollbar(QWidget* _parent)
  1392. : Parent(_parent)
  1393. , m_minimumValue(0)
  1394. , m_maximumValue(500)
  1395. , m_value(10)
  1396. , m_windowScale(1)
  1397. , m_mouseButtons(Qt::NoButton)
  1398. , m_slider(nullptr)
  1399. , m_chronometerIndicator(nullptr)
  1400. , m_histogramItem(nullptr)
  1401. , m_defaultFontHeight(0)
  1402. , m_bScrolling(false)
  1403. , m_bBindMode(false)
  1404. , m_bLocked(false)
  1405. {
  1406. setCacheMode(QGraphicsView::CacheNone);
  1407. setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
  1408. //setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
  1409. setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
  1410. setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
  1411. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  1412. setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  1413. setContentsMargins(0, 0, 0, 0);
  1414. auto selfScene = new QGraphicsScene(this);
  1415. m_defaultFontHeight = QFontMetrics(selfScene->font()).height();
  1416. selfScene->setSceneRect(0, DEFAULT_TOP, 500, DEFAULT_HEIGHT + m_defaultFontHeight + 2);
  1417. setFixedHeight(DEFAULT_HEIGHT + m_defaultFontHeight + 2);
  1418. setScene(selfScene);
  1419. m_histogramItem = new EasyHistogramItem();
  1420. m_histogramItem->setPos(0, 0);
  1421. m_histogramItem->setBoundingRect(0, DEFAULT_TOP + INDICATOR_SIZE, scene()->width(), DEFAULT_HEIGHT - INDICATOR_SIZE_x2);
  1422. selfScene->addItem(m_histogramItem);
  1423. m_histogramItem->hide();
  1424. m_chronometerIndicator = new EasyGraphicsSliderItem(false);
  1425. m_chronometerIndicator->setPos(0, 0);
  1426. m_chronometerIndicator->setColor(0x40000000 | ::profiler_gui::CHRONOMETER_COLOR.rgba());
  1427. selfScene->addItem(m_chronometerIndicator);
  1428. m_chronometerIndicator->hide();
  1429. m_slider = new EasyGraphicsSliderItem(true);
  1430. m_slider->setPos(0, 0);
  1431. m_slider->setColor(0x40c0c0c0);
  1432. selfScene->addItem(m_slider);
  1433. m_slider->hide();
  1434. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, [this]()
  1435. {
  1436. if (m_histogramItem->isVisible())
  1437. {
  1438. m_histogramItem->updateImage();
  1439. scene()->update();
  1440. }
  1441. });
  1442. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::autoAdjustHistogramChanged, [this]()
  1443. {
  1444. if (m_histogramItem->isVisible())
  1445. m_histogramItem->onModeChanged();
  1446. });
  1447. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::displayOnlyFramesOnHistogramChanged, [this]()
  1448. {
  1449. if (m_histogramItem->isVisible())
  1450. m_histogramItem->rebuildSource(EasyHistogramItem::Hist_Id);
  1451. });
  1452. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged);
  1453. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged);
  1454. centerOn(0, 0);
  1455. }
  1456. EasyGraphicsScrollbar::~EasyGraphicsScrollbar()
  1457. {
  1458. }
  1459. //////////////////////////////////////////////////////////////////////////
  1460. void EasyGraphicsScrollbar::onThreadViewChanged()
  1461. {
  1462. if (m_histogramItem->isVisible())
  1463. {
  1464. m_histogramItem->validateName();
  1465. scene()->update();
  1466. }
  1467. }
  1468. //////////////////////////////////////////////////////////////////////////
  1469. void EasyGraphicsScrollbar::clear()
  1470. {
  1471. setHistogramSource(0, nullptr);
  1472. hideChrono();
  1473. setRange(0, 100);
  1474. setSliderWidth(2);
  1475. setValue(0);
  1476. }
  1477. //////////////////////////////////////////////////////////////////////////
  1478. bool EasyGraphicsScrollbar::bindMode() const
  1479. {
  1480. return m_bBindMode;
  1481. }
  1482. qreal EasyGraphicsScrollbar::getWindowScale() const
  1483. {
  1484. return m_windowScale;
  1485. }
  1486. ::profiler::thread_id_t EasyGraphicsScrollbar::hystThread() const
  1487. {
  1488. return m_histogramItem->threadId();
  1489. }
  1490. qreal EasyGraphicsScrollbar::minimum() const
  1491. {
  1492. return m_minimumValue;
  1493. }
  1494. qreal EasyGraphicsScrollbar::maximum() const
  1495. {
  1496. return m_maximumValue;
  1497. }
  1498. qreal EasyGraphicsScrollbar::range() const
  1499. {
  1500. return m_maximumValue - m_minimumValue;
  1501. }
  1502. qreal EasyGraphicsScrollbar::value() const
  1503. {
  1504. return m_value;
  1505. }
  1506. qreal EasyGraphicsScrollbar::sliderWidth() const
  1507. {
  1508. return m_slider->width();
  1509. }
  1510. qreal EasyGraphicsScrollbar::sliderHalfWidth() const
  1511. {
  1512. return m_slider->halfwidth();
  1513. }
  1514. int EasyGraphicsScrollbar::defaultFontHeight() const
  1515. {
  1516. return m_defaultFontHeight;
  1517. }
  1518. //////////////////////////////////////////////////////////////////////////
  1519. void EasyGraphicsScrollbar::setValue(qreal _value)
  1520. {
  1521. m_value = clamp(m_minimumValue, _value, ::std::max(m_minimumValue, m_maximumValue - m_slider->width()));
  1522. m_slider->setX(m_value + m_slider->halfwidth());
  1523. emit valueChanged(m_value);
  1524. if (m_histogramItem->isVisible())
  1525. m_histogramItem->onValueChanged();
  1526. }
  1527. void EasyGraphicsScrollbar::setRange(qreal _minValue, qreal _maxValue)
  1528. {
  1529. const auto oldRange = range();
  1530. const auto oldValue = oldRange < 1e-3 ? 0.0 : m_value / oldRange;
  1531. m_minimumValue = _minValue;
  1532. m_maximumValue = _maxValue;
  1533. scene()->setSceneRect(_minValue, DEFAULT_TOP, _maxValue - _minValue, DEFAULT_HEIGHT + m_defaultFontHeight + 4);
  1534. m_histogramItem->cancelImageUpdate();
  1535. m_histogramItem->setBoundingRect(_minValue, DEFAULT_TOP + INDICATOR_SIZE, _maxValue, DEFAULT_HEIGHT - INDICATOR_SIZE_x2);
  1536. emit rangeChanged();
  1537. setValue(_minValue + oldValue * range());
  1538. onWindowWidthChange(width());
  1539. if (m_histogramItem->isVisible())
  1540. m_histogramItem->updateImage();
  1541. }
  1542. void EasyGraphicsScrollbar::setSliderWidth(qreal _width)
  1543. {
  1544. m_slider->setWidth(_width);
  1545. setValue(m_value);
  1546. }
  1547. //////////////////////////////////////////////////////////////////////////
  1548. void EasyGraphicsScrollbar::setChronoPos(qreal _left, qreal _right)
  1549. {
  1550. m_chronometerIndicator->setWidth(_right - _left);
  1551. m_chronometerIndicator->setX(_left + m_chronometerIndicator->halfwidth());
  1552. }
  1553. void EasyGraphicsScrollbar::showChrono()
  1554. {
  1555. m_chronometerIndicator->show();
  1556. }
  1557. void EasyGraphicsScrollbar::hideChrono()
  1558. {
  1559. m_chronometerIndicator->hide();
  1560. }
  1561. //////////////////////////////////////////////////////////////////////////
  1562. void EasyGraphicsScrollbar::setHistogramSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items)
  1563. {
  1564. if (m_bLocked)
  1565. return;
  1566. m_histogramItem->setSource(_thread_id, _items);
  1567. m_slider->setVisible(m_histogramItem->isVisible());
  1568. scene()->update();
  1569. }
  1570. void EasyGraphicsScrollbar::setHistogramSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id)
  1571. {
  1572. if (m_bLocked)
  1573. return;
  1574. m_histogramItem->setSource(_thread_id, _block_id);
  1575. m_slider->setVisible(m_histogramItem->isVisible());
  1576. scene()->update();
  1577. }
  1578. //////////////////////////////////////////////////////////////////////////
  1579. void EasyGraphicsScrollbar::mousePressEvent(QMouseEvent* _event)
  1580. {
  1581. _event->accept();
  1582. m_mouseButtons = _event->buttons();
  1583. if (m_mouseButtons & Qt::LeftButton)
  1584. {
  1585. if (_event->modifiers() & Qt::ControlModifier)
  1586. {
  1587. m_histogramItem->pickBottomBoundary(mapToScene(_event->pos()).y());
  1588. }
  1589. else if (_event->modifiers() & Qt::ShiftModifier)
  1590. {
  1591. m_histogramItem->pickTopBoundary(mapToScene(_event->pos()).y());
  1592. }
  1593. else
  1594. {
  1595. m_bScrolling = true;
  1596. m_mousePressPos = _event->pos();
  1597. if (!m_bBindMode)
  1598. setValue(mapToScene(m_mousePressPos).x() - m_minimumValue - m_slider->halfwidth());
  1599. }
  1600. }
  1601. if (m_mouseButtons & Qt::RightButton)
  1602. {
  1603. if (_event->modifiers())
  1604. {
  1605. m_histogramItem->pickFrameTime(mapToScene(_event->pos()).y());
  1606. }
  1607. else
  1608. {
  1609. m_bBindMode = !m_bBindMode;
  1610. if (m_histogramItem->isVisible())
  1611. m_histogramItem->onModeChanged();
  1612. }
  1613. }
  1614. //QGraphicsView::mousePressEvent(_event);
  1615. }
  1616. void EasyGraphicsScrollbar::mouseReleaseEvent(QMouseEvent* _event)
  1617. {
  1618. m_mouseButtons = _event->buttons();
  1619. m_bScrolling = false;
  1620. _event->accept();
  1621. //QGraphicsView::mouseReleaseEvent(_event);
  1622. }
  1623. void EasyGraphicsScrollbar::mouseMoveEvent(QMouseEvent* _event)
  1624. {
  1625. const auto pos = _event->pos();
  1626. if (m_mouseButtons & Qt::LeftButton)
  1627. {
  1628. const auto delta = pos - m_mousePressPos;
  1629. m_mousePressPos = pos;
  1630. if (m_bScrolling)
  1631. {
  1632. auto realScale = m_windowScale;
  1633. if (m_bBindMode)
  1634. realScale *= -range() / sliderWidth();
  1635. setValue(m_value + delta.x() / realScale);
  1636. }
  1637. }
  1638. if (m_histogramItem->isVisible())
  1639. {
  1640. m_histogramItem->setMouseY(mapToScene(pos).y());
  1641. scene()->update();
  1642. }
  1643. }
  1644. void EasyGraphicsScrollbar::wheelEvent(QWheelEvent* _event)
  1645. {
  1646. _event->accept();
  1647. if (_event->modifiers() & Qt::ShiftModifier)
  1648. {
  1649. // Shift + mouse wheel will change histogram top boundary
  1650. if (m_histogramItem->isVisible())
  1651. {
  1652. if (_event->delta() > 0)
  1653. m_histogramItem->increaseTopBoundary();
  1654. else
  1655. m_histogramItem->decreaseTopBoundary();
  1656. }
  1657. return;
  1658. }
  1659. if (_event->modifiers() & Qt::ControlModifier)
  1660. {
  1661. // Ctrl + mouse wheel will change histogram bottom boundary
  1662. if (m_histogramItem->isVisible())
  1663. {
  1664. if (_event->delta() > 0)
  1665. m_histogramItem->increaseBottomBoundary();
  1666. else
  1667. m_histogramItem->decreaseBottomBoundary();
  1668. }
  1669. return;
  1670. }
  1671. if (!m_bBindMode)
  1672. {
  1673. const auto w = m_slider->halfwidth() * (_event->delta() < 0 ? ::profiler_gui::SCALING_COEFFICIENT : ::profiler_gui::SCALING_COEFFICIENT_INV);
  1674. setValue(mapToScene(_event->pos()).x() - m_minimumValue - w);
  1675. emit wheeled(w * m_windowScale, _event->delta());
  1676. }
  1677. else
  1678. {
  1679. const auto x = (mapToScene(_event->pos()).x() - m_minimumValue) * m_windowScale;
  1680. emit wheeled(x, _event->delta());
  1681. }
  1682. }
  1683. void EasyGraphicsScrollbar::resizeEvent(QResizeEvent* _event)
  1684. {
  1685. onWindowWidthChange(_event->size().width());
  1686. if (m_histogramItem->isVisible())
  1687. m_histogramItem->updateImage();
  1688. }
  1689. //////////////////////////////////////////////////////////////////////////
  1690. void EasyGraphicsScrollbar::onWindowWidthChange(qreal _width)
  1691. {
  1692. const auto oldScale = m_windowScale;
  1693. const auto scrollingRange = range();
  1694. if (scrollingRange < 1e-3)
  1695. {
  1696. m_windowScale = 1;
  1697. }
  1698. else
  1699. {
  1700. m_windowScale = _width / scrollingRange;
  1701. }
  1702. scale(m_windowScale / oldScale, 1);
  1703. }
  1704. //////////////////////////////////////////////////////////////////////////