From 2c68d5367c5c44aceb4ff23d9672c35642e030f7 Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Sun, 27 Jan 2013 21:35:20 -0800
Subject: [PATCH] SH-3275 WIP interesting Update viewer metrics system to be
 more flexible fixed memory leak fixed glitching of fast timer display

---
 indra/llcommon/llfasttimer.cpp           |   7 +
 indra/llcommon/lltrace.h                 |  18 +-
 indra/llcommon/lltracerecording.cpp      |  10 +
 indra/llcommon/lltracerecording.h        |  11 +-
 indra/llcommon/lltracethreadrecorder.cpp |  33 +--
 indra/llcommon/lltracethreadrecorder.h   |  12 +-
 indra/newview/llfasttimerview.cpp        | 245 +++++++++++------------
 indra/newview/llfasttimerview.h          |  19 +-
 8 files changed, 193 insertions(+), 162 deletions(-)

diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index a144a8c94e0..e58c5c0f984 100644
--- a/indra/llcommon/llfasttimer.cpp
+++ b/indra/llcommon/llfasttimer.cpp
@@ -425,6 +425,13 @@ void TimeBlockAccumulator::reset( const TimeBlockAccumulator* other )
 	mTotalTimeCounter = 0;
 	mChildTimeCounter = 0;
 	mCalls = 0;
+	if (other)
+	{
+		mLastCaller = other->mLastCaller;
+		mActiveCount = other->mActiveCount;
+		mMoveUpTree = other->mMoveUpTree;
+		mParent = other->mParent;
+	}
 }
 
 } // namespace LLTrace
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index 0f927bad535..c38e92962b7 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -243,8 +243,6 @@ namespace LLTrace
 	:	 public LLInstanceTracker<TraceType<ACCUMULATOR>, std::string>
 	{
 	public:
-		typedef typename MeanValueType<TraceType<ACCUMULATOR> >::type  mean_t;
-
 		TraceType(const char* name, const char* description = NULL)
 		:	LLInstanceTracker<TraceType<ACCUMULATOR>, std::string>(name),
 			mName(name),
@@ -468,25 +466,37 @@ namespace LLTrace
 	:	public TraceType<TimeBlockAccumulator>
 	{
 	public:
-		typedef F32 mean_t;
 
 		TraceType(const char* name, const char* description = "")
 		:	TraceType<TimeBlockAccumulator>(name, description)
 		{}
 	};
 
+	template<>
+	struct MeanValueType<TraceType<TimeBlockAccumulator::CallCountAspect> >
+	{
+		typedef F64 type;
+	};
+
+
 	template<>
 	class TraceType<TimeBlockAccumulator::SelfTimeAspect>
 		:	public TraceType<TimeBlockAccumulator>
 	{
 	public:
-		typedef F32 mean_t;
 
 		TraceType(const char* name, const char* description = "")
 			:	TraceType<TimeBlockAccumulator>(name, description)
 		{}
 	};
 
+	template<>
+	struct MeanValueType<TraceType<TimeBlockAccumulator::SelfTimeAspect> >
+	{
+		typedef LLUnit<LLUnits::Seconds, F64> type;
+	};
+
+
 	class TimeBlock;
 	class TimeBlockTreeNode
 	{
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index 913c4cbdad1..737b95cdd5d 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -109,8 +109,18 @@ void Recording::handleSplitTo(Recording& other)
 {
 	stop();
 	other.restart();
+	syncTo(other);
+}
+
+void Recording::syncTo(Recording& other)
+{
+	other.mCountsFloat.write()->reset(mCountsFloat);
 	other.mMeasurementsFloat.write()->reset(mMeasurementsFloat);
+	other.mCounts.write()->reset(mCounts);
 	other.mMeasurements.write()->reset(mMeasurements);
+	other.mStackTimers.write()->reset(mStackTimers);
+	other.mMemStats.write()->reset(mMemStats);
+
 	//TODO: figure out how to get seamless handoff of timing stats
 }
 
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index f575fbd8b23..af9ba02b290 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -119,6 +119,7 @@ namespace LLTrace
 		// gather data from recording, ignoring time relationship (for example, pulling data from slave threads)
 		void mergeRecording(const Recording& other);
 
+		// grab latest recorded data
 		void update();
 
 		// Timer accessors
@@ -217,6 +218,8 @@ namespace LLTrace
 
 		LLUnit<LLUnits::Seconds, F64> getDuration() const { return LLUnit<LLUnits::Seconds, F64>(mElapsedSeconds); }
 
+		void syncTo(Recording& other);
+
 	private:
 		friend class ThreadRecorder;
 
@@ -337,9 +340,9 @@ namespace LLTrace
 		}
 
 		template <typename T>
-		typename TraceType<T>::mean_t getPeriodMean(const TraceType<T>& stat) const
+		typename MeanValueType<TraceType<T> >::type getPeriodMean(const TraceType<T>& stat) const
 		{
-			typename TraceType<T>::mean_t mean = 0.0;
+			typename MeanValueType<TraceType<T> >::type mean = 0.0;
 			for (S32 i = 0; i < mNumPeriods; i++)
 			{
 				if (mRecordingPeriods[i].getDuration() > 0.f)
@@ -352,9 +355,9 @@ namespace LLTrace
 		}
 
 		template <typename T>
-		typename TraceType<T>::mean_t getPeriodMeanPerSec(const TraceType<T>& stat) const
+		typename MeanValueType<TraceType<T> >::type getPeriodMeanPerSec(const TraceType<T>& stat) const
 		{
-			typename TraceType<T>::mean_t mean = 0.0;
+			typename MeanValueType<TraceType<T> >::type mean = 0.0;
 			for (S32 i = 0; i < mNumPeriods; i++)
 			{
 				if (mRecordingPeriods[i].getDuration() > 0.f)
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
index 7b493a651ed..113febcca86 100644
--- a/indra/llcommon/lltracethreadrecorder.cpp
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -76,7 +76,7 @@ ThreadRecorder::~ThreadRecorder()
 
 	while(mActiveRecordings.size())
 	{
-		mActiveRecordings.front().mTargetRecording->stop();
+		mActiveRecordings.front()->mTargetRecording->stop();
 	}
 	set_thread_recorder(NULL);
 	delete[] mTimeBlockTreeNodes;
@@ -94,31 +94,37 @@ TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index)
 
 void ThreadRecorder::activate( Recording* recording )
 {
-	mActiveRecordings.push_front(ActiveRecording(recording));
-	mActiveRecordings.front().mBaseline.makePrimary();
+	ActiveRecording* active_recording = new ActiveRecording(recording);
+	if (!mActiveRecordings.empty())
+	{
+		mActiveRecordings.front()->mBaseline.syncTo(active_recording->mBaseline);
+	}
+	mActiveRecordings.push_front(active_recording);
+
+	mActiveRecordings.front()->mBaseline.makePrimary();
 }
 
-std::list<ThreadRecorder::ActiveRecording>::iterator ThreadRecorder::update( Recording* recording )
+ThreadRecorder::active_recording_list_t::iterator ThreadRecorder::update( Recording* recording )
 {
-	std::list<ActiveRecording>::iterator it, end_it;
+	active_recording_list_t::iterator it, end_it;
 	for (it = mActiveRecordings.begin(), end_it = mActiveRecordings.end();
 		it != end_it;
 		++it)
 	{
-		std::list<ActiveRecording>::iterator next_it = it;
+		active_recording_list_t::iterator next_it = it;
 		++next_it;
 
 		// if we have another recording further down in the stack...
 		if (next_it != mActiveRecordings.end())
 		{
 			// ...push our gathered data down to it
-			next_it->mBaseline.appendRecording(it->mBaseline);
+			(*next_it)->mBaseline.appendRecording((*it)->mBaseline);
 		}
 
 		// copy accumulated measurements into result buffer and clear accumulator (mBaseline)
-		it->moveBaselineToTarget();
+		(*it)->moveBaselineToTarget();
 
-		if (it->mTargetRecording == recording)
+		if ((*it)->mTargetRecording == recording)
 		{
 			// found the recording, so return it
 			break;
@@ -142,17 +148,18 @@ AccumulatorBuffer<MemStatAccumulator>			gMemStats;
 
 void ThreadRecorder::deactivate( Recording* recording )
 {
-	std::list<ActiveRecording>::iterator it = update(recording);
+	active_recording_list_t::iterator it = update(recording);
 	if (it != mActiveRecordings.end())
 	{
 		// and if we've found the recording we wanted to update
-		std::list<ActiveRecording>::iterator next_it = it;
+		active_recording_list_t::iterator next_it = it;
 		++next_it;
 		if (next_it != mActiveRecordings.end())
 		{
-			next_it->mTargetRecording->makePrimary();
+			(*next_it)->mTargetRecording->makePrimary();
 		}
 
+		delete *it;
 		mActiveRecordings.erase(it);
 	}
 }
@@ -244,7 +251,7 @@ void MasterThreadRecorder::pullFromSlaveThreads()
 
 	LLMutexLock lock(&mSlaveListMutex);
 
-	Recording& target_recording = mActiveRecordings.front().mBaseline;
+	Recording& target_recording = mActiveRecordings.front()->mBaseline;
 	for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
 		it != end_it;
 		++it)
diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h
index 337035974ca..0e6c0919000 100644
--- a/indra/llcommon/lltracethreadrecorder.h
+++ b/indra/llcommon/lltracethreadrecorder.h
@@ -39,13 +39,14 @@ namespace LLTrace
 	{
 	protected:
 		struct ActiveRecording;
+		typedef std::list<ActiveRecording*> active_recording_list_t;
 	public:
 		ThreadRecorder();
 
 		virtual ~ThreadRecorder();
 
 		void activate(Recording* recording);
-		std::list<struct ActiveRecording>::iterator update(Recording* recording);
+		active_recording_list_t::iterator update(Recording* recording);
 		void deactivate(Recording* recording);
 
 		virtual void pushToMaster() = 0;
@@ -63,11 +64,12 @@ namespace LLTrace
 			void moveBaselineToTarget();
 		};
 		Recording					mThreadRecording;
-		std::list<ActiveRecording>	mActiveRecordings;
 
-		class BlockTimer*				mRootTimer;
-		TimeBlockTreeNode*				mTimeBlockTreeNodes;
-		size_t							mNumTimeBlockTreeNodes;
+		active_recording_list_t		mActiveRecordings;
+
+		class BlockTimer*			mRootTimer;
+		TimeBlockTreeNode*			mTimeBlockTreeNodes;
+		size_t						mNumTimeBlockTreeNodes;
 	};
 
 	class LL_COMMON_API MasterThreadRecorder : public ThreadRecorder
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index 7858378f00d..948b6f8e89b 100644
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -107,7 +107,7 @@ LLFastTimerView::LLFastTimerView(const LLSD& key)
 	mRecording(&get_frame_recording()),
 	mPauseHistory(false)
 {
-	mBarRects = new std::vector<LLRect>[MAX_VISIBLE_HISTORY + 1];
+	mTimerBars = new std::vector<TimerBar>[MAX_VISIBLE_HISTORY + 1];
 }
 
 LLFastTimerView::~LLFastTimerView()
@@ -117,7 +117,7 @@ LLFastTimerView::~LLFastTimerView()
 		delete mRecording;
 	}
 	mRecording = NULL;
-	delete [] mBarRects;
+	delete [] mTimerBars;
 }
 
 void LLFastTimerView::onPause()
@@ -282,7 +282,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
 			++it, ++i)
 		{
 			// is mouse over bar for this timer?
-			if (mBarRects[mHoverBarIndex][i].pointInRect(x, y))
+			if (mTimerBars[mHoverBarIndex][i].mRect.pointInRect(x, y))
 			{
 				mHoverID = (*it);
 				if (mHoverTimer != *it)
@@ -294,7 +294,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
 					mHoverTimer = (*it);
 				}
 
-				mToolTipRect = mBarRects[mHoverBarIndex][i];
+				mToolTipRect = mTimerBars[mHoverBarIndex][i].mRect;
 			}
 
 			if ((*it)->getCollapsed())
@@ -1311,11 +1311,11 @@ S32 LLFastTimerView::drawHelp( S32 y )
 	}	return y;
 }
 
-void LLFastTimerView::drawTicks( LLUnit<LLUnits::Seconds, F64> total_time )
+void LLFastTimerView::drawTicks()
 {
 	// Draw MS ticks
 	{
-		LLUnit<LLUnits::Milliseconds, U32> ms = total_time;
+		LLUnit<LLUnits::Milliseconds, U32> ms = mTotalTimeDisplay;
 		std::string tick_label;
 		S32 x;
 		S32 barw = mBarRect.getWidth();
@@ -1377,187 +1377,170 @@ void LLFastTimerView::drawBorders( S32 y, const S32 x_start, S32 bar_height, S32
 	}
 }
 
-LLUnit<LLUnits::Seconds, F64> LLFastTimerView::getTotalTime()
+void LLFastTimerView::updateTotalTime()
 {
-	LLUnit<LLUnits::Seconds, F64> total_time;
 	switch(mDisplayMode)
 	{
 	case 0:
-		total_time = mRecording->getPeriodMean(FTM_FRAME)*2;
+		mTotalTimeDisplay = mRecording->getPeriodMean(FTM_FRAME)*2;
 		break;
 	case 1:
-		total_time = mAllTimeMax;
+		mTotalTimeDisplay = mAllTimeMax;
 		break;
 	case 2:
 		// Calculate the max total ticks for the current history
-		total_time = mRecording->getPeriodMax(FTM_FRAME);
+		mTotalTimeDisplay = mRecording->getPeriodMax(FTM_FRAME);
 		break;
 	default:
-		total_time = LLUnit<LLUnits::Milliseconds, F32>(100);
+		mTotalTimeDisplay = LLUnit<LLUnits::Milliseconds, F32>(100);
 		break;
 	}
-	return total_time;
 }
 
 void LLFastTimerView::drawBars()
 {
-	LLUnit<LLUnits::Seconds, F64> total_time = getTotalTime();
-	if (total_time <= 0.0) return;
+	updateTotalTime();
+	if (mTotalTimeDisplay <= 0.0) return;
 
-	LLPointer<LLUIImage> box_imagep = LLUI::getUIImage("Rounded_Square");
 	LLLocalClipRect clip(mBarRect);
 
 	S32 bar_height = (mBarRect.mTop - MARGIN - LINE_GRAPH_HEIGHT) / (MAX_VISIBLE_HISTORY + 2);
 	S32 vpad = llmax(1, bar_height / 4); // spacing between bars
 	bar_height -= vpad;
 
-	drawTicks(total_time);
+	drawTicks();
 	S32 y = mBarRect.mTop - ((S32)LLFontGL::getFontMonospace()->getLineHeight() + 4);
 	drawBorders(y, mBarRect.mLeft, bar_height, vpad);
 
 	// Draw bars for each history entry
 	// Special: -1 = show running average
-	gGL.getTexUnit(0)->bind(box_imagep->getImage());
+	LLPointer<LLUIImage> bar_image = LLUI::getUIImage("Rounded_Square");
+	gGL.getTexUnit(0)->bind(bar_image->getImage());
 	const S32 histmax = llmin(mRecording->getNumPeriods()+1, MAX_VISIBLE_HISTORY);
 
 	for (S32 j = -1; j < histmax && y > LINE_GRAPH_HEIGHT; j++)
 	{
-		mBarRects[llmax(j, 0)].clear();
-		int sublevel_dx[FTV_MAX_DEPTH];
-		int sublevel_left[FTV_MAX_DEPTH];
-		int sublevel_right[FTV_MAX_DEPTH];
-		S32 tidx = (j >= 0)
+		S32 history_index = (j >= 0)
 			? j + 1 + mScrollIndex
 			: -1;
+		S32 rect_index = llmax(j, 0);
+		mTimerBars[rect_index].clear();
 
-		// draw the bars for each stat
-		std::vector<S32> xpos;
-		S32 deltax = 0;
-		xpos.push_back(mBarRect.mLeft);
+		updateTimerBars(&FTM_FRAME, LLRect(mBarRect.mLeft, 0, mBarRect.mRight, -bar_height), mTimerBars[rect_index], history_index);
 
-		TimeBlock* prev_id = NULL;
-
-		S32 i = 0;
-		for(timer_tree_iterator_t it = begin_timer_tree(FTM_FRAME);
-			it != end_timer_tree();
-			++it, ++i)
+		for (std::vector<TimerBar>::iterator it = mTimerBars[rect_index].begin(), end_it = mTimerBars[rect_index].end();
+			it != end_it;
+			++it)
 		{
-			TimeBlock* idp = (*it);
-			F32 frac = tidx == -1
-				? (mRecording->getPeriodMean(*idp) / total_time) 
-				: (mRecording->getPrevRecordingPeriod(tidx).getSum(*idp).value() / total_time.value());
-
-			S32 dx = llround(frac * (F32)mBarRect.getWidth());
-			S32 prev_delta_x = deltax;
-			deltax = dx;
-
-			const int level = get_depth(idp) - 1;
-			while ((S32)xpos.size() > level + 1)
-			{
-				xpos.pop_back();
-			}
-
-			LLRect bar_rect;
-			bar_rect.setLeftTopAndSize(xpos.back(), y, dx, bar_height);
-			mBarRects[llmax(j, 0)].push_back(bar_rect);
-
-			if (level == 0)
-			{
-				sublevel_left[level] = mBarRect.mLeft;
-				sublevel_dx[level] = dx;
-				sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
-			}
-			else if (prev_id && get_depth(prev_id) < get_depth(idp))
-			{
-				F64 sublevelticks = 0;
-
-				for (TimeBlock::child_const_iter it = prev_id->beginChildren();
-					it != prev_id->endChildren();
-					++it)
-				{
-					sublevelticks += (tidx == -1)
-						? mRecording->getPeriodMean(**it).value()
-						: mRecording->getPrevRecordingPeriod(tidx).getSum(**it).value();
-				}
-
-				F32 subfrac = (F32)sublevelticks / (F32)total_time.value();
-				sublevel_dx[level] = (int)(subfrac * (F32)mBarRect.getWidth() + .5f);
+			TimerBar& timer_bar = *it;
+			gGL.color4fv(timer_bar.mColor.mV);
+			gl_segmented_rect_2d_fragment_tex(timer_bar.mRect.mLeft, timer_bar.mRect.mTop, timer_bar.mRect.mRight, timer_bar.mRect.mBottom,
+				bar_image->getTextureWidth(), bar_image->getTextureHeight(), 
+				16, 
+				timer_bar.mStartFraction, timer_bar.mEndFraction);
 
-				if (mDisplayCenter == ALIGN_CENTER)
-				{
-					bar_rect.mLeft += (prev_delta_x - sublevel_dx[level])/2;
-				}
-				else if (mDisplayCenter == ALIGN_RIGHT)
-				{
-					bar_rect.mLeft += (prev_delta_x - sublevel_dx[level]);
-				}
-
-				sublevel_left[level] = bar_rect.mLeft;
-				sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
-			}				
+		}
+			
+		y -= (bar_height + vpad);
+		if (j < 0)
+			y -= bar_height;
+	}
+	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+}
 
-			xpos.back() = bar_rect.mRight;
-			xpos.push_back(bar_rect.mLeft);
+LLRect LLFastTimerView::updateTimerBars(LLTrace::TimeBlock* time_block, LLRect bar_rect, std::vector<TimerBar>& bars, S32 history_index)
+{
+	F32 self_time_frame_fraction = history_index == -1
+		? (mRecording->getPeriodMean(time_block->selfTime()) / mTotalTimeDisplay) 
+		: (mRecording->getPrevRecordingPeriod(history_index).getSum(time_block->selfTime()) / mTotalTimeDisplay);
 
-			if (bar_rect.getWidth() > 0)
-			{
-				LLColor4 color = sTimerColors[idp];
-				S32 scale_offset = 0;
+	S32 self_time_width = llround(self_time_frame_fraction * (F32)mBarRect.getWidth());
+	LLRect child_rect = bar_rect;
 
-				BOOL is_child_of_hover_item = (idp == mHoverID);
-				TimeBlock* next_parent = idp->getParent();
-				while(!is_child_of_hover_item && next_parent)
-				{
-					is_child_of_hover_item = (mHoverID == next_parent);
-					if (next_parent->getParent() == next_parent) break;
-					next_parent = next_parent->getParent();
-				}
+	if (mDisplayCenter == ALIGN_CENTER)
+	{
+		child_rect.mLeft += self_time_width / 2;
+		self_time_width -= self_time_width / 2;
+	}
+	else if (mDisplayCenter == ALIGN_RIGHT)
+	{
+		child_rect.mLeft += self_time_width;
+		self_time_width = 0;
+	}
 
-				if (idp == mHoverID)
-				{
-					scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 3.f);
-					//color = lerp(color, LLColor4::black, -0.4f);
-				}
-				else if (mHoverID != NULL && !is_child_of_hover_item)
-				{
-					color = lerp(color, LLColor4::grey, 0.8f);
-				}
+	if (child_rect.getHeight() > 3)
+	{
+		child_rect.mTop -= 1;
+		child_rect.mBottom += 1;
+	}
+	child_rect.mRight = child_rect.mLeft;
 
-				gGL.color4fv(color.mV);
-				F32 start_fragment = llclamp((F32)(bar_rect.mLeft - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
-				F32 end_fragment = llclamp((F32)(bar_rect.mRight - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
-				gl_segmented_rect_2d_fragment_tex(
-					sublevel_left[level], 
-					bar_rect.mTop - level + scale_offset, 
-					sublevel_right[level], 
-					bar_rect.mBottom + level - scale_offset, 
-					box_imagep->getTextureWidth(), box_imagep->getTextureHeight(), 
-					16, 
-					start_fragment, end_fragment);
-			}
+	// reserve a spot for this bar to be rendered before its children
+	// even though we don't know its size yet
+	S32 first_child_left = child_rect.mLeft;
+	S32 bar_rect_index = bars.size();
+	bars.push_back(TimerBar());
 
-			if ((*it)->getCollapsed())
-			{
-				it.skipDescendants();
-			}
+	for (TimeBlock::child_iter it = time_block->beginChildren(), end_it = time_block->endChildren(); it != end_it; ++it)
+	{
+		child_rect = updateTimerBars(*it, child_rect, bars, history_index);
 
-			prev_id = idp;
-		}
-		y -= (bar_height + vpad);
-		if (j < 0)
-			y -= bar_height;
+		// advance for next child
+		child_rect.mLeft = child_rect.mRight;
 	}
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-}
 
+	// now loop through children and figure out portion of bar image covered by each bar, now that we know the
+	// sum of all children
+	//S32 total_width_children = child_rect.mRight - first_child_left;
+	//S32 child_bar_index = bar_rect_index + 1;
+	//F32 bar_fraction_start = 0.f;
+	//for (TimeBlock::child_iter it = time_block->beginChildren(), end_it = time_block->endChildren(); 
+	//	it != end_it; 
+	//	++it)
+	//{
+	//	TimerBar& child_timer_bar = bars[child_bar_index];
 
+	//	child_timer_bar.mStartFraction = bar_fraction_start;
+	//	child_timer_bar.mEndFraction = (F32)child_timer_bar.mRect.getWidth() / (F32)total_width_children;
 
+	//	bar_fraction_start = child_timer_bar.mEndFraction;
 
+	//	// advance for next child
+	//	child_bar_index++;
+	//}
 
 
+	bar_rect.mRight = child_rect.mRight + self_time_width;
 
+	if (bar_rect.getWidth() > 0)
+	{
+		LLColor4 color = sTimerColors[time_block];
+		S32 scale_offset = 0;
 
+		BOOL is_child_of_hover_item = (time_block == mHoverID);
+		TimeBlock* next_parent = time_block->getParent();
+		while(!is_child_of_hover_item && next_parent)
+		{
+			is_child_of_hover_item = (mHoverID == next_parent);
+			if (next_parent->getParent() == next_parent) break;
+			next_parent = next_parent->getParent();
+		}
 
+		if (time_block == mHoverID)
+		{
+			scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 3.f);
+		}
+		else if (mHoverID != NULL && !is_child_of_hover_item)
+		{
+			color = lerp(color, LLColor4::grey, 0.8f);
+		}
 
+		bar_rect.mTop += scale_offset;
+		bar_rect.mBottom -= scale_offset;
 
+		bars[bar_rect_index].mRect = bar_rect;
+		bars[bar_rect_index].mColor = color;
+	}
 
+	return bar_rect;
+}
diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h
index 7eee3c1cb5f..86781b626cb 100644
--- a/indra/newview/llfasttimerview.h
+++ b/indra/newview/llfasttimerview.h
@@ -70,7 +70,7 @@ class LLFastTimerView : public LLFloater
 	virtual	void	onClickCloseBtn();
 
 private:	
-	void drawTicks(LLUnit<LLUnits::Seconds, F64> total_time);
+	void drawTicks();
 	void drawLineGraph();
 	void drawLegend(S32 y);
 	S32 drawHelp(S32 y);
@@ -79,10 +79,18 @@ class LLFastTimerView : public LLFloater
 
 	void printLineStats();
 	void generateUniqueColors();
-	LLUnit<LLUnits::Seconds, F64> getTotalTime();
+	void updateTotalTime();
 
-
-	std::vector<LLRect>* mBarRects;
+	struct TimerBar
+	{
+		LLRect		mRect;
+		LLColor4	mColor;
+		F32			mStartFraction,
+					mEndFraction;
+	};
+	LLRect updateTimerBars(LLTrace::TimeBlock* time_block, LLRect bar_rect, std::vector<TimerBar>& bars, S32 history_index);
+
+	std::vector<TimerBar>* mTimerBars;
 	S32 mDisplayMode;
 
 	typedef enum child_alignment
@@ -96,7 +104,8 @@ class LLFastTimerView : public LLFloater
 	ChildAlignment                mDisplayCenter;
 	bool                          mDisplayCalls,
 								  mDisplayHz;
-	LLUnit<LLUnits::Seconds, F64> mAllTimeMax;
+	LLUnit<LLUnits::Seconds, F64> mAllTimeMax,
+								  mTotalTimeDisplay;
 	LLRect                        mBarRect;
 	S32						      mScrollIndex;
 	LLTrace::TimeBlock*           mHoverID;
-- 
GitLab