diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index d2c3c6089c4cda40e30ff3ee2638c78db701ad93..21c278fd3bb29706f2c2dc8bd2af2c857bf3d270 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9370,7 +9370,7 @@
   <key>RenderAutoMuteRenderCostLimit</key>
   <map>
     <key>Comment</key>
-    <string>Maximum render cost before an avatar is automatically visually muted (0 for no limit).</string>
+    <string>Maximum render weight before an avatar is automatically visually muted (0 to not use this limit).</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
@@ -9381,7 +9381,7 @@
   <key>RenderAutoMuteSurfaceAreaLimit</key>
   <map>
     <key>Comment</key>
-    <string>Maximum surface area of attachments before an avatar is automatically visually muted (0 for no limit).</string>
+    <string>Maximum surface area of attachments before an avatar is automatically visually muted (0 to not use this limit).</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
@@ -9389,6 +9389,28 @@
     <key>Value</key>
     <integer>0</integer>
   </map>
+  <key>RenderAutoMuteEnabled</key>
+  <map>
+    <key>Comment</key>
+    <string>Apply visual muting to high cost, non-friends, not in IM, or somewhat distant avatars</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+  <key>RenderAutoMuteVisibilityRank</key>
+  <map>
+    <key>Comment</key>
+    <string>Number of avatars to show normally for visual muting mode.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
     
   <key>RenderAutoHideSurfaceAreaLimit</key>
   <map>
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index d5afa25c9c6d6c89217d5319186d3d41b8f7b4c4..3d7407ab54ec7707514db0f085d054ed2d0df1be 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -1163,88 +1163,85 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 	}
 	else if (pass >= 3 && pass <= 9)
 	{ //render rigged attachments
-		if (!avatarp->isVisuallyMuted())
+		if (!avatarp->isVisuallyMuted())		// These details are skipped for visually muted (plain imposter) avatars
 		{
-	if (pass == 3)
-	{
-		if (is_deferred_render)
-		{
-			renderDeferredRiggedSimple(avatarp);
-		}
-		else
-		{
-			renderRiggedSimple(avatarp);
-		}
-	}
+			if (pass == 3)				// To do - use switch statement
+			{
+				if (is_deferred_render)
+				{
+					renderDeferredRiggedSimple(avatarp);
+				}
+				else
+				{
+					renderRiggedSimple(avatarp);
+				}
+			}
 			else if (pass == 4)
-	{
-		if (is_deferred_render)
-		{
-			renderDeferredRiggedBump(avatarp);
-		}
-		else
-		{
-			renderRiggedFullbright(avatarp);
-		}
-	}
+			{
+				if (is_deferred_render)
+				{
+					renderDeferredRiggedBump(avatarp);
+				}
+				else
+				{
+					renderRiggedFullbright(avatarp);
+				}
+			}
 			else if (pass == 5)
-	{
-		renderRiggedShinySimple(avatarp);
-	}
+			{
+				renderRiggedShinySimple(avatarp);
+			}
 			else if (pass == 6)
-	{
-		renderRiggedFullbrightShiny(avatarp);
-	}
-			else if (pass >= 7 && pass < 9)
-	{
-		if (pass == 7)
-		{
-			renderRiggedAlpha(avatarp);
-		}
-				else if (pass == 8)
-		{
-			renderRiggedFullbrightAlpha(avatarp);
-		}
-	}
+			{
+				renderRiggedFullbrightShiny(avatarp);
+			}
+			else if (pass == 7)
+			{
+				renderRiggedAlpha(avatarp);
+			}
+			else if (pass == 8)
+			{
+				renderRiggedFullbrightAlpha(avatarp);
+			}
 			else if (pass == 9)
-	{
-		renderRiggedGlow(avatarp);
-	}
+			{
+				renderRiggedGlow(avatarp);
+			}
 		}
 	}
 	else
 	{
-	if ((sShaderLevel >= SHADER_LEVEL_CLOTH))
-	{
-		LLMatrix4 rot_mat;
-		LLViewerCamera::getInstance()->getMatrixToLocal(rot_mat);
-		LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
-		rot_mat *= cfr;
+		if ((sShaderLevel >= SHADER_LEVEL_CLOTH))
+		{
+			LLMatrix4 rot_mat;
+			LLViewerCamera::getInstance()->getMatrixToLocal(rot_mat);
+			LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+			rot_mat *= cfr;
 		
-		LLVector4 wind;
-		wind.setVec(avatarp->mWindVec);
-		wind.mV[VW] = 0;
-		wind = wind * rot_mat;
-		wind.mV[VW] = avatarp->mWindVec.mV[VW];
-
-		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_WIND, 1, wind.mV);
-		F32 phase = -1.f * (avatarp->mRipplePhase);
-
-		F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
-		LLVector4 sin_params(freq, freq, freq, phase);
-		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_SINWAVE, 1, sin_params.mV);
-
-		LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
-		gravity = gravity * rot_mat;
-		sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_GRAVITY, 1, gravity.mV);
-	}
+			LLVector4 wind;
+			wind.setVec(avatarp->mWindVec);
+			wind.mV[VW] = 0;
+			wind = wind * rot_mat;
+			wind.mV[VW] = avatarp->mWindVec.mV[VW];
+
+			sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_WIND, 1, wind.mV);
+			F32 phase = -1.f * (avatarp->mRipplePhase);
+
+			F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
+			LLVector4 sin_params(freq, freq, freq, phase);
+			sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_SINWAVE, 1, sin_params.mV);
+
+			LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
+			gravity = gravity * rot_mat;
+			sVertexProgram->uniform4fv(LLViewerShaderMgr::AVATAR_GRAVITY, 1, gravity.mV);
+		}
 
-	if( !single_avatar || (avatarp == single_avatar) )
-	{
-		avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+		if( !single_avatar || (avatarp == single_avatar) )
+		{
+			avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+		}
 	}
 }
-}
 
 void LLDrawPoolAvatar::getRiggedGeometry(LLFace* face, LLPointer<LLVertexBuffer>& buffer, U32 data_mask, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face)
 {
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 4c99e65c99cab13c585ed9f0588a5bdd787377c2..aab4d93882045f0c4a9596a16a7e2d1aa3ae30cf 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -2934,6 +2934,63 @@ bool enable_object_unmute()
 	}
 }
 
+
+// 0 = normal, 1 = always, 2 = never
+class LLAvatarCheckImposterMode : public view_listener_t
+{	
+	bool handleEvent(const LLSD& userdata)
+	{
+		LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
+		if (!object) return false;
+
+		LLVOAvatar* avatar = find_avatar_from_object(object); 
+		if (!avatar) return false;
+		
+		U32 mode = userdata.asInteger();
+		switch (mode) 
+		{
+			case 0:
+				return (avatar->getVisualMuteSettings() == LLVOAvatar::VISUAL_MUTE_NOT_SET);
+			case 1:
+				return (avatar->getVisualMuteSettings() == LLVOAvatar::ALWAYS_VISUAL_MUTE);
+			case 2:
+				return (avatar->getVisualMuteSettings() == LLVOAvatar::NEVER_VISUAL_MUTE);
+			default:
+				return false;
+		}
+	}	// handleEvent()
+};
+
+// 0 = normal, 1 = always, 2 = never
+class LLAvatarSetImposterMode : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
+		if (!object) return false;
+
+		LLVOAvatar* avatar = find_avatar_from_object(object); 
+		if (!avatar) return false;
+		
+		U32 mode = userdata.asInteger();
+		switch (mode) 
+		{
+			case 0:
+				avatar->setVisualMuteSettings(LLVOAvatar::VISUAL_MUTE_NOT_SET);
+				return true;
+			case 1:
+				avatar->setVisualMuteSettings(LLVOAvatar::VISUAL_MUTE_NOT_SET);
+				return true;
+			case 2:
+				avatar->setVisualMuteSettings(LLVOAvatar::NEVER_VISUAL_MUTE);
+				return true;
+			default:
+				return false;
+		}
+	}	// handleEvent()
+};
+
+
 class LLObjectMute : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
@@ -8644,6 +8701,8 @@ void initialize_menus()
 	view_listener_t::addMenu( new LLCheckPanelPeopleTab(), "SideTray.CheckPanelPeopleTab");
 
 	 // Avatar pie menu
+	view_listener_t::addMenu(new LLAvatarCheckImposterMode(), "Avatar.CheckImposterMode");
+	view_listener_t::addMenu(new LLAvatarSetImposterMode(), "Avatar.SetImposterMode");
 	view_listener_t::addMenu(new LLObjectMute(), "Avatar.Mute");
 	view_listener_t::addMenu(new LLAvatarAddFriend(), "Avatar.AddFriend");
 	view_listener_t::addMenu(new LLAvatarAddContact(), "Avatar.AddContact");
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 7d099d690a99cdb25f7bc520c206f1d5634de7cc..cce4925e0a6e5dedcb1364cf70bd15246b532685 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -62,6 +62,7 @@
 #include "llhudmanager.h"
 #include "llhudnametag.h"
 #include "llhudtext.h"				// for mText/mDebugText
+#include "llimview.h"
 #include "llinitparam.h"
 #include "llkeyframefallmotion.h"
 #include "llkeyframestandmotion.h"
@@ -784,6 +785,13 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mLastPelvisToFoot = 0.0f;
 	mPelvisFixup = 0.0f;
 	mLastPelvisFixup = 0.0f;
+
+	mCachedVisualMute = !isSelf();
+	mCachedVisualMuteUpdateTime = LLFrameTimer::getTotalSeconds() + 5.0;
+	mVisuallyMuteSetting = VISUAL_MUTE_NOT_SET;
+
+	F32 color_value = (F32) (getID().mData[0]);
+	mMutedAVColor = calcMutedAVColor(color_value, 0, 256);
 }
 
 std::string LLVOAvatar::avString() const
@@ -3435,24 +3443,82 @@ void LLVOAvatar::slamPosition()
 	mRoot.updateWorldMatrixChildren();
 }
 
-bool LLVOAvatar::isVisuallyMuted() const
+bool LLVOAvatar::isVisuallyMuted()
 {
-	bool ret = false;
+	bool muted = false;
+
+	// Priority order (highest priority first)
+	// * own avatar is never visually muted
+	// * if on the "always draw normally" list, draw them normally
+	// * if on the "always visually mute" list, mute them
+	// * draw them normally if they meet the following criteria:
+	//       - within the closest N avatars OR on friends list OR in an IM chat
+	//       - AND aren't over the thresholds
+	// * otherwise visually mute all other avatars
 
 	if (!isSelf())
 	{
+		static LLCachedControl<bool> render_mute_enabled(gSavedSettings, "RenderAutoMuteEnabled");
 		static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit");
 		static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit");
 		static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAutoMuteRenderCostLimit");
-	
-		U32 max_cost = (U32) (max_render_cost*(LLVOAvatar::sLODFactor+0.5));
+		static LLCachedControl<U32> visibility_rank(gSavedSettings, "RenderAutoMuteVisibilityRank");
+
+		if (mVisuallyMuteSetting == ALWAYS_VISUAL_MUTE)
+		{	// Always want to see this AV as an imposter
+			muted = true;
+		}
+		else if (mVisuallyMuteSetting == NEVER_VISUAL_MUTE)
+		{	// Never show as imposter
+			muted = false;
+		}
+		else if (render_mute_enabled)
+		{
+			F64 now = LLFrameTimer::getTotalSeconds();
+
+			if (now < mCachedVisualMuteUpdateTime)
+			{	// Use cached mute value
+				muted = mCachedVisualMute;
+			}
+			else
+			{	// Determine if visually muted or not
+
+				U32 max_cost = (U32) (max_render_cost*(LLVOAvatar::sLODFactor+0.5));
 
-		ret = LLMuteList::getInstance()->isMuted(getID()) ||
-			(mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f) ||
-			(mVisualComplexity > max_cost && max_render_cost > 0);
+				muted = LLMuteList::getInstance()->isMuted(getID()) ||
+					(mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) ||
+					(mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f) ||
+					(mVisualComplexity > max_cost && max_render_cost > 0);
+
+				// Could be part of the grand || collection above, but yanked out to make the logic visible
+				if (!muted)
+				{
+					if (visibility_rank > 0)
+					{	// They are above the visibilty rank - mute them
+						muted = (mVisibilityRank > visibility_rank);
+					}
+			
+					if (muted ||					// Don't mute friends or IMs
+						visibility_rank == 0)
+					{
+						muted = !(LLAvatarTracker::instance().isBuddy(getID()));
+						if (muted)
+						{	// Not a friend, so they are muted ... are they in an IM?
+							LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL,getID());
+							muted = !gIMMgr->hasSession(session_id);
+						}
+					}
+				}
+
+				// Save visual mute state and set interval for updating
+				const F64 SECONDS_BETWEEN_RENDER_AUTO_MUTE_UPDATES = 1.5;
+				mCachedVisualMuteUpdateTime = now + SECONDS_BETWEEN_RENDER_AUTO_MUTE_UPDATES;		
+				mCachedVisualMute = muted;
+			} 
+		}
 	}
 
-	return ret;
+	return muted;
 }
 
 //------------------------------------------------------------------------
@@ -3509,7 +3575,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 	// the rest should only be done occasionally for far away avatars
 	//--------------------------------------------------------------------
 
-	if (visible && (!isSelf() || isVisuallyMuted()) && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter)
+	bool visually_muted = isVisuallyMuted();
+	if (visible && (!isSelf() || visually_muted) && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter)
 	{
 		const LLVector4a* ext = mDrawable->getSpatialExtents();
 		LLVector4a size;
@@ -3518,8 +3585,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 
 		
 		F32 impostor_area = 256.f*512.f*(8.125f - LLVOAvatar::sLODFactor*8.f);
-		if (isVisuallyMuted())
-		{ // muted avatars update at 16 hz
+		if (visually_muted)
+		{ // visually muted avatars update at 16 hz
 			mUpdatePeriod = 16;
 		}
 		else if (mVisibilityRank <= LLVOAvatar::sMaxVisible ||
@@ -4295,23 +4362,23 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 		BOOL first_pass = TRUE;
 		if (!LLDrawPoolAvatar::sSkipOpaque)
 		{
-			bool muted = isVisuallyMuted();
+			bool visually_muted = isVisuallyMuted();
 
 			if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender)
 			{
-				if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy || muted)
+				if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy || visually_muted)
 				{
 					num_indices += mMeshLOD[MESH_ID_HEAD]->render(mAdjustedPixelArea, TRUE, mIsDummy);
 					first_pass = FALSE;
 				}
 			}
-			if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy || muted)
+			if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy || visually_muted)
 			{
 				num_indices += mMeshLOD[MESH_ID_UPPER_BODY]->render(mAdjustedPixelArea, first_pass, mIsDummy);
 				first_pass = FALSE;
 			}
 			
-			if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy || muted)
+			if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy || visually_muted)
 			{
 				num_indices += mMeshLOD[MESH_ID_LOWER_BODY]->render(mAdjustedPixelArea, first_pass, mIsDummy);
 				first_pass = FALSE;
@@ -8396,7 +8463,7 @@ void LLVOAvatar::updateImpostors()
 	LLCharacter::sAllowInstancesChange = TRUE ;
 }
 
-BOOL LLVOAvatar::isImpostor() const
+BOOL LLVOAvatar::isImpostor()
 {
 	return sUseImpostors && (isVisuallyMuted() || (mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE;
 }
@@ -8441,15 +8508,13 @@ void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& d
 	angle.mV[2] = da;
 }
 
+
 void LLVOAvatar::idleUpdateRenderCost()
 {
 	static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAutoMuteRenderCostLimit");
 
-	static const U32 ARC_BODY_PART_COST = 200;
 	static const U32 ARC_LIMIT = 20000;
 
-	static std::set<LLUUID> all_textures;
-
 	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_ATTACHMENT_BYTES))
 	{ //set debug text to attachment geometry bytes here so render cost will override
 		setDebugText(llformat("%.1f KB, %.2f m^2", mAttachmentGeometryBytes/1024.f, mAttachmentSurfaceArea));
@@ -8460,6 +8525,69 @@ void LLVOAvatar::idleUpdateRenderCost()
 		return;
 	}
 
+	calculateUpdateRenderCost();				// Update mVisualComplexity if needed
+	
+	doRenderCostNagging(max_render_cost);		// Remind the user their AV is too complex
+
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME))
+	{
+		std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
+		setDebugText(llformat("%s %d", viz_string.c_str(), mVisualComplexity));
+		F32 green = 1.f-llclamp(((F32) mVisualComplexity-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
+		F32 red = llmin((F32) mVisualComplexity/(F32)ARC_LIMIT, 1.f);
+		mText->setColor(LLColor4(red,green,0,1));
+	}
+}
+
+
+// Remind the user about their expensive avatar
+void LLVOAvatar::doRenderCostNagging(U32 max_render_cost)
+{
+	if (isSelf())
+	{
+		static S32 sOldComplexity = 0;
+		static F64 sLastRenderCostNagTime = 0.0;
+
+		const F64 RENDER_NAG_INTERVAL = 60.0;
+
+		F64 now = LLFrameTimer::getTotalSeconds();
+		if (sLastRenderCostNagTime > 0.0 &&
+			(now - sLastRenderCostNagTime) > RENDER_NAG_INTERVAL &&
+			sOldComplexity != mVisualComplexity)
+		{
+			sOldComplexity = mVisualComplexity;
+
+			if (max_render_cost > 0)
+			{ //pop up notification that you have exceeded a render cost limit
+				if (mVisualComplexity > max_render_cost+max_render_cost/2)
+				{
+					LLNotificationsUtil::add("ExceededHighDetailRenderCost");
+					sLastRenderCostNagTime = now;
+				}
+				else if (mVisualComplexity > max_render_cost)
+				{
+					LLNotificationsUtil::add("ExceededMidDetailRenderCost");
+					sLastRenderCostNagTime = now;
+				}
+				else if (mVisualComplexity > max_render_cost/2)
+				{
+					LLNotificationsUtil::add("ExceededLowDetailRenderCost");
+					sLastRenderCostNagTime = now;
+				}
+			}
+		}
+	}
+}
+
+
+// Calculations for mVisualComplexity value
+void LLVOAvatar::calculateUpdateRenderCost()
+{
+	static const U32 ARC_BODY_PART_COST = 200;
+
+	// Diagnostic list of all textures on our avatar
+	static std::set<LLUUID> all_textures;
+
 	if (mVisualComplexityStale)
 	{
 		mVisualComplexityStale = FALSE;
@@ -8526,8 +8654,6 @@ void LLVOAvatar::idleUpdateRenderCost()
 
 		}
 
-
-
 		// Diagnostic output to identify all avatar-related textures.
 		// Does not affect rendering cost calculation.
 		// Could be wrapped in a debug option if output becomes problematic.
@@ -8568,34 +8694,37 @@ void LLVOAvatar::idleUpdateRenderCost()
 			}
 		}
 
-		if (isSelf() && max_render_cost > 0 && mVisualComplexity != cost)
-		{ //pop up notification that you have exceeded a render cost limit
-			if (cost > max_render_cost+max_render_cost/2)
-			{
-				LLNotificationsUtil::add("ExceededHighDetailRenderCost");
-			}
-			else if (cost > max_render_cost)
-			{
-				LLNotificationsUtil::add("ExceededMidDetailRenderCost");
-			}
-			else if (cost > max_render_cost/2)
-			{
-				LLNotificationsUtil::add("ExceededLowDetailRenderCost");
-			}
-		}
-
 		mVisualComplexity = cost;
 	}
+}
 
-	
-	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME))
-	{
-		std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
-		setDebugText(llformat("%s %d", viz_string.c_str(), mVisualComplexity));
-		F32 green = 1.f-llclamp(((F32) mVisualComplexity-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
-		F32 red = llmin((F32) mVisualComplexity/(F32)ARC_LIMIT, 1.f);
-		mText->setColor(LLColor4(red,green,0,1));
-	}
+
+// static
+LLColor4 LLVOAvatar::calcMutedAVColor(F32 value, S32 range_low, S32 range_high)
+{
+	F32 clamped_value = llmin(value, (F32) range_high);
+	clamped_value = llmax(value, (F32) range_low);
+	F32 spectrum = (clamped_value / range_high);		// spectrum is between 0 and 1.f
+
+	// Array of colors.  These are arranged so only one RGB color changes between each step, 
+	// and it loops back to red so there is an even distribution.  It is not a heat map
+	const S32 NUM_SPECTRUM_COLORS = 7;              
+	static LLColor4 * spectrum_color[NUM_SPECTRUM_COLORS] = { &LLColor4::red, &LLColor4::magenta, &LLColor4::blue, &LLColor4::cyan, &LLColor4::green, &LLColor4::yellow, &LLColor4::red };
+ 
+	spectrum = spectrum * (NUM_SPECTRUM_COLORS - 1);		// Scale to range of number of colors
+	S32 spectrum_index_1  = floor(spectrum);				// Desired color will be after this index
+	S32 spectrum_index_2  = spectrum_index_1 + 1;			//    and before this index (inclusive)
+	F32 fractBetween = spectrum - (F32)(spectrum_index_1);  // distance between the two indexes (0-1)
+ 
+	LLColor4 new_color = lerp(*spectrum_color[spectrum_index_1], *spectrum_color[spectrum_index_2], fractBetween);
+	//new_color.normalize();
+
+	//llinfos << "From value " << std::setprecision(3) << value << " returning color " << new_color 
+	//	<< " using indexes " << spectrum_index_1 << ", " << spectrum_index_2
+	//	<< " and fractBetween " << fractBetween
+	//	<< llendl;
+
+	return new_color;
 }
 
 // static
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 386d9d774610acf3bc9a76a91437456065f4e4e4..0e7bf7a6c9ea5ffda46d0efbeb342c3c1c15d6a0 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -253,7 +253,14 @@ class LLVOAvatar :
 	static void		invalidateNameTags();
 	void			addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);
 	void 			idleUpdateRenderCost();
+	void			doRenderCostNagging(U32 max_render_cost);
+	void			calculateUpdateRenderCost();
 	void			updateVisualComplexity() { mVisualComplexityStale = TRUE; }
+	
+	S32				getVisualComplexity()		{ return mVisualComplexity;		};
+	S32				getUpdatePeriod()			{ return mUpdatePeriod;			};
+	const LLColor4 &  getMutedAVColor()			{ return mMutedAVColor;			};
+
 	void 			idleUpdateBelowWater();
 
 	//--------------------------------------------------------------------
@@ -304,11 +311,14 @@ class LLVOAvatar :
 		return mPhases;
 	}
 
+	static LLColor4 calcMutedAVColor(F32 value, S32 range_low, S32 range_high);
+
 protected:
 	BOOL			updateIsFullyLoaded();
 	BOOL			processFullyLoadedChange(bool loading);
 	void			updateRuthTimer(bool loading);
 	F32 			calcMorphAmount();
+
 private:
 	BOOL			mFirstFullyVisible;
 	BOOL			mFullyLoaded;
@@ -317,6 +327,7 @@ class LLVOAvatar :
 	S32				mFullyLoadedFrameCounter;
 	S32				mVisualComplexity;
 	BOOL			mVisualComplexityStale;
+	LLColor4		mMutedAVColor;
 	LLFrameTimer	mFullyLoadedTimer;
 	LLFrameTimer	mRuthTimer;
 
@@ -437,7 +448,16 @@ class LLVOAvatar :
 
 public:
 	U32 		renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);
-	bool		isVisuallyMuted() const;
+	bool		isVisuallyMuted();
+
+	enum VisualMuteSettings
+	{
+		VISUAL_MUTE_NOT_SET = 0,
+		ALWAYS_VISUAL_MUTE  = 1,
+		NEVER_VISUAL_MUTE   = 2
+	};
+	void		setVisualMuteSettings(VisualMuteSettings set)		{ mVisuallyMuteSetting = set;	};
+	VisualMuteSettings  getVisualMuteSettings()						{ return mVisuallyMuteSetting;	};
 
 	U32 		renderRigid();
 	U32 		renderSkinned(EAvatarRenderPass pass);
@@ -461,6 +481,11 @@ class LLVOAvatar :
 	S32	 		mUpdatePeriod;
 	S32  		mNumInitFaces; //number of faces generated when creating the avatar drawable, does not inculde splitted faces due to long vertex buffer.
 
+	bool		mCachedVisualMute;				// cached return value for isVisuallyMuted()
+	F64			mCachedVisualMuteUpdateTime;	// Time to update mCachedVisualMute
+
+	VisualMuteSettings		mVisuallyMuteSetting;			// Always or never visually mute this AV
+
 	//--------------------------------------------------------------------
 	// Morph masks
 	//--------------------------------------------------------------------
@@ -493,7 +518,7 @@ class LLVOAvatar :
 	// Impostors
 	//--------------------------------------------------------------------
 public:
-	BOOL 		isImpostor() const;
+	BOOL 		isImpostor();
 	BOOL 	    needsImpostorUpdate() const;
 	const LLVector3& getImpostorOffset() const;
 	const LLVector2& getImpostorDim() const;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index df7a3c7593efca5020d213c3828fd490117806db..61e42bbceefe1809152379736dda4a6bd5a870c4 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -2862,7 +2862,7 @@ void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera)
 					llassert(vobj); // trying to catch a bad assumption
 					if (vobj) // this test may not be needed, see above
 					{
-						const LLVOAvatar* av = vobj->asAvatar();
+						LLVOAvatar* av = vobj->asAvatar();
 						if (av && av->isImpostor())
 						{
 							return;
@@ -10103,11 +10103,11 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 
 	assertInitialized();
 
-	bool muted = avatar->isVisuallyMuted();		
+	bool visually_muted = avatar->isVisuallyMuted();		
 
 	pushRenderTypeMask();
 	
-	if (muted)
+	if (visually_muted)
 	{
 		andRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES);
 	}
@@ -10251,7 +10251,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 
 	F32 old_alpha = LLDrawPoolAvatar::sMinimumAlpha;
 
-	if (muted)
+	if (visually_muted)
 	{ //disable alpha masking for muted avatars (get whole skin silhouette)
 		LLDrawPoolAvatar::sMinimumAlpha = 0.f;
 	}
@@ -10282,7 +10282,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 
 		LLGLDisable blend(GL_BLEND);
 
-		if (muted)
+		if (visually_muted)
 		{
 			gGL.setColorMask(true, true);
 
@@ -10317,8 +10317,8 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 			gGL.diffuseColor4ub(64,64,64,255);
 		}
 		else
-		{ //blue visually muted avatar
-			gGL.diffuseColor4ub(72,61,139,255);
+		{	// Visually muted avatar
+			gGL.diffuseColor4fv( avatar->getMutedAVColor().mV );
 		}
 
 		{
diff --git a/indra/newview/skins/default/xui/en/menu_attachment_other.xml b/indra/newview/skins/default/xui/en/menu_attachment_other.xml
index 00d4b1dab162c0053da855414286862ea070f981..87fa7c3f000a734bc1ad6eeecc4c6cbb8a0c176c 100644
--- a/indra/newview/skins/default/xui/en/menu_attachment_other.xml
+++ b/indra/newview/skins/default/xui/en/menu_attachment_other.xml
@@ -104,8 +104,43 @@
          <menu_item_call.on_enable
           function="Object.EnableInspect" />
    </menu_item_call>
+
+   <menu_item_separator />
+    
+      <menu_item_check
+        name="Normal"
+        label="Normal">
+        <menu_item_check.on_check
+          function="Avatar.CheckImposterMode"
+          parameter="0" />
+	    <menu_item_check.on_click
+	      function="Avatar.SetImposterMode"
+	      parameter="0" />
+      </menu_item_check>
+      <menu_item_check
+        name="Always use imposter"
+        label="Always use imposter">
+        <menu_item_check.on_check
+          function="Avatar.CheckImposterMode"
+          parameter="1" />
+	    <menu_item_check.on_click
+	      function="Avatar.SetImposterMode"
+	      parameter="1" />
+      </menu_item_check>
+      <menu_item_check
+        name="Never use imposter"
+        label="Never use imposter">
+        <menu_item_check.on_check
+          function="Avatar.CheckImposterMode"
+          parameter="2" />
+	    <menu_item_check.on_click
+	      function="Avatar.SetImposterMode"
+	      parameter="2" />
+      </menu_item_check>
+
   <menu_item_separator
        layout="topleft" />
+
   <menu_item_call
      enabled="false"
      label="Mute Particle Owner"
diff --git a/indra/newview/skins/default/xui/en/menu_avatar_other.xml b/indra/newview/skins/default/xui/en/menu_avatar_other.xml
index 73fa086f28bf673934e9f01f9cbd9a6cc553d7c0..60b7e3e77c5259dc9a1c0a60ce1a4c28bfd80371 100644
--- a/indra/newview/skins/default/xui/en/menu_avatar_other.xml
+++ b/indra/newview/skins/default/xui/en/menu_avatar_other.xml
@@ -39,8 +39,10 @@
       <menu_item_call.on_click
          function="Avatar.InviteToGroup" />
       </menu_item_call>
+    
    <menu_item_separator />
-    <menu_item_call
+   
+     <menu_item_call
      enabled="false"
      label="Block"
      name="Avatar Mute">
@@ -95,8 +97,43 @@
         <menu_item_call.on_enable
          function="EnablePayAvatar" />
     </menu_item_call>
+
+   <menu_item_separator />
+    
+      <menu_item_check
+        name="Normal"
+        label="Normal">
+        <menu_item_check.on_check
+          function="Avatar.CheckImposterMode"
+          parameter="0" />
+	    <menu_item_check.on_click
+	      function="Avatar.SetImposterMode"
+	      parameter="0" />
+      </menu_item_check>
+      <menu_item_check
+        name="Always use imposter"
+        label="Always use imposter">
+        <menu_item_check.on_check
+          function="Avatar.CheckImposterMode"
+          parameter="1" />
+	    <menu_item_check.on_click
+	      function="Avatar.SetImposterMode"
+	      parameter="1" />
+      </menu_item_check>
+      <menu_item_check
+        name="Never use imposter"
+        label="Never use imposter">
+        <menu_item_check.on_check
+          function="Avatar.CheckImposterMode"
+          parameter="2" />
+	    <menu_item_check.on_click
+	      function="Avatar.SetImposterMode"
+	      parameter="2" />
+      </menu_item_check>
+
   <menu_item_separator
        layout="topleft" />
+
   <menu_item_call
      enabled="false"
      label="Mute Particle Owner"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 544f06ac0c4a2d2bb85c09bcbe00a5af26403ac7..4646d307aa0e2a3ff245af5a64d6f70f28365057 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -543,6 +543,15 @@
             <menu_item_check.on_check
                control="NavBarShowParcelProperties" />
           </menu_item_check>
+          <menu_item_check
+             label="Simple Imposters"
+             name="RenderAutoMuteEnabled">
+            <menu_item_check.on_check
+               control="RenderAutoMuteEnabled" />
+            <menu_item_check.on_click
+               function="ToggleControl"
+               parameter="RenderAutoMuteEnabled" />
+          </menu_item_check>
           <menu_item_separator />
           <menu_item_check
              label="Advanced Menu"