diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 236ded96e6552ddfb63d4ea2a1d99348a2882be3..70e73000333c6d63accaba58634230bc55f3bbb6 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1016,40 +1016,35 @@ void LLMeshRepoThread::run()
         // For the final three request lists, similar goal to above but
         // slightly different queue structures.  Stay off the mutex when
         // performing long-duration actions.
-
-        if (mHttpRequestSet.size() < sRequestHighWater
-            && (!mSkinRequests.empty()
-            || !mDecompositionRequests.empty()
-            || !mPhysicsShapeRequests.empty()))
         {
             // Something to do probably, lock and double-check.  We don't want
             // to hold the lock long here.  That will stall main thread activities
             // so we bounce it.
-
-            if (!mSkinRequests.empty())
+            if (!mSkinReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
             {
-                std::set<UUIDBasedRequest> incomplete;
-                while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
+				std::list<UUIDBasedRequest> incomplete;
+                while (!mSkinReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
                 {
                     mMutex->lock();
-                    std::set<UUIDBasedRequest>::iterator iter = mSkinRequests.begin();
-                    UUIDBasedRequest req = *iter;
-                    mSkinRequests.erase(iter);
+                    auto req = mSkinReqQ.front();
+                    mSkinReqQ.pop();
                     mMutex->unlock();
                     if (req.isDelayed())
                     {
-                        incomplete.insert(req);
+                        incomplete.emplace_back(req);
                     }
-                    else if (!fetchMeshSkinInfo(req.mId))
+                    else if (!fetchMeshSkinInfo(req.mId, req.canRetry()))
                     {
                         if (req.canRetry())
                         {
                             req.updateTime();
-                            incomplete.insert(req);
+                            incomplete.emplace_back(req);
                         }
                         else
                         {
-                            LL_DEBUGS() << "mSkinRequests failed: " << req.mId << LL_ENDL;
+		                    LLMutexLock locker(mMutex);
+							mSkinUnavailableQ.push(req);
+                            LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL;
                         }
                     }
                 }
@@ -1057,7 +1052,10 @@ void LLMeshRepoThread::run()
                 if (!incomplete.empty())
                 {
                     LLMutexLock locker(mMutex);
-                    mSkinRequests.insert(incomplete.begin(), incomplete.end());
+					for (const auto& req : incomplete)
+					{
+						mSkinReqQ.emplace(req);
+					}
                 }
             }
 
@@ -1065,7 +1063,7 @@ void LLMeshRepoThread::run()
             // *TODO:  For UI/debug-oriented lists, we might drop the fine-
             // grained locking as there's a lowered expectation of smoothness
             // in these cases.
-            if (!mDecompositionRequests.empty())
+            if (!mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
             {
                 std::set<UUIDBasedRequest> incomplete;
                 while (!mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
@@ -1101,7 +1099,7 @@ void LLMeshRepoThread::run()
             }
 
             // holding lock, final list
-            if (!mPhysicsShapeRequests.empty())
+            if (!mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
             {
                 std::set<UUIDBasedRequest> incomplete;
                 while (!mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
@@ -1157,7 +1155,7 @@ void LLMeshRepoThread::run()
 // Mutex:  LLMeshRepoThread::mMutex must be held on entry
 void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
 {
-	mSkinRequests.insert(UUIDBasedRequest(mesh_id));
+	mSkinReqQ.emplace(mesh_id);
 }
 
 // Mutex:  LLMeshRepoThread::mMutex must be held on entry
@@ -1260,7 +1258,7 @@ void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * leg
 		}
 		else
 		{
-		LL_WARNS_ONCE(LOG_MESH) << "Current region does not have ViewerAsset capability!  Cannot load "
+			LL_WARNS_ONCE(LOG_MESH) << "Current region does not have ViewerAsset capability!  Cannot load meshes. Region id: "
 									<< gAgent.getRegion()->getRegionID() << LL_ENDL;
 			LL_DEBUGS_ONCE(LOG_MESH) << "Cannot load mesh " << mesh_id << " due to missing capability." << LL_ENDL;
 		}
@@ -1332,7 +1330,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int l
 }
 
 
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
 {
 	
 	if (!mHeaderMutex)
@@ -1413,13 +1411,28 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 									   << LL_ENDL;
 					ret = false;
 				}
-				else
+				else if(can_retry)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
 				}
+				else
+				{
+					LLMutexLock locker(mMutex);
+					mSkinUnavailableQ.emplace(mesh_id);
+				}
+			}
+			else
+			{
+				LLMutexLock locker(mMutex);
+				mSkinUnavailableQ.emplace(mesh_id);
 			}
 		}
+		else
+		{
+			LLMutexLock locker(mMutex);
+			mSkinUnavailableQ.emplace(mesh_id);
+		}
 	}
 	else
 	{	
@@ -1933,7 +1946,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 		// LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
 		{
 			LLMutexLock lock(mMutex);
-			mSkinInfoQ.push_back(info);
+			mSkinInfoQ.emplace(info);
 		}
 	}
 
@@ -2863,31 +2876,58 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
 	}
 
-	if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty())
+	if (!mSkinInfoQ.empty())
 	{
 		if (mMutex->try_lock())
 		{
-			std::list<LLMeshSkinInfo> skin_info_q;
-			std::list<LLModel::Decomposition*> decomp_q;
-
+			std::queue<LLMeshSkinInfo> skin_info_q;
 			if (! mSkinInfoQ.empty())
 			{
 				skin_info_q.swap(mSkinInfoQ);
 			}
-			if (! mDecompositionQ.empty())
+			mMutex->unlock();
+
+			// Process the elements free of the lock
+			while (!skin_info_q.empty())
 			{
-				decomp_q.swap(mDecompositionQ);
+				gMeshRepo.notifySkinInfoReceived(skin_info_q.front());
+				skin_info_q.pop();
 			}
+		}
+	}
 
+	if (!mSkinUnavailableQ.empty())
+	{
+		if (mMutex->try_lock())
+		{
+			std::queue<UUIDBasedRequest> skin_info_unavail_q;
+			if (!mSkinUnavailableQ.empty())
+			{
+				skin_info_unavail_q.swap(mSkinUnavailableQ);
+			}
 			mMutex->unlock();
 
 			// Process the elements free of the lock
-			while (! skin_info_q.empty())
+			while (!skin_info_unavail_q.empty())
 			{
-				gMeshRepo.notifySkinInfoReceived(skin_info_q.front());
-				skin_info_q.pop_front();
+				gMeshRepo.notifySkinInfoUnavailable(skin_info_unavail_q.front().mId);
+				skin_info_unavail_q.pop();
 			}
+		}
+	}
 
+	if (!mDecompositionQ.empty())
+	{
+		if (mMutex->try_lock())
+		{
+			std::list<LLModel::Decomposition*> decomp_q;
+			if (! mDecompositionQ.empty())
+			{
+				decomp_q.swap(mDecompositionQ);
+			}
+			mMutex->unlock();
+
+			// Process the elements free of the lock
 			while (! decomp_q.empty())
 			{
 				gMeshRepo.notifyDecompositionReceived(decomp_q.front());
@@ -3319,9 +3359,8 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
 					   << ", Reason:  " << status.toString()
 					   << " (" << status.toTerseString() << ").  Not retrying."
 					   << LL_ENDL;
-
-	// *TODO:  Mark mesh unavailable on error.  For now, simply leave
-	// request unfulfilled rather than retry forever.
+		LLMutexLock lock(gMeshRepo.mThread->mMutex);
+		gMeshRepo.mThread->mSkinUnavailableQ.emplace(mMeshID);
 }
 
 void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
@@ -3350,7 +3389,8 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /*
 		LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID
 						   << ", Unknown reason.  Not retrying."
 						   << LL_ENDL;
-		// *TODO:  Mark mesh unavailable on error
+		LLMutexLock lock(gMeshRepo.mThread->mMutex);
+		gMeshRepo.mThread->mSkinUnavailableQ.emplace(mMeshID);
 	}
 }
 
@@ -3552,9 +3592,28 @@ S32 LLMeshRepository::update()
 	return size ;
 }
 
+void LLMeshRepository::unregisterMesh(LLVOVolume* vobj)
+{
+	for (auto& lod : mLoadingMeshes)
+	{
+		for (auto& param : lod)
+		{
+			vector_replace_with_last(param.second, vobj);
+		}
+	}
+
+	for (auto& skin_pair : mLoadingSkins)
+	{
+		vector_replace_with_last(skin_pair.second, vobj);
+	}
+}
+
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH_LOAD("Load Mesh");
+
 S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
 {
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
+	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH_LOAD);
 	
 	// Manage time-to-load metrics for mesh download operations.
 	metricsProgress(1);
@@ -3570,12 +3629,15 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 		mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params);
 		if (iter != mLoadingMeshes[detail].end())
 		{ //request pending for this mesh, append volume id to list
-			iter->second.insert(vobj->getID());
+			auto it = std::find(iter->second.begin(), iter->second.end(), vobj);
+			if (it == iter->second.end()) {
+				iter->second.push_back(vobj);
+			}
 		}
 		else
 		{
 			//first request for this mesh
-			mLoadingMeshes[detail][mesh_params].insert(vobj->getID());
+			mLoadingMeshes[detail][mesh_params].push_back(vobj);
 			mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
 			LLMeshRepository::sLODPending++;
 		}
@@ -3635,9 +3697,12 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 	return detail;
 }
 
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH_NOTIFY_LOADED("Notify Loaded Meshes");
+
 void LLMeshRepository::notifyLoadedMeshes()
 { //called from main thread
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
+	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH_NOTIFY_LOADED);
 
     // GetMesh2 operation with keepalives, etc.  With pipelining,
     // we'll increase this.  See llappcorehttp and llcorehttp for
@@ -3817,29 +3882,23 @@ void LLMeshRepository::notifyLoadedMeshes()
 				//calculate "score" for pending requests
 
 				//create score map
-				std::map<LLUUID, F32> score_map;
+				boost::unordered_map<LLUUID, F32> score_map;
 
-				for (U32 i = 0; i < 4; ++i)
+				for (auto& lod : mLoadingMeshes)
 				{
-					for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)
+					for (auto& param : lod)
 					{
 						F32 max_score = 0.f;
-						for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+						for (auto vobj : param.second)
 						{
-							LLViewerObject* object = gObjectList.findObject(*obj_iter);
-							
-							if (object)
+							if (LLDrawable* drawable = vobj->mDrawable)
 							{
-								LLDrawable* drawable = object->mDrawable;
-								if (drawable)
-								{
-									F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
-									max_score = llmax(max_score, cur_score);
-								}
+								F32 cur_score = drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f);
+								max_score = llmax(max_score, cur_score);
 							}
 						}
 				
-						score_map[iter->first.getSculptID()] = max_score;
+						score_map[param.first.getSculptID()] = max_score;
 					}
 				}
 
@@ -3893,20 +3952,35 @@ void LLMeshRepository::notifyLoadedMeshes()
 
 void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
 {
-	mSkinMap[info.mMeshID] = info;
+	auto pair = mSkinMap.emplace(info.mMeshID, info);
 
 	skin_load_map::iterator iter = mLoadingSkins.find(info.mMeshID);
 	if (iter != mLoadingSkins.end())
 	{
-		for (std::set<LLUUID>::iterator obj_id = iter->second.begin(); obj_id != iter->second.end(); ++obj_id)
+		for (auto vobj : iter->second)
 		{
-			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*obj_id);
 			if (vobj)
 			{
-				vobj->notifyMeshLoaded();
+				vobj->notifySkinInfoLoaded(&((*pair.first).second));
 			}
 		}
-		mLoadingSkins.erase(info.mMeshID);
+		mLoadingSkins.erase(iter);
+	}
+}
+
+void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id)
+{
+	skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
+	if (iter != mLoadingSkins.end())
+	{
+		for (auto vobj : iter->second)
+		{
+			if (vobj)
+			{
+				vobj->notifySkinInfoUnavailable();
+			}
+		}
+		mLoadingSkins.erase(iter);
 	}
 }
 
@@ -3958,16 +4032,15 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 		}
 
 		//notify waiting LLVOVolume instances that their requested mesh is available
-		for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		for (auto vobj : obj_iter->second)
 		{
-			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
 			if (vobj)
 			{
 				vobj->notifyMeshLoaded();
 			}
 		}
 		
-		mLoadingMeshes[detail].erase(mesh_params);
+		mLoadingMeshes[detail].erase(obj_iter);
 	}
 }
 
@@ -3976,13 +4049,12 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
 	//get list of objects waiting to be notified this mesh is loaded
 	mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
 
-	F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
-
 	if (obj_iter != mLoadingMeshes[lod].end())
 	{
-		for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
+
+		for (auto vobj : obj_iter->second)
 		{
-			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
 			if (vobj)
 			{
 				LLVolume* obj_volume = vobj->getVolume();
@@ -3996,7 +4068,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
 			}
 		}
 		
-		mLoadingMeshes[lod].erase(mesh_params);
+		mLoadingMeshes[lod].erase(obj_iter);
 	}
 }
 
@@ -4005,9 +4077,12 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo
 	return mThread->getActualMeshLOD(mesh_params, lod);
 }
 
-const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj)
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH_GET_SKIN_INFO("Get Skin Info");
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH_GET_SKIN_INFO_FETCH("Get Skin Info Fetch");
+LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj)
 {
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
+	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH_GET_SKIN_INFO);
 
 	if (mesh_id.notNull())
 	{
@@ -4019,23 +4094,34 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const
 		
 		//no skin info known about given mesh, try to fetch it
 		{
+			LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH_GET_SKIN_INFO_FETCH);
 			LLMutexLock lock(mMeshMutex);
 			//add volume to list of loading meshes
 			skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
-			if (iter == mLoadingSkins.end())
-			{ //no request pending for this skin info
+			if (iter != mLoadingSkins.end())
+			{ //request pending for this mesh, append volume id to list
+				auto it = std::find(iter->second.begin(), iter->second.end(), requesting_obj);
+				if (it == iter->second.end()) {
+					iter->second.push_back(requesting_obj);
+				}
+			}
+			else
+			{
+				//first request for this mesh
+				mLoadingSkins[mesh_id].push_back(requesting_obj);
 				mPendingSkinRequests.push(mesh_id);
 			}
-			mLoadingSkins[mesh_id].insert(requesting_obj->getID());
 		}
 	}
 
 	return nullptr;
 }
 
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH_FETCH_PHYSICS_SHAPE("Fetch Physics Shape");
 void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
 {
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
+	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH_FETCH_PHYSICS_SHAPE);
 
 	if (mesh_id.notNull())
 	{
@@ -4062,9 +4148,11 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
 	}
 }
 
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH_GET_DECOMP("Get Decomposition");
 LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)
 {
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
+	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH_GET_DECOMP);
 
 	LLModel::Decomposition* ret = nullptr;
 
@@ -4126,9 +4214,11 @@ bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)
 	return false;
 }
 
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH_GET_MESH_HEADER("Get Mesh Header");
 LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id)
 {
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
+	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH_GET_MESH_HEADER);
 
 	return mThread->getMeshHeader(mesh_id);
 }
@@ -4327,10 +4417,13 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLSD& header, F32 radius, S32* byte
 	F32 dlow = llmin(radius/0.06f, max_distance);
 	F32 dmid = llmin(radius/0.24f, max_distance);
 	
-	F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount");  //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
-	F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free"
+	static LLCachedControl<U32> metadata_discount_ctrl(gSavedSettings, "MeshMetaDataDiscount");  //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
+	static LLCachedControl<U32> minimum_size_ctrl(gSavedSettings, "MeshMinimumByteSize"); //make sure nothing is "free"
+	static LLCachedControl<U32> bytes_per_triangle_ctrl(gSavedSettings, "MeshBytesPerTriangle");
 
-	F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
+	const F32 METADATA_DISCOUNT = (F32)metadata_discount_ctrl;  //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
+	const F32 MINIMUM_SIZE = (F32)minimum_size_ctrl; //make sure nothing is "free"
+	const F32 bytes_per_triangle = (F32)bytes_per_triangle_ctrl;
 
 	S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
 	S32 bytes_low = header["low_lod"]["size"].asInteger();
@@ -4412,8 +4505,8 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLSD& header, F32 radius, S32* byte
 	{
 		*unscaled_value = weighted_avg;
 	}
-
-	return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
+	static LLCachedControl<U32> triangle_budget(gSavedSettings, "MeshTriangleBudget");
+	return weighted_avg / (F32)triangle_budget * 15000.f;
 }
 
 LLMeshCostData::LLMeshCostData()
@@ -4454,13 +4547,12 @@ bool LLMeshCostData::init(const LLSD& header)
     mSizeByLOD[2] = bytes_med;
     mSizeByLOD[3] = bytes_high;
 
-    F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount");  //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
-    F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free"
-    F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
-
+	static LLCachedControl<U32> METADATA_DISCOUNT(gSavedSettings, "MeshMetaDataDiscount");  //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
+	static LLCachedControl<U32> MINIMUM_SIZE(gSavedSettings, "MeshMinimumByteSize"); //make sure nothing is "free"
+	static LLCachedControl<U32> bytes_per_triangle(gSavedSettings, "MeshBytesPerTriangle");
     for (S32 i=0; i<4; i++)
     {
-        mEstTrisByLOD[i] = llmax((F32) mSizeByLOD[i]-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle; 
+        mEstTrisByLOD[i] = llmax((F32) mSizeByLOD[i]- (F32)METADATA_DISCOUNT, (F32)MINIMUM_SIZE)/(F32)bytes_per_triangle;
     }
 
     return true;
@@ -4568,7 +4660,8 @@ F32 LLMeshCostData::getEstTrisForStreamingCost()
 
 F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius)
 {
-	return getRadiusWeightedTris(radius)/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
+	static LLCachedControl<U32> triangle_budget(gSavedSettings, "MeshTriangleBudget");
+	return getRadiusWeightedTris(radius) / (F32)triangle_budget * 15000.f;
 }
 
 F32 LLMeshCostData::getTriangleBasedStreamingCost()
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 965926e607d76a6e0e0c431970922b5840741ab6..9c6d79a803980426b0f481d364095feef2322d81 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -283,17 +283,20 @@ public:
 	};
 
 	//set of requested skin info
-	std::set<UUIDBasedRequest> mSkinRequests;
-	
-	// list of completed skin info requests
-	std::list<LLMeshSkinInfo> mSkinInfoQ;
+	std::queue<UUIDBasedRequest> mSkinReqQ;
 
-	//set of requested decompositions
-	std::set<UUIDBasedRequest> mDecompositionRequests;
+	// list of skin info requests that have failed or are unavailaibe
+	std::queue<UUIDBasedRequest> mSkinUnavailableQ;
+
+	// list of completed skin info requests
+	std::queue<LLMeshSkinInfo> mSkinInfoQ;
 
 	//set of requested physics shapes
 	std::set<UUIDBasedRequest> mPhysicsShapeRequests;
 
+	//set of requested decompositions
+	std::set<UUIDBasedRequest> mDecompositionRequests;
+
 	// list of completed Decomposition info requests
 	std::list<LLModel::Decomposition*> mDecompositionQ;
 
@@ -358,7 +361,7 @@ public:
 
 	//send request for skin info, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshSkinInfo(const LLUUID& mesh_id);
+	bool fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry = true);
 
 	//send request for decomposition, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
@@ -578,6 +581,7 @@ public:
 	void shutdown();
 	S32 update();
 
+	void unregisterMesh(LLVOVolume* volume);
 	//mesh management functions
 	S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1);
 	
@@ -585,11 +589,12 @@ public:
 	void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume);
 	void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod);
 	void notifySkinInfoReceived(LLMeshSkinInfo& info);
+	void notifySkinInfoUnavailable(const LLUUID& info);
 	void notifyDecompositionReceived(LLModel::Decomposition* info);
 
 	S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
 	static S32 getActualMeshLOD(LLSD& header, S32 lod);
-	const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj);
+	LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj);
 	LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id);
 	void fetchPhysicsShape(const LLUUID& mesh_id);
 	bool hasPhysicsShape(const LLUUID& mesh_id);
@@ -617,12 +622,10 @@ public:
 	static void metricsProgress(unsigned int count);
 	static void metricsUpdate();
 	
-	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
+	typedef std::map<LLVolumeParams, std::vector<LLVOVolume*> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
 	
-	// <alchemy> - War on std::map
-	//typedef std::map<LLUUID, LLMeshSkinInfo> skin_map;
-	typedef boost::unordered_map<LLUUID, LLMeshSkinInfo> skin_map;
+	typedef std::map<LLUUID, LLMeshSkinInfo> skin_map;
 	skin_map mSkinMap;
 
 	typedef std::map<LLUUID, LLModel::Decomposition*> decomposition_map;
@@ -633,7 +636,7 @@ public:
 	std::vector<LLMeshRepoThread::LODRequest> mPendingRequests;
 	
 	//list of mesh ids awaiting skin info
-	typedef std::map<LLUUID, std::set<LLUUID> > skin_load_map;
+	typedef std::map<LLUUID, std::vector<LLVOVolume*> > skin_load_map;
 	skin_load_map mLoadingSkins;
 
 	//list of mesh ids that need to send skin info fetch requests
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 476ff88c670bb6e6ca12d890d3cab846c6a29cf6..cac0e74f3d1f5bf2daada9d69ed50307760c5539 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -6265,9 +6265,9 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LL
         LL_DEBUGS("AnimatedObjects") << "failed to add attachment overrides for root object " << root_object->getID() << " mesh asset not loaded" << LL_ENDL;
 		return;
 	}
-	const LLMeshSkinInfo*  pSkinData = vobj->getSkinInfo();
+	const LLMeshSkinInfo*  pSkinData = nullptr;
 
-	if ( vobj && vobj->isMesh() && pSkinData )
+	if ( vobj && vobj->isMesh() && (pSkinData = vobj->getSkinInfo()) )
 	{
 		const int bindCnt = pSkinData->mAlternateBindMatrix.size();								
         const int jointCnt = pSkinData->mJointNames.size();
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 56369b7edd1fafaf7c1244fd8f261f2429a161be..a1bc0cea82b7aa34fb8c2076d0e0f93d23938241 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -234,6 +234,10 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
 	mSculptChanged = FALSE;
 	mSpotLightPriority = 0.f;
 
+	mSkinInfoReceived = false;
+	mSkinInfoFailed = false;
+	mSkinInfo = NULL;
+
 	mMediaImplList.resize(getNumTEs());
 	mLastFetchedMediaVersion = -1;
 	memset(&mIndexInTex, 0, sizeof(S32) * LLRender::NUM_VOLUME_TEXTURE_CHANNELS);
@@ -248,6 +252,8 @@ LLVOVolume::~LLVOVolume()
 	delete mVolumeImpl;
 	mVolumeImpl = NULL;
 
+	gMeshRepo.unregisterMesh(this);
+
 	if(!mMediaImplList.empty())
 	{
 		for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
@@ -1047,10 +1053,16 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
 
 		if (isSculpted())
 		{
-			updateSculptTexture();
 			// if it's a mesh
 			if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
 			{
+				if (mSkinInfo && mSkinInfo->mMeshID != volume_params.getSculptID())
+				{
+					mSkinInfo = NULL;
+					mSkinInfoReceived = false;
+					mSkinInfoFailed = false;
+				}
+
 				if (!getVolume()->isMeshAssetLoaded())
 				{ 
 					//load request not yet issued, request pipeline load this mesh
@@ -1062,6 +1074,15 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
 					}
 				}
 				
+				if (!mSkinInfo && !isSkinInfoLoaded() && !hasSkinInfoFailed())
+				{
+					mSkinInfo = gMeshRepo.getSkinInfo(volume_params.getSculptID(), this);
+					if (mSkinInfo)
+					{
+						mSkinInfoReceived = true;
+						mSkinInfoFailed = false;
+					}
+				}
 			}
 			else // otherwise is sculptie
 			{
@@ -1094,6 +1115,8 @@ void LLVOVolume::updateSculptTexture()
 		{
 			mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
 		}
+
+		mSkinInfo = NULL;
 	}
 	else
 	{
@@ -1144,6 +1167,21 @@ void LLVOVolume::notifyMeshLoaded()
     updateVisualComplexity();
 }
 
+void LLVOVolume::notifySkinInfoLoaded(LLMeshSkinInfo* skin)
+{
+	mSkinInfoFailed = false;
+	mSkinInfoReceived = true;
+	mSkinInfo = skin;
+	notifyMeshLoaded();
+}
+
+void LLVOVolume::notifySkinInfoUnavailable()
+{
+	mSkinInfoFailed = true;
+	mSkinInfoReceived = false;
+	mSkinInfo = nullptr;
+}
+
 // sculpt replaces generate() for sculpted surfaces
 void LLVOVolume::sculpt()
 {	
@@ -3506,14 +3544,12 @@ BOOL LLVOVolume::setIsFlexible(BOOL is_flexible)
 
 const LLMeshSkinInfo* LLVOVolume::getSkinInfo() const
 {
-    if (getVolume())
-    {
-        return gMeshRepo.getSkinInfo(getVolume()->getParams().getSculptID(), this);
-    }
-    else
+	// TODO(nopjmp): extra protection against state screw-ups
+	if (getVolume())
     {
-        return NULL;
+         return mSkinInfo;
     }
+    return NULL;
 }
 
 // virtual
@@ -4669,8 +4705,7 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
 	}
 
 	LLVolume* volume = getVolume();
-	const LLMeshSkinInfo* skin = getSkinInfo();
-	if (!skin)
+	if (!mSkinInfo)
 	{
 		clearRiggedVolume();
 		return;
@@ -4690,7 +4725,7 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
 		updateRelativeXform();
 	}
 
-	mRiggedVolume->update(skin, avatar, volume);
+	mRiggedVolume->update(mSkinInfo, avatar, volume);
 }
 
 static LLTrace::BlockTimerStatHandle FTM_SKIN_RIGGED("Skin");
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 0c2080e0231281663dccfb478455ee925e9e4608..89e3e7fd74fe474abfef63ade5cbf48657f910db 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -330,6 +330,8 @@ public:
     void updateVisualComplexity();
     
 	void notifyMeshLoaded();
+	void notifySkinInfoLoaded(LLMeshSkinInfo* skin);
+	void notifySkinInfoUnavailable();
 	
 	// Returns 'true' iff the media data for this object is in flight
 	bool isMediaDataBeingFetched() const;
@@ -408,6 +410,13 @@ private:
 
 	LLPointer<LLRiggedVolume> mRiggedVolume;
 
+	bool isSkinInfoLoaded() { return mSkinInfoReceived; }
+	bool hasSkinInfoFailed() { return mSkinInfoFailed; }
+
+	bool mSkinInfoReceived;
+	bool mSkinInfoFailed;
+	LLMeshSkinInfo *mSkinInfo;
+	
 	// statics
 public:
 	static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop