diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index d9670891f8adaaebe69d4835166c80d2a0553ff2..4da9c3fd6cb8e67de6a7c85658e449f89f463eb3 100755
--- a/indra/llcommon/llfasttimer.cpp
+++ b/indra/llcommon/llfasttimer.cpp
@@ -178,43 +178,38 @@ TimeBlockTreeNode& TimeBlock::getTreeNode() const
 	return *nodep;
 }
 
-static LLFastTimer::DeclareTimer FTM_PROCESS_TIMES("Process FastTimer Times");
 
-// not thread safe, so only call on main thread
-//static
-void TimeBlock::processTimes()
+void TimeBlock::bootstrapTimerTree()
 {
-	LLFastTimer _(FTM_PROCESS_TIMES);
-	get_clock_count(); // good place to calculate clock frequency
-	U64 cur_time = getCPUClockCount64();
-
-	// set up initial tree
 	for (LLInstanceTracker<TimeBlock>::instance_iter begin_it = LLInstanceTracker<TimeBlock>::beginInstances(), end_it = LLInstanceTracker<TimeBlock>::endInstances(), it = begin_it; 
 		it != end_it; 
 		++it)
 	{
 		TimeBlock& timer = *it;
 		if (&timer == &TimeBlock::getRootTimeBlock()) continue;
-			
+
 		// bootstrap tree construction by attaching to last timer to be on stack
 		// when this timer was called
 		if (timer.getParent() == &TimeBlock::getRootTimeBlock())
 		{
 			TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
-			
+
 			if (accumulator->mLastCaller)
 			{
 				timer.setParent(accumulator->mLastCaller);
 				accumulator->mParent = accumulator->mLastCaller;
 			}
-				// no need to push up tree on first use, flag can be set spuriously
+			// no need to push up tree on first use, flag can be set spuriously
 			accumulator->mMoveUpTree = false;
 		}
 	}
+}
 
-	// bump timers up tree if they have been flagged as being in the wrong place
-	// do this in a bottom up order to promote descendants first before promoting ancestors
-	// this preserves partial order derived from current frame's observations
+// bump timers up tree if they have been flagged as being in the wrong place
+// do this in a bottom up order to promote descendants first before promoting ancestors
+// this preserves partial order derived from current frame's observations
+void TimeBlock::incrementalUpdateTimerTree()
+{
 	for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(TimeBlock::getRootTimeBlock());
 		it != end_timer_tree_bottom_up();
 		++it)
@@ -240,27 +235,35 @@ void TimeBlock::processTimes()
 				LL_DEBUGS("FastTimers") << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
 					" to child of " << timerp->getParent()->getParent()->getName() << LL_ENDL;
 				timerp->setParent(timerp->getParent()->getParent());
-					accumulator->mParent = timerp->getParent();
-					accumulator->mMoveUpTree = false;
+				accumulator->mParent = timerp->getParent();
+				accumulator->mMoveUpTree = false;
 
 				// don't bubble up any ancestors until descendants are done bubbling up
-					// as ancestors may call this timer only on certain paths, so we want to resolve
-					// child-most block locations before their parents
+				// as ancestors may call this timer only on certain paths, so we want to resolve
+				// child-most block locations before their parents
 				it.skipAncestors();
 			}
 		}
 	}
+}
+
+
+void TimeBlock::updateTimes()
+{
+	U64 cur_time = getCPUClockCount64();
 
 	// walk up stack of active timers and accumulate current time while leaving timing structures active
-	BlockTimerStackRecord* stack_record			= ThreadTimerStack::getInstance();
-	BlockTimer* cur_timer						= stack_record->mActiveTimer;
-	TimeBlockAccumulator* accumulator = stack_record->mTimeBlock->getPrimaryAccumulator();
+	BlockTimerStackRecord* stack_record	= ThreadTimerStack::getInstance();
+	BlockTimer* cur_timer				= stack_record->mActiveTimer;
+	TimeBlockAccumulator* accumulator	= stack_record->mTimeBlock->getPrimaryAccumulator();
 
 	while(cur_timer 
 		&& cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self
 	{
 		U64 cumulative_time_delta = cur_time - cur_timer->mStartTime;
-		accumulator->mTotalTimeCounter += cumulative_time_delta - (accumulator->mTotalTimeCounter - cur_timer->mBlockStartTotalTimeCounter);
+		accumulator->mTotalTimeCounter += cumulative_time_delta 
+			- (accumulator->mTotalTimeCounter 
+			- cur_timer->mBlockStartTotalTimeCounter);
 		accumulator->mSelfTimeCounter += cumulative_time_delta - stack_record->mChildTime;
 		stack_record->mChildTime = 0;
 
@@ -268,11 +271,28 @@ void TimeBlock::processTimes()
 		cur_timer->mBlockStartTotalTimeCounter = accumulator->mTotalTimeCounter;
 
 		stack_record = &cur_timer->mParentTimerData;
-		accumulator = stack_record->mTimeBlock->getPrimaryAccumulator();
-		cur_timer = stack_record->mActiveTimer;
+		accumulator  = stack_record->mTimeBlock->getPrimaryAccumulator();
+		cur_timer    = stack_record->mActiveTimer;
 
 		stack_record->mChildTime += cumulative_time_delta;
 	}
+}
+
+static LLFastTimer::DeclareTimer FTM_PROCESS_TIMES("Process FastTimer Times");
+
+// not thread safe, so only call on main thread
+//static
+void TimeBlock::processTimes()
+{
+	LLFastTimer _(FTM_PROCESS_TIMES);
+	get_clock_count(); // good place to calculate clock frequency
+
+	// set up initial tree
+	bootstrapTimerTree();
+
+	incrementalUpdateTimerTree();
+
+	updateTimes();
 
 	// reset for next frame
 	for (LLInstanceTracker<TimeBlock>::instance_iter it = LLInstanceTracker<TimeBlock>::beginInstances(),
@@ -288,14 +308,13 @@ void TimeBlock::processTimes()
 	}
 }
 
-
 std::vector<TimeBlock*>::iterator TimeBlock::beginChildren()
-		{
+{
 	return getTreeNode().mChildren.begin(); 
-		}
+}
 
 std::vector<TimeBlock*>::iterator TimeBlock::endChildren()
-		{
+{
 	return getTreeNode().mChildren.end();
 }
 
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index fdc6997d45089de348adcd1a7ba8ad0e54137ff6..e800befd9fbb7485641a6b8c7828613e893738eb 100755
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -115,6 +115,7 @@ class TimeBlock
 	static void pushLog(LLSD sd);
 	static void setLogLock(LLMutex* mutex);
 	static void writeLog(std::ostream& os);
+	static void updateTimes();
 
 	// dumps current cumulative frame stats to log
 	// call nextFrame() to reset timers
@@ -262,6 +263,9 @@ class TimeBlock
 	// can be called multiple times in a frame, at any point
 	static void processTimes();
 
+	static void bootstrapTimerTree();
+	static void incrementalUpdateTimerTree();
+
 	// call this once a frame to periodically log timers
 	static void logStats();
 
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index cd377531e81ce76d7e3186f8250db65626137ad5..6292534a03bbac4f546eed636b1948fdf6edcfea 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -157,12 +157,12 @@ class AccumulatorBuffer : public LLRefCount
 		}
 	}
 
-	void flush()
+	void flush(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
 	{
 		llassert(mStorageSize >= sNextStorageSlot);
 		for (size_t i = 0; i < sNextStorageSlot; i++)
 		{
-			mStorage[i].flush();
+			mStorage[i].flush(time_stamp);
 		}
 	}
 
@@ -380,7 +380,7 @@ class EventAccumulator
 		mLastValue = other ? other->mLastValue : 0;
 	}
 
-	void flush() {}
+	void flush(LLUnitImplicit<F64, LLUnits::Seconds>) {}
 
 	F64	getSum() const { return mSum; }
 	F64	getMin() const { return mMin; }
@@ -512,9 +512,8 @@ class SampleAccumulator
 		mHasValue = other ? other->mHasValue : false;
 	}
 
-	void flush()
+	void flush(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
 	{
-		LLUnitImplicit<F64, LLUnits::Seconds> time_stamp = LLTimer::getTotalSeconds();
 		LLUnitImplicit<F64, LLUnits::Seconds> delta_time = time_stamp - mLastSampleTimeStamp;
 
 		if (mHasValue)
@@ -579,7 +578,7 @@ class CountAccumulator
 		mSum = 0;
 	}
 
-	void flush() {}
+	void flush(LLUnitImplicit<F64, LLUnits::Seconds>) {}
 
 	F64	getSum() const { return mSum; }
 
@@ -614,7 +613,7 @@ class TimeBlockAccumulator
 	TimeBlockAccumulator();
 	void addSamples(const self_t& other, bool /*append*/);
 	void reset(const self_t* other);
-	void flush() {}
+	void flush(LLUnitImplicit<F64, LLUnits::Seconds>) {}
 
 	//
 	// members
@@ -780,10 +779,10 @@ struct MemStatAccumulator
 		mDeallocatedCount = 0;
 	}
 
-	void flush() 
+	void flush(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp) 
 	{
-		mSize.flush();
-		mChildSize.flush();
+		mSize.flush(time_stamp);
+		mChildSize.flush(time_stamp);
 	}
 
 	SampleAccumulator	mSize,
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index f2c59410112634f8965ac1c9422b88156dc014e4..33002929ea102e3fb0bcc94a9f0f157fc70b2037 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -107,7 +107,9 @@ void RecordingBuffers::reset(RecordingBuffers* other)
 
 void RecordingBuffers::flush()
 {
-	mSamples.flush();
+	LLUnitImplicit<F64, LLUnits::Seconds> time_stamp = LLTimer::getTotalSeconds();
+
+	mSamples.flush(time_stamp);
 }
 
 ///////////////////////////////////////////////////////////////////////
@@ -205,7 +207,6 @@ void Recording::mergeRecording( const Recording& other)
 LLUnit<F64, LLUnits::Seconds> Recording::getSum(const TraceType<TimeBlockAccumulator>& stat)
 {
 	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
-	update();
 	return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter) 
 				/ (F64)LLTrace::TimeBlock::countsPerSecond();
 }
@@ -213,14 +214,12 @@ LLUnit<F64, LLUnits::Seconds> Recording::getSum(const TraceType<TimeBlockAccumul
 LLUnit<F64, LLUnits::Seconds> Recording::getSum(const TraceType<TimeBlockAccumulator::SelfTimeFacet>& stat)
 {
 	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
-	update();
 	return (F64)(accumulator.mSelfTimeCounter) / (F64)LLTrace::TimeBlock::countsPerSecond();
 }
 
 
 U32 Recording::getSum(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat)
 {
-	update();
 	return mBuffers->mStackTimers[stat.getIndex()].mCalls;
 }
 
@@ -228,7 +227,6 @@ LLUnit<F64, LLUnits::Seconds> Recording::getPerSec(const TraceType<TimeBlockAccu
 {
 	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
 
-	update();
 	return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter) 
 				/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds.value());
 }
@@ -237,105 +235,88 @@ LLUnit<F64, LLUnits::Seconds> Recording::getPerSec(const TraceType<TimeBlockAccu
 {
 	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
 
-	update();
 	return (F64)(accumulator.mSelfTimeCounter) 
 			/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds.value());
 }
 
 F32 Recording::getPerSec(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat)
 {
-	update();
 	return (F32)mBuffers->mStackTimers[stat.getIndex()].mCalls / mElapsedSeconds.value();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getMin(const TraceType<MemStatAccumulator>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mSize.getMin();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getMean(const TraceType<MemStatAccumulator>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mSize.getMean();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getMax(const TraceType<MemStatAccumulator>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mSize.getMax();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getStandardDeviation(const TraceType<MemStatAccumulator>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mSize.getStandardDeviation();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getLastValue(const TraceType<MemStatAccumulator>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mSize.getLastValue();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getMin(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMin();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getMean(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMean();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getMax(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMax();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getStandardDeviation(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mChildSize.getStandardDeviation();
 }
 
 LLUnit<F64, LLUnits::Bytes> Recording::getLastValue(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mChildSize.getLastValue();
 }
 
 U32 Recording::getSum(const TraceType<MemStatAccumulator::AllocationCountFacet>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mAllocatedCount;
 }
 
 U32 Recording::getSum(const TraceType<MemStatAccumulator::DeallocationCountFacet>& stat)
 {
-	update();
 	return mBuffers->mMemStats[stat.getIndex()].mAllocatedCount;
 }
 
 
 F64 Recording::getSum( const TraceType<CountAccumulator>& stat )
 {
-	update();
 	return mBuffers->mCounts[stat.getIndex()].getSum();
 }
 
 F64 Recording::getSum( const TraceType<EventAccumulator>& stat )
 {
-	update();
 	return (F64)mBuffers->mEvents[stat.getIndex()].getSum();
 }
 
 F64 Recording::getPerSec( const TraceType<CountAccumulator>& stat )
 {
-	update();
 	F64 sum = mBuffers->mCounts[stat.getIndex()].getSum();
 	return  (sum != 0.0) 
 		? (sum / mElapsedSeconds.value())
@@ -344,79 +325,66 @@ F64 Recording::getPerSec( const TraceType<CountAccumulator>& stat )
 
 U32 Recording::getSampleCount( const TraceType<CountAccumulator>& stat )
 {
-	update();
 	return mBuffers->mCounts[stat.getIndex()].getSampleCount();
 }
 
 F64 Recording::getMin( const TraceType<SampleAccumulator>& stat )
 {
-	update();
 	return mBuffers->mSamples[stat.getIndex()].getMin();
 }
 
 F64 Recording::getMax( const TraceType<SampleAccumulator>& stat )
 {
-	update();
 	return mBuffers->mSamples[stat.getIndex()].getMax();
 }
 
 F64 Recording::getMean( const TraceType<SampleAccumulator>& stat )
 {
-	update();
 	return mBuffers->mSamples[stat.getIndex()].getMean();
 }
 
 F64 Recording::getStandardDeviation( const TraceType<SampleAccumulator>& stat )
 {
-	update();
 	return mBuffers->mSamples[stat.getIndex()].getStandardDeviation();
 }
 
 F64 Recording::getLastValue( const TraceType<SampleAccumulator>& stat )
 {
-	update();
 	return mBuffers->mSamples[stat.getIndex()].getLastValue();
 }
 
 U32 Recording::getSampleCount( const TraceType<SampleAccumulator>& stat )
 {
-	update();
 	return mBuffers->mSamples[stat.getIndex()].getSampleCount();
 }
 
 F64 Recording::getMin( const TraceType<EventAccumulator>& stat )
 {
-	update();
 	return mBuffers->mEvents[stat.getIndex()].getMin();
 }
 
 F64 Recording::getMax( const TraceType<EventAccumulator>& stat )
 {
-	update();
 	return mBuffers->mEvents[stat.getIndex()].getMax();
 }
 
 F64 Recording::getMean( const TraceType<EventAccumulator>& stat )
 {
-	update();
 	return mBuffers->mEvents[stat.getIndex()].getMean();
 }
 
 F64 Recording::getStandardDeviation( const TraceType<EventAccumulator>& stat )
 {
-	update();
 	return mBuffers->mEvents[stat.getIndex()].getStandardDeviation();
 }
 
 F64 Recording::getLastValue( const TraceType<EventAccumulator>& stat )
 {
-	update();
 	return mBuffers->mEvents[stat.getIndex()].getLastValue();
 }
 
 U32 Recording::getSampleCount( const TraceType<EventAccumulator>& stat )
 {
-	update();
 	return mBuffers->mEvents[stat.getIndex()].getSampleCount();
 }
 
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
index c1a0700eff911436ec68dee3716257fed29b1946..54006f4e5b828d2af27e534bccef60ae8ea10f02 100644
--- a/indra/llcommon/lltracethreadrecorder.cpp
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -111,6 +111,7 @@ ThreadRecorder::active_recording_list_t::reverse_iterator ThreadRecorder::bringU
 	if (mActiveRecordings.empty()) return mActiveRecordings.rend();
 
 	mActiveRecordings.back()->mPartialRecording.flush();
+	TimeBlock::updateTimes();
 
 	active_recording_list_t::reverse_iterator it, end_it;
 	for (it = mActiveRecordings.rbegin(), end_it = mActiveRecordings.rend();
diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp
index 04764f6c2f3c75776cfe57ee417be84590fb7805..a5df51f6deb02a123ac934cdde1d55a629eec02d 100644
--- a/indra/llcommon/tests/llunits_test.cpp
+++ b/indra/llcommon/tests/llunits_test.cpp
@@ -56,6 +56,9 @@ namespace tut
 		LLUnit<F32, Quatloos> float_quatloos;
 		ensure(float_quatloos == 0.f);
 
+		LLUnit<F32, Quatloos> float_initialize_quatloos(1);
+		ensure(float_initialize_quatloos == 1.f);
+
 		LLUnit<S32, Quatloos> int_quatloos;
 		ensure(int_quatloos == 0);
 
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index 231ece4bbdec3333efed628800d30670bdcef309..1355b58f8bd34be8201041fe2d3b7013c7141b6c 100755
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -30,12 +30,14 @@
 
 #include "llviewerwindow.h"
 #include "llrect.h"
+#include "llcombobox.h"
 #include "llerror.h"
 #include "llgl.h"
 #include "llimagepng.h"
 #include "llrender.h"
 #include "llrendertarget.h"
 #include "lllocalcliprect.h"
+#include "lllayoutstack.h"
 #include "llmath.h"
 #include "llfontgl.h"
 #include "llsdserialize.h"
@@ -59,6 +61,8 @@ using namespace LLTrace;
 static const S32 MAX_VISIBLE_HISTORY = 12;
 static const S32 LINE_GRAPH_HEIGHT = 240;
 static const S32 MIN_BAR_HEIGHT = 3;
+static const S32 RUNNING_AVERAGE_WIDTH = 100;
+static const S32 NUM_FRAMES_HISTORY = 256;
 
 std::vector<TimeBlock*> ft_display_idx; // line of table entry for display purposes (for collapse)
 
@@ -95,17 +99,15 @@ LLFastTimerView::LLFastTimerView(const LLSD& key)
 :	LLFloater(key),
 	mHoverTimer(NULL),
 	mDisplayMode(0),
-	mDisplayCenter(ALIGN_CENTER),
-	mDisplayCalls(false),
-	mDisplayHz(false),
+	mDisplayType(TIME),
 	mScrollIndex(0),
 	mHoverID(NULL),
 	mHoverBarIndex(-1),
 	mStatsIndex(-1),
 	mPauseHistory(false),
-	mRecording(512)
+	mRecording(NUM_FRAMES_HISTORY)
 {
-	mTimerBarRows.resize(512);
+	mTimerBarRows.resize(NUM_FRAMES_HISTORY);
 }
 
 LLFastTimerView::~LLFastTimerView()
@@ -172,7 +174,7 @@ BOOL LLFastTimerView::handleRightMouseDown(S32 x, S32 y, MASK mask)
 
 TimeBlock* LLFastTimerView::getLegendID(S32 y)
 {
-	S32 idx = (mBarRect.mTop - y) / (LLFontGL::getFontMonospace()->getLineHeight()+2) - 1;
+	S32 idx = (mLegendRect.mTop - y) / (LLFontGL::getFontMonospace()->getLineHeight() + 2);
 
 	if (idx >= 0 && idx < (S32)ft_display_idx.size())
 	{
@@ -208,26 +210,6 @@ BOOL LLFastTimerView::handleMouseDown(S32 x, S32 y, MASK mask)
 		//left click drills down by expanding timers
 		mHoverTimer->setCollapsed(false);
 	}
-	else if (mask & MASK_ALT)
-	{
-		if (mask & MASK_CONTROL)
-		{
-			mDisplayHz = !mDisplayHz;	
-		}
-		else
-		{
-			mDisplayCalls = !mDisplayCalls;
-		}
-	}
-	else if (mask & MASK_SHIFT)
-	{
-		if (++mDisplayMode > 3)
-			mDisplayMode = 0;
-	}
-	else if (mask & MASK_CONTROL)
-	{
-		mDisplayCenter = (ChildAlignment)((mDisplayCenter + 1) % ALIGN_COUNT);
-	}
 	else if (mGraphRect.pointInRect(x, y))
 	{
 		gFocusMgr.setMouseCapture(this);
@@ -260,7 +242,10 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
 
 	if(mPauseHistory && mBarRect.pointInRect(x, y))
 	{
-		mHoverBarIndex = llmin((mBarRect.mTop - y) / (mBarRect.getHeight() / (MAX_VISIBLE_HISTORY + 2)) - 1,
+		//const S32 bars_top = mBarRect.mTop;
+		const S32 bars_top = mBarRect.mTop - ((S32)LLFontGL::getFontMonospace()->getLineHeight() + 4);
+
+		mHoverBarIndex = llmin((bars_top - y) / (mBarRect.getHeight() / (MAX_VISIBLE_HISTORY + 2)) - 1,
 								(S32)mRecording.getNumRecordedPeriods() - 1,
 								MAX_VISIBLE_HISTORY);
 		if (mHoverBarIndex == 0)
@@ -272,25 +257,33 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
 			mHoverBarIndex = 0;
 		}
 
-		TimerBarRow& row = mHoverBarIndex == 0 ? mAverageTimerRow : mTimerBarRows[mHoverBarIndex - 1];
+		TimerBarRow& row = mHoverBarIndex == 0 ? mAverageTimerRow : mTimerBarRows[mScrollIndex + mHoverBarIndex - 1];
 
 		TimerBar* hover_bar = NULL;
 		LLUnit<F32, LLUnits::Seconds> mouse_time_offset = ((F32)(x - mBarRect.mLeft) / (F32)mBarRect.getWidth()) * mTotalTimeDisplay;
-		for (std::vector<TimerBar>::iterator it = row.mBars.begin(), end_it = row.mBars.end();
-			it != end_it;
-			++it)
+		for (int bar_index = 0, end_index = LLInstanceTracker<LLTrace::TimeBlock>::instanceCount(); 
+			bar_index < end_index; 
+			++bar_index)
 		{
-			if (it->mSelfStart > mouse_time_offset)
+			TimerBar& bar = row.mBars[bar_index];
+			if (bar.mSelfStart > mouse_time_offset)
 			{
 				break;
 			}
-			hover_bar = &(*it);
+			if (bar.mSelfEnd > mouse_time_offset)
+			{
+				hover_bar = &bar;
+				if (bar.mTimeBlock->getCollapsed())
+				{
+					// stop on first collapsed timeblock, since we can't select any children
+					break;
+				}
+			}
 		}
 
 		if (hover_bar)
 		{
 			mHoverID = hover_bar->mTimeBlock;
-			mHoverTimer = mHoverID;
 			if (mHoverTimer != mHoverID)
 			{
 				// could be that existing tooltip is for a parent and is thus
@@ -300,7 +293,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
 				mHoverTimer = mHoverID;
 				mToolTipRect.set(mBarRect.mLeft + (hover_bar->mSelfStart / mTotalTimeDisplay) * mBarRect.getWidth(),
 								row.mTop,
-								mBarRect.mLeft + (hover_bar->mSelfStart / mTotalTimeDisplay) * mBarRect.getWidth(),
+								mBarRect.mLeft + (hover_bar->mSelfEnd / mTotalTimeDisplay) * mBarRect.getWidth(),
 								row.mBottom);
 			}
 		}
@@ -324,7 +317,7 @@ static std::string get_tooltip(TimeBlock& timer, S32 history_index, PeriodicReco
 	if (history_index == 0)
 	{
 		// by default, show average number of call
-		tooltip = llformat("%s (%d ms, %d calls)", timer.getName().c_str(), (S32)LLUnit<F64, LLUnits::Milliseconds>(frame_recording.getPeriodMean(timer)).value(), (S32)frame_recording.getPeriodMean(timer.callCount()));
+		tooltip = llformat("%s (%d ms, %d calls)", timer.getName().c_str(), (S32)LLUnit<F64, LLUnits::Milliseconds>(frame_recording.getPeriodMean (timer, RUNNING_AVERAGE_WIDTH)).value(), (S32)frame_recording.getPeriodMean(timer.callCount(), RUNNING_AVERAGE_WIDTH));
 	}
 	else
 	{
@@ -397,22 +390,34 @@ void LLFastTimerView::draw()
 		mTimerBarRows.push_front(TimerBarRow());
 	}
 
+	mDisplayMode = llclamp(getChild<LLComboBox>("time_scale_combo")->getCurrentIndex(), 0, 3);
+	mDisplayType = (EDisplayType)llclamp(getChild<LLComboBox>("metric_combo")->getCurrentIndex(), 0, 2);
+
 	generateUniqueColors();
 
+	LLView::drawChildren();
+	//getChild<LLLayoutStack>("timer_bars_stack")->updateLayout();
+	//getChild<LLLayoutStack>("legend_stack")->updateLayout();
+	LLView* bars_panel = getChildView("bars_panel");
+	bars_panel->localRectToOtherView(bars_panel->getLocalRect(), &mBarRect, this);
+
+	LLView* lines_panel = getChildView("lines_panel");
+	lines_panel->localRectToOtherView(lines_panel->getLocalRect(), &mGraphRect, this);
+
+	LLView* legend_panel = getChildView("legend");
+	legend_panel->localRectToOtherView(legend_panel->getLocalRect(), &mLegendRect, this);
+
 	// Draw the window background
 	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 	gl_rect_2d(getLocalRect(), LLColor4(0.f, 0.f, 0.f, 0.25f));
 	
-	S32 y = drawHelp(getRect().getHeight() - MARGIN);
-	drawLegend(y - ((S32)LLFontGL::getFontMonospace()->getLineHeight() + 2));
-
-	// update rectangle that includes timer bars
-	const S32 LEGEND_WIDTH = 220;
+	drawHelp(getRect().getHeight() - MARGIN);
+	drawLegend();
 
-	mBarRect.mLeft = MARGIN + LEGEND_WIDTH + 8;
-	mBarRect.mTop = y;
-	mBarRect.mRight = getRect().getWidth() - MARGIN;
-	mBarRect.mBottom = MARGIN + LINE_GRAPH_HEIGHT;
+	//mBarRect.mLeft = MARGIN + LEGEND_WIDTH + 8;
+	//mBarRect.mTop = y;
+	//mBarRect.mRight = getRect().getWidth() - MARGIN;
+	//mBarRect.mBottom = MARGIN + LINE_GRAPH_HEIGHT;
 
 	drawBars();
 	drawLineGraph();
@@ -433,7 +438,8 @@ void LLFastTimerView::onOpen(const LLSD& key)
 		it != end_it; 
 		++it)
 	{
-		it->mBars.clear();
+		delete []it->mBars;
+		it->mBars = NULL;
 	}
 }
 
@@ -993,7 +999,7 @@ void LLFastTimerView::printLineStats()
 			LLUnit<F32, LLUnits::Seconds> ticks;
 			if (mStatsIndex == 0)
 			{
-				ticks = mRecording.getPeriodMean(*idp);
+				ticks = mRecording.getPeriodMean(*idp, RUNNING_AVERAGE_WIDTH);
 			}
 			else
 			{
@@ -1019,8 +1025,6 @@ void LLFastTimerView::drawLineGraph()
 {
 	LLFastTimer _(FTM_DRAW_LINE_GRAPH);
 	//draw line graph history
-	S32 x = mBarRect.mLeft;
-	S32 y = LINE_GRAPH_HEIGHT;
 	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 	LLLocalClipRect clip(mGraphRect);
 
@@ -1029,21 +1033,6 @@ void LLFastTimerView::drawLineGraph()
 	static U32 max_calls = 0;
 	static F32 alpha_interp = 0.f;
 
-	//display y-axis range
-	std::string axis_label;
-	if (mDisplayCalls)
-		axis_label = llformat("%d calls", (int)max_calls);
-	else if (mDisplayHz)
-		axis_label = llformat("%d Hz", (int)(1.f / max_time.value()));
-	else
-		axis_label = llformat("%4.2f ms", LLUnit<F32, LLUnits::Milliseconds>(max_time).value());
-
-	x = mGraphRect.mRight - LLFontGL::getFontMonospace()->getWidth(axis_label)-5;
-	y = mGraphRect.mTop - LLFontGL::getFontMonospace()->getLineHeight();
-
-	LLFontGL::getFontMonospace()->renderUTF8(axis_label, 0, x, y, LLColor4::white,
-		LLFontGL::LEFT, LLFontGL::TOP);
-
 	//highlight visible range
 	{
 		S32 first_frame = mRecording.getNumRecordedPeriods() - mScrollIndex;
@@ -1059,7 +1048,7 @@ void LLFastTimerView::drawLineGraph()
 
 		if (mHoverBarIndex > 0)
 		{
-			S32 bar_frame = first_frame - mHoverBarIndex - 1;
+			S32 bar_frame = first_frame - (mScrollIndex + mHoverBarIndex) - 1;
 			F32 bar = (F32) mGraphRect.mLeft + frame_delta*bar_frame;
 
 			gGL.color4f(0.5f,0.5f,0.5f,1);
@@ -1090,6 +1079,7 @@ void LLFastTimerView::drawLineGraph()
 		const F32 * col = sTimerColors[idp->getIndex()].mV;// ft_display_table[idx].color->mV;
 
 		F32 alpha = 1.f;
+		bool is_hover_timer = true;
 
 		if (mHoverID != NULL &&
 			mHoverID != idp)
@@ -1097,11 +1087,15 @@ void LLFastTimerView::drawLineGraph()
 			if (idp->getParent() != mHoverID)
 			{
 				alpha = alpha_interp;
+				is_hover_timer = false;
 			}
 		}
 
 		gGL.color4f(col[0], col[1], col[2], alpha);				
 		gGL.begin(LLRender::TRIANGLE_STRIP);
+		F32 call_scale_factor = (F32)mGraphRect.getHeight() / (F32)max_calls;
+		F32 time_scale_factor = (F32)mGraphRect.getHeight() / max_time.value();
+		F32 hz_scale_factor = (F32) mGraphRect.getHeight() / (1.f / max_time.value());
 		for (U32 j = mRecording.getNumRecordedPeriods();
 			j > 0;
 			j--)
@@ -1110,16 +1104,26 @@ void LLFastTimerView::drawLineGraph()
 			LLUnit<F32, LLUnits::Seconds> time = llmax(recording.getSum(*idp), LLUnit<F64, LLUnits::Seconds>(0.000001));
 			U32 calls = recording.getSum(idp->callCount());
 
-			if (alpha == 1.f)
+			if (is_hover_timer)
 			{ 
 				//normalize to highlighted timer
 				cur_max = llmax(cur_max, time);
 				cur_max_calls = llmax(cur_max_calls, calls);
 			}
 			F32 x = mGraphRect.mRight - j * (F32)(mGraphRect.getWidth())/(mRecording.getNumRecordedPeriods()-1);
-			F32 y = mDisplayHz 
-				? mGraphRect.mBottom + (1.f / time.value()) * ((F32) mGraphRect.getHeight() / (1.f / max_time.value()))
-				: mGraphRect.mBottom + time / max_time * (F32)mGraphRect.getHeight();
+			F32 y;
+			switch(mDisplayType)
+			{
+			case TIME:
+				y = mGraphRect.mBottom + time.value() * time_scale_factor;
+				break;
+			case CALLS:
+				y = mGraphRect.mBottom + (F32)calls * call_scale_factor;
+				break;
+			case HZ:
+				y = mGraphRect.mBottom + (1.f / time.value()) * hz_scale_factor;
+				break;
+			}
 			gGL.vertex2f(x,y);
 			gGL.vertex2f(x,mGraphRect.mBottom);
 		}
@@ -1140,7 +1144,7 @@ void LLFastTimerView::drawLineGraph()
 	
 	//interpolate towards new maximum
 	max_time = lerp(max_time.value(), cur_max.value(), LLSmoothInterpolation::getInterpolant(0.1f));
-	if (max_time - cur_max <= 1 ||  cur_max - max_time  <= 1)
+	if (llabs((max_time - cur_max).value()) <= 1)
 	{
 		max_time = llmax(LLUnit<F32, LLUnits::Microseconds>(1), LLUnit<F32, LLUnits::Microseconds>(cur_max));
 	}
@@ -1159,8 +1163,8 @@ void LLFastTimerView::drawLineGraph()
 
 	if (mHoverID != NULL)
 	{
-		x = (mGraphRect.mRight + mGraphRect.mLeft)/2;
-		y = mGraphRect.mBottom + 8;
+		S32 x = (mGraphRect.mRight + mGraphRect.mLeft)/2;
+		S32 y = mGraphRect.mBottom + 8;
 
 		LLFontGL::getFontMonospace()->renderUTF8(
 			mHoverID->getName(), 
@@ -1168,18 +1172,40 @@ void LLFastTimerView::drawLineGraph()
 			x, y, 
 			LLColor4::white,
 			LLFontGL::LEFT, LLFontGL::BOTTOM);
-	}					
+	}
+
+	//display y-axis range
+	std::string axis_label;
+	switch(mDisplayType)
+	{
+	case TIME:
+		axis_label = llformat("%4.2f ms", LLUnit<F32, LLUnits::Milliseconds>(max_time).value());
+		break;
+	case CALLS:
+		axis_label = llformat("%d calls", (int)max_calls);
+		break;
+	case HZ:
+		axis_label = llformat("%4.2f Hz", max_time.value() ? 1.f / max_time.value() : 0.f);
+		break;
+	}
+
+	LLFontGL* font = LLFontGL::getFontMonospace();
+	S32 x = mGraphRect.mRight - font->getWidth(axis_label)-5;
+	S32 y = mGraphRect.mTop - font->getLineHeight();;
+
+	font->renderUTF8(axis_label, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
 }
 
-void LLFastTimerView::drawLegend( S32 y )
+void LLFastTimerView::drawLegend()
 {
 	// draw legend
 	S32 dx;
-	S32 x = MARGIN;
+	S32 x = mLegendRect.mLeft;
+	S32 y = mLegendRect.mTop;
 	const S32 TEXT_HEIGHT = (S32)LLFontGL::getFontMonospace()->getLineHeight();
 
 	{
-		LLLocalClipRect clip(LLRect(MARGIN, y, LEGEND_WIDTH, MARGIN));
+		LLLocalClipRect clip(mLegendRect);
 		S32 cur_line = 0;
 		ft_display_idx.clear();
 		std::map<TimeBlock*, S32> display_line;
@@ -1214,18 +1240,22 @@ void LLFastTimerView::drawLegend( S32 y )
 			}
 			else
 			{
-				ms = LLUnit<F64, LLUnits::Seconds>(mRecording.getPeriodMean(*idp));
-				calls = (S32)mRecording.getPeriodMean(idp->callCount());
+				ms = LLUnit<F64, LLUnits::Seconds>(mRecording.getPeriodMean(*idp, RUNNING_AVERAGE_WIDTH));
+				calls = (S32)mRecording.getPeriodMean(idp->callCount(), RUNNING_AVERAGE_WIDTH);
 			}
 
 			std::string timer_label;
-			if (mDisplayCalls)
-			{
-				timer_label = llformat("%s (%d)",idp->getName().c_str(),calls);
-			}
-			else
+			switch(mDisplayType)
 			{
+			case TIME:
 				timer_label = llformat("%s [%.1f]",idp->getName().c_str(),ms.value());
+				break;
+			case CALLS:
+				timer_label = llformat("%s (%d)",idp->getName().c_str(),calls);
+				break;
+			case HZ:
+				timer_label = llformat("%.1f", ms.value() ? (1.f / ms.value()) : 0.f);
+				break;
 			}
 			dx = (TEXT_HEIGHT+4) + get_depth(idp)*8;
 
@@ -1300,36 +1330,16 @@ void LLFastTimerView::generateUniqueColors()
 	}
 }
 
-S32 LLFastTimerView::drawHelp( S32 y )
+void LLFastTimerView::drawHelp( S32 y )
 {
 	// Draw some help
 	const S32 texth = (S32)LLFontGL::getFontMonospace()->getLineHeight();
 
-	char modedesc[][32] = {
-		"2 x Average ",
-		"Max         ",
-		"Recent Max  ",
-		"100 ms      "
-	};
-	char centerdesc[][32] = {
-		"Left      ",
-		"Centered  ",
-		"Ordered   "
-	};
-
-	std::string text;
-	text = llformat("Full bar = %s [Click to pause/reset] [SHIFT-Click to toggle]",modedesc[mDisplayMode]);
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, MARGIN, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
-
 	y -= (texth + 2);
-	text = llformat("Justification = %s [CTRL-Click to toggle]",centerdesc[mDisplayCenter]);
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, MARGIN, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
 	y -= (texth + 2);
 
-	LLFontGL::getFontMonospace()->renderUTF8(std::string("[Right-Click log selected] [ALT-Click toggle counts] [ALT-SHIFT-Click sub hidden]"),
+	LLFontGL::getFontMonospace()->renderUTF8(std::string("[Right-Click log selected] [ALT-Click toggle counts]"),
 		0, MARGIN, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
-	y -= (texth + 2);
-	return y;
 }
 
 void LLFastTimerView::drawTicks()
@@ -1392,7 +1402,7 @@ void LLFastTimerView::drawBorders( S32 y, const S32 x_start, S32 bar_height, S32
 		by = LINE_GRAPH_HEIGHT-dy;
 
 		//line graph
-		mGraphRect = LLRect(x_start-5, by, getRect().getWidth()-5, 5);
+		//mGraphRect = LLRect(x_start-5, by, getRect().getWidth()-5, 5);
 
 		gl_rect_2d(mGraphRect, FALSE);
 	}
@@ -1403,7 +1413,7 @@ void LLFastTimerView::updateTotalTime()
 	switch(mDisplayMode)
 	{
 	case 0:
-		mTotalTimeDisplay = mRecording.getPeriodMean(FTM_FRAME, 100)*2;
+		mTotalTimeDisplay = mRecording.getPeriodMean(FTM_FRAME, RUNNING_AVERAGE_WIDTH)*2;
 		break;
 	case 1:
 		mTotalTimeDisplay = mRecording.getPeriodMax(FTM_FRAME);
@@ -1447,18 +1457,27 @@ void LLFastTimerView::drawBars()
 		const S32 histmax = (S32)mRecording.getNumRecordedPeriods();
 
 		// update widths
-		updateTimerBarWidths(&FTM_FRAME, mAverageTimerRow, -1);
-		updateTimerBarOffsets(&FTM_FRAME, mAverageTimerRow);
-
-		for (S32 history_index = 1; history_index <= histmax; history_index++)
+		if (!mPauseHistory)
 		{
-			llassert(history_index <= mTimerBarRows.size());
-			TimerBarRow& row = mTimerBarRows[history_index - 1];
-			if (row.mBars.empty())
+			U32 bar_index = 0;
+			if (!mAverageTimerRow.mBars)
 			{
-				row.mBars.reserve(LLInstanceTracker<LLTrace::TimeBlock>::instanceCount());
-				updateTimerBarWidths(&FTM_FRAME, row, history_index);
-				updateTimerBarOffsets(&FTM_FRAME, row);
+				mAverageTimerRow.mBars = new TimerBar[LLInstanceTracker<LLTrace::TimeBlock>::instanceCount()];
+			}
+			updateTimerBarWidths(&FTM_FRAME, mAverageTimerRow, -1, bar_index);
+			updateTimerBarOffsets(&FTM_FRAME, mAverageTimerRow);
+
+			for (S32 history_index = 1; history_index <= histmax; history_index++)
+			{
+				llassert(history_index <= mTimerBarRows.size());
+				TimerBarRow& row = mTimerBarRows[history_index - 1];
+				bar_index = 0;
+				if (!row.mBars)
+				{
+					row.mBars = new TimerBar[LLInstanceTracker<LLTrace::TimeBlock>::instanceCount()];
+					updateTimerBarWidths(&FTM_FRAME, row, history_index, bar_index);
+					updateTimerBarOffsets(&FTM_FRAME, row);
+				}
 			}
 		}
 
@@ -1492,32 +1511,28 @@ void LLFastTimerView::drawBars()
 
 static LLFastTimer::DeclareTimer FTM_UPDATE_TIMER_BAR_WIDTHS("Update timer bar widths");
 
-LLUnit<F32, LLUnits::Seconds> LLFastTimerView::updateTimerBarWidths(LLTrace::TimeBlock* time_block, TimerBarRow& row, S32 history_index, bool visible)
+LLUnit<F32, LLUnits::Seconds> LLFastTimerView::updateTimerBarWidths(LLTrace::TimeBlock* time_block, TimerBarRow& row, S32 history_index, U32& bar_index)
 {
 	LLFastTimer _(FTM_UPDATE_TIMER_BAR_WIDTHS);
 	const LLUnit<F32, LLUnits::Seconds> self_time = history_index == -1
-										? mRecording.getPeriodMean(time_block->selfTime()) 
+										? mRecording.getPeriodMean(time_block->selfTime(), RUNNING_AVERAGE_WIDTH) 
 										: mRecording.getPrevRecording(history_index).getSum(time_block->selfTime());
 
 	LLUnit<F32, LLUnits::Seconds> full_time = self_time;
 
 	// reserve a spot for this bar to be rendered before its children
 	// even though we don't know its size yet
-	std::vector<TimerBar>& bars = row.mBars;
-	S32 bar_index = bars.size();
-	bars.push_back(TimerBar());
+	TimerBar& timer_bar = row.mBars[bar_index];
+	bar_index++;
 
-	const bool children_visible = visible && !time_block->getCollapsed();
 	for (TimeBlock::child_iter it = time_block->beginChildren(), end_it = time_block->endChildren(); it != end_it; ++it)
 	{
-		full_time += updateTimerBarWidths(*it, row, history_index, children_visible);
+		full_time += updateTimerBarWidths(*it, row, history_index, bar_index);
 	}
 
-	TimerBar& timer_bar = bars[bar_index];
 	timer_bar.mTotalTime  = full_time;
 	timer_bar.mSelfTime   = self_time;
 	timer_bar.mTimeBlock  = time_block;
-	timer_bar.mVisible    = visible;
 	
 	return full_time;
 }
@@ -1528,21 +1543,16 @@ S32 LLFastTimerView::updateTimerBarOffsets(LLTrace::TimeBlock* time_block, Timer
 {
 	LLFastTimer _(FTM_UPDATE_TIMER_BAR_FRACTIONS);
 
-	std::vector<TimerBar>& bars = row.mBars;
-	llassert(timer_bar_index < bars.size());
-	TimerBar& timer_bar = bars[timer_bar_index];
-	const LLUnit<F32, LLUnits::Seconds> child_time_width = timer_bar.mTotalTime - timer_bar.mSelfTime;
-	timer_bar.mChildrenStart = timer_bar.mSelfStart;
+	TimerBar& timer_bar = row.mBars[timer_bar_index];
+	const LLUnit<F32, LLUnits::Seconds> bar_time = timer_bar.mTotalTime - timer_bar.mSelfTime;
+	timer_bar.mChildrenStart = timer_bar.mSelfStart + timer_bar.mSelfTime / 2;
+	timer_bar.mChildrenEnd = timer_bar.mChildrenStart + timer_bar.mTotalTime - timer_bar.mSelfTime;
 
-	if (mDisplayCenter == ALIGN_CENTER)
+	if (timer_bar_index == 0)
 	{
-		timer_bar.mChildrenStart += timer_bar.mSelfTime / 2;
+		timer_bar.mSelfStart = 0.f;
+		timer_bar.mSelfEnd = bar_time;
 	}
-	else if (mDisplayCenter == ALIGN_RIGHT)
-	{
-		timer_bar.mChildrenStart += timer_bar.mSelfTime;
-	}
-	timer_bar.mChildrenEnd = timer_bar.mChildrenStart + timer_bar.mTotalTime - timer_bar.mSelfTime;
 
 	//now loop through children and figure out portion of bar image covered by each bar, now that we know the
 	//sum of all children
@@ -1556,8 +1566,7 @@ S32 LLFastTimerView::updateTimerBarOffsets(LLTrace::TimeBlock* time_block, Timer
 	{
 		timer_bar_index++;
 		
-		llassert(timer_bar_index < bars.size());
-		TimerBar& child_timer_bar = bars[timer_bar_index];
+		TimerBar& child_timer_bar = row.mBars[timer_bar_index];
 		TimeBlock* child_time_block = *it;
 
 		if (last_child_timer_bar)
@@ -1574,15 +1583,15 @@ S32 LLFastTimerView::updateTimerBarOffsets(LLTrace::TimeBlock* time_block, Timer
 		}
 
 		child_timer_bar.mStartFraction = bar_fraction_start;
-		child_timer_bar.mEndFraction = child_time_width > 0
-										? bar_fraction_start + child_timer_bar.mTotalTime / child_time_width
+		child_timer_bar.mEndFraction = bar_time > 0
+										? bar_fraction_start + child_timer_bar.mTotalTime / bar_time
 										: 1.f;
-		child_timer_bar.mSelfStart = timer_bar.mChildrenStart 
-									+ child_timer_bar.mStartFraction 
-										* (timer_bar.mChildrenEnd - timer_bar.mChildrenStart);
-		child_timer_bar.mSelfEnd =	timer_bar.mChildrenStart 
-									+ child_timer_bar.mEndFraction 
-										* (timer_bar.mChildrenEnd - timer_bar.mChildrenStart);
+		child_timer_bar.mSelfStart = timer_bar.mChildrenStart 
+									+ child_timer_bar.mStartFraction 
+										* (timer_bar.mChildrenEnd - timer_bar.mChildrenStart);
+		child_timer_bar.mSelfEnd =	timer_bar.mChildrenStart 
+									+ child_timer_bar.mEndFraction 
+										* (timer_bar.mChildrenEnd - timer_bar.mChildrenStart);
 
 		timer_bar_index = updateTimerBarOffsets(child_time_block, row, timer_bar_index);
 
@@ -1591,16 +1600,15 @@ S32 LLFastTimerView::updateTimerBarOffsets(LLTrace::TimeBlock* time_block, Timer
 	return timer_bar_index;
 }
 
-S32 LLFastTimerView::drawBar(LLRect bar_rect, TimerBarRow& row, S32 image_width, S32 image_height, bool hovered, S32 bar_index)
+S32 LLFastTimerView::drawBar(LLRect bar_rect, TimerBarRow& row, S32 image_width, S32 image_height, bool hovered, bool visible, S32 bar_index)
 {
-	llassert(bar_index < row.mBars.size());	
 	TimerBar& timer_bar = row.mBars[bar_index];
 	LLTrace::TimeBlock* time_block = timer_bar.mTimeBlock;
 
 	hovered |= mHoverID == time_block;
 
 	// animate scale of bar when hovering over that particular timer
-	if ((F32)bar_rect.getWidth() * (timer_bar.mEndFraction - timer_bar.mStartFraction) > 2.f)
+	if (visible && (F32)bar_rect.getWidth() * (timer_bar.mEndFraction - timer_bar.mStartFraction) > 2.f)
 	{
 		LLRect render_rect(bar_rect);
 		S32 scale_offset = 0;
@@ -1637,15 +1645,17 @@ S32 LLFastTimerView::drawBar(LLRect bar_rect, TimerBarRow& row, S32 image_width,
 		children_rect.mBottom = bar_rect.mBottom;
 	}
 
+	bool children_visible = visible && !time_block->getCollapsed();
+
 	bar_index++;
-	const U32 num_bars = row.mBars.size();
+	const U32 num_bars = LLInstanceTracker<LLTrace::TimeBlock>::instanceCount();
 	if (bar_index < num_bars && row.mBars[bar_index].mFirstChild)
 	{
 		bool is_last = false;
 		do
 		{
 			is_last = row.mBars[bar_index].mLastChild;
-			bar_index = drawBar(children_rect, row, image_width, image_height, hovered, bar_index);
+			bar_index = drawBar(children_rect, row, image_width, image_height, hovered, children_visible, bar_index);
 		}
 		while(!is_last && bar_index < num_bars);
 	}
diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h
index d9ae6348dab00af0603dd3a0a24daee419c25dc2..d931f25a7e3e189c442af9342a80bdf22c60c835 100755
--- a/indra/newview/llfasttimerview.h
+++ b/indra/newview/llfasttimerview.h
@@ -68,8 +68,8 @@ class LLFastTimerView : public LLFloater
 	virtual	void	onClickCloseBtn();
 	void drawTicks();
 	void drawLineGraph();
-	void drawLegend(S32 y);
-	S32 drawHelp(S32 y);
+	void drawLegend();
+	void drawHelp(S32 y);
 	void drawBorders( S32 y, const S32 x_start, S32 barh, S32 dy);
 	void drawBars();
 
@@ -82,7 +82,6 @@ class LLFastTimerView : public LLFloater
 		TimerBar()
 		:	mTotalTime(0),
 			mSelfTime(0),
-			mVisible(true),
 			mStartFraction(0.f),
 			mEndFraction(1.f),
 			mFirstChild(false),
@@ -104,29 +103,26 @@ class LLFastTimerView : public LLFloater
 
 	struct TimerBarRow
 	{
-		S32						mBottom,
-								mTop;
-		std::vector<TimerBar>	mBars;
+		S32			mBottom,
+					mTop;
+		TimerBar*	mBars;
 	};
 
-	LLUnit<F32, LLUnits::Seconds> updateTimerBarWidths(LLTrace::TimeBlock* time_block, TimerBarRow& row, S32 history_index, bool visible = true);
+	LLUnit<F32, LLUnits::Seconds> updateTimerBarWidths(LLTrace::TimeBlock* time_block, TimerBarRow& row, S32 history_index, U32& bar_index);
 	S32 updateTimerBarOffsets(LLTrace::TimeBlock* time_block, TimerBarRow& row, S32 timer_bar_index = 0);
-	S32 drawBar(LLRect bar_rect, TimerBarRow& row, S32 image_width, S32 image_height, bool hovered = false, S32 bar_index = 0);
+	S32 drawBar(LLRect bar_rect, TimerBarRow& row, S32 image_width, S32 image_height, bool hovered = false, bool visible = true, S32 bar_index = 0);
 	void setPauseState(bool pause_state);
 
 	std::deque<TimerBarRow> mTimerBarRows;
 	TimerBarRow				mAverageTimerRow;
 
-	enum ChildAlignment
+	enum EDisplayType
 	{
-		ALIGN_LEFT,
-		ALIGN_CENTER,
-		ALIGN_RIGHT,
-		ALIGN_COUNT
-	}								mDisplayCenter;
-	bool							mDisplayCalls,
-									mDisplayHz,
-									mPauseHistory;
+		TIME,
+		CALLS,
+		HZ
+	}								mDisplayType;
+	bool							mPauseHistory;
 	LLUnit<F64, LLUnits::Seconds>	mAllTimeMax,
 									mTotalTimeDisplay;
 	S32								mScrollIndex,
@@ -137,7 +133,8 @@ class LLFastTimerView : public LLFloater
 	LLTrace::TimeBlock*				mHoverTimer;
 	LLRect							mToolTipRect,
 									mGraphRect,
-									mBarRect;
+									mBarRect,
+									mLegendRect;
 	LLFrameTimer					mHighlightTimer;
 	LLTrace::PeriodicRecording		mRecording;
 };
diff --git a/indra/newview/skins/default/xui/en/floater_fast_timers.xml b/indra/newview/skins/default/xui/en/floater_fast_timers.xml
index 77adb5524e7b073931d9e38744c46734ed250029..671f116df3647e162443008c3d977cee9b01bc45 100755
--- a/indra/newview/skins/default/xui/en/floater_fast_timers.xml
+++ b/indra/newview/skins/default/xui/en/floater_fast_timers.xml
@@ -16,6 +16,27 @@
  width="700">
   <string name="pause" >Pause</string>
   <string name="run">Run</string>
+  <combo_box name="time_scale_combo"
+             follows="left|top"
+             left="10"
+             top="5"
+             width="150"
+             height="20">
+    <item label="2x Average"/>
+    <item label="Max"/>
+    <item label="Recent Max"/>
+    <item label="100ms"/>
+  </combo_box>
+  <combo_box name="metric_combo"
+             follows="left|top"
+             left_pad="10"
+             top="5"
+             width="150"
+             height="20">
+    <item label="Time"/>
+    <item label="Number of Calls"/>
+    <item label="Hz"/>
+  </combo_box>
   <button follows="top|right" 
           name="pause_btn"
           left="-200"
@@ -24,4 +45,52 @@
           height="40"
           label="Pause"
           font="SansSerifHuge"/>
+  <layout_stack name="legend_stack"
+                orientation="horizontal"
+                left="0"
+                top="50"
+                right="695"
+                bottom="500"
+                follows="all">
+    <layout_panel name="legend_panel"
+                  auto_resize="false"
+                  user_resize="true"
+                  width="220"
+                  height="450"
+                  min_width="100">
+      <panel top="0"
+             left="0"
+             width="220"
+             height="440"
+             name="legend"
+             follows="all"/>
+    </layout_panel>
+    <layout_panel name="timers_panel"
+                  auto_resize="true"
+                  user_resize="true"
+                  height="450"
+                  width="475"
+                  min_width="100">
+      <layout_stack name="timer_bars_stack"
+                    orientation="vertical"
+                    left="0"
+                    top="0"
+                    width="475"
+                    height="445"
+                    follows="all">
+        <layout_panel name="bars_panel"
+                      auto_resize="true"
+                      user_resize="true"
+                      top="0"
+                      width="475"
+                      height="210"/>
+        <layout_panel name="lines_panel"
+                      auto_resize="false"
+                      user_resize="true"
+                      width="475"
+                      min_height="50"
+                      height="240"/>
+      </layout_stack>
+    </layout_panel>
+  </layout_stack>
 </floater>