PageRenderTime 2102ms CodeModel.GetById 156ms app.highlight 1326ms RepoModel.GetById 186ms app.codeStats 1ms

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