PageRenderTime 2276ms CodeModel.GetById 599ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llfasttimerview.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1565 lines | 1195 code | 266 blank | 104 comment | 200 complexity | c311d7b8f0b60d2c5fa510fed777a7bf MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llfasttimerview.cpp
  3. * @brief LLFastTimerView class implementation
  4. *
  5. * $LicenseInfo:firstyear=2004&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llfasttimerview.h"
  28. #include "llviewerwindow.h"
  29. #include "llrect.h"
  30. #include "llerror.h"
  31. #include "llgl.h"
  32. #include "llimagepng.h"
  33. #include "llrender.h"
  34. #include "llrendertarget.h"
  35. #include "lllocalcliprect.h"
  36. #include "llmath.h"
  37. #include "llfontgl.h"
  38. #include "llsdserialize.h"
  39. #include "lltooltip.h"
  40. #include "llbutton.h"
  41. #include "llappviewer.h"
  42. #include "llviewertexturelist.h"
  43. #include "llui.h"
  44. #include "llviewercontrol.h"
  45. #include "llstat.h"
  46. #include "llfasttimer.h"
  47. #include "lltreeiterators.h"
  48. #include "llmetricperformancetester.h"
  49. #include "llviewerstats.h"
  50. //////////////////////////////////////////////////////////////////////////////
  51. static const S32 MAX_VISIBLE_HISTORY = 10;
  52. static const S32 LINE_GRAPH_HEIGHT = 240;
  53. //static const int FTV_DISPLAY_NUM = (sizeof(ft_display_table)/sizeof(ft_display_table[0]));
  54. static S32 FTV_NUM_TIMERS;
  55. const S32 FTV_MAX_DEPTH = 8;
  56. std::vector<LLFastTimer::NamedTimer*> ft_display_idx; // line of table entry for display purposes (for collapse)
  57. typedef LLTreeDFSIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_iterator_t;
  58. BOOL LLFastTimerView::sAnalyzePerformance = FALSE;
  59. static timer_tree_iterator_t begin_timer_tree(LLFastTimer::NamedTimer& id)
  60. {
  61. return timer_tree_iterator_t(&id,
  62. boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
  63. boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
  64. }
  65. static timer_tree_iterator_t end_timer_tree()
  66. {
  67. return timer_tree_iterator_t();
  68. }
  69. LLFastTimerView::LLFastTimerView(const LLSD& key)
  70. : LLFloater(key),
  71. mHoverTimer(NULL)
  72. {
  73. mDisplayMode = 0;
  74. mAvgCountTotal = 0;
  75. mMaxCountTotal = 0;
  76. mDisplayCenter = ALIGN_CENTER;
  77. mDisplayCalls = 0;
  78. mDisplayHz = 0;
  79. mScrollIndex = 0;
  80. mHoverID = NULL;
  81. mHoverBarIndex = -1;
  82. FTV_NUM_TIMERS = LLFastTimer::NamedTimer::instanceCount();
  83. mPrintStats = -1;
  84. mAverageCyclesPerTimer = 0;
  85. }
  86. void LLFastTimerView::onPause()
  87. {
  88. LLFastTimer::sPauseHistory = !LLFastTimer::sPauseHistory;
  89. // reset scroll to bottom when unpausing
  90. if (!LLFastTimer::sPauseHistory)
  91. {
  92. mScrollIndex = 0;
  93. LLFastTimer::sResetHistory = true;
  94. getChild<LLButton>("pause_btn")->setLabel(getString("pause"));
  95. }
  96. else
  97. {
  98. getChild<LLButton>("pause_btn")->setLabel(getString("run"));
  99. }
  100. }
  101. BOOL LLFastTimerView::postBuild()
  102. {
  103. LLButton& pause_btn = getChildRef<LLButton>("pause_btn");
  104. pause_btn.setCommitCallback(boost::bind(&LLFastTimerView::onPause, this));
  105. return TRUE;
  106. }
  107. BOOL LLFastTimerView::handleRightMouseDown(S32 x, S32 y, MASK mask)
  108. {
  109. if (mHoverTimer )
  110. {
  111. // right click collapses timers
  112. if (!mHoverTimer->getCollapsed())
  113. {
  114. mHoverTimer->setCollapsed(true);
  115. }
  116. else if (mHoverTimer->getParent())
  117. {
  118. mHoverTimer->getParent()->setCollapsed(true);
  119. }
  120. return TRUE;
  121. }
  122. else if (mBarRect.pointInRect(x, y))
  123. {
  124. S32 bar_idx = MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight());
  125. bar_idx = llclamp(bar_idx, 0, MAX_VISIBLE_HISTORY);
  126. mPrintStats = LLFastTimer::NamedTimer::HISTORY_NUM - mScrollIndex - bar_idx;
  127. return TRUE;
  128. }
  129. return LLFloater::handleRightMouseDown(x, y, mask);
  130. }
  131. LLFastTimer::NamedTimer* LLFastTimerView::getLegendID(S32 y)
  132. {
  133. S32 idx = (getRect().getHeight() - y) / ((S32) LLFontGL::getFontMonospace()->getLineHeight()+2) - 5;
  134. if (idx >= 0 && idx < (S32)ft_display_idx.size())
  135. {
  136. return ft_display_idx[idx];
  137. }
  138. return NULL;
  139. }
  140. BOOL LLFastTimerView::handleDoubleClick(S32 x, S32 y, MASK mask)
  141. {
  142. for(timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  143. it != end_timer_tree();
  144. ++it)
  145. {
  146. (*it)->setCollapsed(false);
  147. }
  148. return TRUE;
  149. }
  150. BOOL LLFastTimerView::handleMouseDown(S32 x, S32 y, MASK mask)
  151. {
  152. if (x < mBarRect.mLeft)
  153. {
  154. LLFastTimer::NamedTimer* idp = getLegendID(y);
  155. if (idp)
  156. {
  157. idp->setCollapsed(!idp->getCollapsed());
  158. }
  159. }
  160. else if (mHoverTimer)
  161. {
  162. //left click drills down by expanding timers
  163. mHoverTimer->setCollapsed(false);
  164. }
  165. else if (mask & MASK_ALT)
  166. {
  167. if (mask & MASK_CONTROL)
  168. {
  169. mDisplayHz = !mDisplayHz;
  170. }
  171. else
  172. {
  173. mDisplayCalls = !mDisplayCalls;
  174. }
  175. }
  176. else if (mask & MASK_SHIFT)
  177. {
  178. if (++mDisplayMode > 3)
  179. mDisplayMode = 0;
  180. }
  181. else if (mask & MASK_CONTROL)
  182. {
  183. mDisplayCenter = (ChildAlignment)((mDisplayCenter + 1) % ALIGN_COUNT);
  184. }
  185. else if (mGraphRect.pointInRect(x, y))
  186. {
  187. gFocusMgr.setMouseCapture(this);
  188. return TRUE;
  189. }
  190. //else
  191. //{
  192. // // pause/unpause
  193. // LLFastTimer::sPauseHistory = !LLFastTimer::sPauseHistory;
  194. // // reset scroll to bottom when unpausing
  195. // if (!LLFastTimer::sPauseHistory)
  196. // {
  197. // mScrollIndex = 0;
  198. // }
  199. //}
  200. return LLFloater::handleMouseDown(x, y, mask);
  201. }
  202. BOOL LLFastTimerView::handleMouseUp(S32 x, S32 y, MASK mask)
  203. {
  204. if (hasMouseCapture())
  205. {
  206. gFocusMgr.setMouseCapture(NULL);
  207. }
  208. return LLFloater::handleMouseUp(x, y, mask);;
  209. }
  210. BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
  211. {
  212. if (hasMouseCapture())
  213. {
  214. F32 lerp = llclamp(1.f - (F32) (x - mGraphRect.mLeft) / (F32) mGraphRect.getWidth(), 0.f, 1.f);
  215. mScrollIndex = llround( lerp * (F32)(LLFastTimer::NamedTimer::HISTORY_NUM - MAX_VISIBLE_HISTORY));
  216. mScrollIndex = llclamp( mScrollIndex, 0, LLFastTimer::getLastFrameIndex());
  217. return TRUE;
  218. }
  219. mHoverTimer = NULL;
  220. mHoverID = NULL;
  221. if(LLFastTimer::sPauseHistory && mBarRect.pointInRect(x, y))
  222. {
  223. mHoverBarIndex = llmin(LLFastTimer::getCurFrameIndex() - 1,
  224. MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight()));
  225. if (mHoverBarIndex == 0)
  226. {
  227. return TRUE;
  228. }
  229. else if (mHoverBarIndex == -1)
  230. {
  231. mHoverBarIndex = 0;
  232. }
  233. S32 i = 0;
  234. for(timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  235. it != end_timer_tree();
  236. ++it, ++i)
  237. {
  238. // is mouse over bar for this timer?
  239. if (x > mBarStart[mHoverBarIndex][i] &&
  240. x < mBarEnd[mHoverBarIndex][i])
  241. {
  242. mHoverID = (*it);
  243. if (mHoverTimer != *it)
  244. {
  245. // could be that existing tooltip is for a parent and is thus
  246. // covering region for this new timer, go ahead and unblock
  247. // so we can create a new tooltip
  248. LLToolTipMgr::instance().unblockToolTips();
  249. mHoverTimer = (*it);
  250. }
  251. mToolTipRect.set(mBarStart[mHoverBarIndex][i],
  252. mBarRect.mBottom + llround(((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex + 1)) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f))),
  253. mBarEnd[mHoverBarIndex][i],
  254. mBarRect.mBottom + llround((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f))));
  255. }
  256. if ((*it)->getCollapsed())
  257. {
  258. it.skipDescendants();
  259. }
  260. }
  261. }
  262. else if (x < mBarRect.mLeft)
  263. {
  264. LLFastTimer::NamedTimer* timer_id = getLegendID(y);
  265. if (timer_id)
  266. {
  267. mHoverID = timer_id;
  268. }
  269. }
  270. return LLFloater::handleHover(x, y, mask);
  271. }
  272. BOOL LLFastTimerView::handleToolTip(S32 x, S32 y, MASK mask)
  273. {
  274. if(LLFastTimer::sPauseHistory && mBarRect.pointInRect(x, y))
  275. {
  276. // tooltips for timer bars
  277. if (mHoverTimer)
  278. {
  279. LLRect screen_rect;
  280. localRectToScreen(mToolTipRect, &screen_rect);
  281. LLToolTipMgr::instance().show(LLToolTip::Params()
  282. .message(mHoverTimer->getToolTip(LLFastTimer::NamedTimer::HISTORY_NUM - mScrollIndex - mHoverBarIndex))
  283. .sticky_rect(screen_rect)
  284. .delay_time(0.f));
  285. return TRUE;
  286. }
  287. }
  288. else
  289. {
  290. // tooltips for timer legend
  291. if (x < mBarRect.mLeft)
  292. {
  293. LLFastTimer::NamedTimer* idp = getLegendID(y);
  294. if (idp)
  295. {
  296. LLToolTipMgr::instance().show(idp->getToolTip());
  297. return TRUE;
  298. }
  299. }
  300. }
  301. return LLFloater::handleToolTip(x, y, mask);
  302. }
  303. BOOL LLFastTimerView::handleScrollWheel(S32 x, S32 y, S32 clicks)
  304. {
  305. LLFastTimer::sPauseHistory = TRUE;
  306. mScrollIndex = llclamp( mScrollIndex + clicks,
  307. 0,
  308. llmin(LLFastTimer::getLastFrameIndex(), (S32)LLFastTimer::NamedTimer::HISTORY_NUM - MAX_VISIBLE_HISTORY));
  309. return TRUE;
  310. }
  311. static LLFastTimer::DeclareTimer FTM_RENDER_TIMER("Timers", true);
  312. static std::map<LLFastTimer::NamedTimer*, LLColor4> sTimerColors;
  313. void LLFastTimerView::draw()
  314. {
  315. LLFastTimer t(FTM_RENDER_TIMER);
  316. std::string tdesc;
  317. F64 clock_freq = (F64)LLFastTimer::countsPerSecond();
  318. F64 iclock_freq = 1000.0 / clock_freq;
  319. S32 margin = 10;
  320. S32 height = getRect().getHeight();
  321. S32 width = getRect().getWidth();
  322. LLRect new_rect;
  323. new_rect.setLeftTopAndSize(getRect().mLeft, getRect().mTop, width, height);
  324. setRect(new_rect);
  325. S32 left, top, right, bottom;
  326. S32 x, y, barw, barh, dx, dy;
  327. S32 texth, textw;
  328. LLPointer<LLUIImage> box_imagep = LLUI::getUIImage("Rounded_Square");
  329. // Draw the window background
  330. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  331. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
  332. S32 xleft = margin;
  333. S32 ytop = margin;
  334. mAverageCyclesPerTimer = LLFastTimer::sTimerCalls == 0
  335. ? 0
  336. : llround(lerp((F32)mAverageCyclesPerTimer, (F32)(LLFastTimer::sTimerCycles / (U64)LLFastTimer::sTimerCalls), 0.1f));
  337. LLFastTimer::sTimerCycles = 0;
  338. LLFastTimer::sTimerCalls = 0;
  339. // Draw some help
  340. {
  341. x = xleft;
  342. y = height - ytop;
  343. texth = (S32)LLFontGL::getFontMonospace()->getLineHeight();
  344. #if TIME_FAST_TIMERS
  345. tdesc = llformat("Cycles per timer call: %d", mAverageCyclesPerTimer);
  346. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
  347. #else
  348. char modedesc[][32] = {
  349. "2 x Average ",
  350. "Max ",
  351. "Recent Max ",
  352. "100 ms "
  353. };
  354. char centerdesc[][32] = {
  355. "Left ",
  356. "Centered ",
  357. "Ordered "
  358. };
  359. tdesc = llformat("Full bar = %s [Click to pause/reset] [SHIFT-Click to toggle]",modedesc[mDisplayMode]);
  360. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
  361. textw = LLFontGL::getFontMonospace()->getWidth(tdesc);
  362. x = xleft, y -= (texth + 2);
  363. tdesc = llformat("Justification = %s [CTRL-Click to toggle]",centerdesc[mDisplayCenter]);
  364. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
  365. y -= (texth + 2);
  366. LLFontGL::getFontMonospace()->renderUTF8(std::string("[Right-Click log selected] [ALT-Click toggle counts] [ALT-SHIFT-Click sub hidden]"),
  367. 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
  368. #endif
  369. y -= (texth + 2);
  370. }
  371. S32 histmax = llmin(LLFastTimer::getLastFrameIndex()+1, MAX_VISIBLE_HISTORY);
  372. // Draw the legend
  373. xleft = margin;
  374. ytop = y;
  375. y -= (texth + 2);
  376. sTimerColors[&LLFastTimer::NamedTimer::getRootNamedTimer()] = LLColor4::grey;
  377. F32 hue = 0.f;
  378. for (timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  379. it != timer_tree_iterator_t();
  380. ++it)
  381. {
  382. LLFastTimer::NamedTimer* idp = (*it);
  383. const F32 HUE_INCREMENT = 0.23f;
  384. hue = fmodf(hue + HUE_INCREMENT, 1.f);
  385. // saturation increases with depth
  386. F32 saturation = clamp_rescale((F32)idp->getDepth(), 0.f, 3.f, 0.f, 1.f);
  387. // lightness alternates with depth
  388. F32 lightness = idp->getDepth() % 2 ? 0.5f : 0.6f;
  389. LLColor4 child_color;
  390. child_color.setHSL(hue, saturation, lightness);
  391. sTimerColors[idp] = child_color;
  392. }
  393. const S32 LEGEND_WIDTH = 220;
  394. {
  395. LLLocalClipRect clip(LLRect(margin, y, LEGEND_WIDTH, margin));
  396. S32 cur_line = 0;
  397. ft_display_idx.clear();
  398. std::map<LLFastTimer::NamedTimer*, S32> display_line;
  399. for (timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  400. it != timer_tree_iterator_t();
  401. ++it)
  402. {
  403. LLFastTimer::NamedTimer* idp = (*it);
  404. display_line[idp] = cur_line;
  405. ft_display_idx.push_back(idp);
  406. cur_line++;
  407. x = xleft;
  408. left = x; right = x + texth;
  409. top = y; bottom = y - texth;
  410. S32 scale_offset = 0;
  411. if (idp == mHoverID)
  412. {
  413. scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 2.f);
  414. }
  415. gl_rect_2d(left - scale_offset, top + scale_offset, right + scale_offset, bottom - scale_offset, sTimerColors[idp]);
  416. F32 ms = 0;
  417. S32 calls = 0;
  418. if (mHoverBarIndex > 0 && mHoverID)
  419. {
  420. S32 hidx = LLFastTimer::NamedTimer::HISTORY_NUM - mScrollIndex - mHoverBarIndex;
  421. U64 ticks = idp->getHistoricalCount(hidx);
  422. ms = (F32)((F64)ticks * iclock_freq);
  423. calls = (S32)idp->getHistoricalCalls(hidx);
  424. }
  425. else
  426. {
  427. U64 ticks = idp->getCountAverage();
  428. ms = (F32)((F64)ticks * iclock_freq);
  429. calls = (S32)idp->getCallAverage();
  430. }
  431. if (mDisplayCalls)
  432. {
  433. tdesc = llformat("%s (%d)",idp->getName().c_str(),calls);
  434. }
  435. else
  436. {
  437. tdesc = llformat("%s [%.1f]",idp->getName().c_str(),ms);
  438. }
  439. dx = (texth+4) + idp->getDepth()*8;
  440. LLColor4 color = LLColor4::white;
  441. if (idp->getDepth() > 0)
  442. {
  443. S32 line_start_y = (top + bottom) / 2;
  444. S32 line_end_y = line_start_y + ((texth + 2) * (cur_line - display_line[idp->getParent()])) - texth;
  445. gl_line_2d(x + dx - 8, line_start_y, x + dx, line_start_y, color);
  446. S32 line_x = x + (texth + 4) + ((idp->getDepth() - 1) * 8);
  447. gl_line_2d(line_x, line_start_y, line_x, line_end_y, color);
  448. if (idp->getCollapsed() && !idp->getChildren().empty())
  449. {
  450. gl_line_2d(line_x+4, line_start_y-3, line_x+4, line_start_y+4, color);
  451. }
  452. }
  453. x += dx;
  454. BOOL is_child_of_hover_item = (idp == mHoverID);
  455. LLFastTimer::NamedTimer* next_parent = idp->getParent();
  456. while(!is_child_of_hover_item && next_parent)
  457. {
  458. is_child_of_hover_item = (mHoverID == next_parent);
  459. next_parent = next_parent->getParent();
  460. }
  461. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0,
  462. x, y,
  463. color,
  464. LLFontGL::LEFT, LLFontGL::TOP,
  465. is_child_of_hover_item ? LLFontGL::BOLD : LLFontGL::NORMAL);
  466. y -= (texth + 2);
  467. textw = dx + LLFontGL::getFontMonospace()->getWidth(idp->getName()) + 40;
  468. if (idp->getCollapsed())
  469. {
  470. it.skipDescendants();
  471. }
  472. }
  473. }
  474. xleft += LEGEND_WIDTH + 8;
  475. // ytop = ytop;
  476. // update rectangle that includes timer bars
  477. mBarRect.mLeft = xleft;
  478. mBarRect.mRight = getRect().getWidth();
  479. mBarRect.mTop = ytop - ((S32)LLFontGL::getFontMonospace()->getLineHeight() + 4);
  480. mBarRect.mBottom = margin + LINE_GRAPH_HEIGHT;
  481. y = ytop;
  482. barh = (ytop - margin - LINE_GRAPH_HEIGHT) / (MAX_VISIBLE_HISTORY + 2);
  483. dy = barh>>2; // spacing between bars
  484. if (dy < 1) dy = 1;
  485. barh -= dy;
  486. barw = width - xleft - margin;
  487. // Draw the history bars
  488. if (LLFastTimer::getLastFrameIndex() >= 0)
  489. {
  490. LLLocalClipRect clip(LLRect(xleft, ytop, getRect().getWidth() - margin, margin));
  491. U64 totalticks;
  492. if (!LLFastTimer::sPauseHistory)
  493. {
  494. U64 ticks = LLFastTimer::NamedTimer::getRootNamedTimer().getHistoricalCount(mScrollIndex);
  495. if (LLFastTimer::getCurFrameIndex() >= 10)
  496. {
  497. U64 framec = LLFastTimer::getCurFrameIndex();
  498. U64 avg = (U64)mAvgCountTotal;
  499. mAvgCountTotal = (avg*framec + ticks) / (framec + 1);
  500. if (ticks > mMaxCountTotal)
  501. {
  502. mMaxCountTotal = ticks;
  503. }
  504. }
  505. if (ticks < mAvgCountTotal/100 || ticks > mAvgCountTotal*100)
  506. {
  507. LLFastTimer::sResetHistory = true;
  508. }
  509. if (LLFastTimer::getCurFrameIndex() < 10 || LLFastTimer::sResetHistory)
  510. {
  511. mAvgCountTotal = ticks;
  512. mMaxCountTotal = ticks;
  513. LLFastTimer::sResetHistory = false;
  514. }
  515. }
  516. if (mDisplayMode == 0)
  517. {
  518. totalticks = mAvgCountTotal*2;
  519. }
  520. else if (mDisplayMode == 1)
  521. {
  522. totalticks = mMaxCountTotal;
  523. }
  524. else if (mDisplayMode == 2)
  525. {
  526. // Calculate the max total ticks for the current history
  527. totalticks = 0;
  528. for (S32 j=0; j<histmax; j++)
  529. {
  530. U64 ticks = LLFastTimer::NamedTimer::getRootNamedTimer().getHistoricalCount(j);
  531. if (ticks > totalticks)
  532. totalticks = ticks;
  533. }
  534. }
  535. else
  536. {
  537. totalticks = (U64)(clock_freq * .1); // 100 ms
  538. }
  539. // Draw MS ticks
  540. {
  541. U32 ms = (U32)((F64)totalticks * iclock_freq) ;
  542. tdesc = llformat("%.1f ms |", (F32)ms*.25f);
  543. x = xleft + barw/4 - LLFontGL::getFontMonospace()->getWidth(tdesc);
  544. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
  545. LLFontGL::LEFT, LLFontGL::TOP);
  546. tdesc = llformat("%.1f ms |", (F32)ms*.50f);
  547. x = xleft + barw/2 - LLFontGL::getFontMonospace()->getWidth(tdesc);
  548. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
  549. LLFontGL::LEFT, LLFontGL::TOP);
  550. tdesc = llformat("%.1f ms |", (F32)ms*.75f);
  551. x = xleft + (barw*3)/4 - LLFontGL::getFontMonospace()->getWidth(tdesc);
  552. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
  553. LLFontGL::LEFT, LLFontGL::TOP);
  554. tdesc = llformat( "%d ms |", ms);
  555. x = xleft + barw - LLFontGL::getFontMonospace()->getWidth(tdesc);
  556. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
  557. LLFontGL::LEFT, LLFontGL::TOP);
  558. }
  559. // Draw borders
  560. {
  561. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  562. gGL.color4f(0.5f,0.5f,0.5f,0.5f);
  563. S32 by = y + 2;
  564. y -= ((S32)LLFontGL::getFontMonospace()->getLineHeight() + 4);
  565. //heading
  566. gl_rect_2d(xleft-5, by, getRect().getWidth()-5, y+5, FALSE);
  567. //tree view
  568. gl_rect_2d(5, by, xleft-10, 5, FALSE);
  569. by = y + 5;
  570. //average bar
  571. gl_rect_2d(xleft-5, by, getRect().getWidth()-5, by-barh-dy-5, FALSE);
  572. by -= barh*2+dy;
  573. //current frame bar
  574. gl_rect_2d(xleft-5, by, getRect().getWidth()-5, by-barh-dy-2, FALSE);
  575. by -= barh+dy+1;
  576. //history bars
  577. gl_rect_2d(xleft-5, by, getRect().getWidth()-5, LINE_GRAPH_HEIGHT-barh-dy-2, FALSE);
  578. by = LINE_GRAPH_HEIGHT-barh-dy-7;
  579. //line graph
  580. mGraphRect = LLRect(xleft-5, by, getRect().getWidth()-5, 5);
  581. gl_rect_2d(mGraphRect, FALSE);
  582. }
  583. mBarStart.clear();
  584. mBarEnd.clear();
  585. // Draw bars for each history entry
  586. // Special: -1 = show running average
  587. gGL.getTexUnit(0)->bind(box_imagep->getImage());
  588. for (S32 j=-1; j<histmax && y > LINE_GRAPH_HEIGHT; j++)
  589. {
  590. mBarStart.push_back(std::vector<S32>());
  591. mBarEnd.push_back(std::vector<S32>());
  592. int sublevel_dx[FTV_MAX_DEPTH];
  593. int sublevel_left[FTV_MAX_DEPTH];
  594. int sublevel_right[FTV_MAX_DEPTH];
  595. S32 tidx;
  596. if (j >= 0)
  597. {
  598. tidx = LLFastTimer::NamedTimer::HISTORY_NUM - j - 1 - mScrollIndex;
  599. }
  600. else
  601. {
  602. tidx = -1;
  603. }
  604. x = xleft;
  605. // draw the bars for each stat
  606. std::vector<S32> xpos;
  607. std::vector<S32> deltax;
  608. xpos.push_back(xleft);
  609. LLFastTimer::NamedTimer* prev_id = NULL;
  610. S32 i = 0;
  611. for(timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  612. it != end_timer_tree();
  613. ++it, ++i)
  614. {
  615. LLFastTimer::NamedTimer* idp = (*it);
  616. F32 frac = tidx == -1
  617. ? (F32)idp->getCountAverage() / (F32)totalticks
  618. : (F32)idp->getHistoricalCount(tidx) / (F32)totalticks;
  619. dx = llround(frac * (F32)barw);
  620. S32 prev_delta_x = deltax.empty() ? 0 : deltax.back();
  621. deltax.push_back(dx);
  622. int level = idp->getDepth() - 1;
  623. while ((S32)xpos.size() > level + 1)
  624. {
  625. xpos.pop_back();
  626. }
  627. left = xpos.back();
  628. if (level == 0)
  629. {
  630. sublevel_left[level] = xleft;
  631. sublevel_dx[level] = dx;
  632. sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
  633. }
  634. else if (prev_id && prev_id->getDepth() < idp->getDepth())
  635. {
  636. U64 sublevelticks = 0;
  637. for (LLFastTimer::NamedTimer::child_const_iter it = prev_id->beginChildren();
  638. it != prev_id->endChildren();
  639. ++it)
  640. {
  641. sublevelticks += (tidx == -1)
  642. ? (*it)->getCountAverage()
  643. : (*it)->getHistoricalCount(tidx);
  644. }
  645. F32 subfrac = (F32)sublevelticks / (F32)totalticks;
  646. sublevel_dx[level] = (int)(subfrac * (F32)barw + .5f);
  647. if (mDisplayCenter == ALIGN_CENTER)
  648. {
  649. left += (prev_delta_x - sublevel_dx[level])/2;
  650. }
  651. else if (mDisplayCenter == ALIGN_RIGHT)
  652. {
  653. left += (prev_delta_x - sublevel_dx[level]);
  654. }
  655. sublevel_left[level] = left;
  656. sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
  657. }
  658. right = left + dx;
  659. xpos.back() = right;
  660. xpos.push_back(left);
  661. mBarStart.back().push_back(left);
  662. mBarEnd.back().push_back(right);
  663. top = y;
  664. bottom = y - barh;
  665. if (right > left)
  666. {
  667. //U32 rounded_edges = 0;
  668. LLColor4 color = sTimerColors[idp];//*ft_display_table[i].color;
  669. S32 scale_offset = 0;
  670. BOOL is_child_of_hover_item = (idp == mHoverID);
  671. LLFastTimer::NamedTimer* next_parent = idp->getParent();
  672. while(!is_child_of_hover_item && next_parent)
  673. {
  674. is_child_of_hover_item = (mHoverID == next_parent);
  675. next_parent = next_parent->getParent();
  676. }
  677. if (idp == mHoverID)
  678. {
  679. scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 3.f);
  680. //color = lerp(color, LLColor4::black, -0.4f);
  681. }
  682. else if (mHoverID != NULL && !is_child_of_hover_item)
  683. {
  684. color = lerp(color, LLColor4::grey, 0.8f);
  685. }
  686. gGL.color4fv(color.mV);
  687. F32 start_fragment = llclamp((F32)(left - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
  688. F32 end_fragment = llclamp((F32)(right - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
  689. gl_segmented_rect_2d_fragment_tex(sublevel_left[level], top - level + scale_offset, sublevel_right[level], bottom + level - scale_offset, box_imagep->getTextureWidth(), box_imagep->getTextureHeight(), 16, start_fragment, end_fragment);
  690. }
  691. if ((*it)->getCollapsed())
  692. {
  693. it.skipDescendants();
  694. }
  695. prev_id = idp;
  696. }
  697. y -= (barh + dy);
  698. if (j < 0)
  699. y -= barh;
  700. }
  701. //draw line graph history
  702. {
  703. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  704. LLLocalClipRect clip(mGraphRect);
  705. //normalize based on last frame's maximum
  706. static U64 last_max = 0;
  707. static F32 alpha_interp = 0.f;
  708. U64 max_ticks = llmax(last_max, (U64) 1);
  709. F32 ms = (F32)((F64)max_ticks * iclock_freq);
  710. //display y-axis range
  711. std::string tdesc;
  712. if (mDisplayCalls)
  713. tdesc = llformat("%d calls", (int)max_ticks);
  714. else if (mDisplayHz)
  715. tdesc = llformat("%d Hz", (int)max_ticks);
  716. else
  717. tdesc = llformat("%4.2f ms", ms);
  718. x = mGraphRect.mRight - LLFontGL::getFontMonospace()->getWidth(tdesc)-5;
  719. y = mGraphRect.mTop - ((S32)LLFontGL::getFontMonospace()->getLineHeight());
  720. LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white,
  721. LLFontGL::LEFT, LLFontGL::TOP);
  722. //highlight visible range
  723. {
  724. S32 first_frame = LLFastTimer::NamedTimer::HISTORY_NUM - mScrollIndex;
  725. S32 last_frame = first_frame - MAX_VISIBLE_HISTORY;
  726. F32 frame_delta = ((F32) (mGraphRect.getWidth()))/(LLFastTimer::NamedTimer::HISTORY_NUM-1);
  727. F32 right = (F32) mGraphRect.mLeft + frame_delta*first_frame;
  728. F32 left = (F32) mGraphRect.mLeft + frame_delta*last_frame;
  729. gGL.color4f(0.5f,0.5f,0.5f,0.3f);
  730. gl_rect_2d((S32) left, mGraphRect.mTop, (S32) right, mGraphRect.mBottom);
  731. if (mHoverBarIndex >= 0)
  732. {
  733. S32 bar_frame = first_frame - mHoverBarIndex;
  734. F32 bar = (F32) mGraphRect.mLeft + frame_delta*bar_frame;
  735. gGL.color4f(0.5f,0.5f,0.5f,1);
  736. gGL.begin(LLRender::LINES);
  737. gGL.vertex2i((S32)bar, mGraphRect.mBottom);
  738. gGL.vertex2i((S32)bar, mGraphRect.mTop);
  739. gGL.end();
  740. }
  741. }
  742. U64 cur_max = 0;
  743. for(timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  744. it != end_timer_tree();
  745. ++it)
  746. {
  747. LLFastTimer::NamedTimer* idp = (*it);
  748. //fatten highlighted timer
  749. if (mHoverID == idp)
  750. {
  751. gGL.flush();
  752. glLineWidth(3);
  753. }
  754. const F32 * col = sTimerColors[idp].mV;// ft_display_table[idx].color->mV;
  755. F32 alpha = 1.f;
  756. if (mHoverID != NULL &&
  757. idp != mHoverID)
  758. { //fade out non-highlighted timers
  759. if (idp->getParent() != mHoverID)
  760. {
  761. alpha = alpha_interp;
  762. }
  763. }
  764. gGL.color4f(col[0], col[1], col[2], alpha);
  765. gGL.begin(LLRender::TRIANGLE_STRIP);
  766. for (U32 j = llmax(0, LLFastTimer::NamedTimer::HISTORY_NUM - LLFastTimer::getLastFrameIndex());
  767. j < LLFastTimer::NamedTimer::HISTORY_NUM;
  768. j++)
  769. {
  770. U64 ticks = idp->getHistoricalCount(j);
  771. if (mDisplayHz)
  772. {
  773. F64 tc = (F64) (ticks+1) * iclock_freq;
  774. tc = 1000.f/tc;
  775. ticks = llmin((U64) tc, (U64) 1024);
  776. }
  777. else if (mDisplayCalls)
  778. {
  779. ticks = (S32)idp->getHistoricalCalls(j);
  780. }
  781. if (alpha == 1.f)
  782. {
  783. //normalize to highlighted timer
  784. cur_max = llmax(cur_max, ticks);
  785. }
  786. F32 x = mGraphRect.mLeft + ((F32) (mGraphRect.getWidth()))/(LLFastTimer::NamedTimer::HISTORY_NUM-1)*j;
  787. F32 y = mGraphRect.mBottom + (F32) mGraphRect.getHeight()/max_ticks*ticks;
  788. gGL.vertex2f(x,y);
  789. gGL.vertex2f(x,mGraphRect.mBottom);
  790. }
  791. gGL.end();
  792. if (mHoverID == idp)
  793. {
  794. gGL.flush();
  795. glLineWidth(1);
  796. }
  797. if (idp->getCollapsed())
  798. {
  799. //skip hidden timers
  800. it.skipDescendants();
  801. }
  802. }
  803. //interpolate towards new maximum
  804. last_max = (U64) lerp((F32)last_max, (F32) cur_max, LLCriticalDamp::getInterpolant(0.1f));
  805. if (last_max - cur_max <= 1 || cur_max - last_max <= 1)
  806. {
  807. last_max = cur_max;
  808. }
  809. F32 alpha_target = last_max > cur_max ?
  810. llmin((F32) last_max/ (F32) cur_max - 1.f,1.f) :
  811. llmin((F32) cur_max/ (F32) last_max - 1.f,1.f);
  812. alpha_interp = lerp(alpha_interp, alpha_target, LLCriticalDamp::getInterpolant(0.1f));
  813. if (mHoverID != NULL)
  814. {
  815. x = (mGraphRect.mRight + mGraphRect.mLeft)/2;
  816. y = mGraphRect.mBottom + 8;
  817. LLFontGL::getFontMonospace()->renderUTF8(
  818. mHoverID->getName(),
  819. 0,
  820. x, y,
  821. LLColor4::white,
  822. LLFontGL::LEFT, LLFontGL::BOTTOM);
  823. }
  824. }
  825. }
  826. // Output stats for clicked bar to log
  827. if (mPrintStats >= 0)
  828. {
  829. std::string legend_stat;
  830. bool first = true;
  831. for(timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  832. it != end_timer_tree();
  833. ++it)
  834. {
  835. LLFastTimer::NamedTimer* idp = (*it);
  836. if (!first)
  837. {
  838. legend_stat += ", ";
  839. }
  840. first = false;
  841. legend_stat += idp->getName();
  842. if (idp->getCollapsed())
  843. {
  844. it.skipDescendants();
  845. }
  846. }
  847. llinfos << legend_stat << llendl;
  848. std::string timer_stat;
  849. first = true;
  850. for(timer_tree_iterator_t it = begin_timer_tree(LLFastTimer::NamedTimer::getRootNamedTimer());
  851. it != end_timer_tree();
  852. ++it)
  853. {
  854. LLFastTimer::NamedTimer* idp = (*it);
  855. if (!first)
  856. {
  857. timer_stat += ", ";
  858. }
  859. first = false;
  860. U64 ticks;
  861. if (mPrintStats > 0)
  862. {
  863. ticks = idp->getHistoricalCount(mPrintStats);
  864. }
  865. else
  866. {
  867. ticks = idp->getCountAverage();
  868. }
  869. F32 ms = (F32)((F64)ticks * iclock_freq);
  870. timer_stat += llformat("%.1f",ms);
  871. if (idp->getCollapsed())
  872. {
  873. it.skipDescendants();
  874. }
  875. }
  876. llinfos << timer_stat << llendl;
  877. mPrintStats = -1;
  878. }
  879. mHoverID = NULL;
  880. mHoverBarIndex = -1;
  881. LLView::draw();
  882. }
  883. F64 LLFastTimerView::getTime(const std::string& name)
  884. {
  885. const LLFastTimer::NamedTimer* timerp = LLFastTimer::getTimerByName(name);
  886. if (timerp)
  887. {
  888. return (F64)timerp->getCountAverage() / (F64)LLFastTimer::countsPerSecond();
  889. }
  890. return 0.0;
  891. }
  892. void saveChart(const std::string& label, const char* suffix, LLImageRaw* scratch)
  893. {
  894. //read result back into raw image
  895. glReadPixels(0, 0, 1024, 512, GL_RGB, GL_UNSIGNED_BYTE, scratch->getData());
  896. //write results to disk
  897. LLPointer<LLImagePNG> result = new LLImagePNG();
  898. result->encode(scratch, 0.f);
  899. std::string ext = result->getExtension();
  900. std::string filename = llformat("%s_%s.%s", label.c_str(), suffix, ext.c_str());
  901. std::string out_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename);
  902. result->save(out_file);
  903. }
  904. //static
  905. void LLFastTimerView::exportCharts(const std::string& base, const std::string& target)
  906. {
  907. //allocate render target for drawing charts
  908. LLRenderTarget buffer;
  909. buffer.allocate(1024,512, GL_RGB, FALSE, FALSE);
  910. LLSD cur;
  911. LLSD base_data;
  912. { //read base log into memory
  913. S32 i = 0;
  914. std::ifstream is(base.c_str());
  915. while (!is.eof() && LLSDSerialize::fromXML(cur, is))
  916. {
  917. base_data[i++] = cur;
  918. }
  919. is.close();
  920. }
  921. LLSD cur_data;
  922. std::set<std::string> chart_names;
  923. { //read current log into memory
  924. S32 i = 0;
  925. std::ifstream is(target.c_str());
  926. while (!is.eof() && LLSDSerialize::fromXML(cur, is))
  927. {
  928. cur_data[i++] = cur;
  929. for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter)
  930. {
  931. std::string label = iter->first;
  932. chart_names.insert(label);
  933. }
  934. }
  935. is.close();
  936. }
  937. //get time domain
  938. LLSD::Real cur_total_time = 0.0;
  939. for (U32 i = 0; i < cur_data.size(); ++i)
  940. {
  941. cur_total_time += cur_data[i]["Total"]["Time"].asReal();
  942. }
  943. LLSD::Real base_total_time = 0.0;
  944. for (U32 i = 0; i < base_data.size(); ++i)
  945. {
  946. base_total_time += base_data[i]["Total"]["Time"].asReal();
  947. }
  948. //allocate raw scratch space
  949. LLPointer<LLImageRaw> scratch = new LLImageRaw(1024, 512, 3);
  950. gGL.pushMatrix();
  951. gGL.loadIdentity();
  952. gGL.matrixMode(LLRender::MM_PROJECTION);
  953. gGL.loadIdentity();
  954. gGL.ortho(-0.05f, 1.05f, -0.05f, 1.05f, -1.0f, 1.0f);
  955. //render charts
  956. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  957. buffer.bindTarget();
  958. for (std::set<std::string>::iterator iter = chart_names.begin(); iter != chart_names.end(); ++iter)
  959. {
  960. std::string label = *iter;
  961. LLSD::Real max_time = 0.0;
  962. LLSD::Integer max_calls = 0;
  963. LLSD::Real max_execution = 0.0;
  964. std::vector<LLSD::Real> cur_execution;
  965. std::vector<LLSD::Real> cur_times;
  966. std::vector<LLSD::Integer> cur_calls;
  967. std::vector<LLSD::Real> base_execution;
  968. std::vector<LLSD::Real> base_times;
  969. std::vector<LLSD::Integer> base_calls;
  970. for (U32 i = 0; i < cur_data.size(); ++i)
  971. {
  972. LLSD::Real time = cur_data[i][label]["Time"].asReal();
  973. LLSD::Integer calls = cur_data[i][label]["Calls"].asInteger();
  974. LLSD::Real execution = 0.0;
  975. if (calls > 0)
  976. {
  977. execution = time/calls;
  978. cur_execution.push_back(execution);
  979. cur_times.push_back(time);
  980. }
  981. cur_calls.push_back(calls);
  982. }
  983. for (U32 i = 0; i < base_data.size(); ++i)
  984. {
  985. LLSD::Real time = base_data[i][label]["Time"].asReal();
  986. LLSD::Integer calls = base_data[i][label]["Calls"].asInteger();
  987. LLSD::Real execution = 0.0;
  988. if (calls > 0)
  989. {
  990. execution = time/calls;
  991. base_execution.push_back(execution);
  992. base_times.push_back(time);
  993. }
  994. base_calls.push_back(calls);
  995. }
  996. std::sort(base_calls.begin(), base_calls.end());
  997. std::sort(base_times.begin(), base_times.end());
  998. std::sort(base_execution.begin(), base_execution.end());
  999. std::sort(cur_calls.begin(), cur_calls.end());
  1000. std::sort(cur_times.begin(), cur_times.end());
  1001. std::sort(cur_execution.begin(), cur_execution.end());
  1002. //remove outliers
  1003. const U32 OUTLIER_CUTOFF = 512;
  1004. if (base_times.size() > OUTLIER_CUTOFF)
  1005. {
  1006. ll_remove_outliers(base_times, 1.f);
  1007. }
  1008. if (base_execution.size() > OUTLIER_CUTOFF)
  1009. {
  1010. ll_remove_outliers(base_execution, 1.f);
  1011. }
  1012. if (cur_times.size() > OUTLIER_CUTOFF)
  1013. {
  1014. ll_remove_outliers(cur_times, 1.f);
  1015. }
  1016. if (cur_execution.size() > OUTLIER_CUTOFF)
  1017. {
  1018. ll_remove_outliers(cur_execution, 1.f);
  1019. }
  1020. max_time = llmax(base_times.empty() ? 0.0 : *base_times.rbegin(), cur_times.empty() ? 0.0 : *cur_times.rbegin());
  1021. max_calls = llmax(base_calls.empty() ? 0 : *base_calls.rbegin(), cur_calls.empty() ? 0 : *cur_calls.rbegin());
  1022. max_execution = llmax(base_execution.empty() ? 0.0 : *base_execution.rbegin(), cur_execution.empty() ? 0.0 : *cur_execution.rbegin());
  1023. LLVector3 last_p;
  1024. //====================================
  1025. // basic
  1026. //====================================
  1027. buffer.clear();
  1028. last_p.clear();
  1029. LLGLDisable cull(GL_CULL_FACE);
  1030. LLVector3 base_col(0, 0.7f, 0.f);
  1031. LLVector3 cur_col(1.f, 0.f, 0.f);
  1032. gGL.setSceneBlendType(LLRender::BT_ADD);
  1033. gGL.color3fv(base_col.mV);
  1034. for (U32 i = 0; i < base_times.size(); ++i)
  1035. {
  1036. gGL.begin(LLRender::TRIANGLE_STRIP);
  1037. gGL.vertex3fv(last_p.mV);
  1038. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1039. last_p.set((F32)i/(F32) base_times.size(), base_times[i]/max_time, 0.f);
  1040. gGL.vertex3fv(last_p.mV);
  1041. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1042. gGL.end();
  1043. }
  1044. gGL.flush();
  1045. last_p.clear();
  1046. {
  1047. LLGLEnable blend(GL_BLEND);
  1048. gGL.color3fv(cur_col.mV);
  1049. for (U32 i = 0; i < cur_times.size(); ++i)
  1050. {
  1051. gGL.begin(LLRender::TRIANGLE_STRIP);
  1052. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1053. gGL.vertex3fv(last_p.mV);
  1054. last_p.set((F32) i / (F32) cur_times.size(), cur_times[i]/max_time, 0.f);
  1055. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1056. gGL.vertex3fv(last_p.mV);
  1057. gGL.end();
  1058. }
  1059. gGL.flush();
  1060. }
  1061. saveChart(label, "time", scratch);
  1062. //======================================
  1063. // calls
  1064. //======================================
  1065. buffer.clear();
  1066. last_p.clear();
  1067. gGL.color3fv(base_col.mV);
  1068. for (U32 i = 0; i < base_calls.size(); ++i)
  1069. {
  1070. gGL.begin(LLRender::TRIANGLE_STRIP);
  1071. gGL.vertex3fv(last_p.mV);
  1072. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1073. last_p.set((F32) i / (F32) base_calls.size(), (F32)base_calls[i]/max_calls, 0.f);
  1074. gGL.vertex3fv(last_p.mV);
  1075. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1076. gGL.end();
  1077. }
  1078. gGL.flush();
  1079. {
  1080. LLGLEnable blend(GL_BLEND);
  1081. gGL.color3fv(cur_col.mV);
  1082. last_p.clear();
  1083. for (U32 i = 0; i < cur_calls.size(); ++i)
  1084. {
  1085. gGL.begin(LLRender::TRIANGLE_STRIP);
  1086. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1087. gGL.vertex3fv(last_p.mV);
  1088. last_p.set((F32) i / (F32) cur_calls.size(), (F32) cur_calls[i]/max_calls, 0.f);
  1089. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1090. gGL.vertex3fv(last_p.mV);
  1091. gGL.end();
  1092. }
  1093. gGL.flush();
  1094. }
  1095. saveChart(label, "calls", scratch);
  1096. //======================================
  1097. // execution
  1098. //======================================
  1099. buffer.clear();
  1100. gGL.color3fv(base_col.mV);
  1101. U32 count = 0;
  1102. U32 total_count = base_execution.size();
  1103. last_p.clear();
  1104. for (std::vector<LLSD::Real>::iterator iter = base_execution.begin(); iter != base_execution.end(); ++iter)
  1105. {
  1106. gGL.begin(LLRender::TRIANGLE_STRIP);
  1107. gGL.vertex3fv(last_p.mV);
  1108. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1109. last_p.set((F32)count/(F32)total_count, *iter/max_execution, 0.f);
  1110. gGL.vertex3fv(last_p.mV);
  1111. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1112. gGL.end();
  1113. count++;
  1114. }
  1115. last_p.clear();
  1116. {
  1117. LLGLEnable blend(GL_BLEND);
  1118. gGL.color3fv(cur_col.mV);
  1119. count = 0;
  1120. total_count = cur_execution.size();
  1121. for (std::vector<LLSD::Real>::iterator iter = cur_execution.begin(); iter != cur_execution.end(); ++iter)
  1122. {
  1123. gGL.begin(LLRender::TRIANGLE_STRIP);
  1124. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1125. gGL.vertex3fv(last_p.mV);
  1126. last_p.set((F32)count/(F32)total_count, *iter/max_execution, 0.f);
  1127. gGL.vertex3f(last_p.mV[0], 0.f, 0.f);
  1128. gGL.vertex3fv(last_p.mV);
  1129. gGL.end();
  1130. count++;
  1131. }
  1132. gGL.flush();
  1133. }
  1134. saveChart(label, "execution", scratch);
  1135. }
  1136. buffer.flush();
  1137. gGL.popMatrix();
  1138. gGL.matrixMode(LLRender::MM_MODELVIEW);
  1139. gGL.popMatrix();
  1140. }
  1141. //static
  1142. LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is)
  1143. {
  1144. LLSD ret;
  1145. LLSD cur;
  1146. LLSD::Real total_time = 0.0;
  1147. LLSD::Integer total_frames = 0;
  1148. typedef std::map<std::string,LLViewerStats::StatsAccumulator> stats_map_t;
  1149. stats_map_t time_stats;
  1150. stats_map_t sample_stats;
  1151. while (!is.eof() && LLSDSerialize::fromXML(cur, is))
  1152. {
  1153. for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter)
  1154. {
  1155. std::string label = iter->first;
  1156. F64 time = iter->second["Time"].asReal();
  1157. // Skip the total figure
  1158. if(label.compare("Total") != 0)
  1159. {
  1160. total_time += time;
  1161. }
  1162. if (time > 0.0)
  1163. {
  1164. LLSD::Integer samples = iter->second["Calls"].asInteger();
  1165. time_stats[label].push(time);
  1166. sample_stats[label].push(samples);
  1167. }
  1168. }
  1169. total_frames++;
  1170. }
  1171. for(stats_map_t::iterator it = time_stats.begin(); it != time_stats.end(); ++it)
  1172. {
  1173. std::string label = it->first;
  1174. ret[label]["TotalTime"] = time_stats[label].mSum;
  1175. ret[label]["MeanTime"] = time_stats[label].getMean();
  1176. ret[label]["MaxTime"] = time_stats[label].getMaxValue();
  1177. ret[label]["MinTime"] = time_stats[label].getMinValue();
  1178. ret[label]["StdDevTime"] = time_stats[label].getStdDev();
  1179. ret[label]["Samples"] = sample_stats[label].mSum;
  1180. ret[label]["MaxSamples"] = sample_stats[label].getMaxValue();
  1181. ret[label]["MinSamples"] = sample_stats[label].getMinValue();
  1182. ret[label]["StdDevSamples"] = sample_stats[label].getStdDev();
  1183. ret[label]["Frames"] = (LLSD::Integer)time_stats[label].getCount();
  1184. }
  1185. ret["SessionTime"] = total_time;
  1186. ret["FrameCount"] = total_frames;
  1187. return ret;
  1188. }
  1189. //static
  1190. void LLFastTimerView::doAnalysisDefault(std::string baseline, std::string target, std::string output)
  1191. {
  1192. // Open baseline and current target, exit if one is inexistent
  1193. std::ifstream base_is(baseline.c_str());
  1194. std::ifstream target_is(target.c_str());
  1195. if (!base_is.is_open() || !target_is.is_open())
  1196. {
  1197. llwarns << "'-analyzeperformance' error : baseline or current target file inexistent" << llendl;
  1198. base_is.close();
  1199. target_is.close();
  1200. return;
  1201. }
  1202. //analyze baseline
  1203. LLSD base = analyzePerformanceLogDefault(base_is);
  1204. base_is.close();
  1205. //analyze current
  1206. LLSD current = analyzePerformanceLogDefault(target_is);
  1207. target_is.close();
  1208. //output comparision
  1209. std::ofstream os(output.c_str());
  1210. LLSD::Real session_time = current["SessionTime"].asReal();
  1211. os <<
  1212. "Label, "
  1213. "% Change, "
  1214. "% of Session, "
  1215. "Cur Min, "
  1216. "Cur Max, "
  1217. "Cur Mean/sample, "
  1218. "Cur Mean/frame, "
  1219. "Cur StdDev/frame, "
  1220. "Cur Total, "
  1221. "Cur Frames, "
  1222. "Cur Samples, "
  1223. "Base Min, "
  1224. "Base Max, "
  1225. "Base Mean/sample, "
  1226. "Base Mean/frame, "
  1227. "Base StdDev/frame, "
  1228. "Base Total, "
  1229. "Base Frames, "
  1230. "Base Samples\n";
  1231. for (LLSD::map_iterator iter = base.beginMap(); iter != base.endMap(); ++iter)
  1232. {
  1233. LLSD::String label = iter->first;
  1234. if (current[label]["Samples"].asInteger() == 0 ||
  1235. base[label]["Samples"].asInteger() == 0)
  1236. {
  1237. //cannot compare
  1238. continue;
  1239. }
  1240. LLSD::Real a = base[label]["TotalTime"].asReal() / base[label]["Samples"].asReal();
  1241. LLSD::Real b = current[label]["TotalTime"].asReal() / current[label]["Samples"].asReal();
  1242. LLSD::Real diff = b-a;
  1243. LLSD::Real perc = diff/a * 100;
  1244. os << llformat("%s, %.2f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %d, %d, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %d, %d\n",
  1245. label.c_str(),
  1246. (F32) perc,
  1247. (F32) (current[label]["TotalTime"].asReal()/session_time * 100.0),
  1248. (F32) current[label]["MinTime"].asReal(),
  1249. (F32) current[label]["MaxTime"].asReal(),
  1250. (F32) b,
  1251. (F32) current[label]["MeanTime"].asReal(),
  1252. (F32) current[label]["StdDevTime"].asReal(),
  1253. (F32) current[label]["TotalTime"].asReal(),
  1254. current[label]["Frames"].asInteger(),
  1255. current[label]["Samples"].asInteger(),
  1256. (F32) base[label]["MinTime"].asReal(),
  1257. (F32) base[label]["MaxTime"].asReal(),
  1258. (F32) a,
  1259. (F32) base[label]["MeanTime"].asReal(),
  1260. (F32) base[label]["StdDevTime"].asReal(),
  1261. (F32) base[label]["TotalTime"].asReal(),
  1262. base[label]["Frames"].asInteger(),
  1263. base[label]["Samples"].asInteger());
  1264. }
  1265. exportCharts(baseline, target);
  1266. os.flush();
  1267. os.close();
  1268. }
  1269. //static
  1270. void LLFastTimerView::outputAllMetrics()
  1271. {
  1272. if (LLMetricPerformanceTesterBasic::hasMetricPerformanceTesters())
  1273. {
  1274. for (LLMetricPerformanceTesterBasic::name_tester_map_t::iterator iter = LLMetricPerformanceTesterBasic::sTesterMap.begin();
  1275. iter != LLMetricPerformanceTesterBasic::sTesterMap.end(); ++iter)
  1276. {
  1277. LLMetricPerformanceTesterBasic* tester = ((LLMetricPerformanceTesterBasic*)iter->second);
  1278. tester->outputTestResults();
  1279. }
  1280. }
  1281. }
  1282. //static
  1283. void LLFastTimerView::doAnalysis(std::string baseline, std::string target, std::string output)
  1284. {
  1285. if(LLFastTimer::sLog)
  1286. {
  1287. doAnalysisDefault(baseline, target, output) ;
  1288. return ;
  1289. }
  1290. if(LLFastTimer::sMetricLog)
  1291. {
  1292. LLMetricPerformanceTesterBasic::doAnalysisMetrics(baseline, target, output) ;
  1293. return ;
  1294. }
  1295. }
  1296. void LLFastTimerView::onClickCloseBtn()
  1297. {
  1298. setVisible(false);
  1299. }