From 0f58ca02cdec62711eadb82ba28fcff08faef2ee Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Fri, 12 Oct 2012 00:20:19 -0700
Subject: [PATCH] SH-3275 WIP Update viewer metrics system to be more flexible
 cleaned up accumulator merging logic introduced frame recording to LLTrace
 directly instead of going through LLViewerStats moved consumer code over to
 frame recording instead of whatever the current active recording was

---
 indra/llcommon/lltrace.h                 |  49 ++---
 indra/llcommon/lltracerecording.cpp      | 214 ++++++++++++++++++-
 indra/llcommon/lltracerecording.h        | 251 ++++++-----------------
 indra/llcommon/lltracethreadrecorder.cpp |  73 +++----
 indra/llcommon/lltracethreadrecorder.h   |   7 +-
 indra/llui/llstatbar.cpp                 |   7 +-
 indra/llui/llstatbar.h                   |  35 ++--
 indra/llui/llstatgraph.cpp               |   6 +-
 indra/newview/llviewerstats.cpp          |   2 +
 indra/newview/llviewerstats.h            |   8 +-
 indra/newview/llviewertexturelist.cpp    |   6 +-
 11 files changed, 355 insertions(+), 303 deletions(-)

diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index a6334e176b3..0c618a2f4bf 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -101,16 +101,6 @@ namespace LLTrace
 			}
 		}
 
-		void addDeltas(const AccumulatorBuffer<ACCUMULATOR>& start, const AccumulatorBuffer<ACCUMULATOR>& finish)
-		{
-			llassert(mNextStorageSlot == start.mNextStorageSlot && mNextStorageSlot == finish.mNextStorageSlot);
-
-			for (size_t i = 0; i < mNextStorageSlot; i++)
-			{
-				mStorage[i].addDeltas(start.mStorage[i], finish.mStorage[i]);
-			}
-		}
-
 		void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other)
 		{
 			for (size_t i = 0; i < mNextStorageSlot; i++)
@@ -203,11 +193,12 @@ namespace LLTrace
 	public:
 		MeasurementAccumulator()
 		:	mSum(0),
-			mMin(0),
-			mMax(0),
+			mMin(std::numeric_limits<T>::max()),
+			mMax(std::numeric_limits<T>::min()),
 			mMean(0),
 			mVarianceSum(0),
-			mNumSamples(0)
+			mNumSamples(0),
+			mLastValue(0)
 		{}
 
 		LL_FORCE_INLINE void sample(T value)
@@ -251,20 +242,27 @@ namespace LLTrace
 				sd_2 = other.getStandardDeviation();
 			// combine variance (and hence standard deviation) of 2 different sized sample groups using
 			// the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm
-			mVarianceSum =  (F32)mNumSamples
+			if (n_1 == 0)
+			{
+				mVarianceSum = other.mVarianceSum;
+			}
+			else if (n_2 == 0)
+			{
+				// don't touch variance
+				// mVarianceSum = mVarianceSum;
+			}
+			else
+			{
+				mVarianceSum =  (F32)mNumSamples
 							* ((((n_1 - 1.f) * sd_1 * sd_1)
 								+ ((n_2 - 1.f) * sd_2 * sd_2)
 								+ (((n_1 * n_2) / (n_1 + n_2))
 									* ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2))))
 							/ (n_1 + n_2 - 1.f));
+			}
 			mLastValue = other.mLastValue;
 		}
 
-		void addDeltas(const MeasurementAccumulator<T>& start, const MeasurementAccumulator<T>& finish)
-		{
-			llerrs << "Delta merge invalid for measurement accumulators" << llendl;
-		}
-
 		void reset()
 		{
 			mNumSamples = 0;
@@ -313,12 +311,6 @@ namespace LLTrace
 			mNumSamples += other.mNumSamples;
 		}
 
-		void addDeltas(const RateAccumulator<T>& start, const RateAccumulator<T>& finish)
-		{
-			mSum += finish.mSum - start.mSum;
-			mNumSamples += finish.mNumSamples - start.mNumSamples;
-		}
-
 		void reset()
 		{
 			mNumSamples = 0;
@@ -464,13 +456,6 @@ namespace LLTrace
 			mCalls += other.mCalls;
 		}
 
-		void addDeltas(const TimerAccumulator& start, const TimerAccumulator& finish)
-		{
-			mTotalTimeCounter += finish.mTotalTimeCounter - start.mTotalTimeCounter;
-			mChildTimeCounter += finish.mChildTimeCounter - start.mChildTimeCounter;
-			mCalls += finish.mCalls - start.mCalls;
-		}
-
 		void reset()
 		{
 			mTotalTimeCounter = 0;
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index 08839303190..5d7b231b7de 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -68,16 +68,16 @@ void Recording::handleReset()
 }
 
 void Recording::handleStart()
-	{
-		mSamplingTimer.reset();
-		LLTrace::get_thread_recorder()->activate(this);
+{
+	mSamplingTimer.reset();
+	LLTrace::get_thread_recorder()->activate(this);
 }
 
 void Recording::handleStop()
-	{
-		mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
-		LLTrace::get_thread_recorder()->deactivate(this);
-	}
+{
+	mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
+	LLTrace::get_thread_recorder()->deactivate(this);
+}
 
 void Recording::handleSplitTo(Recording& other)
 {
@@ -105,10 +105,204 @@ void Recording::mergeRecording( const Recording& other )
 	mStackTimers.write()->addSamples(*other.mStackTimers);
 }
 
-void Recording::mergeRecordingDelta(const Recording& baseline, const Recording& target)
+///////////////////////////////////////////////////////////////////////
+// Recording
+///////////////////////////////////////////////////////////////////////
+
+PeriodicRecording::PeriodicRecording( S32 num_periods ) 
+:	mNumPeriods(num_periods),
+	mCurPeriod(0),
+	mTotalValid(false),
+	mRecordingPeriods( new Recording[num_periods])
+{
+	llassert(mNumPeriods > 0);
+}
+
+PeriodicRecording::~PeriodicRecording()
+{
+	delete[] mRecordingPeriods;
+}
+
+
+void PeriodicRecording::nextPeriod()
+{
+	EPlayState play_state = getPlayState();
+	getCurRecordingPeriod().stop();
+	mCurPeriod = (mCurPeriod + 1) % mNumPeriods;
+	switch(play_state)
+	{
+	case STOPPED:
+		break;
+	case PAUSED:
+		getCurRecordingPeriod().pause();
+		break;
+	case STARTED:
+		getCurRecordingPeriod().start();
+		break;
+	}
+	// new period, need to recalculate total
+	mTotalValid = false;
+}
+
+Recording& PeriodicRecording::getTotalRecording()
+{
+	if (!mTotalValid)
+	{
+		mTotalRecording.reset();
+		for (S32 i = (mCurPeriod + 1) % mNumPeriods; i < mCurPeriod; i++)
+		{
+			mTotalRecording.mergeRecording(mRecordingPeriods[i]);
+		}
+	}
+	mTotalValid = true;
+	return mTotalRecording;
+}
+
+void PeriodicRecording::handleStart()
+{
+	getCurRecordingPeriod().handleStart();
+}
+
+void PeriodicRecording::handleStop()
+{
+	getCurRecordingPeriod().handleStop();
+}
+
+void PeriodicRecording::handleReset()
+{
+	getCurRecordingPeriod().handleReset();
+}
+
+void PeriodicRecording::handleSplitTo( PeriodicRecording& other )
+{
+	getCurRecordingPeriod().handleSplitTo(other.getCurRecordingPeriod());
+}
+
+///////////////////////////////////////////////////////////////////////
+// ExtendableRecording
+///////////////////////////////////////////////////////////////////////
+
+void ExtendableRecording::extend()
 {
-	mRates.write()->addDeltas(*baseline.mRates, *target.mRates);
-	mStackTimers.write()->addDeltas(*baseline.mStackTimers, *target.mStackTimers);
+	mAcceptedRecording.mergeRecording(mPotentialRecording);
+	mPotentialRecording.reset();
 }
 
+void ExtendableRecording::handleStart()
+{
+	mPotentialRecording.handleStart();
+}
+
+void ExtendableRecording::handleStop()
+{
+	mPotentialRecording.handleStop();
+}
+
+void ExtendableRecording::handleReset()
+{
+	mAcceptedRecording.handleReset();
+	mPotentialRecording.handleReset();
+}
+
+void ExtendableRecording::handleSplitTo( ExtendableRecording& other )
+{
+	mPotentialRecording.handleSplitTo(other.mPotentialRecording);
+}
+
+PeriodicRecording& get_frame_recording()
+{
+	static PeriodicRecording sRecording(64);
+	sRecording.start();
+	return sRecording;
+}
+
+}
+
+void LLVCRControlsMixinCommon::start()
+{
+	switch (mPlayState)
+	{
+	case STOPPED:
+		handleReset();
+		handleStart();
+		break;
+	case PAUSED:
+		handleStart();
+		break;
+	case STARTED:
+		handleReset();
+		break;
+	}
+	mPlayState = STARTED;
+}
+
+void LLVCRControlsMixinCommon::stop()
+{
+	switch (mPlayState)
+	{
+	case STOPPED:
+		break;
+	case PAUSED:
+		handleStop();
+		break;
+	case STARTED:
+		handleStop();
+		break;
+	}
+	mPlayState = STOPPED;
+}
+
+void LLVCRControlsMixinCommon::pause()
+{
+	switch (mPlayState)
+	{
+	case STOPPED:
+		break;
+	case PAUSED:
+		break;
+	case STARTED:
+		handleStop();
+		break;
+	}
+	mPlayState = PAUSED;
+}
+
+void LLVCRControlsMixinCommon::resume()
+{
+	switch (mPlayState)
+	{
+	case STOPPED:
+		handleStart();
+		break;
+	case PAUSED:
+		handleStart();
+		break;
+	case STARTED:
+		break;
+	}
+	mPlayState = STARTED;
+}
+
+void LLVCRControlsMixinCommon::restart()
+{
+	switch (mPlayState)
+	{
+	case STOPPED:
+		handleReset();
+		handleStart();
+		break;
+	case PAUSED:
+		handleReset();
+		handleStart();
+		break;
+	case STARTED:
+		handleReset();
+		break;
+	}
+	mPlayState = STARTED;
+}
+
+void LLVCRControlsMixinCommon::reset()
+{
+	handleReset();
 }
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index 0a1a02fa021..924a7bffd5a 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -34,27 +34,11 @@
 #include "lltimer.h"
 #include "lltrace.h"
 
-template<typename DERIVED>
-class LL_COMMON_API LLVCRControlsMixinInterface
+class LL_COMMON_API LLVCRControlsMixinCommon
 {
 public:
-	virtual ~LLVCRControlsMixinInterface() {}
-	// trigger data accumulation (without reset)
-	virtual void handleStart() = 0;
-	// stop data accumulation, should put object in queryable state
-	virtual void handleStop() = 0;
-	// clear accumulated values, can be called while started
-	virtual void handleReset() = 0;
-	// atomically stop this object while starting the other
-	// no data can be missed in between stop and start
-	virtual void handleSplitTo(DERIVED& other) = 0;
-};
+	virtual ~LLVCRControlsMixinCommon() {}
 
-template<typename DERIVED>
-class LL_COMMON_API LLVCRControlsMixin
-:	private LLVCRControlsMixinInterface<DERIVED>
-{
-public:
 	enum EPlayState
 	{
 		STOPPED,
@@ -62,94 +46,39 @@ class LL_COMMON_API LLVCRControlsMixin
 		STARTED
 	};
 
-	void start()
-	{
-		switch (mPlayState)
-		{
-		case STOPPED:
-			handleReset();
-			handleStart();
-			break;
-		case PAUSED:
-			handleStart();
-			break;
-		case STARTED:
-			handleReset();
-			break;
-		}
-		mPlayState = STARTED;
-	}
+	void start();
+	void stop();
+	void pause();
+	void resume();
+	void restart();
+	void reset();
 
-	void stop()
-	{
-		switch (mPlayState)
-		{
-		case STOPPED:
-			break;
-		case PAUSED:
-			handleStop();
-			break;
-		case STARTED:
-			break;
-		}
-		mPlayState = STOPPED;
-	}
-
-	void pause()
-	{
-		switch (mPlayState)
-		{
-		case STOPPED:
-			break;
-		case PAUSED:
-			break;
-		case STARTED:
-			handleStop();
-			break;
-		}
-		mPlayState = PAUSED;
-	}
+	bool isStarted() { return mPlayState == STARTED; }
+	bool isPaused()  { return mPlayState == PAUSED; }
+	bool isStopped() { return mPlayState == STOPPED; }
+	EPlayState getPlayState() { return mPlayState; }
 
-	void resume()
-	{
-		switch (mPlayState)
-		{
-		case STOPPED:
-			handleStart();
-			break;
-		case PAUSED:
-			handleStart();
-			break;
-		case STARTED:
-			break;
-		}
-		mPlayState = STARTED;
-	}
+protected:
+	LLVCRControlsMixinCommon()
+	:	mPlayState(STOPPED)
+	{}
 
-	void restart()
-	{
-		switch (mPlayState)
-		{
-		case STOPPED:
-			handleReset();
-			handleStart();
-			break;
-		case PAUSED:
-			handleReset();
-			handleStart();
-			break;
-		case STARTED:
-			handleReset();
-			break;
-		}
-		mPlayState = STARTED;
-	}
+private:
+	// trigger data accumulation (without reset)
+	virtual void handleStart() = 0;
+	// stop data accumulation, should put object in queryable state
+	virtual void handleStop() = 0;
+	// clear accumulated values, can be called while started
+	virtual void handleReset() = 0;
 
-	void reset()
-	{
-		handleReset();
-	}
+	EPlayState mPlayState;
+};
 
+template<typename DERIVED>
+class LLVCRControlsMixin
+:	public LLVCRControlsMixinCommon
+{
+public:
 	void splitTo(DERIVED& other)
 	{
 		onSplitTo(other);
@@ -159,32 +88,15 @@ class LL_COMMON_API LLVCRControlsMixin
 	{
 		other.onSplitTo(*this);
 	}
-
-	bool isStarted() { return mPlayState == STARTED; }
-	bool isPaused()  { return mPlayState == PAUSED; }
-	bool isStopped() { return mPlayState == STOPPED; }
-	EPlayState getPlayState() { return mPlayState; }
-
-protected:
-
-	LLVCRControlsMixin()
-	:	mPlayState(STOPPED)
-	{}
-
 private:
-	EPlayState mPlayState;
+	// atomically stop this object while starting the other
+	// no data can be missed in between stop and start
+	virtual void handleSplitTo(DERIVED& other) = 0;
+
 };
 
 namespace LLTrace
 {
-	//template<typename T, typename IS_UNIT> class Rate;
-	//template<typename T, typename IS_UNIT> class Measurement;
-	//template<typename T> class Count;
-	//template<typename T> class AccumulatorBuffer;
-	//template<typename T> class RateAccumulator;
-	//template<typename T> class MeasurementAccumulator;
-	//class TimerAccumulator;
-
 	class LL_COMMON_API Recording : public LLVCRControlsMixin<Recording>
 	{
 	public:
@@ -196,7 +108,6 @@ namespace LLTrace
 		bool isPrimary() const;
 
 		void mergeRecording(const Recording& other);
-		void mergeRecordingDelta(const Recording& baseline, const Recording& target);
 
 		void update();
 
@@ -308,15 +219,13 @@ namespace LLTrace
 
 		F64 getSampleTime() const { return mElapsedSeconds; }
 
-	private:
-		friend class PeriodicRecording;
 		// implementation for LLVCRControlsMixin
 		/*virtual*/ void handleStart();
 		/*virtual*/ void handleStop();
 		/*virtual*/ void handleReset();
 		/*virtual*/ void handleSplitTo(Recording& other);
 
-
+	private:
 		friend class ThreadRecorder;
 		// returns data for current thread
 		class ThreadRecorder* getThreadRecorder(); 
@@ -333,38 +242,19 @@ namespace LLTrace
 	:	public LLVCRControlsMixin<PeriodicRecording>
 	{
 	public:
-		PeriodicRecording(S32 num_periods)
-		:	mNumPeriods(num_periods),
-			mCurPeriod(0),
-			mTotalValid(false),
-			mRecordingPeriods(new Recording[num_periods])
-	{
-			llassert(mNumPeriods > 0);
-		}
+		PeriodicRecording(S32 num_periods);
+		~PeriodicRecording();
+
+		void nextPeriod();
 
-		~PeriodicRecording()
+		Recording& getLastRecordingPeriod()
 		{
-			delete[] mRecordingPeriods;
+			return mRecordingPeriods[(mCurPeriod + mNumPeriods - 1) % mNumPeriods];
 		}
 
-		void nextPeriod()
+		const Recording& getLastRecordingPeriod() const
 		{
-			EPlayState play_state = getPlayState();
-			getCurRecordingPeriod().stop();
-			mCurPeriod = (mCurPeriod + 1) % mNumPeriods;
-			switch(play_state)
-			{
-			case STOPPED:
-				break;
-			case PAUSED:
-				getCurRecordingPeriod().pause();
-				break;
-			case STARTED:
-				getCurRecordingPeriod().start();
-				break;
-			}
-			// new period, need to recalculate total
-			mTotalValid = false;
+			return mRecordingPeriods[(mCurPeriod + mNumPeriods - 1) % mNumPeriods];
 		}
 
 		Recording& getCurRecordingPeriod()
@@ -377,41 +267,16 @@ namespace LLTrace
 			return mRecordingPeriods[mCurPeriod];
 		}
 
-		Recording& getTotalRecording()
-		{
-			if (!mTotalValid)
-			{
-				mTotalRecording.reset();
-				for (S32 i = (mCurPeriod + 1) % mNumPeriods; i < mCurPeriod; i++)
-				{
-					mTotalRecording.mergeRecording(mRecordingPeriods[i]);
-				}
-			}
-			mTotalValid = true;
-			return mTotalRecording;
-		}
+		Recording& getTotalRecording();
 
 	private:
-		// implementation for LLVCRControlsMixin
-		/*virtual*/ void handleStart()
-		{
-			getCurRecordingPeriod().handleStart();
-		}
-
-		/*virtual*/ void handleStop()
-		{
-			getCurRecordingPeriod().handleStop();
-		}
 
-		/*virtual*/ void handleReset()
-		{
-			getCurRecordingPeriod().handleReset();
-		}
+		// implementation for LLVCRControlsMixin
+		/*virtual*/ void handleStart();
+		/*virtual*/ void handleStop();
+		/*virtual*/ void handleReset();
 
-		/*virtual*/ void handleSplitTo(PeriodicRecording& other)
-		{
-			getCurRecordingPeriod().handleSplitTo(other.getCurRecordingPeriod());
-		}
+		/*virtual*/ void handleSplitTo(PeriodicRecording& other);
 
 		Recording*	mRecordingPeriods;
 		Recording	mTotalRecording;
@@ -419,6 +284,24 @@ namespace LLTrace
 		S32			mNumPeriods,
 					mCurPeriod;
 	};
+
+	PeriodicRecording& get_frame_recording();
+
+	class ExtendableRecording
+	:	public LLVCRControlsMixin<ExtendableRecording>
+	{
+		void extend();
+
+	private:
+		// implementation for LLVCRControlsMixin
+		/*virtual*/ void handleStart();
+		/*virtual*/ void handleStop();
+		/*virtual*/ void handleReset();
+		/*virtual*/ void handleSplitTo(ExtendableRecording& other);
+
+		Recording mAcceptedRecording;
+		Recording mPotentialRecording;
+	};
 }
 
 #endif // LL_LLTRACERECORDING_H
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
index 3acd06d553e..48aa1a42f2e 100644
--- a/indra/llcommon/lltracethreadrecorder.cpp
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -36,15 +36,13 @@ namespace LLTrace
 ///////////////////////////////////////////////////////////////////////
 
 ThreadRecorder::ThreadRecorder()
-:	mPrimaryRecording(NULL)
 {
 	get_thread_recorder() = this;
 	mFullRecording.start();
 }
 
 ThreadRecorder::ThreadRecorder( const ThreadRecorder& other ) 
-:	mFullRecording(other.mFullRecording),
-	mPrimaryRecording(NULL)
+:	mFullRecording(other.mFullRecording)
 {
 	get_thread_recorder() = this;
 	mFullRecording.start();
@@ -55,45 +53,40 @@ ThreadRecorder::~ThreadRecorder()
 	get_thread_recorder() = NULL;
 }
 
-//TODO: remove this and use llviewerstats recording
-Recording* ThreadRecorder::getPrimaryRecording()
-{
-	return mPrimaryRecording;
-}
-
 void ThreadRecorder::activate( Recording* recording )
 {
-	mActiveRecordings.push_front(ActiveRecording(mPrimaryRecording, recording));
+	mActiveRecordings.push_front(ActiveRecording(recording));
 	mActiveRecordings.front().mBaseline.makePrimary();
-	mPrimaryRecording = &mActiveRecordings.front().mBaseline;
 }
 
 std::list<ThreadRecorder::ActiveRecording>::iterator ThreadRecorder::update( Recording* recording )
 {
-	for (std::list<ActiveRecording>::iterator it = mActiveRecordings.begin(), end_it = mActiveRecordings.end();
+	std::list<ActiveRecording>::iterator it, end_it;
+	for (it = mActiveRecordings.begin(), end_it = mActiveRecordings.end();
 		it != end_it;
 		++it)
 	{
 		std::list<ActiveRecording>::iterator next_it = it;
-		if (++next_it != mActiveRecordings.end())
+		++next_it;
+
+		// if we have another recording further down in the stack...
+		if (next_it != mActiveRecordings.end())
 		{
-			next_it->mergeMeasurements((*it));
+			// ...push our gathered data down to it
+			next_it->mBaseline.mergeRecording(it->mBaseline);
 		}
 
-		it->flushAccumulators(mPrimaryRecording);
+		// copy accumulated measurements into result buffer and clear accumulator (mBaseline)
+		it->moveBaselineToTarget();
 
 		if (it->mTargetRecording == recording)
 		{
-			if (next_it != mActiveRecordings.end())
-			{
-				next_it->mBaseline.makePrimary();
-				mPrimaryRecording = &next_it->mBaseline;
-			}
-			return it;
+			// found the recording, so return it
+			break;
 		}
 	}
 
-	return mActiveRecordings.end();
+	return it;
 }
 
 void ThreadRecorder::deactivate( Recording* recording )
@@ -101,38 +94,34 @@ void ThreadRecorder::deactivate( Recording* recording )
 	std::list<ActiveRecording>::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;
+		++next_it;
+		if (next_it != mActiveRecordings.end())
+		{
+			next_it->mTargetRecording->makePrimary();
+		}
+
 		mActiveRecordings.erase(it);
 	}
 }
 
-ThreadRecorder::ActiveRecording::ActiveRecording( Recording* source, Recording* target ) 
+ThreadRecorder::ActiveRecording::ActiveRecording( Recording* target ) 
 :	mTargetRecording(target)
 {
-	// take snapshots of current values rates and timers
-	if (source)
-	{
-		mBaseline.mRates.write()->copyFrom(*source->mRates);
-		mBaseline.mStackTimers.write()->copyFrom(*source->mStackTimers);
-	}
 }
 
-void ThreadRecorder::ActiveRecording::mergeMeasurements(ThreadRecorder::ActiveRecording& other)
+void ThreadRecorder::ActiveRecording::moveBaselineToTarget()
 {
-	mBaseline.mMeasurements.write()->addSamples(*other.mBaseline.mMeasurements);
-}
-
-void ThreadRecorder::ActiveRecording::flushAccumulators(Recording* current)
-{
-	// accumulate statistics-like measurements
 	mTargetRecording->mMeasurements.write()->addSamples(*mBaseline.mMeasurements);
-	// for rate-like measurements, merge total change since baseline
-	mTargetRecording->mRates.write()->addDeltas(*mBaseline.mRates, *current->mRates);
-	mTargetRecording->mStackTimers.write()->addDeltas(*mBaseline.mStackTimers, *current->mStackTimers);
-	// reset baselines
-	mBaseline.mRates.write()->copyFrom(*current->mRates);
-	mBaseline.mStackTimers.write()->copyFrom(*current->mStackTimers);
+	mTargetRecording->mRates.write()->addSamples(*mBaseline.mRates);
+	mTargetRecording->mStackTimers.write()->addSamples(*mBaseline.mStackTimers);
+	mBaseline.mMeasurements.write()->reset();
+	mBaseline.mRates.write()->reset();
+	mBaseline.mStackTimers.write()->reset();
 }
 
+
 ///////////////////////////////////////////////////////////////////////
 // SlaveThreadRecorder
 ///////////////////////////////////////////////////////////////////////
diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h
index 42230087c04..678b1a89f04 100644
--- a/indra/llcommon/lltracethreadrecorder.h
+++ b/indra/llcommon/lltracethreadrecorder.h
@@ -51,19 +51,16 @@ namespace LLTrace
 
 		virtual void pushToMaster() = 0;
 
-		Recording* getPrimaryRecording();
 	protected:
 		struct ActiveRecording
 		{
-			ActiveRecording(Recording* source, Recording* target);
+			ActiveRecording(Recording* target);
 
 			Recording*	mTargetRecording;
 			Recording	mBaseline;
 
-			void mergeMeasurements(ActiveRecording& other);
-			void flushAccumulators(Recording* current);
+			void moveBaselineToTarget();
 		};
-		Recording*					mPrimaryRecording;
 		Recording					mFullRecording;
 		std::list<ActiveRecording>	mActiveRecordings;
 	};
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index 4cbf6950597..b73007e1077 100644
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -37,7 +37,6 @@
 #include "llstat.h"
 #include "lluictrlfactory.h"
 #include "lltracerecording.h"
-#include "lltracethreadrecorder.h"
 
 ///////////////////////////////////////////////////////////////////////////////////
 
@@ -112,17 +111,17 @@ void LLStatBar::draw()
 	}
 	else if (mFloatStatp)
 	{
-		LLTrace::Recording* recording = LLTrace::get_thread_recorder()->getPrimaryRecording();
+		LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecordingPeriod();
 		if (mPerSec)
 		{
-			current = recording->getSum(*mFloatStatp) / recording->getSampleTime();
+			current = recording.getPerSec(*mFloatStatp);
 			//min = recording->getMin(*mFloatStatp) / recording->getSampleTime();
 			//max = recording->getMax(*mFloatStatp) / recording->getSampleTime();
 			//mean = recording->getMean(*mFloatStatp) / recording->getSampleTime();
 		}
 		else
 		{
-			current = recording->getSum(*mFloatStatp);
+			current = recording.getSum(*mFloatStatp);
 			//min = recording->getMin(*mFloatStatp);
 			//max = recording->getMax(*mFloatStatp);
 			//mean = recording->getMean(*mFloatStatp);
diff --git a/indra/llui/llstatbar.h b/indra/llui/llstatbar.h
index c735e7045b4..bfc49b92047 100644
--- a/indra/llui/llstatbar.h
+++ b/indra/llui/llstatbar.h
@@ -29,8 +29,7 @@
 
 #include "llview.h"
 #include "llframetimer.h"
-#include "lltrace.h"
-
+#include "lltracerecording.h"
 class LLStat;
 
 class LLStatBar : public LLView
@@ -39,19 +38,24 @@ class LLStatBar : public LLView
 
 	struct Params : public LLInitParam::Block<Params, LLView::Params>
 	{
-		Optional<std::string> label;
-		Optional<std::string> unit_label;
-		Optional<F32> bar_min;
-		Optional<F32> bar_max;
-		Optional<F32> tick_spacing;
-		Optional<F32> label_spacing;
-		Optional<U32> precision;
-		Optional<F32> update_rate;
-		Optional<bool> show_per_sec;
-		Optional<bool> show_bar;
-		Optional<bool> show_history;
-		Optional<bool> show_mean;
-		Optional<std::string> stat;
+		Optional<std::string>	label,
+								unit_label;
+
+		Optional<F32>			bar_min,
+								bar_max,
+								tick_spacing,
+								label_spacing,
+								update_rate;
+
+		Optional<U32>			precision;
+
+		Optional<bool>			show_per_sec,
+								show_bar,
+								show_history,
+								show_mean;
+
+		Optional<std::string>	stat;
+
 		Params()
 			: label("label"),
 			  unit_label("unit_label"),
@@ -92,6 +96,7 @@ class LLStatBar : public LLView
 	BOOL mDisplayBar;			// Display the bar graph.
 	BOOL mDisplayHistory;
 	BOOL mDisplayMean;			// If true, display mean, if false, display current value
+	LLTrace::PeriodicRecording* mFrameRecording;
 
 	LLStat* mStatp;
 	LLTrace::Rate<F32>* mFloatStatp;
diff --git a/indra/llui/llstatgraph.cpp b/indra/llui/llstatgraph.cpp
index 1d4527aaa33..21b55c7c5af 100644
--- a/indra/llui/llstatgraph.cpp
+++ b/indra/llui/llstatgraph.cpp
@@ -86,15 +86,15 @@ void LLStatGraph::draw()
 	}
 	else if (mF32Statp)
 	{
-		LLTrace::Recording* recording = LLTrace::get_thread_recorder()->getPrimaryRecording();
+		LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecordingPeriod();
 
 		if (mPerSec)
 		{
-			mValue = recording->getSum(*mF32Statp) / recording->getSampleTime();
+			mValue = recording.getPerSec(*mF32Statp);
 		}
 		else
 		{
-			mValue = recording->getSum(*mF32Statp);
+			mValue = recording.getSum(*mF32Statp);
 		}
 
 	}
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 11d9f5e1ecc..bed2dffb14f 100755
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -497,6 +497,8 @@ void update_statistics()
 			texture_stats_timer.reset();
 		}
 	}
+
+	LLTrace::get_frame_recording().nextPeriod();
 }
 
 class ViewerStatsResponder : public LLHTTPClient::Responder
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index a14518f5363..a164a28a59e 100755
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -412,14 +412,12 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 	};
 
 	LLTrace::Recording& getRecording() { return mRecording; }
-	LLTrace::Recording& getFrameRecording() { return mFrameRecording; }
 
 private:
-	F64	mStats[ST_COUNT];
-	LLTrace::Recording	mRecording;
-	LLTrace::Recording	mFrameRecording;
+	F64								mStats[ST_COUNT];
+	LLTrace::Recording				mRecording;
 
-	F64 mLastTimeDiff;  // used for time stat updates
+	F64								mLastTimeDiff;  // used for time stat updates
 };
 
 static const F32 SEND_STATS_PERIOD = 300.0f;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 81c45cfb84a..af28ea36eb8 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -58,7 +58,7 @@
 #include "pipeline.h"
 #include "llappviewer.h"
 #include "llxuiparser.h"
-#include "lltracethreadrecorder.h"
+#include "lltracerecording.h"
 #include "llviewerdisplay.h"
 
 ////////////////////////////////////////////////////////////////////////////
@@ -621,9 +621,9 @@ void LLViewerTextureList::updateImages(F32 max_time)
 	}
 	cleared = FALSE;
 
-	LLTrace::Recording* recording = LLTrace::get_thread_recorder()->getPrimaryRecording();
+	LLTrace::Recording& recording = LLTrace::get_frame_recording().getTotalRecording();
 
-	LLAppViewer::getTextureFetch()->setTextureBandwidth(recording->getPerSec(LLStatViewer::TEXTURE_KBIT).value());
+	LLAppViewer::getTextureFetch()->setTextureBandwidth(recording.getPerSec(LLStatViewer::TEXTURE_KBIT).value());
 
 	LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages);
 	LLViewerStats::getInstance()->mNumRawImagesStat.addValue(LLImageRaw::sRawImageCount);
-- 
GitLab