diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 4597d0626065b40af486b541f45390dd4090f62c..a12e9caf4c97b49470a256a03d33dfa0e20daf00 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -2282,6 +2282,22 @@ void LLRender::diffuseColor4ubv(const U8* c)
 	}
 }
 
+void LLRender::diffuseColor4ub(U8 r, U8 g, U8 b, U8 a)
+{
+	LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+	llassert(!LLGLSLShader::sNoFixedFunction || shader != NULL);
+
+	if (shader)
+	{
+		shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r/255.f, g/255.f, b/255.f, a/255.f);
+	}
+	else
+	{
+		glColor4ub(r,g,b,a);
+	}
+}
+
+
 void LLRender::debugTexUnits(void)
 {
 	LL_INFOS("TextureUnit") << "Active TexUnit: " << mCurrTextureUnitIndex << LL_ENDL;
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index 78a310e525fbe8e8494d0d22f325e347ae367b3f..90b1ec2f57300f987df212ac73e9e098c26a0283 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -388,6 +388,7 @@ class LLRender
 	void diffuseColor4f(F32 r, F32 g, F32 b, F32 a);
 	void diffuseColor4fv(const F32* c);
 	void diffuseColor4ubv(const U8* c);
+	void diffuseColor4ub(U8 r, U8 g, U8 b, U8 a);
 
 	void vertexBatchPreTransformed(LLVector3* verts, S32 vert_count);
 	void vertexBatchPreTransformed(LLVector3* verts, LLVector2* uvs, S32 vert_count);
diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index e35feda2d5db5986fdc65f94b14c3967bbd78ff7..e501d0495b0a249703b3e37dbaadf886b3180d21 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -458,6 +458,12 @@ U32 LLRenderTarget::getTexture(U32 attachment) const
 	return mTex[attachment];
 }
 
+U32 LLRenderTarget::getNumTextures() const
+{
+	return mTex.size();
+}
+
+
 void LLRenderTarget::bindTexture(U32 index, S32 channel)
 {
 	gGL.getTexUnit(channel)->bindManual(mUsage, getTexture(index));
diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h
index cf15f66d318c8902bff8d4fd74cd33736d8c591c..5cff0a0a21e9d993036aa258d9c9029950ac8000 100644
--- a/indra/llrender/llrendertarget.h
+++ b/indra/llrender/llrendertarget.h
@@ -113,6 +113,7 @@ class LLRenderTarget
 	LLTexUnit::eTextureType getUsage(void) const { return mUsage; }
 
 	U32 getTexture(U32 attachment = 0) const;
+	U32 getNumTextures() const;
 
 	U32 getDepth(void) const { return mDepth; }
 	bool hasStencil() const { return mStencil; }
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index b302f5c9b9ff466ce275ccb91d6530cd2bf78ad6..e06b4bbe13d81ffb04d01bbb0e807e2d555f2432 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9169,6 +9169,17 @@
     <key>Value</key>
     <integer>0</integer>
   </map>
+  <key>RenderAutoMuteRenderCostLimit</key>
+  <map>
+    <key>Comment</key>
+    <string>Maximum render cost before an avatar is automatically visually muted (0 for no limit).</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
   <key>RenderAutoMuteSurfaceAreaLimit</key>
   <map>
     <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
index bfd9b9b3eb4a143cfd35094d1a93f1a4a9473dca..29a6d842d27d7646800d70dca4311f9b4d51ce27 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
@@ -31,6 +31,8 @@ out vec4 frag_data[3];
 
 uniform sampler2D diffuseMap;
 
+uniform float minimum_alpha;
+
 VARYING vec3 vary_normal;
 VARYING vec2 vary_texcoord0;
 
@@ -38,7 +40,7 @@ void main()
 {
 	vec4 diff = texture2D(diffuseMap, vary_texcoord0.xy);
 	
-	if (diff.a < 0.2)
+	if (diff.a < minimum_alpha)
 	{
 		discard;
 	}
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index 38268b102b3069fd988be9cc227b8572b097887f..03641140dc702e2acf3891781a153f2af2614942 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -59,6 +59,7 @@ LLGLSLShader* LLDrawPoolAvatar::sVertexProgram = NULL;
 BOOL	LLDrawPoolAvatar::sSkipOpaque = FALSE;
 BOOL	LLDrawPoolAvatar::sSkipTransparent = FALSE;
 S32 LLDrawPoolAvatar::sDiffuseChannel = 0;
+F32 LLDrawPoolAvatar::sMinimumAlpha = 0.2f;
 
 
 static bool is_deferred_render = false;
@@ -272,7 +273,7 @@ void LLDrawPoolAvatar::beginPostDeferredAlpha()
 
 	gPipeline.bindDeferredShader(*sVertexProgram);
 
-	sVertexProgram->setMinimumAlpha(0.2f);
+	sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
 
 	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 }
@@ -620,7 +621,7 @@ void LLDrawPoolAvatar::beginRigid()
 		if (sVertexProgram != NULL)
 		{	//eyeballs render with the specular shader
 			sVertexProgram->bind();
-			sVertexProgram->setMinimumAlpha(0.2f);
+			sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
 		}
 	}
 	else
@@ -671,7 +672,7 @@ void LLDrawPoolAvatar::beginDeferredRigid()
 	sVertexProgram = &gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram;
 	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 	sVertexProgram->bind();
-	sVertexProgram->setMinimumAlpha(0.2f);
+	sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
 }
 
 void LLDrawPoolAvatar::endDeferredRigid()
@@ -729,7 +730,7 @@ void LLDrawPoolAvatar::beginSkinned()
 
 	if (LLGLSLShader::sNoFixedFunction)
 	{
-		sVertexProgram->setMinimumAlpha(0.2f);
+		sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
 	}
 }
 
@@ -1027,7 +1028,7 @@ void LLDrawPoolAvatar::beginDeferredSkinned()
 	sRenderingSkinned = TRUE;
 
 	sVertexProgram->bind();
-	sVertexProgram->setMinimumAlpha(0.2f);
+	sVertexProgram->setMinimumAlpha(LLDrawPoolAvatar::sMinimumAlpha);
 	
 	sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
 	gGL.getTexUnit(0)->activate();
@@ -1138,7 +1139,10 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 
 		if (impostor)
 		{
-			if (LLPipeline::sRenderDeferred && !LLPipeline::sReflectionRender && avatarp->mImpostor.isComplete()) 
+			if (LLPipeline::sRenderDeferred && //rendering a deferred impostor
+				!LLPipeline::sReflectionRender && 
+				avatarp->mImpostor.isComplete() && //impostor has required data channels
+				avatarp->mImpostor.getNumTextures() >= 3) 
 			{
 				if (normal_channel > -1)
 				{
@@ -1151,113 +1155,95 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 			}
 			avatarp->renderImpostor(LLColor4U(255,255,255,255), sDiffuseChannel);
 		}
-		return;
 	}
-
-	llassert(LLPipeline::sImpostorRender || !avatarp->isVisuallyMuted());
-
-	/*if (single_avatar && avatarp->mSpecialRenderMode >= 1) // 1=anim preview, 2=image preview,  3=morph view
-	{
-		gPipeline.enableLightsAvatarEdit(LLColor4(.5f, .5f, .5f, 1.f));
-	}*/
-	
-	if (pass == 1)
+	else if (pass == 1)
 	{
 		// render rigid meshes (eyeballs) first
 		avatarp->renderRigid();
-		return;
 	}
-
-	if (pass == 3)
-	{
-		if (is_deferred_render)
-		{
-			renderDeferredRiggedSimple(avatarp);
-		}
-		else
-		{
-			renderRiggedSimple(avatarp);
-		}
-		return;
-	}
-
-	if (pass == 4)
-	{
-		if (is_deferred_render)
-		{
-			renderDeferredRiggedBump(avatarp);
-		}
-		else
+	else if (pass >= 3 && pass <= 9)
+	{ //render rigged attachments
+		if (!avatarp->isVisuallyMuted())
 		{
-			renderRiggedFullbright(avatarp);
+			if (pass == 3)
+			{
+				if (is_deferred_render)
+				{
+					renderDeferredRiggedSimple(avatarp);
+				}
+				else
+				{
+					renderRiggedSimple(avatarp);
+				}
+			}
+			else if (pass == 4)
+			{
+				if (is_deferred_render)
+				{
+					renderDeferredRiggedBump(avatarp);
+				}
+				else
+				{
+					renderRiggedFullbright(avatarp);
+				}
+			}
+			else if (pass == 5)
+			{
+				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);
+				}
+			}
+			else if (pass == 9)
+			{
+				renderRiggedGlow(avatarp);
+			}
 		}
-
-		return;
 	}
-
-	if (pass == 5)
-	{
-		renderRiggedShinySimple(avatarp);
-		return;
-	}
-
-	if (pass == 6)
-	{
-		renderRiggedFullbrightShiny(avatarp);
-		return;
-	}
-
-	if (pass >= 7 && pass < 9)
+	else
 	{
-		if (pass == 7)
+		if ((sShaderLevel >= SHADER_LEVEL_CLOTH))
 		{
-			renderRiggedAlpha(avatarp);
-			return;
+			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);
 		}
 
-		if (pass == 8)
+		if( !single_avatar || (avatarp == single_avatar) )
 		{
-			renderRiggedFullbrightAlpha(avatarp);
-			return;
+			avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
 		}
 	}
-
-	if (pass == 9)
-	{
-		renderRiggedGlow(avatarp);
-		
-		return;
-	}
-	
-	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);
-	}
-
-	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/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index 5551d8f6d8bc9dc5bf9201b781f861419f071df1..544969001d2ac1085c1667aa3f13869685042a43 100644
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -209,6 +209,7 @@ class LLDrawPoolAvatar : public LLFacePool
 	static BOOL sSkipOpaque;
 	static BOOL sSkipTransparent;
 	static S32 sDiffuseChannel;
+	static F32 sMinimumAlpha;
 
 	static LLGLSLShader* sVertexProgram;
 };
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 28e4b3279303bb8771db59d5b66100a744e6d52b..a7e225843cec91b9bf8ce7cd877a8481f0e7692f 100755
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -51,6 +51,7 @@
 #include "llviewerregion.h"
 #include "llviewerwindow.h"
 #include "llviewershadermgr.h"
+#include "llvoavatar.h"
 
 
 #define LL_MAX_INDICES_COUNT 1000000
@@ -325,6 +326,12 @@ void LLFace::dirtyTexture()
 		if (vobj)
 		{
 			vobj->mLODChanged = TRUE;
+
+			LLVOAvatar* avatar = vobj->getAvatar();
+			if (avatar)
+			{ //avatar render cost may have changed
+				avatar->updateVisualComplexity();
+			}
 		}
 		gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_VOLUME, FALSE);
 	}		
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 1223615079772e274ff703f6ea17e85964ca2c5f..5b6568709053584534c0bb8973882c34ec3f4385 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1200,8 +1200,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat
 			LLMutexLock lock(mHeaderMutex);
 			mMeshHeaderSize[mesh_id] = header_size;
 			mMeshHeader[mesh_id] = header;
-			}
-
+		}
 
 		LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time.
 
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 62e93b7a53b79a50f5f398a75dd53aaff5adab16..4efd59685e9359fce36c87da64ed71bb2f160e12 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -688,6 +688,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mFullyLoaded(FALSE),
 	mPreviousFullyLoaded(FALSE),
 	mFullyLoadedInitialized(FALSE),
+	mVisualComplexity(0),
+	mVisualComplexityStale(TRUE),
 	mSupportsAlphaLayers(FALSE),
 	mLoadedCallbacksPaused(FALSE),
 	mHasPelvisOffset( FALSE ),
@@ -3434,12 +3436,23 @@ void LLVOAvatar::slamPosition()
 
 bool LLVOAvatar::isVisuallyMuted() const
 {
-	static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit");
-	static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit");
+	bool ret = false;
+
+	if (!isSelf())
+	{
+		static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit");
+		static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit");
+		static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAutoMuteRenderCostLimit");
 	
-	return LLMuteList::getInstance()->isMuted(getID()) ||
+		U32 max_cost = (U32) (max_render_cost*(LLVOAvatar::sLODFactor+0.5));
+
+		ret = LLMuteList::getInstance()->isMuted(getID()) ||
 			(mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) ||
-			(mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f);
+			(mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f) ||
+			(mVisualComplexity > max_cost && max_render_cost > 0);
+	}
+
+	return ret;
 }
 
 //------------------------------------------------------------------------
@@ -4139,46 +4152,6 @@ bool LLVOAvatar::shouldAlphaMask()
 
 }
 
-U32 LLVOAvatar::renderSkinnedAttachments()
-{
-	/*U32 num_indices = 0;
-	
-	const U32 data_mask =	LLVertexBuffer::MAP_VERTEX | 
-							LLVertexBuffer::MAP_NORMAL | 
-							LLVertexBuffer::MAP_TEXCOORD0 |
-							LLVertexBuffer::MAP_COLOR |
-							LLVertexBuffer::MAP_WEIGHT4;
-
-	for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); 
-		 iter != mAttachmentPoints.end();
-		 ++iter)
-	{
-		LLViewerJointAttachment* attachment = iter->second;
-		for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
-			 attachment_iter != attachment->mAttachedObjects.end();
-			 ++attachment_iter)
-		{
-			const LLViewerObject* attached_object = (*attachment_iter);
-			if (attached_object && !attached_object->isHUDAttachment())
-			{
-				const LLDrawable* drawable = attached_object->mDrawable;
-				if (drawable)
-				{
-					for (S32 i = 0; i < drawable->getNumFaces(); ++i)
-					{
-						LLFace* face = drawable->getFace(i);
-						if (face->isState(LLFace::RIGGED))
-						{
-							
-				}
-			}
-		}
-	}
-
-	return num_indices;*/
-	return 0;
-}
-
 //-----------------------------------------------------------------------------
 // renderSkinned()
 //-----------------------------------------------------------------------------
@@ -4336,21 +4309,23 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 		BOOL first_pass = TRUE;
 		if (!LLDrawPoolAvatar::sSkipOpaque)
 		{
+			bool muted = isVisuallyMuted();
+
 			if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender)
 			{
-				if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy)
+				if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy || muted)
 				{
 					num_indices += mMeshLOD[MESH_ID_HEAD]->render(mAdjustedPixelArea, TRUE, mIsDummy);
 					first_pass = FALSE;
 				}
 			}
-			if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy)
+			if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy || muted)
 			{
 				num_indices += mMeshLOD[MESH_ID_UPPER_BODY]->render(mAdjustedPixelArea, first_pass, mIsDummy);
 				first_pass = FALSE;
 			}
 			
-			if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy)
+			if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy || muted)
 			{
 				num_indices += mMeshLOD[MESH_ID_LOWER_BODY]->render(mAdjustedPixelArea, first_pass, mIsDummy);
 				first_pass = FALSE;
@@ -6090,6 +6065,8 @@ const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_o
 		return 0;
 	}
 
+	mVisualComplexityStale = TRUE;
+
 	if (viewer_object->isSelected())
 	{
 		LLSelectMgr::getInstance()->updateSelectionCenter();
@@ -6244,6 +6221,7 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
 		
 		if (attachment->isObjectAttached(viewer_object))
 		{
+			mVisualComplexityStale = TRUE;
 			cleanupAttachedMesh( viewer_object );
 			attachment->removeObject(viewer_object);
 			lldebugs << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << llendl;
@@ -8456,7 +8434,7 @@ void LLVOAvatar::updateImpostors()
 
 BOOL LLVOAvatar::isImpostor() const
 {
-	return (isVisuallyMuted() || (sUseImpostors && mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE;
+	return sUseImpostors && (isVisuallyMuted() || (mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE;
 }
 
 
@@ -8501,6 +8479,8 @@ void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& d
 
 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;
 
@@ -8511,123 +8491,147 @@ void LLVOAvatar::idleUpdateRenderCost()
 		setDebugText(llformat("%.1f KB, %.2f m^2", mAttachmentGeometryBytes/1024.f, mAttachmentSurfaceArea));
 	}
 
-	if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME))
+	if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME) && max_render_cost == 0)
 	{
 		return;
 	}
 
-	U32 cost = 0;
-	LLVOVolume::texture_cost_t textures;
-
-	for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
+	if (mVisualComplexityStale)
 	{
-		const LLVOAvatarDictionary::BakedEntry *baked_dict = LLVOAvatarDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index);
-		ETextureIndex tex_index = baked_dict->mTextureIndex;
-		if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT)))
+		mVisualComplexityStale = FALSE;
+		U32 cost = 0;
+		LLVOVolume::texture_cost_t textures;
+
+		for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
 		{
-			if (isTextureVisible(tex_index))
+			const LLVOAvatarDictionary::BakedEntry *baked_dict = LLVOAvatarDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index);
+			ETextureIndex tex_index = baked_dict->mTextureIndex;
+			if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT)))
 			{
-				cost +=ARC_BODY_PART_COST;
+				if (isTextureVisible(tex_index))
+				{
+					cost +=ARC_BODY_PART_COST;
+				}
 			}
 		}
-	}
 
 
-	for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); 
-		 iter != mAttachmentPoints.end();
-		 ++iter)
-	{
-		LLViewerJointAttachment* attachment = iter->second;
-		for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
-			 attachment_iter != attachment->mAttachedObjects.end();
-			 ++attachment_iter)
+		for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); 
+			 iter != mAttachmentPoints.end();
+			 ++iter)
 		{
-			const LLViewerObject* attached_object = (*attachment_iter);
-			if (attached_object && !attached_object->isHUDAttachment())
+			LLViewerJointAttachment* attachment = iter->second;
+			for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+				 attachment_iter != attachment->mAttachedObjects.end();
+				 ++attachment_iter)
 			{
-				textures.clear();
-				const LLDrawable* drawable = attached_object->mDrawable;
-				if (drawable)
+				const LLViewerObject* attached_object = (*attachment_iter);
+				if (attached_object && !attached_object->isHUDAttachment())
 				{
-					const LLVOVolume* volume = drawable->getVOVolume();
-					if (volume)
+					textures.clear();
+					const LLDrawable* drawable = attached_object->mDrawable;
+					if (drawable)
 					{
-						cost += volume->getRenderCost(textures);
-
-						const_child_list_t children = volume->getChildren();
-						for (const_child_list_t::const_iterator child_iter = children.begin();
-							  child_iter != children.end();
-							  ++child_iter)
+						const LLVOVolume* volume = drawable->getVOVolume();
+						if (volume)
 						{
-							LLViewerObject* child_obj = *child_iter;
-							LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
-							if (child)
+							cost += volume->getRenderCost(textures);
+
+							const_child_list_t children = volume->getChildren();
+							for (const_child_list_t::const_iterator child_iter = children.begin();
+								  child_iter != children.end();
+								  ++child_iter)
 							{
-								cost += child->getRenderCost(textures);
+								LLViewerObject* child_obj = *child_iter;
+								LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
+								if (child)
+								{
+									cost += child->getRenderCost(textures);
+								}
 							}
-						}
 
-						for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
-						{
-							// add the cost of each individual texture in the linkset
-							cost += iter->second;
+							for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+							{
+								// add the cost of each individual texture in the linkset
+								cost += iter->second;
+							}
 						}
 					}
 				}
 			}
-		}
 
-	}
+		}
 
 
 
-	// 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.
-	if (isSelf())
-	{
-		// print any attachment textures we didn't already know about.
-		for (LLVOVolume::texture_cost_t::iterator it = textures.begin(); it != textures.end(); ++it)
+		// 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.
+		if (isSelf())
 		{
-			LLUUID image_id = it->first;
-			if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
-				continue;
-			if (all_textures.find(image_id) == all_textures.end())
+			// print any attachment textures we didn't already know about.
+			for (LLVOVolume::texture_cost_t::iterator it = textures.begin(); it != textures.end(); ++it)
+			{
+				LLUUID image_id = it->first;
+				if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
+					continue;
+				if (all_textures.find(image_id) == all_textures.end())
+				{
+					// attachment texture not previously seen.
+					llinfos << "attachment_texture: " << image_id.asString() << llendl;
+					all_textures.insert(image_id);
+				}
+			}
+
+			// print any avatar textures we didn't already know about
+			for (LLVOAvatarDictionary::Textures::const_iterator iter = LLVOAvatarDictionary::getInstance()->getTextures().begin();
+				 iter != LLVOAvatarDictionary::getInstance()->getTextures().end();
+				 ++iter)
 			{
-				// attachment texture not previously seen.
-				llinfos << "attachment_texture: " << image_id.asString() << llendl;
-				all_textures.insert(image_id);
+				const LLVOAvatarDictionary::TextureEntry *texture_dict = iter->second;
+				// TODO: MULTI-WEARABLE: handle multiple textures for self
+				const LLViewerTexture* te_image = getImage(iter->first,0);
+				if (!te_image)
+					continue;
+				LLUUID image_id = te_image->getID();
+				if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
+					continue;
+				if (all_textures.find(image_id) == all_textures.end())
+				{
+					llinfos << "local_texture: " << texture_dict->mName << ": " << image_id << llendl;
+					all_textures.insert(image_id);
+				}
 			}
 		}
 
-		// print any avatar textures we didn't already know about
-		for (LLVOAvatarDictionary::Textures::const_iterator iter = LLVOAvatarDictionary::getInstance()->getTextures().begin();
-			 iter != LLVOAvatarDictionary::getInstance()->getTextures().end();
-			 ++iter)
-		{
-			const LLVOAvatarDictionary::TextureEntry *texture_dict = iter->second;
-			// TODO: MULTI-WEARABLE: handle multiple textures for self
-			const LLViewerTexture* te_image = getImage(iter->first,0);
-			if (!te_image)
-				continue;
-			LLUUID image_id = te_image->getID();
-			if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
-				continue;
-			if (all_textures.find(image_id) == all_textures.end())
+		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)
 			{
-				llinfos << "local_texture: " << texture_dict->mName << ": " << image_id << llendl;
-				all_textures.insert(image_id);
+				LLNotificationsUtil::add("ExceededMidDetailRenderCost");
+			}
+			else if (cost > max_render_cost/2)
+			{
+				LLNotificationsUtil::add("ExceededLowDetailRenderCost");
 			}
 		}
+
+		mVisualComplexity = cost;
 	}
 
 	
-	std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
-	setDebugText(llformat("%s %d", viz_string.c_str(), cost));
-	mVisualComplexity = cost;
-	F32 green = 1.f-llclamp(((F32) cost-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
-	F32 red = llmin((F32) cost/(F32)ARC_LIMIT, 1.f);
-	mText->setColor(LLColor4(red,green,0,1));
+	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
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 1adb6809625ef16385e38874a1b142d661559eab..e6569c557c10f12bc54bc20dbca173609d56e329 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -252,6 +252,7 @@ class LLVOAvatar :
 	static void		invalidateNameTags();
 	void			addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);
 	void 			idleUpdateRenderCost();
+	void			updateVisualComplexity() { mVisualComplexityStale = TRUE; }
 	void 			idleUpdateBelowWater();
 
 	//--------------------------------------------------------------------
@@ -314,6 +315,7 @@ class LLVOAvatar :
 	BOOL			mFullyLoadedInitialized;
 	S32				mFullyLoadedFrameCounter;
 	S32				mVisualComplexity;
+	BOOL			mVisualComplexityStale;
 	LLFrameTimer	mFullyLoadedTimer;
 	LLFrameTimer	mRuthTimer;
 
@@ -437,7 +439,6 @@ class LLVOAvatar :
 	U32 		renderRigid();
 	U32 		renderSkinned(EAvatarRenderPass pass);
 	F32			getLastSkinTime() { return mLastSkinTime; }
-	U32			renderSkinnedAttachments();
 	U32 		renderTransparent(BOOL first_pass);
 	void 		renderCollisionVolumes();
 	static void	deleteCachedImages(bool clearAll=true);
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index c0f80cf8556f1298678ffb4c6085433e85f27e17..b853112f7474d960a1a0b899336bf22dfced2550 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1116,6 +1116,12 @@ void LLVOVolume::notifyMeshLoaded()
 { 
 	mSculptChanged = TRUE;
 	gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+
+	LLVOAvatar* avatar = getAvatar();
+	if (avatar)
+	{
+		avatar->updateVisualComplexity();
+	}
 }
 
 // sculpt replaces generate() for sculpted surfaces
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 4306b3da12f3e04b0f7b26b5506f4d0aae48873f..45d6d23b512b0be709c23c79d3f4679d416a46bd 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -10247,6 +10247,13 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 		avatar->mImpostor.bindTarget();
 	}
 
+	F32 old_alpha = LLDrawPoolAvatar::sMinimumAlpha;
+
+	if (muted)
+	{ //disable alpha masking for muted avatars (get whole skin silhouette)
+		LLDrawPoolAvatar::sMinimumAlpha = 0.f;
+	}
+
 	if (LLPipeline::sRenderDeferred)
 	{
 		avatar->mImpostor.clear();
@@ -10260,7 +10267,9 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 		avatar->mImpostor.clear();
 		renderGeom(camera);
 	}
-	
+
+	LLDrawPoolAvatar::sMinimumAlpha = old_alpha;
+		
 	{ //create alpha mask based on depth buffer (grey out if muted)
 		LLFastTimer t(FTM_IMPOSTOR_BACKGROUND);
 		if (LLPipeline::sRenderDeferred)
@@ -10274,6 +10283,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 		if (muted)
 		{
 			gGL.setColorMask(true, true);
+
 		}
 		else
 		{
@@ -10292,25 +10302,36 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
 		gGL.pushMatrix();
 		gGL.loadIdentity();
 
-		static const F32 clip_plane = 0.99999f;
+		static const F32 clip_plane = 0.999f;
 
 		if (LLGLSLShader::sNoFixedFunction)
 		{
-			gUIProgram.bind();
+			gDebugProgram.bind();
 		}
 
-		gGL.color4ub(64,64,64,255);
-		gGL.begin(LLRender::QUADS);
-		gGL.vertex3f(-1, -1, clip_plane);
-		gGL.vertex3f(1, -1, clip_plane);
-		gGL.vertex3f(1, 1, clip_plane);
-		gGL.vertex3f(-1, 1, clip_plane);
-		gGL.end();
-		gGL.flush();
+
+		if (LLMuteList::getInstance()->isMuted(avatar->getID()))
+		{ //grey muted avatar
+			gGL.diffuseColor4ub(64,64,64,255);
+		}
+		else
+		{ //blue visually muted avatar
+			gGL.diffuseColor4ub(72,61,139,255);
+		}
+
+		{
+			gGL.begin(LLRender::QUADS);
+			gGL.vertex3f(-1, -1, clip_plane);
+			gGL.vertex3f(1, -1, clip_plane);
+			gGL.vertex3f(1, 1, clip_plane);
+			gGL.vertex3f(-1, 1, clip_plane);
+			gGL.end();
+			gGL.flush();
+		}
 
 		if (LLGLSLShader::sNoFixedFunction)
 		{
-			gUIProgram.unbind();
+			gDebugProgram.unbind();
 		}
 
 		gGL.popMatrix();
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 832e05a06fa6facbd3faeb938268023825a0fa77..fb530ef22d4acf8905d489627bcc6afb5f4d780b 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -202,6 +202,45 @@ Message Template [PATH] not found.
      yestext="OK"/>
   </notification>
 
+  <notification
+   icon="notify.tga"
+   name="ExceededHighDetailRenderCost"
+   persist="false"
+   type="alertmodal">
+    Your avatar has become too complex to be rendered by even high performance computers.  Almost all Residents will see a low detail stand in instead of your actual avatar.
+    <unique/>
+    <usetemplate
+     ignoretext="Avatar exceeded high detail complexity."
+     name="okignore"
+     yestext="Close"/>
+  </notification>
+
+  <notification
+   icon="notify.tga"
+   name="ExceededMidDetailRenderCost"
+   persist="false"
+   type="alertmodal">
+    Your avatar has become too complex to be rendered by most computers.  Many Residents will see a low detail stand in instead of your actual avatar.
+  <unique/>
+  <usetemplate
+   ignoretext="Avatar exceeded mid detail complexity."
+   name="okignore"
+   yestext="Close"/>
+</notification>
+
+  <notification
+ icon="notify.tga"
+ name="ExceededLowDetailRenderCost"
+ persist="false"
+ type="alertmodal">
+    Your avatar has become too complex to be rendered by some computers.  Some Residents will see a low detail stand in instead of your actual avatar.
+  <unique/>
+  <usetemplate
+   ignoretext="Avatar exceeded low detail complexity."
+   name="okignore"
+   yestext="Close"/>
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="WearableSave"
@@ -9935,5 +9974,5 @@ An internal error prevented us from properly updating your viewer.  The L$ balan
    <tag>fail</tag>
 Cannot create large prims that intersect other players.  Please re-try when other players have moved.
   </notification>
-
+  
 </notifications>