From 12c34dc30f0cb6270c11e100fcaceb3fa6b27e81 Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Thu, 16 May 2013 00:53:01 -0700
Subject: [PATCH] SH-3931 WIP Interesting: Add graphs to visualize scene load
 metrics renamed LLView::handleVisibilityChange to onVisibilityChange to
 reflect cleaned up scene monitor stats recording, now all trace stats dumped
 to csv also fixed extendablerecording, periodicrecording, etc. to properly
 implement start/stop/etc

---
 indra/llcommon/lltracerecording.cpp     | 228 ++++++++----------------
 indra/llcommon/lltracerecording.h       |  73 ++++----
 indra/newview/app_settings/settings.xml |  25 ++-
 indra/newview/llappviewer.cpp           |  11 +-
 indra/newview/llscenemonitor.cpp        | 196 ++++++++++++++------
 indra/newview/llscenemonitor.h          |  15 +-
 indra/newview/llstartup.cpp             |   6 +-
 indra/newview/llworld.cpp               |   2 +-
 8 files changed, 297 insertions(+), 259 deletions(-)

diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index e562f2bce21..6b5c6c7d3ef 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -179,8 +179,6 @@ void Recording::handleStop()
 
 void Recording::handleSplitTo(Recording& other)
 {
-	stop();
-	other.restart();
 	handOffTo(other);
 }
 
@@ -375,94 +373,86 @@ PeriodicRecording::PeriodicRecording( U32 num_periods, EPlayState state)
 
 void PeriodicRecording::nextPeriod()
 {
-	EPlayState play_state = getPlayState();
-	Recording& old_recording = getCurRecording();
 	if (mAutoResize)
 	{
 		mRecordingPeriods.push_back(Recording());
 	}
-	U32 num_periods = mRecordingPeriods.size();
-	mCurPeriod = (num_periods > 0) 
-				? (mCurPeriod + 1) % num_periods 
-				: mCurPeriod + 1;
-	old_recording.splitTo(getCurRecording());
 
-	switch(play_state)
-	{
-	case STOPPED:
-		getCurRecording().stop();
-		break;
-	case PAUSED:
-		getCurRecording().pause();
-		break;
-	case STARTED:
-		break;
-	}
+	Recording& old_recording = getCurRecording();
+
+	mCurPeriod = mRecordingPeriods.empty()
+				? mCurPeriod + 1
+				: (mCurPeriod + 1) % mRecordingPeriods.size();
+	old_recording.splitTo(getCurRecording());
 }
 
 
 void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other )
 {
-	if (other.mRecordingPeriods.size() < 2) return;
+	if (other.mRecordingPeriods.empty()) return;
 
 	EPlayState play_state = getPlayState();
-	pause();
+	stop();
 
 	EPlayState other_play_state = other.getPlayState();
 	other.pause();
 
-	if (mAutoResize)
-	{
-		// copy everything after current period of other recording to end of buffer
-		// this will only apply if other recording is using a fixed circular buffer
-		if (other.mCurPeriod < other.mRecordingPeriods.size() - 1)
-		{
-			std::copy(	other.mRecordingPeriods.begin() + other.mCurPeriod + 1,
-						other.mRecordingPeriods.end(),
-						std::back_inserter(mRecordingPeriods));
-		}
+	U32 other_recording_count = other.mRecordingPeriods.size();
 
-		// copy everything from beginning of other recording's buffer up to, but not including
-		// current period
-		std::copy(	other.mRecordingPeriods.begin(),
-					other.mRecordingPeriods.begin() + other.mCurPeriod,
-					std::back_inserter(mRecordingPeriods));
+	Recording& other_oldest_recording = other.mRecordingPeriods[(other.mCurPeriod + 1) % other.mRecordingPeriods.size()];
 
-		mCurPeriod = mRecordingPeriods.size() - 1;
+	// if I have a recording of any length, then close it off and start a fresh one
+	if (getCurRecording().getDuration().value())
+	{
+		nextPeriod();
 	}
-	else
+	getCurRecording().appendRecording(other_oldest_recording);
+
+	if (other_recording_count > 1)
 	{
-		size_t num_to_copy = llmin(	mRecordingPeriods.size(), other.mRecordingPeriods.size() );
-		std::vector<Recording>::iterator src_it = other.mRecordingPeriods.begin() 
-													+ (	(other.mCurPeriod + 1)									// cur period
-															+ (other.mRecordingPeriods.size() - num_to_copy)	// minus room for copy
-														% other.mRecordingPeriods.size());
-		std::vector<Recording>::iterator dest_it = mRecordingPeriods.begin() + ((mCurPeriod + 1) % mRecordingPeriods.size());
-
-		for(S32 i = 0; i < num_to_copy; i++)
+		if (mAutoResize)
 		{
-			*dest_it = *src_it;
-
-			if (++src_it == other.mRecordingPeriods.end())
+			for (S32 other_index = (other.mCurPeriod + 2) % other_recording_count; 
+				other_index != other.mCurPeriod; 
+				other_index = (other_index + 1) % other_recording_count)
 			{
-				src_it = other.mRecordingPeriods.begin();
+				llassert(other.mRecordingPeriods[other_index].getDuration() != 0.f 
+							&& (mRecordingPeriods.empty() 
+								|| other.mRecordingPeriods[other_index].getDuration() != mRecordingPeriods.back().getDuration()));
+				mRecordingPeriods.push_back(other.mRecordingPeriods[other_index]);
 			}
 
-			if (++dest_it == mRecordingPeriods.end())
+			mCurPeriod = mRecordingPeriods.size() - 1;
+		}
+		else
+		{
+			size_t num_to_copy = llmin(	mRecordingPeriods.size(), other.mRecordingPeriods.size() - 1);
+			std::vector<Recording>::iterator src_it = other.mRecordingPeriods.begin() 
+														+ (	(other.mCurPeriod + 1									// oldest period
+																+ (other.mRecordingPeriods.size() - num_to_copy))	// minus room for copy
+															% other.mRecordingPeriods.size());
+			std::vector<Recording>::iterator dest_it = mRecordingPeriods.begin() + ((mCurPeriod + 1) % mRecordingPeriods.size());
+
+			for(S32 i = 0; i < num_to_copy; i++)
 			{
-				dest_it = mRecordingPeriods.begin();
+				*dest_it = *src_it;
+
+				if (++src_it == other.mRecordingPeriods.end())
+				{
+					src_it = other.mRecordingPeriods.begin();
+				}
+
+				if (++dest_it == mRecordingPeriods.end())
+				{
+					dest_it = mRecordingPeriods.begin();
+				}
 			}
-		}
 		
-		mCurPeriod = (mCurPeriod + num_to_copy) % mRecordingPeriods.size();
+			mCurPeriod = (mCurPeriod + num_to_copy) % mRecordingPeriods.size();
+		}
 	}
 
-	// if copying from periodic recording that wasn't active advance our period to the next available one
-	// otherwise continue recording on top of the last period of data received from the other recording
-	if (other_play_state != STARTED)
-	{
-		nextPeriod();
-	}
+	nextPeriod();
 
 	setPlayState(play_state);
 	other.setPlayState(other_play_state);
@@ -524,47 +514,28 @@ const Recording& PeriodicRecording::getPrevRecording( U32 offset ) const
 	return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods];
 }
 
-void PeriodicRecording::start()
+void PeriodicRecording::handleStart()
 {
 	getCurRecording().start();
 }
 
-void PeriodicRecording::stop()
-{
-	getCurRecording().stop();
-}
-
-void PeriodicRecording::pause()
+void PeriodicRecording::handleStop()
 {
 	getCurRecording().pause();
 }
 
-void PeriodicRecording::resume()
-{
-	getCurRecording().resume();
-}
-
-void PeriodicRecording::restart()
+void PeriodicRecording::handleReset()
 {
-	getCurRecording().restart();
+	mRecordingPeriods.clear();
+	mRecordingPeriods.push_back(Recording());
+	mCurPeriod = 0;
 }
 
-void PeriodicRecording::reset()
+void PeriodicRecording::handleSplitTo(PeriodicRecording& other)
 {
-	getCurRecording().reset();
+	getCurRecording().handOffTo(other.getCurRecording());
 }
 
-void PeriodicRecording::splitTo(PeriodicRecording& other)
-{
-	getCurRecording().splitTo(other.getCurRecording());
-}
-
-void PeriodicRecording::splitFrom(PeriodicRecording& other)
-{
-	getCurRecording().splitFrom(other.getCurRecording());
-}
-
-
 ///////////////////////////////////////////////////////////////////////
 // ExtendableRecording
 ///////////////////////////////////////////////////////////////////////
@@ -581,55 +552,27 @@ void ExtendableRecording::extend()
 	mPotentialRecording.setPlayState(getPlayState());
 }
 
-void ExtendableRecording::start()
+void ExtendableRecording::handleStart()
 {
-	LLStopWatchControlsMixin<ExtendableRecording>::start();
 	mPotentialRecording.start();
 }
 
-void ExtendableRecording::stop()
-{
-	LLStopWatchControlsMixin<ExtendableRecording>::stop();
-	mPotentialRecording.stop();
-}
-
-void ExtendableRecording::pause()
+void ExtendableRecording::handleStop()
 {
-	LLStopWatchControlsMixin<ExtendableRecording>::pause();
 	mPotentialRecording.pause();
 }
 
-void ExtendableRecording::resume()
-{
-	LLStopWatchControlsMixin<ExtendableRecording>::resume();
-	mPotentialRecording.resume();
-}
-
-void ExtendableRecording::restart()
-{
-	LLStopWatchControlsMixin<ExtendableRecording>::restart();
-	mAcceptedRecording.reset();
-	mPotentialRecording.restart();
-}
-
-void ExtendableRecording::reset()
+void ExtendableRecording::handleReset()
 {
-	LLStopWatchControlsMixin<ExtendableRecording>::reset();
 	mAcceptedRecording.reset();
 	mPotentialRecording.reset();
 }
 
-void ExtendableRecording::splitTo(ExtendableRecording& other)
+void ExtendableRecording::handleSplitTo(ExtendableRecording& other)
 {
-	LLStopWatchControlsMixin<ExtendableRecording>::splitTo(other);
-	mPotentialRecording.splitTo(other.mPotentialRecording);
+	mPotentialRecording.handOffTo(other.mPotentialRecording);
 }
 
-void ExtendableRecording::splitFrom(ExtendableRecording& other)
-{
-	LLStopWatchControlsMixin<ExtendableRecording>::splitFrom(other);
-	mPotentialRecording.splitFrom(other.mPotentialRecording);
-}
 
 ///////////////////////////////////////////////////////////////////////
 // ExtendablePeriodicRecording
@@ -639,13 +582,13 @@ void ExtendableRecording::splitFrom(ExtendableRecording& other)
 ExtendablePeriodicRecording::ExtendablePeriodicRecording() 
 :	mAcceptedRecording(0), 
 	mPotentialRecording(0)
-{
-}
+{}
 
 void ExtendablePeriodicRecording::extend()
 {
+	llassert(mPotentialRecording.getPlayState() == getPlayState());
 	// stop recording to get latest data
-	mPotentialRecording.stop();
+	mPotentialRecording.pause();
 	// push the data back to accepted recording
 	mAcceptedRecording.appendPeriodicRecording(mPotentialRecording);
 	// flush data, so we can start from scratch
@@ -654,55 +597,28 @@ void ExtendablePeriodicRecording::extend()
 	mPotentialRecording.setPlayState(getPlayState());
 }
 
-void ExtendablePeriodicRecording::start()
-{
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::start();
-	mPotentialRecording.start();
-}
 
-void ExtendablePeriodicRecording::stop()
+void ExtendablePeriodicRecording::handleStart()
 {
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::stop();
-	mPotentialRecording.stop();
+	mPotentialRecording.start();
 }
 
-void ExtendablePeriodicRecording::pause()
+void ExtendablePeriodicRecording::handleStop()
 {
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::pause();
 	mPotentialRecording.pause();
 }
 
-void ExtendablePeriodicRecording::resume()
-{
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::resume();
-	mPotentialRecording.resume();
-}
-
-void ExtendablePeriodicRecording::restart()
-{
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::restart();
-	mAcceptedRecording.reset();
-	mPotentialRecording.restart();
-}
-
-void ExtendablePeriodicRecording::reset()
+void ExtendablePeriodicRecording::handleReset()
 {
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::reset();
 	mAcceptedRecording.reset();
 	mPotentialRecording.reset();
 }
 
-void ExtendablePeriodicRecording::splitTo(ExtendablePeriodicRecording& other)
+void ExtendablePeriodicRecording::handleSplitTo(ExtendablePeriodicRecording& other)
 {
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::splitTo(other);
 	mPotentialRecording.splitTo(other.mPotentialRecording);
 }
 
-void ExtendablePeriodicRecording::splitFrom(ExtendablePeriodicRecording& other)
-{
-	LLStopWatchControlsMixin<ExtendablePeriodicRecording>::splitFrom(other);
-	mPotentialRecording.splitFrom(other.mPotentialRecording);
-}
 
 PeriodicRecording& get_frame_recording()
 {
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index 84006a10b85..be8618a2990 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -46,12 +46,12 @@ class LLStopWatchControlsMixinCommon
 		STARTED
 	};
 
-	virtual void start();
-	virtual void stop();
-	virtual void pause();
-	virtual void resume();
-	virtual void restart();
-	virtual void reset();
+	void start();
+	void stop();
+	void pause();
+	void resume();
+	void restart();
+	void reset();
 
 	bool isStarted() const { return mPlayState == STARTED; }
 	bool isPaused() const  { return mPlayState == PAUSED; }
@@ -67,11 +67,11 @@ class LLStopWatchControlsMixinCommon
 
 private:
 	// trigger active behavior (without reset)
-	virtual void handleStart(){};
+	virtual void handleStart() = 0;
 	// stop active behavior
-	virtual void handleStop(){};
+	virtual void handleStop() = 0;
 	// clear accumulated state, can be called while started
-	virtual void handleReset(){};
+	virtual void handleReset() = 0;
 
 	EPlayState mPlayState;
 };
@@ -84,7 +84,13 @@ class LLStopWatchControlsMixin
 	typedef LLStopWatchControlsMixin<DERIVED> self_t;
 	virtual void splitTo(DERIVED& other)
 	{
+		EPlayState play_state = getPlayState();
+		stop();
+		other.reset();
+
 		handleSplitTo(other);
+
+		other.setPlayState(play_state);
 	}
 
 	virtual void splitFrom(DERIVED& other)
@@ -124,7 +130,9 @@ namespace LLTrace
 		LLCopyOnWritePointer<AccumulatorBuffer<MemStatAccumulator> >			mMemStats;
 	};
 
-	class Recording : public LLStopWatchControlsMixin<Recording>, public RecordingBuffers
+	class Recording 
+	:	public LLStopWatchControlsMixin<Recording>, 
+		public RecordingBuffers
 	{
 	public:
 		Recording();
@@ -367,15 +375,12 @@ namespace LLTrace
 			return mean;
 		}
 
+	private:
 		// implementation for LLStopWatchControlsMixin
-		/*virtual*/ void start();
-		/*virtual*/ void stop();
-		/*virtual*/ void pause();
-		/*virtual*/ void resume();
-		/*virtual*/ void restart();
-		/*virtual*/ void reset();
-		/*virtual*/ void splitTo(PeriodicRecording& other);
-		/*virtual*/ void splitFrom(PeriodicRecording& other);
+		/*virtual*/ void handleStart();
+		/*virtual*/ void handleStop();
+		/*virtual*/ void handleReset();
+		/*virtual*/ void handleSplitTo(PeriodicRecording& other);
 
 	private:
 		std::vector<Recording>	mRecordingPeriods;
@@ -395,15 +400,15 @@ namespace LLTrace
 		Recording& getAcceptedRecording() { return mAcceptedRecording; }
 		const Recording& getAcceptedRecording() const {return mAcceptedRecording;}
 
+		Recording& getPotentialRecording()				{ return mPotentialRecording; }
+		const Recording& getPotentialRecording() const	{ return mPotentialRecording;}
+
+	private:
 		// implementation for LLStopWatchControlsMixin
-		/*virtual*/ void start();
-		/*virtual*/ void stop();
-		/*virtual*/ void pause();
-		/*virtual*/ void resume();
-		/*virtual*/ void restart();
-		/*virtual*/ void reset();
-		/*virtual*/ void splitTo(ExtendableRecording& other);
-		/*virtual*/ void splitFrom(ExtendableRecording& other);
+		/*virtual*/ void handleStart();
+		/*virtual*/ void handleStop();
+		/*virtual*/ void handleReset();
+		/*virtual*/ void handleSplitTo(ExtendableRecording& other);
 
 	private:
 		Recording mAcceptedRecording;
@@ -419,16 +424,16 @@ namespace LLTrace
 
 		PeriodicRecording& getAcceptedRecording()				{ return mAcceptedRecording; }
 		const PeriodicRecording& getAcceptedRecording() const	{return mAcceptedRecording;}
+		
+		PeriodicRecording& getPotentialRecording()				{ return mPotentialRecording; }
+		const PeriodicRecording& getPotentialRecording() const	{return mPotentialRecording;}
 
+	private:
 		// implementation for LLStopWatchControlsMixin
-		/*virtual*/ void start();
-		/*virtual*/ void stop();
-		/*virtual*/ void pause();
-		/*virtual*/ void resume();
-		/*virtual*/ void restart();
-		/*virtual*/ void reset();
-		/*virtual*/ void splitTo(ExtendablePeriodicRecording& other);
-		/*virtual*/ void splitFrom(ExtendablePeriodicRecording& other);
+		/*virtual*/ void handleStart();
+		/*virtual*/ void handleStop();
+		/*virtual*/ void handleReset();
+		/*virtual*/ void handleSplitTo(ExtendablePeriodicRecording& other);
 
 	private:
 		PeriodicRecording mAcceptedRecording;
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index ed8d0bf10de..94f032be673 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -6847,6 +6847,17 @@
       <string>F32</string>
       <key>Value</key>
       <real>6.0</real>
+    </map>
+    <key>ClothingLoadingDelay</key>
+    <map>
+      <key>Comment</key>
+      <string>Time to wait for avatar appearance to resolve before showing world (seconds)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>10.0</real>
     </map>
 	<key>PreferredMaturity</key>
     <map>
@@ -7403,7 +7414,6 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
-
   <key>OctreeMaxNodeCapacity</key>
   <map>
     <key>Comment</key>
@@ -9478,7 +9488,18 @@
       <key>Type</key>
       <string>F32</string>
       <key>Value</key>
-      <real>1</real>
+      <real>0.25</real>
+    </map>
+    <key>SceneLoadingPixelDiffThreshold</key>
+    <map>
+      <key>Comment</key>
+      <string>Amount of pixels changed required to consider the scene as still loading (fraction of pixels on screen)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>0.0003</real>
     </map>
     <key>ScriptHelpFollowsCursor</key>
     <map>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 0b0db432c89..3a3fe2b6563 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1203,6 +1203,8 @@ LLFastTimer::DeclareTimer FTM_FRAME("Frame", true);
 
 bool LLAppViewer::mainLoop()
 {
+	llinfos << "***********************Entering main_loop***********************" << llendflush;
+
 	mMainloopTimeout = new LLWatchdogTimeout();
 	
 	//-------------------------------------------
@@ -1539,7 +1541,7 @@ bool LLAppViewer::mainLoop()
 
 	destroyMainloopTimeout();
 
-	llinfos << "Exiting main_loop" << llendflush;
+	llinfos << "***********************Exiting main_loop***********************" << llendflush;
 
 	return true;
 }
@@ -1568,11 +1570,9 @@ bool LLAppViewer::cleanup()
 	LLEventPumps::instance().reset();
 
 	//dump scene loading monitor results
-	if(LLSceneMonitor::getInstance()->hasResults())
+	if(LLSceneMonitor::instance().hasResults())
 	{
-		std::string file_name = "scene_monitor_results.csv";
-		LLSceneMonitor::getInstance()->dumpToFile(
-			gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name));
+		LLSceneMonitor::instance().dumpToFile(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv"));
 	}
 
 	if (LLFastTimerView::sAnalyzePerformance)
@@ -4257,6 +4257,7 @@ void LLAppViewer::idle()
 	{
 		if (gRenderStartTime.getElapsedTimeF32() > qas)
 		{
+			llinfos << "Quitting after " << qas << " seconds. See setting \"QuitAfterSeconds\"." << llendl;
 			LLAppViewer::instance()->forceQuit();
 		}
 	}
diff --git a/indra/newview/llscenemonitor.cpp b/indra/newview/llscenemonitor.cpp
index 3d8e1513dd8..c101fe7debf 100644
--- a/indra/newview/llscenemonitor.cpp
+++ b/indra/newview/llscenemonitor.cpp
@@ -60,11 +60,8 @@ LLSceneMonitor::LLSceneMonitor() :
 	mDiff(NULL),
 	mDiffResult(0.f),
 	mDiffTolerance(0.1f),
-	mNeedsUpdateDiff(false),
-	mHasNewDiff(false),
-	mHasNewQueryResult(false),
+	mDiffState(WAITING_FOR_NEXT_DIFF),
 	mDebugViewerVisible(false),
-	mQuitting(false),
 	mQueryObject(0),
 	mDiffPixelRatio(0.5f)
 {
@@ -72,12 +69,11 @@ LLSceneMonitor::LLSceneMonitor() :
 	mFrames[1] = NULL;
 
 	mRecording = new LLTrace::ExtendablePeriodicRecording();
-	mRecording->start();
 }
 
 LLSceneMonitor::~LLSceneMonitor()
 {
-	mQuitting = true;
+	mDiffState = VIEWER_QUITTING;
 	destroyClass();
 }
 
@@ -100,6 +96,8 @@ void LLSceneMonitor::reset()
 	mFrames[1] = NULL;
 	mDiff = NULL;
 
+	mRecording->reset();
+
 	unfreezeScene();
 
 	if(mQueryObject > 0)
@@ -248,7 +246,7 @@ void LLSceneMonitor::unfreezeScene()
 	//thaw all avatars
 	mAvatarPauseHandles.clear();
 
-	if(mQuitting)
+	if(mDiffState == VIEWER_QUITTING)
 	{
 		return; //we are quitting viewer.
 	}
@@ -267,7 +265,7 @@ void LLSceneMonitor::unfreezeScene()
 void LLSceneMonitor::capture()
 {
 	static U32 last_capture_time = 0;
-	static LLCachedControl<bool> monitor_enabled(gSavedSettings,"SceneLoadingMonitorEnabled");
+	static LLCachedControl<bool> monitor_enabled(gSavedSettings, "SceneLoadingMonitorEnabled");
 	static LLCachedControl<F32>  scene_load_sample_time(gSavedSettings, "SceneLoadingMonitorSampleTime");
 	static LLFrameTimer timer;	
 
@@ -275,7 +273,7 @@ void LLSceneMonitor::capture()
 	if (last_frame_recording.getSum(*LLViewerCamera::getVelocityStat()) > 0.001f
 		|| last_frame_recording.getSum(*LLViewerCamera::getAngularVelocityStat()) > 0.01f)
 	{
-		mRecording->reset();
+		reset();
 	}
 
 	bool enabled = monitor_enabled || mDebugViewerVisible;
@@ -283,11 +281,11 @@ void LLSceneMonitor::capture()
 	{
 		if(mEnabled)
 		{
-			reset();
 			unfreezeScene();
 		}
 		else
 		{
+			reset();
 			freezeScene();
 		}
 
@@ -299,7 +297,10 @@ void LLSceneMonitor::capture()
 		&& LLGLSLShader::sNoFixedFunction
 		&& last_capture_time != gFrameCount)
 	{
+		mRecording->resume();
+
 		timer.reset();
+
 		last_capture_time = gFrameCount;
 
 		LLRenderTarget& cur_target = getCaptureTarget();
@@ -315,13 +316,13 @@ void LLSceneMonitor::capture()
 		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);		
 		glBindFramebuffer(GL_FRAMEBUFFER, old_FBO);
 
-		mNeedsUpdateDiff = true;
+		mDiffState = NEED_DIFF;
 	}
 }
 
 bool LLSceneMonitor::needsUpdate() const
 {
-	return mNeedsUpdateDiff;
+	return mDiffState == NEED_DIFF;
 }
 
 static LLFastTimer::DeclareTimer FTM_GENERATE_SCENE_LOAD_DITHER_TEXTURE("Generate Scene Load Dither Texture");
@@ -329,11 +330,10 @@ static LLFastTimer::DeclareTimer FTM_SCENE_LOAD_IMAGE_DIFF("Scene Load Image Dif
 
 void LLSceneMonitor::compare()
 {
-	if(!mNeedsUpdateDiff)
+	if(mDiffState != NEED_DIFF)
 	{
 		return;
 	}
-	mNeedsUpdateDiff = false;
 
 	if(!mFrames[0] || !mFrames[1])
 	{
@@ -345,6 +345,7 @@ void LLSceneMonitor::compare()
 	}
 
 	LLFastTimer _(FTM_SCENE_LOAD_IMAGE_DIFF);
+	mDiffState = EXECUTE_DIFF;
 
 	S32 width = gViewerWindow->getWindowWidthRaw();
 	S32 height = gViewerWindow->getWindowHeightRaw();
@@ -400,8 +401,6 @@ void LLSceneMonitor::compare()
 	gGL.getTexUnit(2)->disable();
 	gGL.getTexUnit(2)->unbind(LLTexUnit::TT_TEXTURE);
 
-	mHasNewDiff = true;
-	
 	if (!mDebugViewerVisible)
 	{
 		calcDiffAggregate();
@@ -413,7 +412,7 @@ void LLSceneMonitor::calcDiffAggregate()
 {
 	LLFastTimer _(FTM_SCENE_LOAD_IMAGE_DIFF);
 
-	if(!mHasNewDiff && !mDebugViewerVisible)
+	if(mDiffState != EXECUTE_DIFF && !mDebugViewerVisible)
 	{
 		return;
 	}	
@@ -435,18 +434,17 @@ void LLSceneMonitor::calcDiffAggregate()
 	gOneTextureFilterProgram.bind();
 	gOneTextureFilterProgram.uniform1f("tolerance", mDiffTolerance);
 
-	if(mHasNewDiff)
+	if(mDiffState == EXECUTE_DIFF)
 	{
 		glBeginQueryARB(GL_SAMPLES_PASSED_ARB, mQueryObject);
 	}
 
 	gl_draw_scaled_target(0, 0, S32(mDiff->getWidth() * mDiffPixelRatio), S32(mDiff->getHeight() * mDiffPixelRatio), mDiff);
 
-	if(mHasNewDiff)
+	if(mDiffState == EXECUTE_DIFF)
 	{
 		glEndQueryARB(GL_SAMPLES_PASSED_ARB);
-		mHasNewDiff = false;	
-		mHasNewQueryResult = true;
+		mDiffState = WAIT_ON_RESULT;
 	}
 		
 	gOneTextureFilterProgram.unbind();
@@ -467,31 +465,30 @@ void LLSceneMonitor::fetchQueryResult()
 {
 	LLFastTimer _(FTM_SCENE_LOAD_IMAGE_DIFF);
 
-	if(!mHasNewQueryResult)
-	{
-		return;
-	}
-	mHasNewQueryResult = false;
-
-	GLuint available = 0;
-	glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_AVAILABLE_ARB, &available);
-	if(!available)
+	if(mDiffState == WAIT_ON_RESULT)
 	{
-		return;
-	}
+		mDiffState = WAITING_FOR_NEXT_DIFF;
 
-	GLuint count = 0;
-	glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_ARB, &count);
+		GLuint available = 0;
+		glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_AVAILABLE_ARB, &available);
+		if(available)
+		{
+			GLuint count = 0;
+			glGetQueryObjectuivARB(mQueryObject, GL_QUERY_RESULT_ARB, &count);
 	
-	mDiffResult = count * 0.5f / (mDiff->getWidth() * mDiff->getHeight() * mDiffPixelRatio * mDiffPixelRatio); //0.5 -> (front face + back face)
+			mDiffResult = count * 0.5f / (mDiff->getWidth() * mDiff->getHeight() * mDiffPixelRatio * mDiffPixelRatio); //0.5 -> (front face + back face)
 
-	LL_DEBUGS("SceneMonitor") << "Frame difference: " << std::setprecision(4) << mDiffResult << LL_ENDL;
-	sample(sFramePixelDiff, mDiffResult);
+			LL_DEBUGS("SceneMonitor") << "Frame difference: " << std::setprecision(4) << mDiffResult << LL_ENDL;
+			sample(sFramePixelDiff, mDiffResult);
 
-	const F32 diff_threshold = 0.001f;
-	if(mDiffResult > diff_threshold)
-	{
-		mRecording->extend();
+			mRecording->getPotentialRecording().nextPeriod();
+
+			static LLCachedControl<F32> diff_threshold(gSavedSettings,"SceneLoadingPixelDiffThreshold");
+			if(mDiffResult > diff_threshold())
+			{
+				mRecording->extend();
+			}
+		}
 	}
 }
 
@@ -503,29 +500,124 @@ void LLSceneMonitor::addMonitorResult()
 	mMonitorResults.push_back(result);
 }
 
-//dump results to a file _scene_monitor_results.csv
+//dump results to a file _scene_xmonitor_results.csv
 void LLSceneMonitor::dumpToFile(std::string file_name)
 {
-	if(mMonitorResults.empty() || !getRecording())
-	{
-		return; //nothing to dump
-	}
+	LL_INFOS("SceneMonitor") << "Saving scene load stats to " << file_name << LL_ENDL; 
 
 	std::ofstream os(file_name.c_str());
 
 	//total scene loading time
-	os << llformat("Scene Loading time: %.4f seconds\n", (F32)getRecording()->getAcceptedRecording().getDuration().value());
+	os << std::setprecision(4);
+
+	LLTrace::PeriodicRecording& scene_load_recording = mRecording->getAcceptedRecording();
+	U32 frame_count = scene_load_recording.getNumPeriods();
+
+	LLUnit<LLUnits::Seconds, F64> frame_time;
+
+	os << "Stat";
+	for (S32 frame = 0; frame < frame_count; frame++)
+	{
+		frame_time += scene_load_recording.getPrevRecording(frame_count - frame).getDuration();
+		os << ", " << frame_time.value();
+	}
+	os << std::endl;
+
+	for (LLTrace::CountStatHandle<F64>::instance_iter it = LLTrace::CountStatHandle<F64>::beginInstances(), end_it = LLTrace::CountStatHandle<F64>::endInstances();
+		it != end_it;
+		++it)
+	{
+		std::ostringstream row;
+		row << it->getName();
+
+		S32 samples = 0;
+
+		for (S32 i = frame_count - 1; i >= 0; --i)
+		{
+			samples += scene_load_recording.getPrevRecording(i).getSampleCount(*it);
+			row << ", " << scene_load_recording.getPrevRecording(i).getSum(*it);
+		}
+
+		row << std::endl;
+
+		if (samples > 0)
+		{
+			os << row.str();
+		}
+	}
+
+	for (LLTrace::CountStatHandle<S64>::instance_iter it = LLTrace::CountStatHandle<S64>::beginInstances(), end_it = LLTrace::CountStatHandle<S64>::endInstances();
+		it != end_it;
+		++it)
+	{
+		std::ostringstream row;
+		row << it->getName();
+
+		S32 samples = 0;
+
+		for (S32 i = frame_count - 1; i >= 0; --i)
+		{
+			samples += scene_load_recording.getPrevRecording(i).getSampleCount(*it);
+			row << ", " << scene_load_recording.getPrevRecording(i).getSum(*it);
+		}
+
+		row << std::endl;
+
+		if (samples > 0)
+		{
+			os << row.str();
+		}
+	}
+
+	for (LLTrace::MeasurementStatHandle<F64>::instance_iter it = LLTrace::MeasurementStatHandle<F64>::beginInstances(), end_it = LLTrace::MeasurementStatHandle<F64>::endInstances();
+		it != end_it;
+		++it)
+	{
+		std::ostringstream row;
+		row << it->getName();
+
+		S32 samples = 0;
 
-	S32 num_results = mMonitorResults.size();
-	for(S32 i = 0; i < num_results; i++)
+		for (S32 i = frame_count - 1; i >= 0; --i)
+		{
+			samples += scene_load_recording.getPrevRecording(i).getSampleCount(*it);
+			row << ", " << scene_load_recording.getPrevRecording(i).getMean(*it);
+		}
+
+		row << std::endl;
+
+		if (samples > 0)
+		{
+			os << row.str();
+		}
+	}
+
+	for (LLTrace::MeasurementStatHandle<S64>::instance_iter it = LLTrace::MeasurementStatHandle<S64>::beginInstances(), end_it = LLTrace::MeasurementStatHandle<S64>::endInstances();
+		it != end_it;
+		++it)
 	{
-		os << llformat("%.4f %.4f\n", mMonitorResults[i].mTimeStamp, mMonitorResults[i].mDiff);
+		std::ostringstream row;
+		row << it->getName();
+
+		S32 samples = 0;
+
+		for (S32 i = frame_count - 1; i >= 0; --i)
+		{
+			samples += scene_load_recording.getPrevRecording(i).getSampleCount(*it);
+			row << ", " << scene_load_recording.getPrevRecording(i).getMean(*it);
+		}
+
+		row << std::endl;
+
+		if (samples > 0)
+		{
+			os << row.str();
+		}
 	}
 
 	os.flush();
 	os.close();
 
-	mMonitorResults.clear();
 }
 
 //-------------------------------------------------------------------------------------------------------------
@@ -563,8 +655,6 @@ void LLSceneMonitorView::draw()
 	F32 ratio = LLSceneMonitor::getInstance()->getDiffPixelRatio();
 	S32 height = (S32)(target->getHeight() * ratio);
 	S32 width = (S32)(target->getWidth() * ratio);
-	//S32 height = (S32) (gViewerWindow->getWindowRectScaled().getHeight()*0.5f);
-	//S32 width = (S32) (gViewerWindow->getWindowRectScaled().getWidth() * 0.5f);
 	
 	LLRect new_rect;
 	new_rect.setLeftTopAndSize(getRect().mLeft, getRect().mTop, width, height);
diff --git a/indra/newview/llscenemonitor.h b/indra/newview/llscenemonitor.h
index 6dccbbe335e..f43c455f2fd 100644
--- a/indra/newview/llscenemonitor.h
+++ b/indra/newview/llscenemonitor.h
@@ -64,7 +64,7 @@ class LLSceneMonitor :  public LLSingleton<LLSceneMonitor>
 	
 	LLTrace::ExtendablePeriodicRecording* getRecording() const {return mRecording;}
 	void dumpToFile(std::string file_name);
-	bool hasResults() const { return !mMonitorResults.empty();}
+	bool hasResults() const { return mRecording->getAcceptedRecording().getDuration() != 0;}
 
 private:
 	void freezeScene();
@@ -76,11 +76,16 @@ class LLSceneMonitor :  public LLSingleton<LLSceneMonitor>
 	void addMonitorResult();
 private:
 	bool mEnabled;
-	bool mNeedsUpdateDiff;
-	bool mHasNewDiff;
-	bool mHasNewQueryResult;
 	bool mDebugViewerVisible;
-	bool mQuitting;
+
+	enum EDiffState
+	{
+		WAITING_FOR_NEXT_DIFF,
+		NEED_DIFF,
+		EXECUTE_DIFF,
+		WAIT_ON_RESULT,
+		VIEWER_QUITTING
+	}	mDiffState;
 
 	LLRenderTarget* mFrames[2];
 	LLRenderTarget* mDiff;
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index cf2b491d6c5..f748344cc85 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2048,7 +2048,7 @@ bool idle_startup()
 		static LLFrameTimer wearables_timer;
 
 		const F32 wearables_time = wearables_timer.getElapsedTimeF32();
-		const F32 MAX_WEARABLES_TIME = 10.f;
+		static LLCachedControl<F32> max_wearables_time(gSavedSettings, "ClothingLoadingDelay");
 
 		if (!gAgent.isGenderChosen() && isAgentAvatarValid())
 		{
@@ -2068,7 +2068,7 @@ bool idle_startup()
 		
 		display_startup();
 
-		if (wearables_time > MAX_WEARABLES_TIME)
+		if (wearables_time > max_wearables_time())
 		{
 			LLNotificationsUtil::add("ClothingLoading");
 			add(LLStatViewer::LOADING_WEARABLES_LONG_DELAY, 1);
@@ -2102,7 +2102,7 @@ bool idle_startup()
 		display_startup();
 		update_texture_fetch();
 		display_startup();
-		set_startup_status(0.9f + 0.1f * wearables_time / MAX_WEARABLES_TIME,
+		set_startup_status(0.9f + 0.1f * wearables_time / max_wearables_time(),
 						 LLTrans::getString("LoginDownloadingClothing").c_str(),
 						 gAgent.mMOTD.c_str());
 		display_startup();
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index a123c12811a..c88df93119e 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -135,7 +135,7 @@ void LLWorld::destroyClass()
 	//make all visible drawbles invisible.
 	LLDrawable::incrementVisible();
 
-	LLSceneMonitor::getInstance()->destroyClass();
+	LLSceneMonitor::deleteSingleton();
 }
 
 
-- 
GitLab