diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 95322cce6dbf4682d413c8094fd0de420f3edce3..d7d359cd1287596e76130fc1664d8daf521427e0 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -79,6 +79,7 @@
 #include "llfloaterreg.h"
 
 #include "boost/lexical_cast.hpp"
+#include <boost/smart_ptr/make_shared.hpp>
 
 #ifndef LL_WINDOWS
 #include "netdb.h"
@@ -1008,129 +1009,122 @@ 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())
-            {
-                std::set<UUIDBasedRequest> incomplete;
-                while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
-                {
-                    mMutex->lock();
-                    std::set<UUIDBasedRequest>::iterator iter = mSkinRequests.begin();
-                    UUIDBasedRequest req = *iter;
-                    mSkinRequests.erase(iter);
-                    mMutex->unlock();
-                    if (req.isDelayed())
-                    {
-                        incomplete.insert(req);
-                    }
-                    else if (!fetchMeshSkinInfo(req.mId))
-                    {
-                        if (req.canRetry())
-                        {
-                            req.updateTime();
-                            incomplete.insert(req);
-                        }
-                        else
-                        {
-                            LL_DEBUGS() << "mSkinRequests failed: " << req.mId << LL_ENDL;
-                        }
-                    }
-                }
+		// 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 (!mSkinReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
+		{
+			std::list<UUIDBasedRequest> incomplete;
+			while (!mSkinReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
+			{
+				mMutex->lock();
+				auto req = mSkinReqQ.front();
+				mSkinReqQ.pop();
+				mMutex->unlock();
+				if (req.isDelayed())
+				{
+					incomplete.emplace_back(req);
+				}
+				else if (!fetchMeshSkinInfo(req.mId, req.canRetry()))
+				{
+					if (req.canRetry())
+					{
+						req.updateTime();
+						incomplete.emplace_back(req);
+					}
+					else
+					{
+						LLMutexLock locker(mMutex);
+						mSkinUnavailableQ.push(req);
+						LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL;
+					}
+				}
+			}
 
-                if (!incomplete.empty())
-                {
-                    LLMutexLock locker(mMutex);
-                    mSkinRequests.insert(incomplete.begin(), incomplete.end());
-                }
-            }
+			if (!incomplete.empty())
+			{
+				LLMutexLock locker(mMutex);
+				for (const auto& req : incomplete)
+				{
+					mSkinReqQ.emplace(req);
+				}
+			}
+		}
 
-            // holding lock, try next list
-            // *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())
-            {
-                std::set<UUIDBasedRequest> incomplete;
-                while (!mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
-                {
-                    mMutex->lock();
-                    std::set<UUIDBasedRequest>::iterator iter = mDecompositionRequests.begin();
-                    UUIDBasedRequest req = *iter;
-                    mDecompositionRequests.erase(iter);
-                    mMutex->unlock();
-                    if (req.isDelayed())
-                    {
-                        incomplete.insert(req);
-                    }
-                    else if (!fetchMeshDecomposition(req.mId))
-                    {
-                        if (req.canRetry())
-                        {
-                            req.updateTime();
-                            incomplete.insert(req);
-                        }
-                        else
-                        {
-                            LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL;
-                        }
-                    }
-                }
+		// holding lock, try next list
+		// *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() && mHttpRequestSet.size() < sRequestHighWater)
+		{
+			std::set<UUIDBasedRequest> incomplete;
+			while (!mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
+			{
+				mMutex->lock();
+				std::set<UUIDBasedRequest>::iterator iter = mDecompositionRequests.begin();
+				UUIDBasedRequest req = *iter;
+				mDecompositionRequests.erase(iter);
+				mMutex->unlock();
+				if (req.isDelayed())
+				{
+					incomplete.insert(req);
+				}
+				else if (!fetchMeshDecomposition(req.mId))
+				{
+					if (req.canRetry())
+					{
+						req.updateTime();
+						incomplete.insert(req);
+					}
+					else
+					{
+						LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL;
+					}
+				}
+			}
 
-                if (!incomplete.empty())
-                {
-                    LLMutexLock locker(mMutex);
-                    mDecompositionRequests.insert(incomplete.begin(), incomplete.end());
-                }
-            }
+			if (!incomplete.empty())
+			{
+				LLMutexLock locker(mMutex);
+				mDecompositionRequests.insert(incomplete.begin(), incomplete.end());
+			}
+		}
 
-            // holding lock, final list
-            if (!mPhysicsShapeRequests.empty())
-            {
-                std::set<UUIDBasedRequest> incomplete;
-                while (!mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
-                {
-                    mMutex->lock();
-                    std::set<UUIDBasedRequest>::iterator iter = mPhysicsShapeRequests.begin();
-                    UUIDBasedRequest req = *iter;
-                    mPhysicsShapeRequests.erase(iter);
-                    mMutex->unlock();
-                    if (req.isDelayed())
-                    {
-                        incomplete.insert(req);
-                    }
-                    else if (!fetchMeshPhysicsShape(req.mId))
-                    {
-                        if (req.canRetry())
-                        {
-                            req.updateTime();
-                            incomplete.insert(req);
-                        }
-                        else
-                        {
-                            LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;
-                        }
-                    }
-                }
+		// holding lock, final list
+		if (!mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
+		{
+			std::set<UUIDBasedRequest> incomplete;
+			while (!mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
+			{
+				mMutex->lock();
+				std::set<UUIDBasedRequest>::iterator iter = mPhysicsShapeRequests.begin();
+				UUIDBasedRequest req = *iter;
+				mPhysicsShapeRequests.erase(iter);
+				mMutex->unlock();
+				if (req.isDelayed())
+				{
+					incomplete.insert(req);
+				}
+				else if (!fetchMeshPhysicsShape(req.mId))
+				{
+					if (req.canRetry())
+					{
+						req.updateTime();
+						incomplete.insert(req);
+					}
+					else
+					{
+						LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL;
+					}
+				}
+			}
 
-                if (!incomplete.empty())
-                {
-                    LLMutexLock locker(mMutex);
-                    mPhysicsShapeRequests.insert(incomplete.begin(), incomplete.end());
-                }
-            }
-        }
+			if (!incomplete.empty())
+			{
+				LLMutexLock locker(mMutex);
+				mPhysicsShapeRequests.insert(incomplete.begin(), incomplete.end());
+			}
+		}
 
 		// For dev purposes only.  A dynamic change could make this false
 		// and that shouldn't assert.
@@ -1152,7 +1146,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
@@ -1307,7 +1301,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,
 }
 
 
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
 {
 	
 	if (!mHeaderMutex)
@@ -1317,7 +1311,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 
 	mHeaderMutex->lock();
 
-	if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+	auto header_it = mMeshHeader.find(mesh_id);
+	if (header_it == mMeshHeader.end())
 	{ //we have no header info for this mesh, do nothing
 		mHeaderMutex->unlock();
 		return false;
@@ -1329,9 +1324,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 	
 	if (header_size > 0)
 	{
-		S32 version = mMeshHeader[mesh_id]["version"].asInteger();
-		S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger();
-		S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger();
+		const auto& header = header_it->second;
+		S32 version = header["version"].asInteger();
+		S32 offset = header_size + header["skin"]["offset"].asInteger();
+		S32 size = header["skin"]["size"].asInteger();
 
 		mHeaderMutex->unlock();
 
@@ -1377,7 +1373,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 
 			if (!http_url.empty())
 			{
-                LLMeshHandlerBase::ptr_t handler(new LLMeshSkinInfoHandler(mesh_id, offset, size));
+                LLMeshHandlerBase::ptr_t handler = boost::make_shared<LLMeshSkinInfoHandler>(mesh_id, offset, size);
 				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
@@ -1387,12 +1383,27 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 									   << LL_ENDL;
 					ret = false;
 				}
-				else
+				else if(can_retry)
 				{
 					handler->mHttpHandle = handle;
-					mHttpRequestSet.insert(handler);
+					mHttpRequestSet.emplace(std::move(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
@@ -1938,7 +1949,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.push(info);
 		}
 	}
 
@@ -2863,31 +2874,58 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
 	}
 
-	if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty())
+	if (!mSkinInfoQ.empty())
 	{
 		if (mMutex->trylock())
 		{
-			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->trylock())
+		{
+			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->trylock())
+		{
+			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());
@@ -3558,6 +3596,22 @@ 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);
+	}
+}
+
 S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
 {
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
@@ -3576,13 +3630,16 @@ 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());
-			mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
+			mLoadingMeshes[detail][mesh_params].push_back(vobj);
+			mPendingRequests.emplace_back(mesh_params, detail);
 			LLMeshRepository::sLODPending++;
 		}
 	}
@@ -3803,34 +3860,28 @@ void LLMeshRepository::notifyLoadedMeshes()
 				//create score map
 				std::map<LLUUID, F32> score_map;
 
-				for (U32 i = 0; i < 4; ++i)
+				for (const auto& lod : mLoadingMeshes)
 				{
-					for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)
+					for (const 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 (LLVOVolume* 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;
 					}
 				}
 
 				//set "score" for pending requests
-				for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
-				{
-					iter->mScore = score_map[iter->mMeshParams.getSculptID()];
+				for (auto& request : mPendingRequests)
+                {
+                    request.mScore = score_map[request.mMeshParams.getSculptID()];
 				}
 
 				//sort by "score"
@@ -3877,20 +3928,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);
 	}
 }
 
@@ -3942,16 +4008,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);
 	}
 }
 
@@ -3960,13 +4025,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();
@@ -3980,7 +4044,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
 			}
 		}
 		
-		mLoadingMeshes[lod].erase(mesh_params);
+		mLoadingMeshes[lod].erase(obj_iter);
 	}
 }
 
@@ -3989,7 +4053,7 @@ 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)
+LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj)
 {
 	LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
 
@@ -4006,15 +4070,23 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const
 			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 NULL;
+	return nullptr;
 }
 
 void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index bba0c9f2cb1542b56f5cbf326089407a468e0bf9..7b6409968fadaf040e57d7e8040b0304f82833c2 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -281,10 +281,13 @@ class LLMeshRepoThread : public LLThread
 	};
 
 	//set of requested skin info
-	std::set<UUIDBasedRequest> mSkinRequests;
-	
+	std::queue<UUIDBasedRequest> mSkinReqQ;
+
+	// list of skin info requests that have failed or are unavailaibe
+	std::queue<UUIDBasedRequest> mSkinUnavailableQ;
+
 	// list of completed skin info requests
-	std::list<LLMeshSkinInfo> mSkinInfoQ;
+	std::queue<LLMeshSkinInfo> mSkinInfoQ;
 
 	//set of requested decompositions
 	std::set<UUIDBasedRequest> mDecompositionRequests;
@@ -352,7 +355,7 @@ class LLMeshRepoThread : public LLThread
 
 	//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)
@@ -572,6 +575,7 @@ class LLMeshRepository
 	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);
 	
@@ -579,11 +583,12 @@ class LLMeshRepository
 	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);
@@ -611,7 +616,7 @@ class LLMeshRepository
 	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];
 	
 	typedef std::map<LLUUID, LLMeshSkinInfo> skin_map;
@@ -625,7 +630,7 @@ class LLMeshRepository
 	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 8a87f3af42c0fc4aff21971f67b4ed171adf9c7b..970f8454f49714fa2e28d9a9342f31845689effd 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -6247,15 +6247,15 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LL
 
 	LLViewerObject *root_object = (LLViewerObject*)vobj->getRoot();
     LL_DEBUGS("AnimatedObjects") << "trying to add attachment overrides for root object " << root_object->getID() << " prim is " << vobj << LL_ENDL;
-	if (vobj->isMesh() &&
+	if (vobj->isRiggedMesh() &&
 		((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled()))
 	{
         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 6e7f33d758a64d22640859d2caf88be5628e381e..b13867cf83c4f57975f1f167602dc70577fb0ddb 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -235,6 +235,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);
@@ -249,6 +253,8 @@ LLVOVolume::~LLVOVolume()
 	delete mVolumeImpl;
 	mVolumeImpl = NULL;
 
+	gMeshRepo.unregisterMesh(this);
+
 	if(!mMediaImplList.empty())
 	{
 		for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
@@ -803,8 +809,6 @@ void LLVOVolume::updateTextureVirtualSize(bool forced)
 		LLUUID id =  sculpt_params->getSculptTexture();
 		
 		updateSculptTexture();
-		
-		
 
 		if (mSculptTexture.notNull())
 		{
@@ -1038,14 +1042,19 @@ 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
-					LLUUID asset_id = volume_params.getSculptID();
 					S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod);
 					if (available_lod != lod)
 					{
@@ -1053,6 +1062,14 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
 					}
 				}
 				
+				if (!mSkinInfo && !isSkinInfoLoaded() && !hasSkinInfoFailed())
+				{
+					mSkinInfo = gMeshRepo.getSkinInfo(volume_params.getSculptID(), this);
+					if (mSkinInfo)
+					{
+						notifySkinInfoLoaded(mSkinInfo);
+					}
+				}
 			}
 			else // otherwise is sculptie
 			{
@@ -1105,6 +1122,8 @@ void LLVOVolume::updateSculptTexture()
 		{
 			mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
 		}
+
+		mSkinInfo = NULL;
 	}
 	else
 	{
@@ -1155,6 +1174,22 @@ 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()
 {	
@@ -3535,14 +3570,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 nullptr;
 }
 
 // virtual
@@ -4714,8 +4747,13 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
 	}
 
 	LLVolume* volume = getVolume();
-	const LLMeshSkinInfo* skin = getSkinInfo();
-	if (!skin)
+	if (!volume)
+	{
+		clearRiggedVolume();
+		return;
+	}
+
+	if (!mSkinInfo)
 	{
 		clearRiggedVolume();
 		return;
@@ -4735,7 +4773,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 dd5c0dfeefb8084f735cc8d5ed4a970157891be1..d9f40de9449d2e62103aad3313aa5f0c9e3a12ed 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -335,6 +335,8 @@ class LLVOVolume : public LLViewerObject
     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;
@@ -413,6 +415,13 @@ class LLVOVolume : public LLViewerObject
 
 	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