From ab5106535758393e02b075d1e404e4e1fcf81abf Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Mon, 20 May 2013 19:27:50 -0700
Subject: [PATCH] SH-3931 WIP Interesting: Add graphs to visualize scene load
 metrics removed extra dereference for copy on write pointer moved copyonwrite
 mechanism to RecordingBuffers from individual buffer fixed logic that was
 leaving scene unfrozen when camera moved during metrics gathering

---
 indra/llcommon/llpointer.h                    |  33 ++--
 indra/llcommon/lltracerecording.cpp           | 173 ++++++++----------
 indra/llcommon/lltracerecording.h             |  37 ++--
 indra/llcommon/lltracethreadrecorder.cpp      |  18 +-
 indra/llcommon/lltracethreadrecorder.h        |   3 +-
 indra/newview/llscenemonitor.cpp              |   6 +-
 indra/newview/llviewerstats.cpp               |   2 +-
 .../skins/default/xui/en/floater_stats.xml    |   2 +-
 8 files changed, 125 insertions(+), 149 deletions(-)

diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h
index f03551045ea..b9a9bf1ef0c 100644
--- a/indra/llcommon/llpointer.h
+++ b/indra/llcommon/llpointer.h
@@ -166,7 +166,7 @@ template <class Type> class LLPointer
 };
 
 template<typename Type>
-class LLCopyOnWritePointer
+class LLCopyOnWritePointer : public LLPointer<Type>
 {
 public:
 	typedef LLCopyOnWritePointer<Type> self_t;
@@ -175,43 +175,40 @@ class LLCopyOnWritePointer
 	{}
 
 	LLCopyOnWritePointer(Type* ptr) 
-	:	mPointer(ptr)
+	:	LLPointer(ptr)
 	{}
 
 	LLCopyOnWritePointer(LLPointer<Type>& ptr)
-	:	mPointer(ptr)
+	:	LLPointer(ptr)
 	{}
 
 	Type* write()
 	{
 		makeUnique();
-		return mPointer.get();
+		return mPointer;
 	}
 
 	void makeUnique()
 	{
-		if (mPointer.notNull() && mPointer.get()->getNumRefs() > 1)
+		if (notNull() && mPointer->getNumRefs() > 1)
 		{
-			mPointer = new Type(*mPointer.get());
+			*(LLPointer*)(this) = new Type(*mPointer);
 		}
 	}
+	/*operator BOOL()  const						{ return (mPointer != NULL); }
+	operator bool()  const						{ return (mPointer != NULL); }
+	bool operator!() const						{ return (mPointer == NULL); }
+	bool isNull() const							{ return (mPointer == NULL); }
+	bool notNull() const						{ return (mPointer != NULL); }
 
-	operator BOOL()  const						{ return (BOOL)mPointer; }
-	operator bool()  const						{ return (bool)mPointer; }
-	bool operator!() const						{ return !mPointer; }
-	bool isNull() const							{ return mPointer.isNull(); }
-	bool notNull() const						{ return mPointer.notNull(); }
-
-	bool operator !=(Type* ptr) const           { return (mPointer.get() != ptr); 	}
-	bool operator ==(Type* ptr) const           { return (mPointer.get() == ptr); 	}
+	bool operator !=(Type* ptr) const           { return (mPointer != ptr); 	}
+	bool operator ==(Type* ptr) const           { return (mPointer == ptr); 	}
 	bool operator ==(const LLCopyOnWritePointer<Type>& ptr) const     { return (mPointer == ptr.mPointer); 	}
 	bool operator < (const LLCopyOnWritePointer<Type>& ptr) const     { return (mPointer < ptr.mPointer); 	}
 	bool operator > (const LLCopyOnWritePointer<Type>& ptr) const     { return (mPointer > ptr.mPointer); 	}
 
-	operator const Type*()   const				{ return mPointer.get(); }
-	const Type*	operator->() const				{ return mPointer.get(); }
-protected:
-	 LLPointer<Type> mPointer;
+	operator const Type*()   const				{ return mPointer; }
+	const Type*	operator->() const				{ return mPointer; }*/
 };
 
 #endif
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index 4aa3a5a0f7c..cced6546baa 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -40,37 +40,31 @@ namespace LLTrace
 ///////////////////////////////////////////////////////////////////////
 
 RecordingBuffers::RecordingBuffers() 
-:	mCountsFloat(new AccumulatorBuffer<CountAccumulator<F64> >()),
-	mMeasurementsFloat(new AccumulatorBuffer<MeasurementAccumulator<F64> >()),
-	mCounts(new AccumulatorBuffer<CountAccumulator<S64> >()),
-	mMeasurements(new AccumulatorBuffer<MeasurementAccumulator<S64> >()),
-	mStackTimers(new AccumulatorBuffer<TimeBlockAccumulator>()),
-	mMemStats(new AccumulatorBuffer<MemStatAccumulator>())
 {}
 
 void RecordingBuffers::handOffTo(RecordingBuffers& 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);
+	other.mCountsFloat.reset(&mCountsFloat);
+	other.mMeasurementsFloat.reset(&mMeasurementsFloat);
+	other.mCounts.reset(&mCounts);
+	other.mMeasurements.reset(&mMeasurements);
+	other.mStackTimers.reset(&mStackTimers);
+	other.mMemStats.reset(&mMemStats);
 }
 
 void RecordingBuffers::makePrimary()
 {
-	mCountsFloat.write()->makePrimary();
-	mMeasurementsFloat.write()->makePrimary();
-	mCounts.write()->makePrimary();
-	mMeasurements.write()->makePrimary();
-	mStackTimers.write()->makePrimary();
-	mMemStats.write()->makePrimary();
+	mCountsFloat.makePrimary();
+	mMeasurementsFloat.makePrimary();
+	mCounts.makePrimary();
+	mMeasurements.makePrimary();
+	mStackTimers.makePrimary();
+	mMemStats.makePrimary();
 
 	ThreadRecorder* thread_recorder = get_thread_recorder().get();
-	AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = *mStackTimers.write();
+	AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers;
 	// update stacktimer parent pointers
-	for (S32 i = 0, end_i = mStackTimers->size(); i < end_i; i++)
+	for (S32 i = 0, end_i = mStackTimers.size(); i < end_i; i++)
 	{
 		TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(i);
 		if (tree_node)
@@ -82,46 +76,36 @@ void RecordingBuffers::makePrimary()
 
 bool RecordingBuffers::isPrimary() const
 {
-	return mCounts->isPrimary();
+	return mCounts.isPrimary();
 }
 
-void RecordingBuffers::makeUnique()
+void RecordingBuffers::append( const RecordingBuffers& other )
 {
-	mCountsFloat.makeUnique();
-	mMeasurementsFloat.makeUnique();
-	mCounts.makeUnique();
-	mMeasurements.makeUnique();
-	mStackTimers.makeUnique();
-	mMemStats.makeUnique();
+	mCountsFloat.addSamples(other.mCountsFloat);
+	mMeasurementsFloat.addSamples(other.mMeasurementsFloat);
+	mCounts.addSamples(other.mCounts);
+	mMeasurements.addSamples(other.mMeasurements);
+	mMemStats.addSamples(other.mMemStats);
+	mStackTimers.addSamples(other.mStackTimers);
 }
 
-void RecordingBuffers::appendBuffers( const RecordingBuffers& other )
+void RecordingBuffers::merge( const RecordingBuffers& other)
 {
-	mCountsFloat.write()->addSamples(*other.mCountsFloat);
-	mMeasurementsFloat.write()->addSamples(*other.mMeasurementsFloat);
-	mCounts.write()->addSamples(*other.mCounts);
-	mMeasurements.write()->addSamples(*other.mMeasurements);
-	mMemStats.write()->addSamples(*other.mMemStats);
-	mStackTimers.write()->addSamples(*other.mStackTimers);
+	mCountsFloat.addSamples(other.mCountsFloat);
+	mMeasurementsFloat.addSamples(other.mMeasurementsFloat);
+	mCounts.addSamples(other.mCounts);
+	mMeasurements.addSamples(other.mMeasurements);
+	mMemStats.addSamples(other.mMemStats);
 }
 
-void RecordingBuffers::mergeBuffers( const RecordingBuffers& other)
+void RecordingBuffers::reset(RecordingBuffers* other)
 {
-	mCountsFloat.write()->addSamples(*other.mCountsFloat);
-	mMeasurementsFloat.write()->addSamples(*other.mMeasurementsFloat);
-	mCounts.write()->addSamples(*other.mCounts);
-	mMeasurements.write()->addSamples(*other.mMeasurements);
-	mMemStats.write()->addSamples(*other.mMemStats);
-}
-
-void RecordingBuffers::resetBuffers(RecordingBuffers* other)
-{
-	mCountsFloat.write()->reset(other ? other->mCountsFloat : LLCopyOnWritePointer<AccumulatorBuffer<CountAccumulator<F64> > >());
-	mMeasurementsFloat.write()->reset(other ? other->mMeasurementsFloat : LLCopyOnWritePointer<AccumulatorBuffer<MeasurementAccumulator<F64> > >());
-	mCounts.write()->reset(other ? other->mCounts : LLCopyOnWritePointer<AccumulatorBuffer<CountAccumulator<S64> > >());
-	mMeasurements.write()->reset(other ? other->mMeasurements : LLCopyOnWritePointer<AccumulatorBuffer<MeasurementAccumulator<S64> > >());
-	mStackTimers.write()->reset(other ? other->mStackTimers : LLCopyOnWritePointer<AccumulatorBuffer<TimeBlockAccumulator> >());
-	mMemStats.write()->reset(other ? other->mMemStats : LLCopyOnWritePointer<AccumulatorBuffer<MemStatAccumulator> >());
+	mCountsFloat.reset(other ? &other->mCountsFloat : NULL);
+	mMeasurementsFloat.reset(other ? &other->mMeasurementsFloat : NULL);
+	mCounts.reset(other ? &other->mCounts : NULL);
+	mMeasurements.reset(other ? &other->mMeasurements : NULL);
+	mStackTimers.reset(other ? &other->mStackTimers : NULL);
+	mMemStats.reset(other ? &other->mMemStats : NULL);
 }
 
 ///////////////////////////////////////////////////////////////////////
@@ -130,22 +114,24 @@ void RecordingBuffers::resetBuffers(RecordingBuffers* other)
 
 Recording::Recording() 
 :	mElapsedSeconds(0)
-{}
+{
+	mBuffers = new RecordingBuffers();
+}
 
 Recording::Recording( const Recording& other )
-:	mSamplingTimer(other.mSamplingTimer)
 {
 	Recording& mutable_other = const_cast<Recording&>(other);
 	EPlayState other_play_state = other.getPlayState();
 	mutable_other.pause();
 
-	appendBuffers(other);
+	mBuffers = other.mBuffers;
 
 	LLStopWatchControlsMixin<Recording>::setPlayState(other_play_state);
 	mutable_other.setPlayState(other_play_state);
 
 	// above call will clear mElapsedSeconds as a side effect, so copy it here
 	mElapsedSeconds = other.mElapsedSeconds;
+	mSamplingTimer = other.mSamplingTimer;
 }
 
 
@@ -166,7 +152,7 @@ void Recording::update()
 
 void Recording::handleReset()
 {
-	resetBuffers();
+	mBuffers.write()->reset();
 
 	mElapsedSeconds = 0.0;
 	mSamplingTimer.reset();
@@ -187,42 +173,42 @@ void Recording::handleStop()
 
 void Recording::handleSplitTo(Recording& other)
 {
-	handOffTo(other);
+	mBuffers.write()->handOffTo(*other.mBuffers.write());
 }
 
 void Recording::appendRecording( const Recording& other )
 {
-	appendBuffers(other);
+	mBuffers.write()->append(*other.mBuffers);
 	mElapsedSeconds += other.mElapsedSeconds;
 }
 
 void Recording::mergeRecording( const Recording& other)
 {
-	mergeBuffers(other);
+	mBuffers.write()->merge(*other.mBuffers);
 }
 
 LLUnit<LLUnits::Seconds, F64> Recording::getSum(const TraceType<TimeBlockAccumulator>& stat) const
 {
-	const TimeBlockAccumulator& accumulator = (*mStackTimers)[stat.getIndex()];
+	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
 	return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter) 
 				/ (F64)LLTrace::TimeBlock::countsPerSecond();
 }
 
 LLUnit<LLUnits::Seconds, F64> Recording::getSum(const TraceType<TimeBlockAccumulator::SelfTimeAspect>& stat) const
 {
-	const TimeBlockAccumulator& accumulator = (*mStackTimers)[stat.getIndex()];
+	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
 	return (F64)(accumulator.mSelfTimeCounter) / (F64)LLTrace::TimeBlock::countsPerSecond();
 }
 
 
 U32 Recording::getSum(const TraceType<TimeBlockAccumulator::CallCountAspect>& stat) const
 {
-	return (*mStackTimers)[stat.getIndex()].mCalls;
+	return mBuffers->mStackTimers[stat.getIndex()].mCalls;
 }
 
 LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccumulator>& stat) const
 {
-	const TimeBlockAccumulator& accumulator = (*mStackTimers)[stat.getIndex()];
+	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
 
 	return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter) 
 				/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds);
@@ -230,7 +216,7 @@ LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccu
 
 LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccumulator::SelfTimeAspect>& stat) const
 {
-	const TimeBlockAccumulator& accumulator = (*mStackTimers)[stat.getIndex()];
+	const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
 
 	return (F64)(accumulator.mSelfTimeCounter) 
 			/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds);
@@ -238,45 +224,45 @@ LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccu
 
 F32 Recording::getPerSec(const TraceType<TimeBlockAccumulator::CallCountAspect>& stat) const
 {
-	return (F32)(*mStackTimers)[stat.getIndex()].mCalls / mElapsedSeconds;
+	return (F32)mBuffers->mStackTimers[stat.getIndex()].mCalls / mElapsedSeconds;
 }
 
 LLUnit<LLUnits::Bytes, U32> Recording::getSum(const TraceType<MemStatAccumulator>& stat) const
 {
-	return (*mMemStats)[stat.getIndex()].mAllocatedCount;
+	return mBuffers->mMemStats[stat.getIndex()].mAllocatedCount;
 }
 
 LLUnit<LLUnits::Bytes, F32> Recording::getPerSec(const TraceType<MemStatAccumulator>& stat) const
 {
-	return (F32)(*mMemStats)[stat.getIndex()].mAllocatedCount / mElapsedSeconds;
+	return (F32)mBuffers->mMemStats[stat.getIndex()].mAllocatedCount / mElapsedSeconds;
 }
 
 
 F64 Recording::getSum( const TraceType<CountAccumulator<F64> >& stat ) const
 {
-	return (*mCountsFloat)[stat.getIndex()].getSum();
+	return mBuffers->mCountsFloat[stat.getIndex()].getSum();
 }
 
 S64 Recording::getSum( const TraceType<CountAccumulator<S64> >& stat ) const
 {
-	return (*mCounts)[stat.getIndex()].getSum();
+	return mBuffers->mCounts[stat.getIndex()].getSum();
 }
 
 F64 Recording::getSum( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (F64)(*mMeasurementsFloat)[stat.getIndex()].getSum();
+	return (F64)mBuffers->mMeasurementsFloat[stat.getIndex()].getSum();
 }
 
 S64 Recording::getSum( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (S64)(*mMeasurements)[stat.getIndex()].getSum();
+	return (S64)mBuffers->mMeasurements[stat.getIndex()].getSum();
 }
 
 
 
 F64 Recording::getPerSec( const TraceType<CountAccumulator<F64> >& stat ) const
 {
-	F64 sum = (*mCountsFloat)[stat.getIndex()].getSum();
+	F64 sum = mBuffers->mCountsFloat[stat.getIndex()].getSum();
 	return  (sum != 0.0) 
 		? (sum / mElapsedSeconds)
 		: 0.0;
@@ -284,7 +270,7 @@ F64 Recording::getPerSec( const TraceType<CountAccumulator<F64> >& stat ) const
 
 F64 Recording::getPerSec( const TraceType<CountAccumulator<S64> >& stat ) const
 {
-	S64 sum = (*mCounts)[stat.getIndex()].getSum();
+	S64 sum = mBuffers->mCounts[stat.getIndex()].getSum();
 	return (sum != 0) 
 		? ((F64)sum / mElapsedSeconds)
 		: 0.0;
@@ -292,72 +278,72 @@ F64 Recording::getPerSec( const TraceType<CountAccumulator<S64> >& stat ) const
 
 U32 Recording::getSampleCount( const TraceType<CountAccumulator<F64> >& stat ) const
 {
-	return (*mCountsFloat)[stat.getIndex()].getSampleCount();
+	return mBuffers->mCountsFloat[stat.getIndex()].getSampleCount();
 }
 
 U32 Recording::getSampleCount( const TraceType<CountAccumulator<S64> >& stat ) const
 {
-	return (*mMeasurementsFloat)[stat.getIndex()].getSampleCount();
+	return mBuffers->mMeasurementsFloat[stat.getIndex()].getSampleCount();
 }
 
 F64 Recording::getMin( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (*mMeasurementsFloat)[stat.getIndex()].getMin();
+	return mBuffers->mMeasurementsFloat[stat.getIndex()].getMin();
 }
 
 S64 Recording::getMin( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (*mMeasurements)[stat.getIndex()].getMin();
+	return mBuffers->mMeasurements[stat.getIndex()].getMin();
 }
 
 F64 Recording::getMax( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (*mMeasurementsFloat)[stat.getIndex()].getMax();
+	return mBuffers->mMeasurementsFloat[stat.getIndex()].getMax();
 }
 
 S64 Recording::getMax( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (*mMeasurements)[stat.getIndex()].getMax();
+	return mBuffers->mMeasurements[stat.getIndex()].getMax();
 }
 
 F64 Recording::getMean( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (*mMeasurementsFloat)[stat.getIndex()].getMean();
+	return mBuffers->mMeasurementsFloat[stat.getIndex()].getMean();
 }
 
 F64 Recording::getMean( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (*mMeasurements)[stat.getIndex()].getMean();
+	return mBuffers->mMeasurements[stat.getIndex()].getMean();
 }
 
 F64 Recording::getStandardDeviation( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (*mMeasurementsFloat)[stat.getIndex()].getStandardDeviation();
+	return mBuffers->mMeasurementsFloat[stat.getIndex()].getStandardDeviation();
 }
 
 F64 Recording::getStandardDeviation( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (*mMeasurements)[stat.getIndex()].getStandardDeviation();
+	return mBuffers->mMeasurements[stat.getIndex()].getStandardDeviation();
 }
 
 F64 Recording::getLastValue( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (*mMeasurementsFloat)[stat.getIndex()].getLastValue();
+	return mBuffers->mMeasurementsFloat[stat.getIndex()].getLastValue();
 }
 
 S64 Recording::getLastValue( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (*mMeasurements)[stat.getIndex()].getLastValue();
+	return mBuffers->mMeasurements[stat.getIndex()].getLastValue();
 }
 
 U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (*mMeasurementsFloat)[stat.getIndex()].getSampleCount();
+	return mBuffers->mMeasurementsFloat[stat.getIndex()].getSampleCount();
 }
 
 U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (*mMeasurements)[stat.getIndex()].getSampleCount();
+	return mBuffers->mMeasurements[stat.getIndex()].getSampleCount();
 }
 
 ///////////////////////////////////////////////////////////////////////
@@ -366,16 +352,9 @@ U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<S64> >& st
 
 PeriodicRecording::PeriodicRecording( U32 num_periods, EPlayState state) 
 :	mAutoResize(num_periods == 0),
-	mCurPeriod(0)
+	mCurPeriod(0),
+	mRecordingPeriods(num_periods ? num_periods : 1)
 {
-	if (mAutoResize) 
-	{
-		num_periods = 1;
-	}
-	if (num_periods)
-	{
-		mRecordingPeriods.resize(num_periods);
-	}
 	setPlayState(state);
 }
 
@@ -552,7 +531,7 @@ void PeriodicRecording::handleReset()
 
 void PeriodicRecording::handleSplitTo(PeriodicRecording& other)
 {
-	getCurRecording().handOffTo(other.getCurRecording());
+	getCurRecording().splitTo(other.getCurRecording());
 }
 
 ///////////////////////////////////////////////////////////////////////
@@ -589,7 +568,7 @@ void ExtendableRecording::handleReset()
 
 void ExtendableRecording::handleSplitTo(ExtendableRecording& other)
 {
-	mPotentialRecording.handOffTo(other.mPotentialRecording);
+	mPotentialRecording.splitTo(other.mPotentialRecording);
 }
 
 
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index be8618a2990..b339e72e5cc 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -106,33 +106,28 @@ class LLStopWatchControlsMixin
 
 namespace LLTrace
 {
-	class RecordingBuffers
+	struct RecordingBuffers : public LLRefCount
 	{
-	public:
 		RecordingBuffers();
 
 		void handOffTo(RecordingBuffers& other);
 		void makePrimary();
 		bool isPrimary() const;
 
-		void makeUnique();
-
-		void appendBuffers(const RecordingBuffers& other);
-		void mergeBuffers(const RecordingBuffers& other);
-		void resetBuffers(RecordingBuffers* other = NULL);
+		void append(const RecordingBuffers& other);
+		void merge(const RecordingBuffers& other);
+		void reset(RecordingBuffers* other = NULL);
 
-	protected:
-		LLCopyOnWritePointer<AccumulatorBuffer<CountAccumulator<F64> > >		mCountsFloat;
-		LLCopyOnWritePointer<AccumulatorBuffer<MeasurementAccumulator<F64> > >	mMeasurementsFloat;
-		LLCopyOnWritePointer<AccumulatorBuffer<CountAccumulator<S64> > >		mCounts;
-		LLCopyOnWritePointer<AccumulatorBuffer<MeasurementAccumulator<S64> > >	mMeasurements;
-		LLCopyOnWritePointer<AccumulatorBuffer<TimeBlockAccumulator> >			mStackTimers;
-		LLCopyOnWritePointer<AccumulatorBuffer<MemStatAccumulator> >			mMemStats;
+		AccumulatorBuffer<CountAccumulator<F64> > 		mCountsFloat;
+		AccumulatorBuffer<MeasurementAccumulator<F64> > mMeasurementsFloat;
+		AccumulatorBuffer<CountAccumulator<S64> > 		mCounts;
+		AccumulatorBuffer<MeasurementAccumulator<S64> > mMeasurements;
+		AccumulatorBuffer<TimeBlockAccumulator> 		mStackTimers;
+		AccumulatorBuffer<MemStatAccumulator> 			mMemStats;
 	};
 
 	class Recording 
-	:	public LLStopWatchControlsMixin<Recording>, 
-		public RecordingBuffers
+	:	public LLStopWatchControlsMixin<Recording>
 	{
 	public:
 		Recording();
@@ -149,6 +144,9 @@ namespace LLTrace
 		// grab latest recorded data
 		void update();
 
+		// ensure that buffers are exclusively owned by this recording
+		void makeUnique() { mBuffers.makeUnique(); }
+
 		// Timer accessors
 		LLUnit<LLUnits::Seconds, F64> getSum(const TraceType<TimeBlockAccumulator>& stat) const;
 		LLUnit<LLUnits::Seconds, F64> getSum(const TraceType<TimeBlockAccumulator::SelfTimeAspect>& stat) const;
@@ -237,7 +235,7 @@ namespace LLTrace
 
 		LLUnit<LLUnits::Seconds, F64> getDuration() const { return LLUnit<LLUnits::Seconds, F64>(mElapsedSeconds); }
 
-	private:
+	protected:
 		friend class ThreadRecorder;
 
 		// implementation for LLStopWatchControlsMixin
@@ -249,8 +247,9 @@ namespace LLTrace
 		// returns data for current thread
 		class ThreadRecorder* getThreadRecorder(); 
 
-		LLTimer			mSamplingTimer;
-		F64				mElapsedSeconds;
+		LLTimer				mSamplingTimer;
+		F64					mElapsedSeconds;
+		LLCopyOnWritePointer<RecordingBuffers>	mBuffers;
 	};
 
 	class LL_COMMON_API PeriodicRecording
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
index 9bef040cf7b..2001b9cd7f4 100644
--- a/indra/llcommon/lltracethreadrecorder.cpp
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -118,7 +118,7 @@ ThreadRecorder::active_recording_list_t::iterator ThreadRecorder::update( Record
 		if (next_it != mActiveRecordings.end())
 		{
 			// ...push our gathered data down to it
-			(*next_it)->mPartialRecording.appendBuffers((*it)->mPartialRecording);
+			(*next_it)->mPartialRecording.append((*it)->mPartialRecording);
 		}
 
 		// copy accumulated measurements into result buffer and clear accumulator (mPartialRecording)
@@ -156,7 +156,7 @@ void ThreadRecorder::deactivate( Recording* recording )
 		++next_it;
 		if (next_it != mActiveRecordings.end())
 		{
-			(*next_it)->mTargetRecording->makePrimary();
+			(*next_it)->mTargetRecording->mBuffers.write()->makePrimary();
 		}
 
 		delete *it;
@@ -171,8 +171,8 @@ ThreadRecorder::ActiveRecording::ActiveRecording( Recording* target )
 
 void ThreadRecorder::ActiveRecording::moveBaselineToTarget()
 {
-	mTargetRecording->appendBuffers(mPartialRecording);
-	mPartialRecording.resetBuffers();
+	mTargetRecording->mBuffers.write()->append(mPartialRecording);
+	mPartialRecording.reset();
 }
 
 
@@ -203,31 +203,31 @@ void SlaveThreadRecorder::pushToMaster()
 void SlaveThreadRecorder::SharedData::appendFrom( const Recording& source )
 {
 	LLMutexLock lock(&mRecordingMutex);
-	mRecording.appendRecording(source);
+	appendRecording(source);
 }
 
 void SlaveThreadRecorder::SharedData::appendTo( Recording& sink )
 {
 	LLMutexLock lock(&mRecordingMutex);
-	sink.appendRecording(mRecording);
+	sink.appendRecording(*this);
 }
 
 void SlaveThreadRecorder::SharedData::mergeFrom( const RecordingBuffers& source )
 {
 	LLMutexLock lock(&mRecordingMutex);
-	mRecording.mergeBuffers(source);
+	mBuffers.write()->merge(source);
 }
 
 void SlaveThreadRecorder::SharedData::mergeTo( RecordingBuffers& sink )
 {
 	LLMutexLock lock(&mRecordingMutex);
-	sink.mergeBuffers(mRecording);
+	sink.merge(*mBuffers);
 }
 
 void SlaveThreadRecorder::SharedData::reset()
 {
 	LLMutexLock lock(&mRecordingMutex);
-	mRecording.reset();
+	Recording::reset();
 }
 
 
diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h
index 3e24303d92f..c44bcbd12d1 100644
--- a/indra/llcommon/lltracethreadrecorder.h
+++ b/indra/llcommon/lltracethreadrecorder.h
@@ -106,7 +106,7 @@ namespace LLTrace
 
 		MasterThreadRecorder* 	mMaster;
 
-		class SharedData
+		class SharedData : public Recording
 		{
 		public:
 			void appendFrom(const Recording& source);
@@ -116,7 +116,6 @@ namespace LLTrace
 			void reset();
 		private:
 			LLMutex		mRecordingMutex;
-			Recording	mRecording;
 		};
 		SharedData		mSharedData;
 	};
diff --git a/indra/newview/llscenemonitor.cpp b/indra/newview/llscenemonitor.cpp
index b7517a057e6..b303dfbdb43 100644
--- a/indra/newview/llscenemonitor.cpp
+++ b/indra/newview/llscenemonitor.cpp
@@ -270,10 +270,12 @@ void LLSceneMonitor::capture()
 	static LLFrameTimer timer;	
 
 	LLTrace::Recording& last_frame_recording = LLTrace::get_frame_recording().getLastRecording();
-	if (last_frame_recording.getSum(*LLViewerCamera::getVelocityStat()) > 0.001f
-		|| last_frame_recording.getSum(*LLViewerCamera::getAngularVelocityStat()) > 0.01f)
+	if (mEnabled 
+		&&	(last_frame_recording.getSum(*LLViewerCamera::getVelocityStat()) > 0.001f
+			|| last_frame_recording.getSum(*LLViewerCamera::getAngularVelocityStat()) > 0.01f))
 	{
 		reset();
+		freezeScene();
 	}
 
 	bool enabled = monitor_enabled || mDebugViewerVisible;
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 002f0c7aa3c..e8196e96557 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -64,7 +64,7 @@
 namespace LLStatViewer
 {
 
-LLTrace::CountStatHandle<>	FPS("fpsstat"),
+LLTrace::CountStatHandle<>	FPS("framesrendered"),
 							PACKETS_IN("packetsinstat"),
 							PACKETS_LOST("packetsloststat"),
 							PACKETS_OUT("packetsoutstat"),
diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml
index f98fcc349e9..0493f487d45 100644
--- a/indra/newview/skins/default/xui/en/floater_stats.xml
+++ b/indra/newview/skins/default/xui/en/floater_stats.xml
@@ -38,7 +38,7 @@
 			   name="fps"
 			   label="FPS"
 			   unit_label="fps"
-			   stat="fpsstat"
+			   stat="framesrendered"
 			   bar_min="0"
 			   bar_max="60"
 			   tick_spacing="6"
-- 
GitLab