diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index b46a99e03034160b3598e24427f3072f1f8fc692..39211bf7fa5158f1ccce04111f67a3d3159fd1c6 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -274,11 +274,11 @@ LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components)
 	++sRawImageCount;
 }
 
-LLImageRaw::LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only)
-	: LLImageBase()
-{
-	createFromFile(filename, j2c_lowest_mip_only);
-}
+//LLImageRaw::LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only)
+//	: LLImageBase()
+//{
+//	createFromFile(filename, j2c_lowest_mip_only);
+//}
 
 LLImageRaw::~LLImageRaw()
 {
@@ -1178,7 +1178,7 @@ file_extensions[] =
 	{ "png", IMG_CODEC_PNG }
 };
 #define NUM_FILE_EXTENSIONS LL_ARRAY_SIZE(file_extensions)
-
+#if 0
 static std::string find_file(std::string &name, S8 *codec)
 {
 	std::string tname;
@@ -1196,7 +1196,7 @@ static std::string find_file(std::string &name, S8 *codec)
 	}
 	return std::string("");
 }
-
+#endif
 EImageCodec LLImageBase::getCodecFromExtension(const std::string& exten)
 {
 	for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++)
@@ -1206,7 +1206,7 @@ EImageCodec LLImageBase::getCodecFromExtension(const std::string& exten)
 	}
 	return IMG_CODEC_INVALID;
 }
-
+#if 0
 bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip_only)
 {
 	std::string name = filename;
@@ -1313,7 +1313,7 @@ bool LLImageRaw::createFromFile(const std::string &filename, bool j2c_lowest_mip
 
 	return true;
 }
-
+#endif
 //---------------------------------------------------------------------------
 // LLImageFormatted
 //---------------------------------------------------------------------------
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index bca7e915fac8d9f469f6566b46540ba0fbf1f351..825b9aab1a49cefd54b092e05d869d00b15a32b7 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -164,7 +164,7 @@ class LLImageRaw : public LLImageBase
 	LLImageRaw(U16 width, U16 height, S8 components);
 	LLImageRaw(U8 *data, U16 width, U16 height, S8 components);
 	// Construct using createFromFile (used by tools)
-	LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false);
+	//LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false);
 
 	/*virtual*/ void deleteData();
 	/*virtual*/ U8* allocateData(S32 size = -1);
@@ -226,7 +226,7 @@ class LLImageRaw : public LLImageBase
 
 protected:
 	// Create an image from a local file (generally used in tools)
-	bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false);
+	//bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false);
 
 	void copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step );
 	void compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len );
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 14e1ca8d43681ddb7ea7057a89cdb19fa80b88ad..71b92962fb0fc93a20f8a51dcdcf47eead89d9d4 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -4406,19 +4406,54 @@ std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
 
 BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
 {
+	BOOL ret = FALSE ;
 	if (mTypeMask & CAP_MASK)
 	{
-		return createCap(volume, partial_build);
+		ret = createCap(volume, partial_build);
 	}
 	else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
 	{
-		return createSide(volume, partial_build);
+		ret = createSide(volume, partial_build);
 	}
 	else
 	{
 		llerrs << "Unknown/uninitialized face type!" << llendl;
-		return FALSE;
 	}
+
+	//update the range of the texture coordinates
+	if(ret)
+	{
+		mTexCoordExtents[0].setVec(1.f, 1.f) ;
+		mTexCoordExtents[1].setVec(0.f, 0.f) ;
+
+		U32 end = mVertices.size() ;
+		for(U32 i = 0 ; i < end ; i++)
+		{
+			if(mTexCoordExtents[0].mV[0] > mVertices[i].mTexCoord.mV[0])
+			{
+				mTexCoordExtents[0].mV[0] = mVertices[i].mTexCoord.mV[0] ;
+			}
+			if(mTexCoordExtents[1].mV[0] < mVertices[i].mTexCoord.mV[0])
+			{
+				mTexCoordExtents[1].mV[0] = mVertices[i].mTexCoord.mV[0] ;
+			}
+
+			if(mTexCoordExtents[0].mV[1] > mVertices[i].mTexCoord.mV[1])
+			{
+				mTexCoordExtents[0].mV[1] = mVertices[i].mTexCoord.mV[1] ;
+			}
+			if(mTexCoordExtents[1].mV[1] < mVertices[i].mTexCoord.mV[1])
+			{
+				mTexCoordExtents[1].mV[1] = mVertices[i].mTexCoord.mV[1] ;
+			}			
+		}
+		mTexCoordExtents[0].mV[0] = llmax(0.f, mTexCoordExtents[0].mV[0]) ;
+		mTexCoordExtents[0].mV[1] = llmax(0.f, mTexCoordExtents[0].mV[1]) ;
+		mTexCoordExtents[1].mV[0] = llmin(1.f, mTexCoordExtents[1].mV[0]) ;
+		mTexCoordExtents[1].mV[1] = llmin(1.f, mTexCoordExtents[1].mV[1]) ;
+	}
+
+	return ret ;
 }
 
 void	LerpPlanarVertex(LLVolumeFace::VertexData& v0,
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index d48a79ee46aa4206868fa69f83445dbf2aec69db..28b9895ff39ce3f3e347590ff7873a8512d779d5 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -831,6 +831,7 @@ class LLVolumeFace
 	S32 mNumT;
 
 	LLVector3 mExtents[2]; //minimum and maximum point of face
+	LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face.
 
 	std::vector<VertexData> mVertices;
 	std::vector<U16>	mIndices;
diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
index 3c1d031ff5d8b72d46d1233c63446b9c88a7cc23..861bde5c89285bceae0bb9e730a233ab353ae812 100644
--- a/indra/llprimitive/lltextureentry.cpp
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -403,7 +403,7 @@ S32 LLTextureEntry::setOffsetT(F32 t)
 
 S32 LLTextureEntry::setRotation(F32 theta)
 {
-	if (mRotation != theta)
+	if (mRotation != theta && llfinite(theta))
 	{
 		mRotation = theta;
 		return TEM_CHANGE_TEXTURE;
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 65940cb0671927e00db801963b8be315c4c73424..e8e98211f1c1db2e2380bfa848712946f5be6afc 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1063,16 +1063,6 @@ BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_
 {
 	if (gGL.getTexUnit(0)->bind(this, false, true))
 	{
-		if(gGLManager.mDebugGPU)
-		{
-			llinfos << "Calling glCopyTexSubImage2D(...)" << llendl ;
-			checkTexSize(true) ;
-			llcallstacks << fb_x << " : " << fb_y << " : " << x_pos << " : " << y_pos << " : " << width << " : " << height <<
-				" : " << (S32)mComponents << llcallstacksendl ;
-
-			log_glerror() ;
-		}
-
 		glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height);
 		mGLTextureCreated = true;
 		stop_glerror();
diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp
index 64556bcb4c34dcacd2b48f391dbfebf0eb528421..cb898e385f91b84a9d027eebce9923240526d92e 100644
--- a/indra/llvfs/lldir.cpp
+++ b/indra/llvfs/lldir.cpp
@@ -101,10 +101,18 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
 		{
 			if (0 != LLFile::remove(fullpath))
 			{
+				retry_count++;
 				result = errno;
 				llwarns << "Problem removing " << fullpath << " - errorcode: "
 						<< result << " attempt " << retry_count << llendl;
-				ms_sleep(1000);
+
+				if(retry_count >= 5)
+				{
+					llwarns << "Failed to remove " << fullpath << llendl ;
+					return count ;
+				}
+
+				ms_sleep(100);
 			}
 			else
 			{
@@ -113,8 +121,7 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
 					llwarns << "Successfully removed " << fullpath << llendl;
 				}
 				break;
-			}
-			retry_count++;
+			}			
 		}
 		count++;
 	}
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index a22f197b85bf885b16a33d950ea03adb1c2f9c27..fec7e9f1bd33a60a383a7f3d6133fd724609fd0c 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1918,10 +1918,21 @@
     <key>Value</key>
     <integer>0</integer>
   </map>
+  <key>DebugShowTextureInfo</key>
+    <map>
+      <key>Comment</key>
+      <string>Show inertested texture info</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
   <key>DebugShowTime</key>
     <map>
       <key>Comment</key>
-      <string>Show depth buffer contents</string>
+      <string>Show time info</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
diff --git a/indra/newview/llcapabilityprovider.h b/indra/newview/llcapabilityprovider.h
index a6e743f625db2ca3e58b66c006247eb41deacfd0..9d912455977289d779a4a2ba27cbe26a478a2e02 100644
--- a/indra/newview/llcapabilityprovider.h
+++ b/indra/newview/llcapabilityprovider.h
@@ -46,7 +46,7 @@ class LLCapabilityProvider
     /**
      * Get host to which to send that capability request.
      */
-    virtual LLHost getHost() const = 0;
+    virtual const LLHost& getHost() const = 0;
     /**
      * Describe this LLCapabilityProvider for logging etc.
      */
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index a3d2941114f39d4124e0cc9b550202676debb977..f781d5f3ff96274388f40bc2a12bfcebc53ba944 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -177,10 +177,6 @@ void LLViewerDynamicTexture::postRender(BOOL success)
 				generateGLTexture() ;
 			}
 
-			if(gGLManager.mDebugGPU)
-			{
-				LLGLState::dumpStates() ;
-			}
 			success = mGLTexturep->setSubImageFromFrameBuffer(0, 0, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight);
 		}
 	}
@@ -220,12 +216,6 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 			LLViewerDynamicTexture *dynamicTexture = *iter;
 			if (dynamicTexture->needsRender())
 			{				
-				if(gGLManager.mDebugGPU)
-				{				
-					llinfos << "class type: " << (S32)dynamicTexture->getType() << llendl;
-					LLGLState::dumpStates() ;
-				}
-
 				glClear(GL_DEPTH_BUFFER_BIT);
 				gDepthDirty = TRUE;
 								
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 2471da9da57e83230abce89622d4a481b544a096..fe201a6773f754bdebe2af2469fe0a397fd47423 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -1155,7 +1155,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		if (rebuild_tcoord)
 		{
 			LLVector2 tc = vf.mVertices[i].mTexCoord;
-		
+		   
 			if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
 			{
 				LLVector3 vec = vf.mVertices[i].mPosition; 
@@ -1331,7 +1331,14 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		mTexExtents[0].setVec(0,0);
 		mTexExtents[1].setVec(1,1);
 		xform(mTexExtents[0], cos_ang, sin_ang, os, ot, ms, mt);
-		xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt);		
+		xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt);
+		
+		F32 es = vf.mTexCoordExtents[1].mV[0] - vf.mTexCoordExtents[0].mV[0] ;
+		F32 et = vf.mTexCoordExtents[1].mV[1] - vf.mTexCoordExtents[0].mV[1] ;
+		mTexExtents[0][0] *= es ;
+		mTexExtents[1][0] *= es ;
+		mTexExtents[0][1] *= et ;
+		mTexExtents[1][1] *= et ;
 	}
 
 	mLastVertexBuffer = mVertexBuffer;
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
index 7657cccd4ea3b5f6602d15d6e98f6fd9e8d28ed3..6cfb708112ab10ab270e7eaa90ac6672e8af4427 100644
--- a/indra/newview/llpreviewtexture.cpp
+++ b/indra/newview/llpreviewtexture.cpp
@@ -273,6 +273,8 @@ void LLPreviewTexture::saveAs()
 	mSaveFileName = file_picker.getFirstFile();
 	mLoadingFullImage = TRUE;
 	getWindow()->incBusyCount();
+
+	mImage->forceToSaveRawImage(0) ;//re-fetch the raw image if the old one is removed.
 	mImage->setLoadedCallback( LLPreviewTexture::onFileLoadedForSave, 
 								0, TRUE, FALSE, new LLUUID( mItemUUID ), &mCallbackTextureList );
 }
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
index 92080d1fd75757d37706711dd6153628aabc8951..dfec3238d8ce81da8e211febe8f2ba34da4c94b0 100644
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -326,6 +326,7 @@ bool LLTextureCacheRemoteWorker::doRead()
 	// First state / stage : find out if the file is local
 	if (mState == INIT)
 	{
+#if 0
 		std::string filename = mCache->getLocalFileName(mID);	
 		// Is it a JPEG2000 file? 
 		{
@@ -360,6 +361,11 @@ bool LLTextureCacheRemoteWorker::doRead()
 		}
 		// Determine the next stage: if we found a file, then LOCAL else CACHE
 		mState = (local_size > 0 ? LOCAL : CACHE);
+
+		llassert_always(mState == CACHE) ;
+#else
+		mState = CACHE;
+#endif
 	}
 
 	// Second state / stage : if the file is local, load it and leave
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 4f63abb152dc59faaca838d5938d28e056034f81..5cdf1706e652cf057214653d1d730a7860c8d8df 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -1,3000 +1,3011 @@
-/** 
- * @file lltexturefetch.cpp
- * @brief Object which fetches textures from the cache and/or network
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include <iostream>
-#include <map>
-
-#include "llstl.h"
-
-#include "lltexturefetch.h"
-
-#include "llcurl.h"
-#include "lldir.h"
-#include "llhttpclient.h"
-#include "llhttpstatuscodes.h"
-#include "llimage.h"
-#include "llimagej2c.h"
-#include "llimageworker.h"
-#include "llworkerthread.h"
-#include "message.h"
-
-#include "llagent.h"
-#include "lltexturecache.h"
-#include "llviewercontrol.h"
-#include "llviewertexturelist.h"
-#include "llviewertexture.h"
-#include "llviewerregion.h"
-#include "llviewerstats.h"
-#include "llviewerassetstats.h"
-#include "llworld.h"
-
-//////////////////////////////////////////////////////////////////////////////
-class LLTextureFetchWorker : public LLWorkerClass
-{
-	friend class LLTextureFetch;
-	friend class HTTPGetResponder;
-	
-private:
-	class CacheReadResponder : public LLTextureCache::ReadResponder
-	{
-	public:
-		CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image)
-			: mFetcher(fetcher), mID(id)
-		{
-			setImage(image);
-		}
-		virtual void completed(bool success)
-		{
-			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
-			if (worker)
-			{
- 				worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal);
-			}
-		}
-	private:
-		LLTextureFetch* mFetcher;
-		LLUUID mID;
-	};
-
-	class CacheWriteResponder : public LLTextureCache::WriteResponder
-	{
-	public:
-		CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id)
-			: mFetcher(fetcher), mID(id)
-		{
-		}
-		virtual void completed(bool success)
-		{
-			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
-			if (worker)
-			{
-				worker->callbackCacheWrite(success);
-			}
-		}
-	private:
-		LLTextureFetch* mFetcher;
-		LLUUID mID;
-	};
-	
-	class DecodeResponder : public LLImageDecodeThread::Responder
-	{
-	public:
-		DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker)
-			: mFetcher(fetcher), mID(id), mWorker(worker)
-		{
-		}
-		virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
-		{
-			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
-			if (worker)
-			{
- 				worker->callbackDecoded(success, raw, aux);
-			}
-		}
-	private:
-		LLTextureFetch* mFetcher;
-		LLUUID mID;
-		LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID)
-	};
-
-	struct Compare
-	{
-		// lhs < rhs
-		bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const
-		{
-			// greater priority is "less"
-			const F32 lpriority = lhs->mImagePriority;
-			const F32 rpriority = rhs->mImagePriority;
-			if (lpriority > rpriority) // higher priority
-				return true;
-			else if (lpriority < rpriority)
-				return false;
-			else
-				return lhs < rhs;
-		}
-	};
-	
-public:
-	/*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
-	/*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
-	/*virtual*/ bool deleteOK(); // called from update() (WORK THREAD)
-
-	~LLTextureFetchWorker();
-	// void relese() { --mActiveCount; }
-
-	S32 callbackHttpGet(const LLChannelDescriptors& channels,
-						 const LLIOPipe::buffer_ptr_t& buffer,
-						 bool partial, bool success);
-	void callbackCacheRead(bool success, LLImageFormatted* image,
-						   S32 imagesize, BOOL islocal);
-	void callbackCacheWrite(bool success);
-	void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux);
-	
-	void setGetStatus(U32 status, const std::string& reason)
-	{
-		LLMutexLock lock(&mWorkMutex);
-
-		mGetStatus = status;
-		mGetReason = reason;
-	}
-
-	void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; }
-	bool getCanUseHTTP() const { return mCanUseHTTP; }
-
-	LLTextureFetch & getFetcher() { return *mFetcher; }
-	
-protected:
-	LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
-						 F32 priority, S32 discard, S32 size);
-
-private:
-	/*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD)
-	/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
-
-	void resetFormattedData();
-	
-	void setImagePriority(F32 priority);
-	void setDesiredDiscard(S32 discard, S32 size);
-	bool insertPacket(S32 index, U8* data, S32 size);
-	void clearPackets();
-	void setupPacketData();
-	U32 calcWorkPriority();
-	void removeFromCache();
-	bool processSimulatorPackets();
-	bool writeToCacheComplete();
-	
-	void lockWorkMutex() { mWorkMutex.lock(); }
-	void unlockWorkMutex() { mWorkMutex.unlock(); }
-
-private:
-	enum e_state // mState
-	{
-		// NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
-		INVALID = 0,
-		INIT,
-		LOAD_FROM_TEXTURE_CACHE,
-		CACHE_POST,
-		LOAD_FROM_NETWORK,
-		LOAD_FROM_SIMULATOR,
-		SEND_HTTP_REQ,
-		WAIT_HTTP_REQ,
-		DECODE_IMAGE,
-		DECODE_IMAGE_UPDATE,
-		WRITE_TO_CACHE,
-		WAIT_ON_WRITE,
-		DONE
-	};
-	enum e_request_state // mSentRequest
-	{
-		UNSENT = 0,
-		QUEUED = 1,
-		SENT_SIM = 2
-	};
-	enum e_write_to_cache_state //mWriteToCacheState
-	{
-		NOT_WRITE = 0,
-		CAN_WRITE = 1,
-		SHOULD_WRITE = 2
-	};
-	static const char* sStateDescs[];
-	e_state mState;
-	e_write_to_cache_state mWriteToCacheState;
-	LLTextureFetch* mFetcher;
-	LLPointer<LLImageFormatted> mFormattedImage;
-	LLPointer<LLImageRaw> mRawImage;
-	LLPointer<LLImageRaw> mAuxImage;
-	LLUUID mID;
-	LLHost mHost;
-	std::string mUrl;
-	U8 mType;
-	F32 mImagePriority;
-	U32 mWorkPriority;
-	F32 mRequestedPriority;
-	S32 mDesiredDiscard;
-	S32 mSimRequestedDiscard;
-	S32 mRequestedDiscard;
-	S32 mLoadedDiscard;
-	S32 mDecodedDiscard;
-	LLFrameTimer mRequestedTimer;
-	LLFrameTimer mFetchTimer;
-	LLTextureCache::handle_t mCacheReadHandle;
-	LLTextureCache::handle_t mCacheWriteHandle;
-	U8* mBuffer;
-	S32 mBufferSize;
-	S32 mRequestedSize;
-	S32 mDesiredSize;
-	S32 mFileSize;
-	S32 mCachedSize;	
-	e_request_state mSentRequest;
-	handle_t mDecodeHandle;
-	BOOL mLoaded;
-	BOOL mDecoded;
-	BOOL mWritten;
-	BOOL mNeedsAux;
-	BOOL mHaveAllData;
-	BOOL mInLocalCache;
-	bool mCanUseHTTP ;
-	bool mCanUseNET ; //can get from asset server.
-	S32 mHTTPFailCount;
-	S32 mRetryAttempt;
-	S32 mActiveCount;
-	U32 mGetStatus;
-	std::string mGetReason;
-	
-	// Work Data
-	LLMutex mWorkMutex;
-	struct PacketData
-	{
-		PacketData(U8* data, S32 size) { mData = data; mSize = size; }
-		~PacketData() { clearData(); }
-		void clearData() { delete[] mData; mData = NULL; }
-		U8* mData;
-		U32 mSize;
-	};
-	std::vector<PacketData*> mPackets;
-	S32 mFirstPacket;
-	S32 mLastPacket;
-	U16 mTotalPackets;
-	U8 mImageCodec;
-
-	LLViewerAssetStats::duration_t mMetricsStartTime;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-class HTTPGetResponder : public LLCurl::Responder
-{
-	LOG_CLASS(HTTPGetResponder);
-public:
-	HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir)
-		: mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir)
-	{
-	}
-	~HTTPGetResponder()
-	{
-	}
-
-	virtual void completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer)
-	{
-		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
-		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
-		static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
-
-		if (log_to_viewer_log || log_to_sim)
-		{
-			mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime);
-			U64 timeNow = LLTimer::getTotalTime();
-			mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
-			mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
-			mFetcher->mTextureInfo.setRequestOffset(mID, mOffset);
-			mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
-		}
-
-		lldebugs << "HTTP COMPLETE: " << mID << llendl;
-		LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
-		if (worker)
-		{
-			bool success = false;
-			bool partial = false;
-			if (HTTP_OK <= status &&  status < HTTP_MULTIPLE_CHOICES)
-			{
-				success = true;
-				if (HTTP_PARTIAL_CONTENT == status) // partial information
-				{
-					partial = true;
-				}
-			}
-
-			if (!success)
-			{
-				worker->setGetStatus(status, reason);
-// 				llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl;
-			}
-			
-			S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success);
-			
-			if(log_texture_traffic && data_size > 0)
-			{
-				LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ;
-				if(tex)
-				{
-					gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
-				}
-			}
-
-			mFetcher->removeFromHTTPQueue(mID, data_size);
-
-			if (worker->mMetricsStartTime)
-			{
-				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
-															  true,
-															  LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
-															  LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
-				worker->mMetricsStartTime = 0;
-			}
-			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
-														 true,
-														 LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
-		}
-		else
-		{
-			mFetcher->removeFromHTTPQueue(mID);
- 			llwarns << "Worker not found: " << mID << llendl;
-		}
-	}
-
-	virtual bool followRedir()
-	{
-		return mFollowRedir;
-	}
-	
-private:
-	LLTextureFetch* mFetcher;
-	LLUUID mID;
-	U64 mStartTime;
-	S32 mRequestedSize;
-	U32 mOffset;
-	bool mFollowRedir;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-// Cross-thread messaging for asset metrics.
-
-/**
- * @brief Base class for cross-thread requests made of the fetcher
- *
- * I believe the intent of the LLQueuedThread class was to
- * have these operations derived from LLQueuedThread::QueuedRequest
- * but the texture fetcher has elected to manage the queue
- * in its own manner.  So these are free-standing objects which are
- * managed in simple FIFO order on the mCommands queue of the
- * LLTextureFetch object.
- *
- * What each represents is a simple command sent from an
- * outside thread into the TextureFetch thread to be processed
- * in order and in a timely fashion (though not an absolute
- * higher priority than other operations of the thread).
- * Each operation derives a new class from the base customizing
- * members, constructors and the doWork() method to effect
- * the command.
- *
- * The flow is one-directional.  There are two global instances
- * of the LLViewerAssetStats collector, one for the main program's
- * thread pointed to by gViewerAssetStatsMain and one for the
- * TextureFetch thread pointed to by gViewerAssetStatsThread1.
- * Common operations has each thread recording metrics events
- * into the respective collector unconcerned with locking and
- * the state of any other thread.  But when the agent moves into
- * a different region or the metrics timer expires and a report
- * needs to be sent back to the grid, messaging across threads
- * is required to distribute data and perform global actions.
- * In pseudo-UML, it looks like:
- *
- *                       Main                 Thread1
- *                        .                      .
- *                        .                      .
- *                     +-----+                   .
- *                     | AM  |                   .
- *                     +--+--+                   .
- *      +-------+         |                      .
- *      | Main  |      +--+--+                   .
- *      |       |      | SRE |---.               .
- *      | Stats |      +-----+    \              .
- *      |       |         |        \  (uuid)  +-----+
- *      | Coll. |      +--+--+      `-------->| SR  |
- *      +-------+      | MSC |                +--+--+
- *         | ^         +-----+                   |
- *         | |  (uuid)  / .                   +-----+ (uuid)
- *         |  `--------'  .                   | MSC |---------.
- *         |              .                   +-----+         |
- *         |           +-----+                   .            v
- *         |           | TE  |                   .        +-------+
- *         |           +--+--+                   .        | Thd1  |
- *         |              |                      .        |       |
- *         |           +-----+                   .        | Stats |
- *          `--------->| RSC |                   .        |       |
- *                     +--+--+                   .        | Coll. |
- *                        |                      .        +-------+
- *                     +--+--+                   .            |
- *                     | SME |---.               .            |
- *                     +-----+    \              .            |
- *                        .        \ (clone)  +-----+         |
- *                        .         `-------->| SM  |         |
- *                        .                   +--+--+         |
- *                        .                      |            |
- *                        .                   +-----+         |
- *                        .                   | RSC |<--------'
- *                        .                   +-----+
- *                        .                      |
- *                        .                   +-----+
- *                        .                   | CP  |--> HTTP POST
- *                        .                   +-----+
- *                        .                      .
- *                        .                      .
- *
- *
- * Key:
- *
- * SRE - Set Region Enqueued.  Enqueue a 'Set Region' command in
- *       the other thread providing the new UUID of the region.
- *       TFReqSetRegion carries the data.
- * SR  - Set Region.  New region UUID is sent to the thread-local
- *       collector.
- * SME - Send Metrics Enqueued.  Enqueue a 'Send Metrics' command
- *       including an ownership transfer of a cloned LLViewerAssetStats.
- *       TFReqSendMetrics carries the data.
- * SM  - Send Metrics.  Global metrics reporting operation.  Takes
- *       the cloned stats from the command, merges it with the
- *       thread's local stats, converts to LLSD and sends it on
- *       to the grid.
- * AM  - Agent Moved.  Agent has completed some sort of move to a
- *       new region.
- * TE  - Timer Expired.  Metrics timer has expired (on the order
- *       of 10 minutes).
- * CP  - CURL Post
- * MSC - Modify Stats Collector.  State change in the thread-local
- *       collector.  Typically a region change which affects the
- *       global pointers used to find the 'current stats'.
- * RSC - Read Stats Collector.  Extract collector data cloning it
- *       (i.e. deep copy) when necessary.
- *
- */
-class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
-{
-public:
-	// Default ctors and assignment operator are correct.
-
-	virtual ~TFRequest()
-		{}
-
-	// Patterned after QueuedRequest's method but expected behavior
-	// is different.  Always expected to complete on the first call
-	// and work dispatcher will assume the same and delete the
-	// request after invocation.
-	virtual bool doWork(LLTextureFetch * fetcher) = 0;
-};
-
-namespace 
-{
-
-/**
- * @brief Implements a 'Set Region' cross-thread command.
- *
- * When an agent moves to a new region, subsequent metrics need
- * to be binned into a new or existing stats collection in 1:1
- * relationship with the region.  We communicate this region
- * change across the threads involved in the communication with
- * this message.
- *
- * Corresponds to LLTextureFetch::commandSetRegion()
- */
-class TFReqSetRegion : public LLTextureFetch::TFRequest
-{
-public:
-	TFReqSetRegion(U64 region_handle)
-		: LLTextureFetch::TFRequest(),
-		  mRegionHandle(region_handle)
-		{}
-	TFReqSetRegion & operator=(const TFReqSetRegion &);	// Not defined
-
-	virtual ~TFReqSetRegion()
-		{}
-
-	virtual bool doWork(LLTextureFetch * fetcher);
-		
-public:
-	const U64 mRegionHandle;
-};
-
-
-/**
- * @brief Implements a 'Send Metrics' cross-thread command.
- *
- * This is the big operation.  The main thread gathers metrics
- * for a period of minutes into LLViewerAssetStats and other
- * objects then makes a snapshot of the data by cloning the
- * collector.  This command transfers the clone, along with a few
- * additional arguments (UUIDs), handing ownership to the
- * TextureFetch thread.  It then merges its own data into the
- * cloned copy, converts to LLSD and kicks off an HTTP POST of
- * the resulting data to the currently active metrics collector.
- *
- * Corresponds to LLTextureFetch::commandSendMetrics()
- */
-class TFReqSendMetrics : public LLTextureFetch::TFRequest
-{
-public:
-    /**
-	 * Construct the 'Send Metrics' command to have the TextureFetch
-	 * thread add and log metrics data.
-	 *
-	 * @param	caps_url		URL of a "ViewerMetrics" Caps target
-	 *							to receive the data.  Does not have to
-	 *							be associated with a particular region.
-	 *
-	 * @param	session_id		UUID of the agent's session.
-	 *
-	 * @param	agent_id		UUID of the agent.  (Being pure here...)
-	 *
-	 * @param	main_stats		Pointer to a clone of the main thread's
-	 *							LLViewerAssetStats data.  Thread1 takes
-	 *							ownership of the copy and disposes of it
-	 *							when done.
-	 */
-	TFReqSendMetrics(const std::string & caps_url,
-					 const LLUUID & session_id,
-					 const LLUUID & agent_id,
-					 LLViewerAssetStats * main_stats)
-		: LLTextureFetch::TFRequest(),
-		  mCapsURL(caps_url),
-		  mSessionID(session_id),
-		  mAgentID(agent_id),
-		  mMainStats(main_stats)
-		{}
-	TFReqSendMetrics & operator=(const TFReqSendMetrics &);	// Not defined
-
-	virtual ~TFReqSendMetrics();
-
-	virtual bool doWork(LLTextureFetch * fetcher);
-		
-public:
-	const std::string mCapsURL;
-	const LLUUID mSessionID;
-	const LLUUID mAgentID;
-	LLViewerAssetStats * mMainStats;
-};
-
-/*
- * Examines the merged viewer metrics report and if found to be too long,
- * will attempt to truncate it in some reasonable fashion.
- *
- * @param		max_regions		Limit of regions allowed in report.
- *
- * @param		metrics			Full, merged viewer metrics report.
- *
- * @returns		If data was truncated, returns true.
- */
-bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
-
-} // end of anonymous namespace
-
-
-//////////////////////////////////////////////////////////////////////////////
-
-//static
-const char* LLTextureFetchWorker::sStateDescs[] = {
-	"INVALID",
-	"INIT",
-	"LOAD_FROM_TEXTURE_CACHE",
-	"CACHE_POST",
-	"LOAD_FROM_NETWORK",
-	"LOAD_FROM_SIMULATOR",
-	"SEND_HTTP_REQ",
-	"WAIT_HTTP_REQ",
-	"DECODE_IMAGE",
-	"DECODE_IMAGE_UPDATE",
-	"WRITE_TO_CACHE",
-	"WAIT_ON_WRITE",
-	"DONE",
-};
-
-// static
-volatile bool LLTextureFetch::svMetricsDataBreak(true);	// Start with a data break
-
-// called from MAIN THREAD
-
-LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
-										   const std::string& url, // Optional URL
-										   const LLUUID& id,	// Image UUID
-										   const LLHost& host,	// Simulator host
-										   F32 priority,		// Priority
-										   S32 discard,			// Desired discard
-										   S32 size)			// Desired size
-	: LLWorkerClass(fetcher, "TextureFetch"),
-	  mState(INIT),
-	  mWriteToCacheState(NOT_WRITE),
-	  mFetcher(fetcher),
-	  mID(id),
-	  mHost(host),
-	  mUrl(url),
-	  mImagePriority(priority),
-	  mWorkPriority(0),
-	  mRequestedPriority(0.f),
-	  mDesiredDiscard(-1),
-	  mSimRequestedDiscard(-1),
-	  mRequestedDiscard(-1),
-	  mLoadedDiscard(-1),
-	  mDecodedDiscard(-1),
-	  mCacheReadHandle(LLTextureCache::nullHandle()),
-	  mCacheWriteHandle(LLTextureCache::nullHandle()),
-	  mBuffer(NULL),
-	  mBufferSize(0),
-	  mRequestedSize(0),
-	  mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
-	  mFileSize(0),
-	  mCachedSize(0),
-	  mLoaded(FALSE),
-	  mSentRequest(UNSENT),
-	  mDecodeHandle(0),
-	  mDecoded(FALSE),
-	  mWritten(FALSE),
-	  mNeedsAux(FALSE),
-	  mHaveAllData(FALSE),
-	  mInLocalCache(FALSE),
-	  mCanUseHTTP(true),
-	  mHTTPFailCount(0),
-	  mRetryAttempt(0),
-	  mActiveCount(0),
-	  mGetStatus(0),
-	  mWorkMutex(NULL),
-	  mFirstPacket(0),
-	  mLastPacket(-1),
-	  mTotalPackets(0),
-	  mImageCodec(IMG_CODEC_INVALID),
-	  mMetricsStartTime(0)
-{
-	mCanUseNET = mUrl.empty() ;
-
-	calcWorkPriority();
-	mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL;
-// 	llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl;
-	if (!mFetcher->mDebugPause)
-	{
-		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
-		addWork(0, work_priority );
-	}
-	setDesiredDiscard(discard, size);
-}
-
-LLTextureFetchWorker::~LLTextureFetchWorker()
-{
-// 	llinfos << "Destroy: " << mID
-// 			<< " Decoded=" << mDecodedDiscard
-// 			<< " Requested=" << mRequestedDiscard
-// 			<< " Desired=" << mDesiredDiscard << llendl;
-	llassert_always(!haveWork());
-	lockWorkMutex();
-	if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
-	{
-		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
-	}
-	if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
-	{
-		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
-	}
-	mFormattedImage = NULL;
-	clearPackets();
-	unlockWorkMutex();
-	mFetcher->removeFromHTTPQueue(mID);
-}
-
-void LLTextureFetchWorker::clearPackets()
-{
-	for_each(mPackets.begin(), mPackets.end(), DeletePointer());
-	mPackets.clear();
-	mTotalPackets = 0;
-	mLastPacket = -1;
-	mFirstPacket = 0;
-}
-
-void LLTextureFetchWorker::setupPacketData()
-{
-	S32 data_size = 0;
-	if (mFormattedImage.notNull())
-	{
-		data_size = mFormattedImage->getDataSize();
-	}
-	if (data_size > 0)
-	{
-		// Only used for simulator requests
-		mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
-		if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
-		{
-			llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
-			removeFromCache();
-			resetFormattedData();
-			clearPackets();
-		}
-		else if (mFileSize > 0)
-		{
-			mLastPacket = mFirstPacket-1;
-			mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
-		}
-		else
-		{
-			// This file was cached using HTTP so we have to refetch the first packet
-			resetFormattedData();
-			clearPackets();
-		}
-	}
-}
-
-U32 LLTextureFetchWorker::calcWorkPriority()
-{
- 	//llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority());
-	static const F32 PRIORITY_SCALE = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerFetchedTexture::maxDecodePriority();
-
-	mWorkPriority = llmin((U32)LLWorkerThread::PRIORITY_LOWBITS, (U32)(mImagePriority * PRIORITY_SCALE));
-	return mWorkPriority;
-}
-
-// mWorkMutex is locked
-void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
-{
-	bool prioritize = false;
-	if (mDesiredDiscard != discard)
-	{
-		if (!haveWork())
-		{
-			calcWorkPriority();
-			if (!mFetcher->mDebugPause)
-			{
-				U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
-				addWork(0, work_priority);
-			}
-		}
-		else if (mDesiredDiscard < discard)
-		{
-			prioritize = true;
-		}
-		mDesiredDiscard = discard;
-		mDesiredSize = size;
-	}
-	else if (size > mDesiredSize)
-	{
-		mDesiredSize = size;
-		prioritize = true;
-	}
-	mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE);
-	if ((prioritize && mState == INIT) || mState == DONE)
-	{
-		mState = INIT;
-		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
-		setPriority(work_priority);
-	}
-}
-
-void LLTextureFetchWorker::setImagePriority(F32 priority)
-{
-// 	llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority());
-	F32 delta = fabs(priority - mImagePriority);
-	if (delta > (mImagePriority * .05f) || mState == DONE)
-	{
-		mImagePriority = priority;
-		calcWorkPriority();
-		U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS);
-		setPriority(work_priority);
-	}
-}
-
-void LLTextureFetchWorker::resetFormattedData()
-{
-	delete[] mBuffer;
-	mBuffer = NULL;
-	mBufferSize = 0;
-	if (mFormattedImage.notNull())
-	{
-		mFormattedImage->deleteData();
-	}
-	mHaveAllData = FALSE;
-}
-
-// Called from MAIN thread
-void LLTextureFetchWorker::startWork(S32 param)
-{
-	llassert(mFormattedImage.isNull());
-}
-
-#include "llviewertexturelist.h" // debug
-
-// Called from LLWorkerThread::processRequest()
-bool LLTextureFetchWorker::doWork(S32 param)
-{
-	LLMutexLock lock(&mWorkMutex);
-
-	if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
-	{
-		if (mState < DECODE_IMAGE)
-		{
-			return true; // abort
-		}
-	}
-
-	if(mImagePriority < F_ALMOST_ZERO)
-	{
-		if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR)
-		{
-			return true; // abort
-		}
-	}
-	if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP)
-	{
-		//nowhere to get data, abort.
-		return true ;
-	}
-
-	if (mFetcher->mDebugPause)
-	{
-		return false; // debug: don't do any work
-	}
-	if (mID == mFetcher->mDebugID)
-	{
-		mFetcher->mDebugCount++; // for setting breakpoints
-	}
-
-	if (mState != DONE)
-	{
-		mFetchTimer.reset();
-	}
-
-	if (mState == INIT)
-	{		
-		mRawImage = NULL ;
-		mRequestedDiscard = -1;
-		mLoadedDiscard = -1;
-		mDecodedDiscard = -1;
-		mRequestedSize = 0;
-		mFileSize = 0;
-		mCachedSize = 0;
-		mLoaded = FALSE;
-		mSentRequest = UNSENT;
-		mDecoded  = FALSE;
-		mWritten  = FALSE;
-		delete[] mBuffer;
-		mBuffer = NULL;
-		mBufferSize = 0;
-		mHaveAllData = FALSE;
-		clearPackets(); // TODO: Shouldn't be necessary
-		mCacheReadHandle = LLTextureCache::nullHandle();
-		mCacheWriteHandle = LLTextureCache::nullHandle();
-		mState = LOAD_FROM_TEXTURE_CACHE;
-		mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
-		LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
-							 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
-		// fall through
-	}
-
-	if (mState == LOAD_FROM_TEXTURE_CACHE)
-	{
-		if (mCacheReadHandle == LLTextureCache::nullHandle())
-		{
-			U32 cache_priority = mWorkPriority;
-			S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
-			S32 size = mDesiredSize - offset;
-			if (size <= 0)
-			{
-				mState = CACHE_POST;
-				return false;
-			}
-			mFileSize = 0;
-			mLoaded = FALSE;			
-			
-			if (mUrl.compare(0, 7, "file://") == 0)
-			{
-				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-
-				// read file from local disk
-				std::string filename = mUrl.substr(7, std::string::npos);
-				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
-				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
-																		  offset, size, responder);
-			}
-			else if (mUrl.empty())
-			{
-				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-
-				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
-				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
-																		  offset, size, responder);
-			}
-			else if(mCanUseHTTP)
-			{
-				if (!(mUrl.compare(0, 7, "http://") == 0))
-				{
-					// *TODO:?remove this warning
-					llwarns << "Unknown URL Type: " << mUrl << llendl;
-				}
-				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-				mState = SEND_HTTP_REQ;
-			}
-			else
-			{
-				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-				mState = LOAD_FROM_NETWORK;
-			}
-		}
-
-		if (mLoaded)
-		{
-			// Make sure request is complete. *TODO: make this auto-complete
-			if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false))
-			{
-				mCacheReadHandle = LLTextureCache::nullHandle();
-				mState = CACHE_POST;
-				// fall through
-			}
-			else
-			{
-				return false;
-			}
-		}
-		else
-		{
-			return false;
-		}
-	}
-
-	if (mState == CACHE_POST)
-	{
-		mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
-		// Successfully loaded
-		if ((mCachedSize >= mDesiredSize) || mHaveAllData)
-		{
-			// we have enough data, decode it
-			llassert_always(mFormattedImage->getDataSize() > 0);
-			mLoadedDiscard = mDesiredDiscard;
-			mState = DECODE_IMAGE;
-			mWriteToCacheState = NOT_WRITE ;
-			LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
-								 << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
-								 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
-			// fall through
-		}
-		else
-		{
-			if (mUrl.compare(0, 7, "file://") == 0)
-			{
-				// failed to load local file, we're done.
-				return true;
-			}
-			// need more data
-			else
-			{
-				LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
-				mState = LOAD_FROM_NETWORK;
-			}
-			// fall through
-		}
-	}
-
-	if (mState == LOAD_FROM_NETWORK)
-	{
-		static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP");
-
-// 		if (mHost != LLHost::invalid) get_url = false;
-		if ( use_http && mCanUseHTTP && mUrl.empty())//get http url.
-		{
-			LLViewerRegion* region = NULL;
-			if (mHost == LLHost::invalid)
-				region = gAgent.getRegion();
-			else
-				region = LLWorld::getInstance()->getRegion(mHost);
-
-			if (region)
-			{
-				std::string http_url = region->getHttpUrl() ;
-				if (!http_url.empty())
-				{
-					mUrl = http_url + "/?texture_id=" + mID.asString().c_str();
-					mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id.
-				}
-				else
-				{
-					mCanUseHTTP = false ;
-				}
-			}
-			else
-			{
-				// This will happen if not logged in or if a region deoes not have HTTP Texture enabled
-				//llwarns << "Region not found for host: " << mHost << llendl;
-				mCanUseHTTP = false;
-			}
-		}
-		if (mCanUseHTTP && !mUrl.empty())
-		{
-			mState = LLTextureFetchWorker::SEND_HTTP_REQ;
-			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-			if(mWriteToCacheState != NOT_WRITE)
-			{
-				mWriteToCacheState = CAN_WRITE ;
-			}
-			// don't return, fall through to next state
-		}
-		else if (mSentRequest == UNSENT && mCanUseNET)
-		{
-			// Add this to the network queue and sit here.
-			// LLTextureFetch::update() will send off a request which will change our state
-			mWriteToCacheState = CAN_WRITE ;
-			mRequestedSize = mDesiredSize;
-			mRequestedDiscard = mDesiredDiscard;
-			mSentRequest = QUEUED;
-			mFetcher->addToNetworkQueue(this);
-			if (! mMetricsStartTime)
-			{
-				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
-			}
-			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
-														 false,
-														 LLImageBase::TYPE_AVATAR_BAKE == mType);
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			
-			return false;
-		}
-		else
-		{
-			// Shouldn't need to do anything here
-			//llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
-			// Make certain this is in the network queue
-			//mFetcher->addToNetworkQueue(this);
-			//if (! mMetricsStartTime)
-			//{
-			//   mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
-			//}
-			//LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
-			//                                             LLImageBase::TYPE_AVATAR_BAKE == mType);
-			//setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			return false;
-		}
-	}
-	
-	if (mState == LOAD_FROM_SIMULATOR)
-	{
-		if (mFormattedImage.isNull())
-		{
-			mFormattedImage = new LLImageJ2C;
-		}
-		if (processSimulatorPackets())
-		{
-			LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL;
-			mFetcher->removeFromNetworkQueue(this, false);
-			if (mFormattedImage.isNull() || !mFormattedImage->getDataSize())
-			{
-				// processSimulatorPackets() failed
-// 				llwarns << "processSimulatorPackets() failed to load buffer" << llendl;
-				return true; // failed
-			}
-			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-			mState = DECODE_IMAGE;
-			mWriteToCacheState = SHOULD_WRITE;
-
-			if (mMetricsStartTime)
-			{
-				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
-															  false,
-															  LLImageBase::TYPE_AVATAR_BAKE == mType,
-															  LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
-				mMetricsStartTime = 0;
-			}
-			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
-														 false,
-														 LLImageBase::TYPE_AVATAR_BAKE == mType);
-		}
-		else
-		{
-			mFetcher->addToNetworkQueue(this); // failsafe
-			if (! mMetricsStartTime)
-			{
-				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
-			}
-			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
-														 false,
-														 LLImageBase::TYPE_AVATAR_BAKE == mType);
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-		}
-		return false;
-	}
-	
-	if (mState == SEND_HTTP_REQ)
-	{
-		if(mCanUseHTTP)
-		{
-			//NOTE:
-			//control the number of the http requests issued for:
-			//1, not openning too many file descriptors at the same time;
-			//2, control the traffic of http so udp gets bandwidth.
-			//
-			static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 32 ;
-			if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE)
-			{
-				return false ; //wait.
-			}
-
-			mFetcher->removeFromNetworkQueue(this, false);
-			
-			S32 cur_size = 0;
-			if (mFormattedImage.notNull())
-			{
-				cur_size = mFormattedImage->getDataSize(); // amount of data we already have
-				if (mFormattedImage->getDiscardLevel() == 0)
-				{
-					if(cur_size > 0)
-					{
-						// We already have all the data, just decode it
-						mLoadedDiscard = mFormattedImage->getDiscardLevel();
-						mState = DECODE_IMAGE;
-						return false;
-					}
-					else
-					{
-						return true ; //abort.
-					}
-				}
-			}
-			mRequestedSize = mDesiredSize;
-			mRequestedDiscard = mDesiredDiscard;
-			mRequestedSize -= cur_size;
-			S32 offset = cur_size;
-			mBufferSize = cur_size; // This will get modified by callbackHttpGet()
-			
-			bool res = false;
-			if (!mUrl.empty())
-			{
-				mLoaded = FALSE;
-				mGetStatus = 0;
-				mGetReason.clear();
-				LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset
-									 << " Bytes: " << mRequestedSize
-									 << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
-									 << LL_ENDL;
-				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-				mState = WAIT_HTTP_REQ;	
-
-				mFetcher->addToHTTPQueue(mID);
-				if (! mMetricsStartTime)
-				{
-					mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
-				}
-				LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
-															 true,
-															 LLImageBase::TYPE_AVATAR_BAKE == mType);
-
-				// Will call callbackHttpGet when curl request completes
-				std::vector<std::string> headers;
-				headers.push_back("Accept: image/x-j2c");
-				res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
-															  new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
-			}
-			if (!res)
-			{
-				llwarns << "HTTP GET request failed for " << mID << llendl;
-				resetFormattedData();
-				++mHTTPFailCount;
-				return true; // failed
-			}
-			// fall through
-		}
-		else //can not use http fetch.
-		{
-			return true ; //abort
-		}
-	}
-	
-	if (mState == WAIT_HTTP_REQ)
-	{
-		if (mLoaded)
-		{
-			S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
-			if (mRequestedSize < 0)
-			{
-				S32 max_attempts;
-				if (mGetStatus == HTTP_NOT_FOUND)
-				{
-					mHTTPFailCount = max_attempts = 1; // Don't retry
-					llwarns << "Texture missing from server (404): " << mUrl << llendl;
-
-					//roll back to try UDP
-					if(mCanUseNET)
-					{
-						mState = INIT ;
-						mCanUseHTTP = false ;
-						setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-						return false ;
-					}
-				}
-				else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE)
-				{
-					// *TODO: Should probably introduce a timer here to delay future HTTP requsts
-					// for a short time (~1s) to ease server load? Ideally the server would queue
-					// requests instead of returning 503... we already limit the number pending.
-					++mHTTPFailCount;
-					max_attempts = mHTTPFailCount+1; // Keep retrying
-					LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL;
-				}
-				else
-				{
-					const S32 HTTP_MAX_RETRY_COUNT = 3;
-					max_attempts = HTTP_MAX_RETRY_COUNT + 1;
-					++mHTTPFailCount;
-					llinfos << "HTTP GET failed for: " << mUrl
-							<< " Status: " << mGetStatus << " Reason: '" << mGetReason << "'"
-							<< " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl;
-				}
-
-				if (mHTTPFailCount >= max_attempts)
-				{
-					if (cur_size > 0)
-					{
-						// Use available data
-						mLoadedDiscard = mFormattedImage->getDiscardLevel();
-						mState = DECODE_IMAGE;
-						return false; 
-					}
-					else
-					{
-						resetFormattedData();
-						mState = DONE;
-						return true; // failed
-					}
-				}
-				else
-				{
-					mState = SEND_HTTP_REQ;
-					return false; // retry
-				}
-			}
-			
-			llassert_always(mBufferSize == cur_size + mRequestedSize);
-			if(!mBufferSize)//no data received.
-			{
-				delete[] mBuffer; 
-				mBuffer = NULL;
-
-				//abort.
-				mState = DONE;
-				return true;
-			}
-
-			if (mFormattedImage.isNull())
-			{
-				// For now, create formatted image based on extension
-				std::string extension = gDirUtilp->getExtension(mUrl);
-				mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
-				if (mFormattedImage.isNull())
-				{
-					mFormattedImage = new LLImageJ2C; // default
-				}
-			}
-						
-			if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded.
-			{
-				mFileSize = mBufferSize;
-			}
-			else //the file size is unknown.
-			{
-				mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded.
-			}
-			
-			U8* buffer = new U8[mBufferSize];
-			if (cur_size > 0)
-			{
-				memcpy(buffer, mFormattedImage->getData(), cur_size);
-			}
-			memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append
-			// NOTE: setData releases current data and owns new data (buffer)
-			mFormattedImage->setData(buffer, mBufferSize);
-			// delete temp data
-			delete[] mBuffer; // Note: not 'buffer' (assigned in setData())
-			mBuffer = NULL;
-			mBufferSize = 0;
-			mLoadedDiscard = mRequestedDiscard;
-			mState = DECODE_IMAGE;
-			if(mWriteToCacheState != NOT_WRITE)
-			{
-				mWriteToCacheState = SHOULD_WRITE ;
-			}
-			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-			return false;
-		}
-		else
-		{
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			return false;
-		}
-	}
-	
-	if (mState == DECODE_IMAGE)
-	{
-		static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
-		if(textures_decode_disabled)
-		{
-			// for debug use, don't decode
-			mState = DONE;
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			return true;
-		}
-
-		if (mDesiredDiscard < 0)
-		{
-			// We aborted, don't decode
-			mState = DONE;
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			return true;
-		}
-		
-		if (mFormattedImage->getDataSize() <= 0)
-		{
-			//llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl;
-			
-			//abort, don't decode
-			mState = DONE;
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			return true;
-		}
-		if (mLoadedDiscard < 0)
-		{
-			//llerrs << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl;
-
-			//abort, don't decode
-			mState = DONE;
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			return true;
-		}
-		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-		mRawImage = NULL;
-		mAuxImage = NULL;
-		llassert_always(mFormattedImage.notNull());
-		S32 discard = mHaveAllData ? 0 : mLoadedDiscard;
-		U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
-		mDecoded  = FALSE;
-		mState = DECODE_IMAGE_UPDATE;
-		LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard
-				<< " All Data: " << mHaveAllData << LL_ENDL;
-		mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux,
-																  new DecodeResponder(mFetcher, mID, this));
-		// fall though
-	}
-	
-	if (mState == DECODE_IMAGE_UPDATE)
-	{
-		if (mDecoded)
-		{
-			if (mDecodedDiscard < 0)
-			{
-				LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
-				if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0)
-				{
-					// Cache file should be deleted, try again
-// 					llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl;
-					llassert_always(mDecodeHandle == 0);
-					mFormattedImage = NULL;
-					++mRetryAttempt;
-					setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-					mState = INIT;
-					return false;
-				}
-				else
-				{
-// 					llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl;
-					mState = DONE; // failed
-				}
-			}
-			else
-			{
-				llassert_always(mRawImage.notNull());
-				LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard
-						<< " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
-				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-				mState = WRITE_TO_CACHE;
-			}
-			// fall through
-		}
-		else
-		{
-			return false;
-		}
-	}
-
-	if (mState == WRITE_TO_CACHE)
-	{
-		if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull())
-		{
-			// If we're in a local cache or we didn't actually receive any new data,
-			// or we failed to load anything, skip
-			mState = DONE;
-			return false;
-		}
-		S32 datasize = mFormattedImage->getDataSize();
-		if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed.
-		{
-			if(mHaveAllData)
-			{
-				mFileSize = datasize ;
-			}
-			else
-			{
-				mFileSize = datasize + 1 ; //flag not fully loaded.
-			}
-		}
-		llassert_always(datasize);
-		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-		U32 cache_priority = mWorkPriority;
-		mWritten = FALSE;
-		mState = WAIT_ON_WRITE;
-		CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
-		mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
-																  mFormattedImage->getData(), datasize,
-																  mFileSize, responder);
-		// fall through
-	}
-	
-	if (mState == WAIT_ON_WRITE)
-	{
-		if (writeToCacheComplete())
-		{
-			mState = DONE;
-			// fall through
-		}
-		else
-		{
-			if (mDesiredDiscard < mDecodedDiscard)
-			{
-				// We're waiting for this write to complete before we can receive more data
-				// (we can't touch mFormattedImage until the write completes)
-				// Prioritize the write
-				mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle);
-			}
-			return false;
-		}
-	}
-
-	if (mState == DONE)
-	{
-		if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard)
-		{
-			// More data was requested, return to INIT
-			mState = INIT;
-			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-			return false;
-		}
-		else
-		{
-			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-			return true;
-		}
-	}
-	
-	return false;
-}
-
-// Called from MAIN thread
-void LLTextureFetchWorker::endWork(S32 param, bool aborted)
-{
-	if (mDecodeHandle != 0)
-	{
-		mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false);
-		mDecodeHandle = 0;
-	}
-	mFormattedImage = NULL;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// virtual
-void LLTextureFetchWorker::finishWork(S32 param, bool completed)
-{
-	// The following are required in case the work was aborted
-	if (mCacheReadHandle != LLTextureCache::nullHandle())
-	{
-		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
-		mCacheReadHandle = LLTextureCache::nullHandle();
-	}
-	if (mCacheWriteHandle != LLTextureCache::nullHandle())
-	{
-		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
-		mCacheWriteHandle = LLTextureCache::nullHandle();
-	}
-}
-
-// virtual
-bool LLTextureFetchWorker::deleteOK()
-{
-	bool delete_ok = true;
-	// Allow any pending reads or writes to complete
-	if (mCacheReadHandle != LLTextureCache::nullHandle())
-	{
-		if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
-		{
-			mCacheReadHandle = LLTextureCache::nullHandle();
-		}
-		else
-		{
-			delete_ok = false;
-		}
-	}
-	if (mCacheWriteHandle != LLTextureCache::nullHandle())
-	{
-		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
-		{
-			mCacheWriteHandle = LLTextureCache::nullHandle();
-		}
-		else
-		{
-			delete_ok = false;
-		}
-	}
-
-	if ((haveWork() &&
-		 // not ok to delete from these states
-		 ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE))))
-	{
-		delete_ok = false;
-	}
-	
-	return delete_ok;
-}
-
-void LLTextureFetchWorker::removeFromCache()
-{
-	if (!mInLocalCache)
-	{
-		mFetcher->mTextureCache->removeFromCache(mID);
-	}
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool LLTextureFetchWorker::processSimulatorPackets()
-{
-	if (mFormattedImage.isNull() || mRequestedSize < 0)
-	{
-		// not sure how we got here, but not a valid state, abort!
-		llassert_always(mDecodeHandle == 0);
-		mFormattedImage = NULL;
-		return true;
-	}
-	
-	if (mLastPacket >= mFirstPacket)
-	{
-		S32 buffer_size = mFormattedImage->getDataSize();
-		for (S32 i = mFirstPacket; i<=mLastPacket; i++)
-		{
-			llassert_always(mPackets[i]);
-			buffer_size += mPackets[i]->mSize;
-		}
-		bool have_all_data = mLastPacket >= mTotalPackets-1;
-		if (mRequestedSize <= 0)
-		{
-			// We received a packed but haven't requested anything yet (edge case)
-			// Return true (we're "done") since we didn't request anything
-			return true;
-		}
-		if (buffer_size >= mRequestedSize || have_all_data)
-		{
-			/// We have enough (or all) data
-			if (have_all_data)
-			{
-				mHaveAllData = TRUE;
-			}
-			S32 cur_size = mFormattedImage->getDataSize();
-			if (buffer_size > cur_size)
-			{
-				/// We have new data
-				U8* buffer = new U8[buffer_size];
-				S32 offset = 0;
-				if (cur_size > 0 && mFirstPacket > 0)
-				{
-					memcpy(buffer, mFormattedImage->getData(), cur_size);
-					offset = cur_size;
-				}
-				for (S32 i=mFirstPacket; i<=mLastPacket; i++)
-				{
-					memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
-					offset += mPackets[i]->mSize;
-				}
-				// NOTE: setData releases current data
-				mFormattedImage->setData(buffer, buffer_size);
-			}
-			mLoadedDiscard = mRequestedDiscard;
-			return true;
-		}
-	}
-	return false;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
-										   const LLIOPipe::buffer_ptr_t& buffer,
-										   bool partial, bool success)
-{
-	S32 data_size = 0 ;
-
-	LLMutexLock lock(&mWorkMutex);
-
-	if (mState != WAIT_HTTP_REQ)
-	{
-		llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
-				<< " req=" << mSentRequest << " state= " << mState << llendl;
-		return data_size;
-	}
-	if (mLoaded)
-	{
-		llwarns << "Duplicate callback for " << mID.asString() << llendl;
-		return data_size ; // ignore duplicate callback
-	}
-	if (success)
-	{
-		// get length of stream:
-		data_size = buffer->countAfter(channels.in(), NULL);		
-	
-		LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
-		if (data_size > 0)
-		{
-			// *TODO: set the formatted image data here directly to avoid the copy
-			mBuffer = new U8[data_size];
-			buffer->readAfter(channels.in(), NULL, mBuffer, data_size);
-			mBufferSize += data_size;
-			if (data_size < mRequestedSize && mRequestedDiscard == 0)
-			{
-				mHaveAllData = TRUE;
-			}
-			else if (data_size > mRequestedSize)
-			{
-				// *TODO: This shouldn't be happening any more
-				llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
-				mHaveAllData = TRUE;
-				llassert_always(mDecodeHandle == 0);
-				mFormattedImage = NULL; // discard any previous data we had
-				mBufferSize = data_size;
-			}
-		}
-		else
-		{
-			// We requested data but received none (and no error),
-			// so presumably we have all of it
-			mHaveAllData = TRUE;
-		}
-		mRequestedSize = data_size;
-	}
-	else
-	{
-		mRequestedSize = -1; // error
-	}
-	mLoaded = TRUE;
-	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-
-	return data_size ;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image,
-											 S32 imagesize, BOOL islocal)
-{
-	LLMutexLock lock(&mWorkMutex);
-	if (mState != LOAD_FROM_TEXTURE_CACHE)
-	{
-// 		llwarns << "Read callback for " << mID << " with state = " << mState << llendl;
-		return;
-	}
-	if (success)
-	{
-		llassert_always(imagesize >= 0);
-		mFileSize = imagesize;
-		mFormattedImage = image;
-		mImageCodec = image->getCodec();
-		mInLocalCache = islocal;
-		if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize)
-		{
-			mHaveAllData = TRUE;
-		}
-	}
-	mLoaded = TRUE;
-	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-}
-
-void LLTextureFetchWorker::callbackCacheWrite(bool success)
-{
-	LLMutexLock lock(&mWorkMutex);
-	if (mState != WAIT_ON_WRITE)
-	{
-// 		llwarns << "Write callback for " << mID << " with state = " << mState << llendl;
-		return;
-	}
-	mWritten = TRUE;
-	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux)
-{
-	LLMutexLock lock(&mWorkMutex);
-	if (mDecodeHandle == 0)
-	{
-		return; // aborted, ignore
-	}
-	if (mState != DECODE_IMAGE_UPDATE)
-	{
-// 		llwarns << "Decode callback for " << mID << " with state = " << mState << llendl;
-		mDecodeHandle = 0;
-		return;
-	}
-	llassert_always(mFormattedImage.notNull());
-	
-	mDecodeHandle = 0;
-	if (success)
-	{
-		llassert_always(raw);
-		mRawImage = raw;
-		mAuxImage = aux;
-		mDecodedDiscard = mFormattedImage->getDiscardLevel();
- 		LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard
-							 << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
-	}
-	else
-	{
-		llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
-		removeFromCache();
-		mDecodedDiscard = -1; // Redundant, here for clarity and paranoia
-	}
-	mDecoded = TRUE;
-// 	llinfos << mID << " : DECODE COMPLETE " << llendl;
-	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool LLTextureFetchWorker::writeToCacheComplete()
-{
-	// Complete write to cache
-	if (mCacheWriteHandle != LLTextureCache::nullHandle())
-	{
-		if (!mWritten)
-		{
-			return false;
-		}
-		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
-		{
-			mCacheWriteHandle = LLTextureCache::nullHandle();
-		}
-		else
-		{
-			return false;
-		}
-	}
-	return true;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-// public
-
-LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode)
-	: LLWorkerThread("TextureFetch", threaded),
-	  mDebugCount(0),
-	  mDebugPause(FALSE),
-	  mPacketCount(0),
-	  mBadPacketCount(0),
-	  mQueueMutex(getAPRPool()),
-	  mNetworkQueueMutex(getAPRPool()),
-	  mTextureCache(cache),
-	  mImageDecodeThread(imagedecodethread),
-	  mTextureBandwidth(0),
-	  mHTTPTextureBits(0),
-	  mCurlGetRequest(NULL),
-	  mQAMode(qa_mode)
-{
-	mCurlPOSTRequestCount = 0;
-	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
-	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
-}
-
-LLTextureFetch::~LLTextureFetch()
-{
-	clearDeleteList() ;
-
-	while (! mCommands.empty())
-	{
-		TFRequest * req(mCommands.front());
-		mCommands.erase(mCommands.begin());
-		delete req;
-	}
-	
-	// ~LLQueuedThread() called here
-}
-
-bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
-								   S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http)
-{
-	if (mDebugPause)
-	{
-		return false;
-	}
-	
-	LLTextureFetchWorker* worker = getWorker(id) ;
-	if (worker)
-	{
-		if (worker->mHost != host)
-		{
-			llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts: "
-					<< host << " != " << worker->mHost << llendl;
-			removeRequest(worker, true);
-			worker = NULL;
-			return false;
-		}
-	}
-
-	S32 desired_size;
-	std::string exten = gDirUtilp->getExtension(url);
-	if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
-	{
-		// Only do partial requests for J2C at the moment
-		desired_size = MAX_IMAGE_DATA_SIZE;
-		desired_discard = 0;
-	}
-	else if (desired_discard == 0)
-	{
-		// if we want the entire image, and we know its size, then get it all
-		// (calcDataSizeJ2C() below makes assumptions about how the image
-		// was compressed - this code ensures that when we request the entire image,
-		// we really do get it.)
-		desired_size = MAX_IMAGE_DATA_SIZE;
-	}
-	else if (w*h*c > 0)
-	{
-		// If the requester knows the dimensions of the image,
-		// this will calculate how much data we need without having to parse the header
-
-		desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard);
-	}
-	else
-	{
-		desired_size = TEXTURE_CACHE_ENTRY_SIZE;
-		desired_discard = MAX_DISCARD_LEVEL;
-	}
-
-	
-	if (worker)
-	{
-		if (worker->wasAborted())
-		{
-			return false; // need to wait for previous aborted request to complete
-		}
-		worker->lockWorkMutex();
-		worker->mActiveCount++;
-		worker->mNeedsAux = needs_aux;
-		worker->setImagePriority(priority);
-		worker->setDesiredDiscard(desired_discard, desired_size);
-		worker->setCanUseHTTP(can_use_http) ;
-		if (!worker->haveWork())
-		{
-			worker->mState = LLTextureFetchWorker::INIT;
-			worker->unlockWorkMutex();
-
-			worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
-		}
-		else
-		{
-			worker->unlockWorkMutex();
-		}
-	}
-	else
-	{
-		worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size);
-		lockQueue() ;
-		mRequestMap[id] = worker;
-		unlockQueue() ;
-
-		worker->lockWorkMutex();
-		worker->mActiveCount++;
-		worker->mNeedsAux = needs_aux;
-		worker->setCanUseHTTP(can_use_http) ;
-		worker->unlockWorkMutex();
-	}
-	
-// 	llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl;
-	return true;
-}
-
-// protected
-void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
-{
-	lockQueue() ;
-	bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ;
-	unlockQueue() ;
-
-	LLMutexLock lock(&mNetworkQueueMutex);
-	if (in_request_map)
-	{
-		// only add to the queue if in the request map
-		// i.e. a delete has not been requested
-		mNetworkQueue.insert(worker->mID);
-	}
-	for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
-		 iter1 != mCancelQueue.end(); ++iter1)
-	{
-		iter1->second.erase(worker->mID);
-	}
-}
-
-void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel)
-{
-	LLMutexLock lock(&mNetworkQueueMutex);
-	size_t erased = mNetworkQueue.erase(worker->mID);
-	if (cancel && erased > 0)
-	{
-		mCancelQueue[worker->mHost].insert(worker->mID);
-	}
-}
-
-// protected
-void LLTextureFetch::addToHTTPQueue(const LLUUID& id)
-{
-	LLMutexLock lock(&mNetworkQueueMutex);
-	mHTTPTextureQueue.insert(id);
-}
-
-void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size)
-{
-	LLMutexLock lock(&mNetworkQueueMutex);
-	mHTTPTextureQueue.erase(id);
-	mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits	
-}
-
-void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
-{
-	lockQueue() ;
-	LLTextureFetchWorker* worker = getWorkerAfterLock(id);
-	if (worker)
-	{		
-		size_t erased_1 = mRequestMap.erase(worker->mID);
-		unlockQueue() ;
-
-		llassert_always(erased_1 > 0) ;
-
-		removeFromNetworkQueue(worker, cancel);
-		llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
-
-		worker->scheduleDelete();	
-	}
-	else
-	{
-		unlockQueue() ;
-	}
-}
-
-void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel)
-{
-	lockQueue() ;
-	size_t erased_1 = mRequestMap.erase(worker->mID);
-	unlockQueue() ;
-
-	llassert_always(erased_1 > 0) ;
-	removeFromNetworkQueue(worker, cancel);
-	llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
-
-	worker->scheduleDelete();	
-}
-
-S32 LLTextureFetch::getNumRequests() 
-{ 
-	lockQueue() ;
-	S32 size = (S32)mRequestMap.size(); 
-	unlockQueue() ;
-
-	return size ;
-}
-
-S32 LLTextureFetch::getNumHTTPRequests() 
-{ 
-	mNetworkQueueMutex.lock() ;
-	S32 size = (S32)mHTTPTextureQueue.size(); 
-	mNetworkQueueMutex.unlock() ;
-
-	return size ;
-}
-
-// call lockQueue() first!
-LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id)
-{
-	LLTextureFetchWorker* res = NULL;
-	map_t::iterator iter = mRequestMap.find(id);
-	if (iter != mRequestMap.end())
-	{
-		res = iter->second;
-	}
-	return res;
-}
-
-LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
-{
-	LLMutexLock lock(&mQueueMutex) ;
-
-	return getWorkerAfterLock(id) ;
-}
-
-
-bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
-										LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux)
-{
-	bool res = false;
-	LLTextureFetchWorker* worker = getWorker(id);
-	if (worker)
-	{
-		if (worker->wasAborted())
-		{
-			res = true;
-		}
-		else if (!worker->haveWork())
-		{
-			// Should only happen if we set mDebugPause...
-			if (!mDebugPause)
-			{
-// 				llwarns << "Adding work for inactive worker: " << id << llendl;
-				worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
-			}
-		}
-		else if (worker->checkWork())
-		{
-			worker->lockWorkMutex();
-			discard_level = worker->mDecodedDiscard;
-			raw = worker->mRawImage;
-			aux = worker->mAuxImage;
-			res = true;
-			LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
-			worker->unlockWorkMutex();
-		}
-		else
-		{
-			worker->lockWorkMutex();
-			if ((worker->mDecodedDiscard >= 0) &&
-				(worker->mDecodedDiscard < discard_level || discard_level < 0) &&
-				(worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE))
-			{
-				// Not finished, but data is ready
-				discard_level = worker->mDecodedDiscard;
-				raw = worker->mRawImage;
-				aux = worker->mAuxImage;
-			}
-			worker->unlockWorkMutex();
-		}
-	}
-	else
-	{
-		res = true;
-	}
-	return res;
-}
-
-bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
-{
-	bool res = false;
-	LLTextureFetchWorker* worker = getWorker(id);
-	if (worker)
-	{
-		worker->lockWorkMutex();
-		worker->setImagePriority(priority);
-		worker->unlockWorkMutex();
-		res = true;
-	}
-	return res;
-}
-
-// Replicates and expands upon the base class's
-// getPending() implementation.  getPending() and
-// runCondition() replicate one another's logic to
-// an extent and are sometimes used for the same
-// function (deciding whether or not to sleep/pause
-// a thread).  So the implementations need to stay
-// in step, at least until this can be refactored and
-// the redundancy eliminated.
-//
-// May be called from any thread
-
-//virtual
-S32 LLTextureFetch::getPending()
-{
-	S32 res;
-	lockData();
-    {
-        LLMutexLock lock(&mQueueMutex);
-        
-        res = mRequestQueue.size();
-        res += mCurlPOSTRequestCount;
-        res += mCommands.size();
-    }
-	unlockData();
-	return res;
-}
-
-// virtual
-bool LLTextureFetch::runCondition()
-{
-	// Caller is holding the lock on LLThread's condition variable.
-	
-	// LLQueuedThread, unlike its base class LLThread, makes this a
-	// private method which is unfortunate.  I want to use it directly
-	// but I'm going to have to re-implement the logic here (or change
-	// declarations, which I don't want to do right now).
-	//
-	// Changes here may need to be reflected in getPending().
-	
-	bool have_no_commands(false);
-	{
-		LLMutexLock lock(&mQueueMutex);
-		
-		have_no_commands = mCommands.empty();
-	}
-	
-    bool have_no_curl_requests(0 == mCurlPOSTRequestCount);
-	
-	return ! (have_no_commands
-			  && have_no_curl_requests
-			  && (mRequestQueue.empty() && mIdleThread));		// From base class
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
-void LLTextureFetch::commonUpdate()
-{
-	// Run a cross-thread command, if any.
-	cmdDoWork();
-	
-	// Update Curl on same thread as mCurlGetRequest was constructed
-	S32 processed = mCurlGetRequest->process();
-	if (processed > 0)
-	{
-		lldebugs << "processed: " << processed << " messages." << llendl;
-	}
-}
-
-
-// MAIN THREAD
-//virtual
-S32 LLTextureFetch::update(U32 max_time_ms)
-{
-	static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS");
-
-	{
-		mNetworkQueueMutex.lock() ;
-		mMaxBandwidth = band_width ;
-
-		gTextureList.sTextureBits += mHTTPTextureBits ;
-		mHTTPTextureBits = 0 ;
-
-		mNetworkQueueMutex.unlock() ;
-	}
-
-	S32 res = LLWorkerThread::update(max_time_ms);
-	
-	if (!mDebugPause)
-	{
-		sendRequestListToSimulators();
-	}
-
-	if (!mThreaded)
-	{
-		commonUpdate();
-	}
-
-	return res;
-}
-
-//called in the MAIN thread after the TextureCacheThread shuts down.
-void LLTextureFetch::shutDownTextureCacheThread() 
-{
-	if(mTextureCache)
-	{
-		llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ;
-		mTextureCache = NULL ;
-	}
-}
-	
-//called in the MAIN thread after the ImageDecodeThread shuts down.
-void LLTextureFetch::shutDownImageDecodeThread() 
-{
-	if(mImageDecodeThread)
-	{
-		llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ;
-		mImageDecodeThread = NULL ;
-	}
-}
-
-// WORKER THREAD
-void LLTextureFetch::startThread()
-{
-	// Construct mCurlGetRequest from Worker Thread
-	mCurlGetRequest = new LLCurlRequest();
-}
-
-// WORKER THREAD
-void LLTextureFetch::endThread()
-{
-	// Destroy mCurlGetRequest from Worker Thread
-	delete mCurlGetRequest;
-	mCurlGetRequest = NULL;
-}
-
-// WORKER THREAD
-void LLTextureFetch::threadedUpdate()
-{
-	llassert_always(mCurlGetRequest);
-	
-	// Limit update frequency
-	const F32 PROCESS_TIME = 0.05f; 
-	static LLFrameTimer process_timer;
-	if (process_timer.getElapsedTimeF32() < PROCESS_TIME)
-	{
-		return;
-	}
-	process_timer.reset();
-	
-	commonUpdate();
-
-#if 0
-	const F32 INFO_TIME = 1.0f; 
-	static LLFrameTimer info_timer;
-	if (info_timer.getElapsedTimeF32() >= INFO_TIME)
-	{
-		S32 q = mCurlGetRequest->getQueued();
-		if (q > 0)
-		{
-			llinfos << "Queued gets: " << q << llendl;
-			info_timer.reset();
-		}
-	}
-#endif
-	
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-void LLTextureFetch::sendRequestListToSimulators()
-{
-	// All requests
-	const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps
-	
-	// Sim requests
-	const S32 IMAGES_PER_REQUEST = 50;
-	const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp
-	const F32 MIN_REQUEST_TIME = 1.0f;
-	const F32 MIN_DELTA_PRIORITY = 1000.f;
-
-	// Periodically, gather the list of textures that need data from the network
-	// And send the requests out to the simulators
-	static LLFrameTimer timer;
-	if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME)
-	{
-		return;
-	}
-	timer.reset();
-	
-	// Send requests
-	typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t;
-	typedef std::map< LLHost, request_list_t > work_request_map_t;
-	work_request_map_t requests;
-	{
-	LLMutexLock lock2(&mNetworkQueueMutex);
-	for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); )
-	{
-		queue_t::iterator curiter = iter++;
-		LLTextureFetchWorker* req = getWorker(*curiter);
-		if (!req)
-		{
-			mNetworkQueue.erase(curiter);
-			continue; // paranoia
-		}
-		if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) &&
-			(req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR))
-		{
-			// We already received our URL, remove from the queue
-			llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl;
-			mNetworkQueue.erase(curiter);
-			continue;
-		}
-		if (req->mID == mDebugID)
-		{
-			mDebugCount++; // for setting breakpoints
-		}
-		if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM &&
-			req->mTotalPackets > 0 &&
-			req->mLastPacket >= req->mTotalPackets-1)
-		{
-			// We have all the packets... make sure this is high priority
-// 			req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority);
-			continue;
-		}
-		F32 elapsed = req->mRequestedTimer.getElapsedTimeF32();
-		{
-			F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
-			if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
-				(delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
-				(elapsed >= SIM_LAZY_FLUSH_TIMEOUT))
-			{
-				requests[req->mHost].insert(req);
-			}
-		}
-	}
-	}
-
-	for (work_request_map_t::iterator iter1 = requests.begin();
-		 iter1 != requests.end(); ++iter1)
-	{
-		LLHost host = iter1->first;
-		// invalid host = use agent host
-		if (host == LLHost::invalid)
-		{
-			host = gAgent.getRegionHost();
-		}
-
-		S32 sim_request_count = 0;
-		
-		for (request_list_t::iterator iter2 = iter1->second.begin();
-			 iter2 != iter1->second.end(); ++iter2)
-		{
-			LLTextureFetchWorker* req = *iter2;
-			if (gMessageSystem)
-			{
-				if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM)
-				{
-					// Initialize packet data based on data read from cache
-					req->lockWorkMutex();
-					req->setupPacketData();
-					req->unlockWorkMutex();
-				}
-				if (0 == sim_request_count)
-				{
-					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
-					gMessageSystem->nextBlockFast(_PREHASH_AgentData);
-					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-				}
-				S32 packet = req->mLastPacket + 1;
-				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
-				gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
-				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mDesiredDiscard);
-				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority);
-				gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
-				gMessageSystem->addU8Fast(_PREHASH_Type, req->mType);
-// 				llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard
-// 						<< " Packet: " << packet << " Priority: " << req->mImagePriority << llendl;
-
-				static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
-				static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
-				if (log_to_viewer_log || log_to_sim)
-				{
-					mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime());
-					mTextureInfo.setRequestOffset(req->mID, 0);
-					mTextureInfo.setRequestSize(req->mID, 0);
-					mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP);
-				}
-
-				req->lockWorkMutex();
-				req->mSentRequest = LLTextureFetchWorker::SENT_SIM;
-				req->mSimRequestedDiscard = req->mDesiredDiscard;
-				req->mRequestedPriority = req->mImagePriority;
-				req->mRequestedTimer.reset();
-				req->unlockWorkMutex();
-				sim_request_count++;
-				if (sim_request_count >= IMAGES_PER_REQUEST)
-				{
-// 					llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
-
-					gMessageSystem->sendSemiReliable(host, NULL, NULL);
-					sim_request_count = 0;
-				}
-			}
-		}
-		if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST)
-		{
-// 			llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
-			gMessageSystem->sendSemiReliable(host, NULL, NULL);
-			sim_request_count = 0;
-		}
-	}
-	
-	// Send cancelations
-	{
-	LLMutexLock lock2(&mNetworkQueueMutex);
-	if (gMessageSystem && !mCancelQueue.empty())
-	{
-		for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
-			 iter1 != mCancelQueue.end(); ++iter1)
-		{
-			LLHost host = iter1->first;
-			if (host == LLHost::invalid)
-			{
-				host = gAgent.getRegionHost();
-			}
-			S32 request_count = 0;
-			for (queue_t::iterator iter2 = iter1->second.begin();
-				 iter2 != iter1->second.end(); ++iter2)
-			{
-				if (0 == request_count)
-				{
-					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
-					gMessageSystem->nextBlockFast(_PREHASH_AgentData);
-					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-				}
-				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
-				gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2);
-				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1);
-				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0);
-				gMessageSystem->addU32Fast(_PREHASH_Packet, 0);
-				gMessageSystem->addU8Fast(_PREHASH_Type, 0);
-// 				llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl;
-
-				request_count++;
-				if (request_count >= IMAGES_PER_REQUEST)
-				{
-					gMessageSystem->sendSemiReliable(host, NULL, NULL);
-					request_count = 0;
-				}
-			}
-			if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
-			{
-				gMessageSystem->sendSemiReliable(host, NULL, NULL);
-			}
-		}
-		mCancelQueue.clear();
-	}
-	}
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
-{
-	mRequestedTimer.reset();
-	if (index >= mTotalPackets)
-	{
-// 		llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << llendl;
-		return false;
-	}
-	if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE)
-	{
-// 		llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << llendl;
-		return false;
-	}
-	
-	if (index >= (S32)mPackets.size())
-	{
-		mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
-	}
-	else if (mPackets[index] != NULL)
-	{
-// 		llwarns << "Received duplicate packet: " << index << " for image: " << mID << llendl;
-		return false;
-	}
-
-	mPackets[index] = new PacketData(data, size);
-	while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
-	{
-		++mLastPacket;
-	}
-	return true;
-}
-
-bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes,
-										U16 data_size, U8* data)
-{
-	LLTextureFetchWorker* worker = getWorker(id);
-	bool res = true;
-
-	++mPacketCount;
-	
-	if (!worker)
-	{
-// 		llwarns << "Received header for non active worker: " << id << llendl;
-		res = false;
-	}
-	else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK ||
-			 worker->mSentRequest != LLTextureFetchWorker::SENT_SIM)
-	{
-// 		llwarns << "receiveImageHeader for worker: " << id
-// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState]
-// 				<< " sent: " << worker->mSentRequest << llendl;
-		res = false;
-	}
-	else if (worker->mLastPacket != -1)
-	{
-		// check to see if we've gotten this packet before
-// 		llwarns << "Received duplicate header for: " << id << llendl;
-		res = false;
-	}
-	else if (!data_size)
-	{
-// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
-		res = false;
-	}
-	if (!res)
-	{
-		++mBadPacketCount;
-		mNetworkQueueMutex.lock() ;
-		mCancelQueue[host].insert(id);
-		mNetworkQueueMutex.unlock() ;
-		return false;
-	}
-
-	worker->lockWorkMutex();
-
-	//	Copy header data into image object
-	worker->mImageCodec = codec;
-	worker->mTotalPackets = packets;
-	worker->mFileSize = (S32)totalbytes;	
-	llassert_always(totalbytes > 0);
-	llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
-	res = worker->insertPacket(0, data, data_size);
-	worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
-	worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
-	worker->unlockWorkMutex();
-	return res;
-}
-
-bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data)
-{
-	LLTextureFetchWorker* worker = getWorker(id);
-	bool res = true;
-
-	++mPacketCount;
-	
-	if (!worker)
-	{
-// 		llwarns << "Received packet " << packet_num << " for non active worker: " << id << llendl;
-		res = false;
-	}
-	else if (worker->mLastPacket == -1)
-	{
-// 		llwarns << "Received packet " << packet_num << " before header for: " << id << llendl;
-		res = false;
-	}
-	else if (!data_size)
-	{
-// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
-		res = false;
-	}
-	if (!res)
-	{
-		++mBadPacketCount;
-		mNetworkQueueMutex.lock() ;
-		mCancelQueue[host].insert(id);
-		mNetworkQueueMutex.unlock() ;
-		return false;
-	}
-
-	worker->lockWorkMutex();
-	
-	res = worker->insertPacket(packet_num, data, data_size);
-	
-	if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) ||
-		(worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK))
-	{
-		worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
-		worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
-	}
-	else
-	{
-// 		llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id
-// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl;
-		removeFromNetworkQueue(worker, true); // failsafe
-	}
-
-	if(packet_num >= (worker->mTotalPackets - 1))
-	{
-		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
-		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
-
-		if (log_to_viewer_log || log_to_sim)
-		{
-			U64 timeNow = LLTimer::getTotalTime();
-			mTextureInfo.setRequestSize(id, worker->mFileSize);
-			mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow);
-		}
-	}
-	worker->unlockWorkMutex();
-
-	return res;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id)
-{
-	BOOL from_cache = FALSE ;
-
-	LLTextureFetchWorker* worker = getWorker(id);
-	if (worker)
-	{
-		worker->lockWorkMutex() ;
-		from_cache = worker->mInLocalCache ;
-		worker->unlockWorkMutex() ;
-	}
-
-	return from_cache ;
-}
-
-S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p,
-								  U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http)
-{
-	S32 state = LLTextureFetchWorker::INVALID;
-	F32 data_progress = 0.0f;
-	F32 requested_priority = 0.0f;
-	F32 fetch_dtime = 999999.f;
-	F32 request_dtime = 999999.f;
-	U32 fetch_priority = 0;
-	
-	LLTextureFetchWorker* worker = getWorker(id);
-	if (worker && worker->haveWork())
-	{
-		worker->lockWorkMutex();
-		state = worker->mState;
-		fetch_dtime = worker->mFetchTimer.getElapsedTimeF32();
-		request_dtime = worker->mRequestedTimer.getElapsedTimeF32();
-		if (worker->mFileSize > 0)
-		{
-			if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR)
-			{
-				S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE;
-				data_size = llmax(data_size, 0);
-				data_progress = (F32)data_size / (F32)worker->mFileSize;
-			}
-			else if (worker->mFormattedImage.notNull())
-			{
-				data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize;
-			}
-		}
-		if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ)
-		{
-			requested_priority = worker->mRequestedPriority;
-		}
-		else
-		{
-			requested_priority = worker->mImagePriority;
-		}
-		fetch_priority = worker->getPriority();
-		can_use_http = worker->getCanUseHTTP() ;
-		worker->unlockWorkMutex();
-	}
-	data_progress_p = data_progress;
-	requested_priority_p = requested_priority;
-	fetch_priority_p = fetch_priority;
-	fetch_dtime_p = fetch_dtime;
-	request_dtime_p = request_dtime;
-	return state;
-}
-
-void LLTextureFetch::dump()
-{
-	llinfos << "LLTextureFetch REQUESTS:" << llendl;
-	for (request_queue_t::iterator iter = mRequestQueue.begin();
-		 iter != mRequestQueue.end(); ++iter)
-	{
-		LLQueuedThread::QueuedRequest* qreq = *iter;
-		LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq;
-		LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass();
-		llinfos << " ID: " << worker->mID
-				<< " PRI: " << llformat("0x%08x",wreq->getPriority())
-				<< " STATE: " << worker->sStateDescs[worker->mState]
-				<< llendl;
-	}
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// cross-thread command methods
-
-void LLTextureFetch::commandSetRegion(U64 region_handle)
-{
-	TFReqSetRegion * req = new TFReqSetRegion(region_handle);
-
-	cmdEnqueue(req);
-}
-
-void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
-										const LLUUID & session_id,
-										const LLUUID & agent_id,
-										LLViewerAssetStats * main_stats)
-{
-	TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
-
-	cmdEnqueue(req);
-}
-
-void LLTextureFetch::commandDataBreak()
-{
-	// The pedantically correct way to implement this is to create a command
-	// request object in the above fashion and enqueue it.  However, this is
-	// simple data of an advisorial not operational nature and this case
-	// of shared-write access is tolerable.
-
-	LLTextureFetch::svMetricsDataBreak = true;
-}
-
-void LLTextureFetch::cmdEnqueue(TFRequest * req)
-{
-	lockQueue();
-	mCommands.push_back(req);
-	unlockQueue();
-
-	unpause();
-}
-
-LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
-{
-	TFRequest * ret = 0;
-	
-	lockQueue();
-	if (! mCommands.empty())
-	{
-		ret = mCommands.front();
-		mCommands.erase(mCommands.begin());
-	}
-	unlockQueue();
-
-	return ret;
-}
-
-void LLTextureFetch::cmdDoWork()
-{
-	if (mDebugPause)
-	{
-		return;  // debug: don't do any work
-	}
-
-	TFRequest * req = cmdDequeue();
-	if (req)
-	{
-		// One request per pass should really be enough for this.
-		req->doWork(this);
-		delete req;
-	}
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-
-// Private (anonymous) class methods implementing the command scheme.
-
-namespace
-{
-
-/**
- * Implements the 'Set Region' command.
- *
- * Thread:  Thread1 (TextureFetch)
- */
-bool
-TFReqSetRegion::doWork(LLTextureFetch *)
-{
-	LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
-
-	return true;
-}
-
-
-TFReqSendMetrics::~TFReqSendMetrics()
-{
-	delete mMainStats;
-	mMainStats = 0;
-}
-
-
-/**
- * Implements the 'Send Metrics' command.  Takes over
- * ownership of the passed LLViewerAssetStats pointer.
- *
- * Thread:  Thread1 (TextureFetch)
- */
-bool
-TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
-{
-	/*
-	 * HTTP POST responder.  Doesn't do much but tries to
-	 * detect simple breaks in recording the metrics stream.
-	 *
-	 * The 'volatile' modifiers don't indicate signals,
-	 * mmap'd memory or threads, really.  They indicate that
-	 * the referenced data is part of a pseudo-closure for
-	 * this responder rather than being required for correct
-	 * operation.
-     *
-     * We don't try very hard with the POST request.  We give
-     * it one shot and that's more-or-less it.  With a proper
-     * refactoring of the LLQueuedThread usage, these POSTs
-     * could be put in a request object and made more reliable.
-	 */
-	class lcl_responder : public LLCurl::Responder
-	{
-	public:
-		lcl_responder(LLTextureFetch * fetcher,
-					  S32 expected_sequence,
-                      volatile const S32 & live_sequence,
-                      volatile bool & reporting_break,
-					  volatile bool & reporting_started)
-			: LLCurl::Responder(),
-			  mFetcher(fetcher),
-              mExpectedSequence(expected_sequence),
-              mLiveSequence(live_sequence),
-			  mReportingBreak(reporting_break),
-			  mReportingStarted(reporting_started)
-			{
-                mFetcher->incrCurlPOSTCount();
-            }
-        
-        ~lcl_responder()
-            {
-                mFetcher->decrCurlPOSTCount();
-            }
-
-		// virtual
-		void error(U32 status_num, const std::string & reason)
-			{
-                if (mLiveSequence == mExpectedSequence)
-                {
-                    mReportingBreak = true;
-                }
-				LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service.  Reason:  "
-									<< reason << LL_ENDL;
-			}
-
-		// virtual
-		void result(const LLSD & content)
-			{
-                if (mLiveSequence == mExpectedSequence)
-                {
-                    mReportingBreak = false;
-                    mReportingStarted = true;
-                }
-			}
-
-	private:
-		LLTextureFetch * mFetcher;
-        S32 mExpectedSequence;
-        volatile const S32 & mLiveSequence;
-		volatile bool & mReportingBreak;
-		volatile bool & mReportingStarted;
-
-	}; // class lcl_responder
-	
-	if (! gViewerAssetStatsThread1)
-		return true;
-
-	static volatile bool reporting_started(false);
-	static volatile S32 report_sequence(0);
-    
-	// We've taken over ownership of the stats copy at this
-	// point.  Get a working reference to it for merging here
-	// but leave it in 'this'.  Destructor will rid us of it.
-	LLViewerAssetStats & main_stats = *mMainStats;
-
-	// Merge existing stats into those from main, convert to LLSD
-	main_stats.merge(*gViewerAssetStatsThread1);
-	LLSD merged_llsd = main_stats.asLLSD(true);
-
-	// Add some additional meta fields to the content
-	merged_llsd["session_id"] = mSessionID;
-	merged_llsd["agent_id"] = mAgentID;
-	merged_llsd["message"] = "ViewerAssetMetrics";					// Identifies the type of metrics
-	merged_llsd["sequence"] = report_sequence;						// Sequence number
-	merged_llsd["initial"] = ! reporting_started;					// Initial data from viewer
-	merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak;		// Break in data prior to this report
-		
-	// Update sequence number
-	if (S32_MAX == ++report_sequence)
-		report_sequence = 0;
-
-	// Limit the size of the stats report if necessary.
-	merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
-
-	if (! mCapsURL.empty())
-	{
-		LLCurlRequest::headers_t headers;
-		fetcher->getCurlRequest().post(mCapsURL,
-									   headers,
-									   merged_llsd,
-									   new lcl_responder(fetcher,
-														 report_sequence,
-                                                         report_sequence,
-                                                         LLTextureFetch::svMetricsDataBreak,
-														 reporting_started));
-	}
-	else
-	{
-		LLTextureFetch::svMetricsDataBreak = true;
-	}
-
-	// In QA mode, Metrics submode, log the result for ease of testing
-	if (fetcher->isQAMode())
-	{
-		LL_INFOS("Textures") << merged_llsd << LL_ENDL;
-	}
-
-	gViewerAssetStatsThread1->reset();
-
-	return true;
-}
-
-
-bool
-truncate_viewer_metrics(int max_regions, LLSD & metrics)
-{
-	static const LLSD::String reg_tag("regions");
-	static const LLSD::String duration_tag("duration");
-	
-	LLSD & reg_map(metrics[reg_tag]);
-	if (reg_map.size() <= max_regions)
-	{
-		return false;
-	}
-
-	// Build map of region hashes ordered by duration
-	typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
-	reg_ordered_list_t regions_by_duration;
-
-	int ind(0);
-	LLSD::array_const_iterator it_end(reg_map.endArray());
-	for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
-	{
-		LLSD::Real duration = (*it)[duration_tag].asReal();
-		regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
-	}
-
-	// Build a replacement regions array with the longest-persistence regions
-	LLSD new_region(LLSD::emptyArray());
-	reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
-	reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
-	for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
-	{
-		new_region.append(reg_map[it2->second]);
-	}
-	reg_map = new_region;
-	
-	return true;
-}
-
-} // end of anonymous namespace
-
-
-
+/** 
+ * @file lltexturefetch.cpp
+ * @brief Object which fetches textures from the cache and/or network
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <iostream>
+#include <map>
+
+#include "llstl.h"
+
+#include "lltexturefetch.h"
+
+#include "llcurl.h"
+#include "lldir.h"
+#include "llhttpclient.h"
+#include "llhttpstatuscodes.h"
+#include "llimage.h"
+#include "llimagej2c.h"
+#include "llimageworker.h"
+#include "llworkerthread.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "lltexturecache.h"
+#include "llviewercontrol.h"
+#include "llviewertexturelist.h"
+#include "llviewertexture.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerassetstats.h"
+#include "llworld.h"
+
+//////////////////////////////////////////////////////////////////////////////
+class LLTextureFetchWorker : public LLWorkerClass
+{
+	friend class LLTextureFetch;
+	friend class HTTPGetResponder;
+	
+private:
+	class CacheReadResponder : public LLTextureCache::ReadResponder
+	{
+	public:
+		CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image)
+			: mFetcher(fetcher), mID(id)
+		{
+			setImage(image);
+		}
+		virtual void completed(bool success)
+		{
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+ 				worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal);
+			}
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+	};
+
+	class CacheWriteResponder : public LLTextureCache::WriteResponder
+	{
+	public:
+		CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id)
+			: mFetcher(fetcher), mID(id)
+		{
+		}
+		virtual void completed(bool success)
+		{
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+				worker->callbackCacheWrite(success);
+			}
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+	};
+	
+	class DecodeResponder : public LLImageDecodeThread::Responder
+	{
+	public:
+		DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker)
+			: mFetcher(fetcher), mID(id), mWorker(worker)
+		{
+		}
+		virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
+		{
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+ 				worker->callbackDecoded(success, raw, aux);
+			}
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+		LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID)
+	};
+
+	struct Compare
+	{
+		// lhs < rhs
+		bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const
+		{
+			// greater priority is "less"
+			const F32 lpriority = lhs->mImagePriority;
+			const F32 rpriority = rhs->mImagePriority;
+			if (lpriority > rpriority) // higher priority
+				return true;
+			else if (lpriority < rpriority)
+				return false;
+			else
+				return lhs < rhs;
+		}
+	};
+	
+public:
+	/*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
+	/*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
+	/*virtual*/ bool deleteOK(); // called from update() (WORK THREAD)
+
+	~LLTextureFetchWorker();
+	// void relese() { --mActiveCount; }
+
+	S32 callbackHttpGet(const LLChannelDescriptors& channels,
+						 const LLIOPipe::buffer_ptr_t& buffer,
+						 bool partial, bool success);
+	void callbackCacheRead(bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal);
+	void callbackCacheWrite(bool success);
+	void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux);
+	
+	void setGetStatus(U32 status, const std::string& reason)
+	{
+		LLMutexLock lock(&mWorkMutex);
+
+		mGetStatus = status;
+		mGetReason = reason;
+	}
+
+	void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; }
+	bool getCanUseHTTP() const { return mCanUseHTTP; }
+
+	LLTextureFetch & getFetcher() { return *mFetcher; }
+	
+protected:
+	LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
+						 F32 priority, S32 discard, S32 size);
+
+private:
+	/*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD)
+	/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
+
+	void resetFormattedData();
+	
+	void setImagePriority(F32 priority);
+	void setDesiredDiscard(S32 discard, S32 size);
+	bool insertPacket(S32 index, U8* data, S32 size);
+	void clearPackets();
+	void setupPacketData();
+	U32 calcWorkPriority();
+	void removeFromCache();
+	bool processSimulatorPackets();
+	bool writeToCacheComplete();
+	
+	void lockWorkMutex() { mWorkMutex.lock(); }
+	void unlockWorkMutex() { mWorkMutex.unlock(); }
+
+private:
+	enum e_state // mState
+	{
+		// NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
+		INVALID = 0,
+		INIT,
+		LOAD_FROM_TEXTURE_CACHE,
+		CACHE_POST,
+		LOAD_FROM_NETWORK,
+		LOAD_FROM_SIMULATOR,
+		SEND_HTTP_REQ,
+		WAIT_HTTP_REQ,
+		DECODE_IMAGE,
+		DECODE_IMAGE_UPDATE,
+		WRITE_TO_CACHE,
+		WAIT_ON_WRITE,
+		DONE
+	};
+	enum e_request_state // mSentRequest
+	{
+		UNSENT = 0,
+		QUEUED = 1,
+		SENT_SIM = 2
+	};
+	enum e_write_to_cache_state //mWriteToCacheState
+	{
+		NOT_WRITE = 0,
+		CAN_WRITE = 1,
+		SHOULD_WRITE = 2
+	};
+	static const char* sStateDescs[];
+	e_state mState;
+	e_write_to_cache_state mWriteToCacheState;
+	LLTextureFetch* mFetcher;
+	LLPointer<LLImageFormatted> mFormattedImage;
+	LLPointer<LLImageRaw> mRawImage;
+	LLPointer<LLImageRaw> mAuxImage;
+	LLUUID mID;
+	LLHost mHost;
+	std::string mUrl;
+	U8 mType;
+	F32 mImagePriority;
+	U32 mWorkPriority;
+	F32 mRequestedPriority;
+	S32 mDesiredDiscard;
+	S32 mSimRequestedDiscard;
+	S32 mRequestedDiscard;
+	S32 mLoadedDiscard;
+	S32 mDecodedDiscard;
+	LLFrameTimer mRequestedTimer;
+	LLFrameTimer mFetchTimer;
+	LLTextureCache::handle_t mCacheReadHandle;
+	LLTextureCache::handle_t mCacheWriteHandle;
+	U8* mBuffer;
+	S32 mBufferSize;
+	S32 mRequestedSize;
+	S32 mDesiredSize;
+	S32 mFileSize;
+	S32 mCachedSize;	
+	e_request_state mSentRequest;
+	handle_t mDecodeHandle;
+	BOOL mLoaded;
+	BOOL mDecoded;
+	BOOL mWritten;
+	BOOL mNeedsAux;
+	BOOL mHaveAllData;
+	BOOL mInLocalCache;
+	bool mCanUseHTTP ;
+	bool mCanUseNET ; //can get from asset server.
+	S32 mHTTPFailCount;
+	S32 mRetryAttempt;
+	S32 mActiveCount;
+	U32 mGetStatus;
+	std::string mGetReason;
+	
+	// Work Data
+	LLMutex mWorkMutex;
+	struct PacketData
+	{
+		PacketData(U8* data, S32 size) { mData = data; mSize = size; }
+		~PacketData() { clearData(); }
+		void clearData() { delete[] mData; mData = NULL; }
+		U8* mData;
+		U32 mSize;
+	};
+	std::vector<PacketData*> mPackets;
+	S32 mFirstPacket;
+	S32 mLastPacket;
+	U16 mTotalPackets;
+	U8 mImageCodec;
+
+	LLViewerAssetStats::duration_t mMetricsStartTime;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class HTTPGetResponder : public LLCurl::Responder
+{
+	LOG_CLASS(HTTPGetResponder);
+public:
+	HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir)
+		: mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir)
+	{
+	}
+	~HTTPGetResponder()
+	{
+	}
+
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+	{
+		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
+		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
+		static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
+
+		if (log_to_viewer_log || log_to_sim)
+		{
+			mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime);
+			U64 timeNow = LLTimer::getTotalTime();
+			mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+			mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
+			mFetcher->mTextureInfo.setRequestOffset(mID, mOffset);
+			mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
+		}
+
+		lldebugs << "HTTP COMPLETE: " << mID << llendl;
+		LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+		if (worker)
+		{
+			bool success = false;
+			bool partial = false;
+			if (HTTP_OK <= status &&  status < HTTP_MULTIPLE_CHOICES)
+			{
+				success = true;
+				if (HTTP_PARTIAL_CONTENT == status) // partial information
+				{
+					partial = true;
+				}
+			}
+
+			if (!success)
+			{
+				worker->setGetStatus(status, reason);
+// 				llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl;
+			}
+			
+			S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success);
+			
+			if(log_texture_traffic && data_size > 0)
+			{
+				LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ;
+				if(tex)
+				{
+					gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
+				}
+			}
+
+			mFetcher->removeFromHTTPQueue(mID, data_size);
+
+			if (worker->mMetricsStartTime)
+			{
+				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
+															  true,
+															  LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
+															  LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
+				worker->mMetricsStartTime = 0;
+			}
+			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 true,
+														 LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
+		}
+		else
+		{
+			mFetcher->removeFromHTTPQueue(mID);
+ 			llwarns << "Worker not found: " << mID << llendl;
+		}
+	}
+
+	virtual bool followRedir()
+	{
+		return mFollowRedir;
+	}
+	
+private:
+	LLTextureFetch* mFetcher;
+	LLUUID mID;
+	U64 mStartTime;
+	S32 mRequestedSize;
+	U32 mOffset;
+	bool mFollowRedir;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Cross-thread messaging for asset metrics.
+
+/**
+ * @brief Base class for cross-thread requests made of the fetcher
+ *
+ * I believe the intent of the LLQueuedThread class was to
+ * have these operations derived from LLQueuedThread::QueuedRequest
+ * but the texture fetcher has elected to manage the queue
+ * in its own manner.  So these are free-standing objects which are
+ * managed in simple FIFO order on the mCommands queue of the
+ * LLTextureFetch object.
+ *
+ * What each represents is a simple command sent from an
+ * outside thread into the TextureFetch thread to be processed
+ * in order and in a timely fashion (though not an absolute
+ * higher priority than other operations of the thread).
+ * Each operation derives a new class from the base customizing
+ * members, constructors and the doWork() method to effect
+ * the command.
+ *
+ * The flow is one-directional.  There are two global instances
+ * of the LLViewerAssetStats collector, one for the main program's
+ * thread pointed to by gViewerAssetStatsMain and one for the
+ * TextureFetch thread pointed to by gViewerAssetStatsThread1.
+ * Common operations has each thread recording metrics events
+ * into the respective collector unconcerned with locking and
+ * the state of any other thread.  But when the agent moves into
+ * a different region or the metrics timer expires and a report
+ * needs to be sent back to the grid, messaging across threads
+ * is required to distribute data and perform global actions.
+ * In pseudo-UML, it looks like:
+ *
+ *                       Main                 Thread1
+ *                        .                      .
+ *                        .                      .
+ *                     +-----+                   .
+ *                     | AM  |                   .
+ *                     +--+--+                   .
+ *      +-------+         |                      .
+ *      | Main  |      +--+--+                   .
+ *      |       |      | SRE |---.               .
+ *      | Stats |      +-----+    \              .
+ *      |       |         |        \  (uuid)  +-----+
+ *      | Coll. |      +--+--+      `-------->| SR  |
+ *      +-------+      | MSC |                +--+--+
+ *         | ^         +-----+                   |
+ *         | |  (uuid)  / .                   +-----+ (uuid)
+ *         |  `--------'  .                   | MSC |---------.
+ *         |              .                   +-----+         |
+ *         |           +-----+                   .            v
+ *         |           | TE  |                   .        +-------+
+ *         |           +--+--+                   .        | Thd1  |
+ *         |              |                      .        |       |
+ *         |           +-----+                   .        | Stats |
+ *          `--------->| RSC |                   .        |       |
+ *                     +--+--+                   .        | Coll. |
+ *                        |                      .        +-------+
+ *                     +--+--+                   .            |
+ *                     | SME |---.               .            |
+ *                     +-----+    \              .            |
+ *                        .        \ (clone)  +-----+         |
+ *                        .         `-------->| SM  |         |
+ *                        .                   +--+--+         |
+ *                        .                      |            |
+ *                        .                   +-----+         |
+ *                        .                   | RSC |<--------'
+ *                        .                   +-----+
+ *                        .                      |
+ *                        .                   +-----+
+ *                        .                   | CP  |--> HTTP POST
+ *                        .                   +-----+
+ *                        .                      .
+ *                        .                      .
+ *
+ *
+ * Key:
+ *
+ * SRE - Set Region Enqueued.  Enqueue a 'Set Region' command in
+ *       the other thread providing the new UUID of the region.
+ *       TFReqSetRegion carries the data.
+ * SR  - Set Region.  New region UUID is sent to the thread-local
+ *       collector.
+ * SME - Send Metrics Enqueued.  Enqueue a 'Send Metrics' command
+ *       including an ownership transfer of a cloned LLViewerAssetStats.
+ *       TFReqSendMetrics carries the data.
+ * SM  - Send Metrics.  Global metrics reporting operation.  Takes
+ *       the cloned stats from the command, merges it with the
+ *       thread's local stats, converts to LLSD and sends it on
+ *       to the grid.
+ * AM  - Agent Moved.  Agent has completed some sort of move to a
+ *       new region.
+ * TE  - Timer Expired.  Metrics timer has expired (on the order
+ *       of 10 minutes).
+ * CP  - CURL Post
+ * MSC - Modify Stats Collector.  State change in the thread-local
+ *       collector.  Typically a region change which affects the
+ *       global pointers used to find the 'current stats'.
+ * RSC - Read Stats Collector.  Extract collector data cloning it
+ *       (i.e. deep copy) when necessary.
+ *
+ */
+class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
+{
+public:
+	// Default ctors and assignment operator are correct.
+
+	virtual ~TFRequest()
+		{}
+
+	// Patterned after QueuedRequest's method but expected behavior
+	// is different.  Always expected to complete on the first call
+	// and work dispatcher will assume the same and delete the
+	// request after invocation.
+	virtual bool doWork(LLTextureFetch * fetcher) = 0;
+};
+
+namespace 
+{
+
+/**
+ * @brief Implements a 'Set Region' cross-thread command.
+ *
+ * When an agent moves to a new region, subsequent metrics need
+ * to be binned into a new or existing stats collection in 1:1
+ * relationship with the region.  We communicate this region
+ * change across the threads involved in the communication with
+ * this message.
+ *
+ * Corresponds to LLTextureFetch::commandSetRegion()
+ */
+class TFReqSetRegion : public LLTextureFetch::TFRequest
+{
+public:
+	TFReqSetRegion(U64 region_handle)
+		: LLTextureFetch::TFRequest(),
+		  mRegionHandle(region_handle)
+		{}
+	TFReqSetRegion & operator=(const TFReqSetRegion &);	// Not defined
+
+	virtual ~TFReqSetRegion()
+		{}
+
+	virtual bool doWork(LLTextureFetch * fetcher);
+		
+public:
+	const U64 mRegionHandle;
+};
+
+
+/**
+ * @brief Implements a 'Send Metrics' cross-thread command.
+ *
+ * This is the big operation.  The main thread gathers metrics
+ * for a period of minutes into LLViewerAssetStats and other
+ * objects then makes a snapshot of the data by cloning the
+ * collector.  This command transfers the clone, along with a few
+ * additional arguments (UUIDs), handing ownership to the
+ * TextureFetch thread.  It then merges its own data into the
+ * cloned copy, converts to LLSD and kicks off an HTTP POST of
+ * the resulting data to the currently active metrics collector.
+ *
+ * Corresponds to LLTextureFetch::commandSendMetrics()
+ */
+class TFReqSendMetrics : public LLTextureFetch::TFRequest
+{
+public:
+    /**
+	 * Construct the 'Send Metrics' command to have the TextureFetch
+	 * thread add and log metrics data.
+	 *
+	 * @param	caps_url		URL of a "ViewerMetrics" Caps target
+	 *							to receive the data.  Does not have to
+	 *							be associated with a particular region.
+	 *
+	 * @param	session_id		UUID of the agent's session.
+	 *
+	 * @param	agent_id		UUID of the agent.  (Being pure here...)
+	 *
+	 * @param	main_stats		Pointer to a clone of the main thread's
+	 *							LLViewerAssetStats data.  Thread1 takes
+	 *							ownership of the copy and disposes of it
+	 *							when done.
+	 */
+	TFReqSendMetrics(const std::string & caps_url,
+					 const LLUUID & session_id,
+					 const LLUUID & agent_id,
+					 LLViewerAssetStats * main_stats)
+		: LLTextureFetch::TFRequest(),
+		  mCapsURL(caps_url),
+		  mSessionID(session_id),
+		  mAgentID(agent_id),
+		  mMainStats(main_stats)
+		{}
+	TFReqSendMetrics & operator=(const TFReqSendMetrics &);	// Not defined
+
+	virtual ~TFReqSendMetrics();
+
+	virtual bool doWork(LLTextureFetch * fetcher);
+		
+public:
+	const std::string mCapsURL;
+	const LLUUID mSessionID;
+	const LLUUID mAgentID;
+	LLViewerAssetStats * mMainStats;
+};
+
+/*
+ * Examines the merged viewer metrics report and if found to be too long,
+ * will attempt to truncate it in some reasonable fashion.
+ *
+ * @param		max_regions		Limit of regions allowed in report.
+ *
+ * @param		metrics			Full, merged viewer metrics report.
+ *
+ * @returns		If data was truncated, returns true.
+ */
+bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
+
+} // end of anonymous namespace
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+//static
+const char* LLTextureFetchWorker::sStateDescs[] = {
+	"INVALID",
+	"INIT",
+	"LOAD_FROM_TEXTURE_CACHE",
+	"CACHE_POST",
+	"LOAD_FROM_NETWORK",
+	"LOAD_FROM_SIMULATOR",
+	"SEND_HTTP_REQ",
+	"WAIT_HTTP_REQ",
+	"DECODE_IMAGE",
+	"DECODE_IMAGE_UPDATE",
+	"WRITE_TO_CACHE",
+	"WAIT_ON_WRITE",
+	"DONE",
+};
+
+// static
+volatile bool LLTextureFetch::svMetricsDataBreak(true);	// Start with a data break
+
+// called from MAIN THREAD
+
+LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
+										   const std::string& url, // Optional URL
+										   const LLUUID& id,	// Image UUID
+										   const LLHost& host,	// Simulator host
+										   F32 priority,		// Priority
+										   S32 discard,			// Desired discard
+										   S32 size)			// Desired size
+	: LLWorkerClass(fetcher, "TextureFetch"),
+	  mState(INIT),
+	  mWriteToCacheState(NOT_WRITE),
+	  mFetcher(fetcher),
+	  mID(id),
+	  mHost(host),
+	  mUrl(url),
+	  mImagePriority(priority),
+	  mWorkPriority(0),
+	  mRequestedPriority(0.f),
+	  mDesiredDiscard(-1),
+	  mSimRequestedDiscard(-1),
+	  mRequestedDiscard(-1),
+	  mLoadedDiscard(-1),
+	  mDecodedDiscard(-1),
+	  mCacheReadHandle(LLTextureCache::nullHandle()),
+	  mCacheWriteHandle(LLTextureCache::nullHandle()),
+	  mBuffer(NULL),
+	  mBufferSize(0),
+	  mRequestedSize(0),
+	  mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
+	  mFileSize(0),
+	  mCachedSize(0),
+	  mLoaded(FALSE),
+	  mSentRequest(UNSENT),
+	  mDecodeHandle(0),
+	  mDecoded(FALSE),
+	  mWritten(FALSE),
+	  mNeedsAux(FALSE),
+	  mHaveAllData(FALSE),
+	  mInLocalCache(FALSE),
+	  mCanUseHTTP(true),
+	  mHTTPFailCount(0),
+	  mRetryAttempt(0),
+	  mActiveCount(0),
+	  mGetStatus(0),
+	  mWorkMutex(NULL),
+	  mFirstPacket(0),
+	  mLastPacket(-1),
+	  mTotalPackets(0),
+	  mImageCodec(IMG_CODEC_INVALID),
+	  mMetricsStartTime(0)
+{
+	mCanUseNET = mUrl.empty() ;
+
+	calcWorkPriority();
+	mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL;
+// 	llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl;
+	if (!mFetcher->mDebugPause)
+	{
+		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+		addWork(0, work_priority );
+	}
+	setDesiredDiscard(discard, size);
+}
+
+LLTextureFetchWorker::~LLTextureFetchWorker()
+{
+// 	llinfos << "Destroy: " << mID
+// 			<< " Decoded=" << mDecodedDiscard
+// 			<< " Requested=" << mRequestedDiscard
+// 			<< " Desired=" << mDesiredDiscard << llendl;
+	llassert_always(!haveWork());
+	lockWorkMutex();
+	if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
+	{
+		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
+	}
+	if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
+	{
+		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
+	}
+	mFormattedImage = NULL;
+	clearPackets();
+	unlockWorkMutex();
+	mFetcher->removeFromHTTPQueue(mID);
+}
+
+void LLTextureFetchWorker::clearPackets()
+{
+	for_each(mPackets.begin(), mPackets.end(), DeletePointer());
+	mPackets.clear();
+	mTotalPackets = 0;
+	mLastPacket = -1;
+	mFirstPacket = 0;
+}
+
+void LLTextureFetchWorker::setupPacketData()
+{
+	S32 data_size = 0;
+	if (mFormattedImage.notNull())
+	{
+		data_size = mFormattedImage->getDataSize();
+	}
+	if (data_size > 0)
+	{
+		// Only used for simulator requests
+		mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
+		if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
+		{
+			llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
+			removeFromCache();
+			resetFormattedData();
+			clearPackets();
+		}
+		else if (mFileSize > 0)
+		{
+			mLastPacket = mFirstPacket-1;
+			mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
+		}
+		else
+		{
+			// This file was cached using HTTP so we have to refetch the first packet
+			resetFormattedData();
+			clearPackets();
+		}
+	}
+}
+
+U32 LLTextureFetchWorker::calcWorkPriority()
+{
+ 	//llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority());
+	static const F32 PRIORITY_SCALE = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerFetchedTexture::maxDecodePriority();
+
+	mWorkPriority = llmin((U32)LLWorkerThread::PRIORITY_LOWBITS, (U32)(mImagePriority * PRIORITY_SCALE));
+	return mWorkPriority;
+}
+
+// mWorkMutex is locked
+void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
+{
+	bool prioritize = false;
+	if (mDesiredDiscard != discard)
+	{
+		if (!haveWork())
+		{
+			calcWorkPriority();
+			if (!mFetcher->mDebugPause)
+			{
+				U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+				addWork(0, work_priority);
+			}
+		}
+		else if (mDesiredDiscard < discard)
+		{
+			prioritize = true;
+		}
+		mDesiredDiscard = discard;
+		mDesiredSize = size;
+	}
+	else if (size > mDesiredSize)
+	{
+		mDesiredSize = size;
+		prioritize = true;
+	}
+	mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE);
+	if ((prioritize && mState == INIT) || mState == DONE)
+	{
+		mState = INIT;
+		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+		setPriority(work_priority);
+	}
+}
+
+void LLTextureFetchWorker::setImagePriority(F32 priority)
+{
+// 	llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority());
+	F32 delta = fabs(priority - mImagePriority);
+	if (delta > (mImagePriority * .05f) || mState == DONE)
+	{
+		mImagePriority = priority;
+		calcWorkPriority();
+		U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS);
+		setPriority(work_priority);
+	}
+}
+
+void LLTextureFetchWorker::resetFormattedData()
+{
+	delete[] mBuffer;
+	mBuffer = NULL;
+	mBufferSize = 0;
+	if (mFormattedImage.notNull())
+	{
+		mFormattedImage->deleteData();
+	}
+	mHaveAllData = FALSE;
+}
+
+// Called from MAIN thread
+void LLTextureFetchWorker::startWork(S32 param)
+{
+	llassert(mFormattedImage.isNull());
+}
+
+#include "llviewertexturelist.h" // debug
+
+// Called from LLWorkerThread::processRequest()
+bool LLTextureFetchWorker::doWork(S32 param)
+{
+	LLMutexLock lock(&mWorkMutex);
+
+	if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
+	{
+		if (mState < DECODE_IMAGE)
+		{
+			return true; // abort
+		}
+	}
+
+	if(mImagePriority < F_ALMOST_ZERO)
+	{
+		if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR)
+		{
+			return true; // abort
+		}
+	}
+	if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP)
+	{
+		//nowhere to get data, abort.
+		return true ;
+	}
+
+	if (mFetcher->mDebugPause)
+	{
+		return false; // debug: don't do any work
+	}
+	if (mID == mFetcher->mDebugID)
+	{
+		mFetcher->mDebugCount++; // for setting breakpoints
+	}
+
+	if (mState != DONE)
+	{
+		mFetchTimer.reset();
+	}
+
+	if (mState == INIT)
+	{		
+		mRawImage = NULL ;
+		mRequestedDiscard = -1;
+		mLoadedDiscard = -1;
+		mDecodedDiscard = -1;
+		mRequestedSize = 0;
+		mFileSize = 0;
+		mCachedSize = 0;
+		mLoaded = FALSE;
+		mSentRequest = UNSENT;
+		mDecoded  = FALSE;
+		mWritten  = FALSE;
+		delete[] mBuffer;
+		mBuffer = NULL;
+		mBufferSize = 0;
+		mHaveAllData = FALSE;
+		clearPackets(); // TODO: Shouldn't be necessary
+		mCacheReadHandle = LLTextureCache::nullHandle();
+		mCacheWriteHandle = LLTextureCache::nullHandle();
+		mState = LOAD_FROM_TEXTURE_CACHE;
+		mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
+		LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
+							 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
+		// fall through
+	}
+
+	if (mState == LOAD_FROM_TEXTURE_CACHE)
+	{
+		if (mCacheReadHandle == LLTextureCache::nullHandle())
+		{
+			U32 cache_priority = mWorkPriority;
+			S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+			S32 size = mDesiredSize - offset;
+			if (size <= 0)
+			{
+				mState = CACHE_POST;
+				return false;
+			}
+			mFileSize = 0;
+			mLoaded = FALSE;			
+			
+			if (mUrl.compare(0, 7, "file://") == 0)
+			{
+				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+
+				// read file from local disk
+				std::string filename = mUrl.substr(7, std::string::npos);
+				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
+				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
+																		  offset, size, responder);
+			}
+			else if (mUrl.empty())
+			{
+				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+
+				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
+				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
+																		  offset, size, responder);
+			}
+			else if(mCanUseHTTP)
+			{
+				if (!(mUrl.compare(0, 7, "http://") == 0))
+				{
+					// *TODO:?remove this warning
+					llwarns << "Unknown URL Type: " << mUrl << llendl;
+				}
+				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+				mState = SEND_HTTP_REQ;
+			}
+			else
+			{
+				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+				mState = LOAD_FROM_NETWORK;
+			}
+		}
+
+		if (mLoaded)
+		{
+			// Make sure request is complete. *TODO: make this auto-complete
+			if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false))
+			{
+				mCacheReadHandle = LLTextureCache::nullHandle();
+				mState = CACHE_POST;
+				// fall through
+			}
+			else
+			{
+				return false;
+			}
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	if (mState == CACHE_POST)
+	{
+		mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+		// Successfully loaded
+		if ((mCachedSize >= mDesiredSize) || mHaveAllData)
+		{
+			// we have enough data, decode it
+			llassert_always(mFormattedImage->getDataSize() > 0);
+			mLoadedDiscard = mDesiredDiscard;
+			mState = DECODE_IMAGE;
+			mWriteToCacheState = NOT_WRITE ;
+			LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
+								 << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
+								 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
+			// fall through
+		}
+		else
+		{
+			if (mUrl.compare(0, 7, "file://") == 0)
+			{
+				// failed to load local file, we're done.
+				return true;
+			}
+			// need more data
+			else
+			{
+				LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
+				mState = LOAD_FROM_NETWORK;
+			}
+			// fall through
+		}
+	}
+
+	if (mState == LOAD_FROM_NETWORK)
+	{
+		static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP");
+
+// 		if (mHost != LLHost::invalid) get_url = false;
+		if ( use_http && mCanUseHTTP && mUrl.empty())//get http url.
+		{
+			LLViewerRegion* region = NULL;
+			if (mHost == LLHost::invalid)
+				region = gAgent.getRegion();
+			else
+				region = LLWorld::getInstance()->getRegion(mHost);
+
+			if (region)
+			{
+				std::string http_url = region->getHttpUrl() ;
+				if (!http_url.empty())
+				{
+					mUrl = http_url + "/?texture_id=" + mID.asString().c_str();
+					mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id.
+				}
+				else
+				{
+					mCanUseHTTP = false ;
+				}
+			}
+			else
+			{
+				// This will happen if not logged in or if a region deoes not have HTTP Texture enabled
+				//llwarns << "Region not found for host: " << mHost << llendl;
+				mCanUseHTTP = false;
+			}
+		}
+		if (mCanUseHTTP && !mUrl.empty())
+		{
+			mState = LLTextureFetchWorker::SEND_HTTP_REQ;
+			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+			if(mWriteToCacheState != NOT_WRITE)
+			{
+				mWriteToCacheState = CAN_WRITE ;
+			}
+			// don't return, fall through to next state
+		}
+		else if (mSentRequest == UNSENT && mCanUseNET)
+		{
+			// Add this to the network queue and sit here.
+			// LLTextureFetch::update() will send off a request which will change our state
+			mWriteToCacheState = CAN_WRITE ;
+			mRequestedSize = mDesiredSize;
+			mRequestedDiscard = mDesiredDiscard;
+			mSentRequest = QUEUED;
+			mFetcher->addToNetworkQueue(this);
+			if (! mMetricsStartTime)
+			{
+				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+			}
+			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 false,
+														 LLImageBase::TYPE_AVATAR_BAKE == mType);
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			
+			return false;
+		}
+		else
+		{
+			// Shouldn't need to do anything here
+			//llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
+			// Make certain this is in the network queue
+			//mFetcher->addToNetworkQueue(this);
+			//if (! mMetricsStartTime)
+			//{
+			//   mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+			//}
+			//LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
+			//                                             LLImageBase::TYPE_AVATAR_BAKE == mType);
+			//setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return false;
+		}
+	}
+	
+	if (mState == LOAD_FROM_SIMULATOR)
+	{
+		if (mFormattedImage.isNull())
+		{
+			mFormattedImage = new LLImageJ2C;
+		}
+		if (processSimulatorPackets())
+		{
+			LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL;
+			mFetcher->removeFromNetworkQueue(this, false);
+			if (mFormattedImage.isNull() || !mFormattedImage->getDataSize())
+			{
+				// processSimulatorPackets() failed
+// 				llwarns << "processSimulatorPackets() failed to load buffer" << llendl;
+				return true; // failed
+			}
+			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+			mState = DECODE_IMAGE;
+			mWriteToCacheState = SHOULD_WRITE;
+
+			if (mMetricsStartTime)
+			{
+				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
+															  false,
+															  LLImageBase::TYPE_AVATAR_BAKE == mType,
+															  LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
+				mMetricsStartTime = 0;
+			}
+			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 false,
+														 LLImageBase::TYPE_AVATAR_BAKE == mType);
+		}
+		else
+		{
+			mFetcher->addToNetworkQueue(this); // failsafe
+			if (! mMetricsStartTime)
+			{
+				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+			}
+			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 false,
+														 LLImageBase::TYPE_AVATAR_BAKE == mType);
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+		}
+		return false;
+	}
+	
+	if (mState == SEND_HTTP_REQ)
+	{
+		if(mCanUseHTTP)
+		{
+			//NOTE:
+			//control the number of the http requests issued for:
+			//1, not openning too many file descriptors at the same time;
+			//2, control the traffic of http so udp gets bandwidth.
+			//
+			static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8 ;
+			if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE)
+			{
+				return false ; //wait.
+			}
+
+			mFetcher->removeFromNetworkQueue(this, false);
+			
+			S32 cur_size = 0;
+			if (mFormattedImage.notNull())
+			{
+				cur_size = mFormattedImage->getDataSize(); // amount of data we already have
+				if (mFormattedImage->getDiscardLevel() == 0)
+				{
+					if(cur_size > 0)
+					{
+						// We already have all the data, just decode it
+						mLoadedDiscard = mFormattedImage->getDiscardLevel();
+						mState = DECODE_IMAGE;
+						return false;
+					}
+					else
+					{
+						return true ; //abort.
+					}
+				}
+			}
+			mRequestedSize = mDesiredSize;
+			mRequestedDiscard = mDesiredDiscard;
+			mRequestedSize -= cur_size;
+			S32 offset = cur_size;
+			mBufferSize = cur_size; // This will get modified by callbackHttpGet()
+			
+			bool res = false;
+			if (!mUrl.empty())
+			{
+				mLoaded = FALSE;
+				mGetStatus = 0;
+				mGetReason.clear();
+				LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset
+									 << " Bytes: " << mRequestedSize
+									 << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
+									 << LL_ENDL;
+				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+				mState = WAIT_HTTP_REQ;	
+
+				mFetcher->addToHTTPQueue(mID);
+				if (! mMetricsStartTime)
+				{
+					mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+				}
+				LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+															 true,
+															 LLImageBase::TYPE_AVATAR_BAKE == mType);
+
+				// Will call callbackHttpGet when curl request completes
+				std::vector<std::string> headers;
+				headers.push_back("Accept: image/x-j2c");
+				res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
+															  new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
+			}
+			if (!res)
+			{
+				llwarns << "HTTP GET request failed for " << mID << llendl;
+				resetFormattedData();
+				++mHTTPFailCount;
+				return true; // failed
+			}
+			// fall through
+		}
+		else //can not use http fetch.
+		{
+			return true ; //abort
+		}
+	}
+	
+	if (mState == WAIT_HTTP_REQ)
+	{
+		if (mLoaded)
+		{
+			S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+			if (mRequestedSize < 0)
+			{
+				S32 max_attempts;
+				if (mGetStatus == HTTP_NOT_FOUND)
+				{
+					mHTTPFailCount = max_attempts = 1; // Don't retry
+					llwarns << "Texture missing from server (404): " << mUrl << llendl;
+
+					//roll back to try UDP
+					if(mCanUseNET)
+					{
+						mState = INIT ;
+						mCanUseHTTP = false ;
+						setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+						return false ;
+					}
+				}
+				else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE)
+				{
+					// *TODO: Should probably introduce a timer here to delay future HTTP requsts
+					// for a short time (~1s) to ease server load? Ideally the server would queue
+					// requests instead of returning 503... we already limit the number pending.
+					++mHTTPFailCount;
+					max_attempts = mHTTPFailCount+1; // Keep retrying
+					LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL;
+				}
+				else
+				{
+					const S32 HTTP_MAX_RETRY_COUNT = 3;
+					max_attempts = HTTP_MAX_RETRY_COUNT + 1;
+					++mHTTPFailCount;
+					llinfos << "HTTP GET failed for: " << mUrl
+							<< " Status: " << mGetStatus << " Reason: '" << mGetReason << "'"
+							<< " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl;
+				}
+
+				if (mHTTPFailCount >= max_attempts)
+				{
+					if (cur_size > 0)
+					{
+						// Use available data
+						mLoadedDiscard = mFormattedImage->getDiscardLevel();
+						mState = DECODE_IMAGE;
+						return false; 
+					}
+					else
+					{
+						resetFormattedData();
+						mState = DONE;
+						return true; // failed
+					}
+				}
+				else
+				{
+					mState = SEND_HTTP_REQ;
+					return false; // retry
+				}
+			}
+			
+			llassert_always(mBufferSize == cur_size + mRequestedSize);
+			if(!mBufferSize)//no data received.
+			{
+				delete[] mBuffer; 
+				mBuffer = NULL;
+
+				//abort.
+				mState = DONE;
+				return true;
+			}
+
+			if (mFormattedImage.isNull())
+			{
+				// For now, create formatted image based on extension
+				std::string extension = gDirUtilp->getExtension(mUrl);
+				mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
+				if (mFormattedImage.isNull())
+				{
+					mFormattedImage = new LLImageJ2C; // default
+				}
+			}
+						
+			if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded.
+			{
+				mFileSize = mBufferSize;
+			}
+			else //the file size is unknown.
+			{
+				mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded.
+			}
+			
+			U8* buffer = new U8[mBufferSize];
+			if (cur_size > 0)
+			{
+				memcpy(buffer, mFormattedImage->getData(), cur_size);
+			}
+			memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append
+			// NOTE: setData releases current data and owns new data (buffer)
+			mFormattedImage->setData(buffer, mBufferSize);
+			// delete temp data
+			delete[] mBuffer; // Note: not 'buffer' (assigned in setData())
+			mBuffer = NULL;
+			mBufferSize = 0;
+			mLoadedDiscard = mRequestedDiscard;
+			mState = DECODE_IMAGE;
+			if(mWriteToCacheState != NOT_WRITE)
+			{
+				mWriteToCacheState = SHOULD_WRITE ;
+			}
+			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+			return false;
+		}
+		else
+		{
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return false;
+		}
+	}
+	
+	if (mState == DECODE_IMAGE)
+	{
+		static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
+		if(textures_decode_disabled)
+		{
+			// for debug use, don't decode
+			mState = DONE;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return true;
+		}
+
+		if (mDesiredDiscard < 0)
+		{
+			// We aborted, don't decode
+			mState = DONE;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return true;
+		}
+		
+		if (mFormattedImage->getDataSize() <= 0)
+		{
+			//llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl;
+			
+			//abort, don't decode
+			mState = DONE;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return true;
+		}
+		if (mLoadedDiscard < 0)
+		{
+			//llerrs << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl;
+
+			//abort, don't decode
+			mState = DONE;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return true;
+		}
+		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+		mRawImage = NULL;
+		mAuxImage = NULL;
+		llassert_always(mFormattedImage.notNull());
+		S32 discard = mHaveAllData ? 0 : mLoadedDiscard;
+		U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
+		mDecoded  = FALSE;
+		mState = DECODE_IMAGE_UPDATE;
+		LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard
+				<< " All Data: " << mHaveAllData << LL_ENDL;
+		mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux,
+																  new DecodeResponder(mFetcher, mID, this));
+		// fall though
+	}
+	
+	if (mState == DECODE_IMAGE_UPDATE)
+	{
+		if (mDecoded)
+		{
+			if (mDecodedDiscard < 0)
+			{
+				LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
+				if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0)
+				{
+					// Cache file should be deleted, try again
+// 					llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl;
+					llassert_always(mDecodeHandle == 0);
+					mFormattedImage = NULL;
+					++mRetryAttempt;
+					setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+					mState = INIT;
+					return false;
+				}
+				else
+				{
+// 					llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl;
+					mState = DONE; // failed
+				}
+			}
+			else
+			{
+				llassert_always(mRawImage.notNull());
+				LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard
+						<< " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
+				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+				mState = WRITE_TO_CACHE;
+			}
+			// fall through
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	if (mState == WRITE_TO_CACHE)
+	{
+		if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull())
+		{
+			// If we're in a local cache or we didn't actually receive any new data,
+			// or we failed to load anything, skip
+			mState = DONE;
+			return false;
+		}
+		S32 datasize = mFormattedImage->getDataSize();
+		if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed.
+		{
+			if(mHaveAllData)
+			{
+				mFileSize = datasize ;
+			}
+			else
+			{
+				mFileSize = datasize + 1 ; //flag not fully loaded.
+			}
+		}
+		llassert_always(datasize);
+		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+		U32 cache_priority = mWorkPriority;
+		mWritten = FALSE;
+		mState = WAIT_ON_WRITE;
+		CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
+		mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
+																  mFormattedImage->getData(), datasize,
+																  mFileSize, responder);
+		// fall through
+	}
+	
+	if (mState == WAIT_ON_WRITE)
+	{
+		if (writeToCacheComplete())
+		{
+			mState = DONE;
+			// fall through
+		}
+		else
+		{
+			if (mDesiredDiscard < mDecodedDiscard)
+			{
+				// We're waiting for this write to complete before we can receive more data
+				// (we can't touch mFormattedImage until the write completes)
+				// Prioritize the write
+				mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle);
+			}
+			return false;
+		}
+	}
+
+	if (mState == DONE)
+	{
+		if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard)
+		{
+			// More data was requested, return to INIT
+			mState = INIT;
+			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+			return false;
+		}
+		else
+		{
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return true;
+		}
+	}
+	
+	return false;
+}
+
+// Called from MAIN thread
+void LLTextureFetchWorker::endWork(S32 param, bool aborted)
+{
+	if (mDecodeHandle != 0)
+	{
+		mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false);
+		mDecodeHandle = 0;
+	}
+	mFormattedImage = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// virtual
+void LLTextureFetchWorker::finishWork(S32 param, bool completed)
+{
+	// The following are required in case the work was aborted
+	if (mCacheReadHandle != LLTextureCache::nullHandle())
+	{
+		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
+		mCacheReadHandle = LLTextureCache::nullHandle();
+	}
+	if (mCacheWriteHandle != LLTextureCache::nullHandle())
+	{
+		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
+		mCacheWriteHandle = LLTextureCache::nullHandle();
+	}
+}
+
+// virtual
+bool LLTextureFetchWorker::deleteOK()
+{
+	bool delete_ok = true;
+	// Allow any pending reads or writes to complete
+	if (mCacheReadHandle != LLTextureCache::nullHandle())
+	{
+		if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
+		{
+			mCacheReadHandle = LLTextureCache::nullHandle();
+		}
+		else
+		{
+			delete_ok = false;
+		}
+	}
+	if (mCacheWriteHandle != LLTextureCache::nullHandle())
+	{
+		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
+		{
+			mCacheWriteHandle = LLTextureCache::nullHandle();
+		}
+		else
+		{
+			delete_ok = false;
+		}
+	}
+
+	if ((haveWork() &&
+		 // not ok to delete from these states
+		 ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE))))
+	{
+		delete_ok = false;
+	}
+	
+	return delete_ok;
+}
+
+void LLTextureFetchWorker::removeFromCache()
+{
+	if (!mInLocalCache)
+	{
+		mFetcher->mTextureCache->removeFromCache(mID);
+	}
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::processSimulatorPackets()
+{
+	if (mFormattedImage.isNull() || mRequestedSize < 0)
+	{
+		// not sure how we got here, but not a valid state, abort!
+		llassert_always(mDecodeHandle == 0);
+		mFormattedImage = NULL;
+		return true;
+	}
+	
+	if (mLastPacket >= mFirstPacket)
+	{
+		S32 buffer_size = mFormattedImage->getDataSize();
+		for (S32 i = mFirstPacket; i<=mLastPacket; i++)
+		{
+			llassert_always(mPackets[i]);
+			buffer_size += mPackets[i]->mSize;
+		}
+		bool have_all_data = mLastPacket >= mTotalPackets-1;
+		if (mRequestedSize <= 0)
+		{
+			// We received a packed but haven't requested anything yet (edge case)
+			// Return true (we're "done") since we didn't request anything
+			return true;
+		}
+		if (buffer_size >= mRequestedSize || have_all_data)
+		{
+			/// We have enough (or all) data
+			if (have_all_data)
+			{
+				mHaveAllData = TRUE;
+			}
+			S32 cur_size = mFormattedImage->getDataSize();
+			if (buffer_size > cur_size)
+			{
+				/// We have new data
+				U8* buffer = new U8[buffer_size];
+				S32 offset = 0;
+				if (cur_size > 0 && mFirstPacket > 0)
+				{
+					memcpy(buffer, mFormattedImage->getData(), cur_size);
+					offset = cur_size;
+				}
+				for (S32 i=mFirstPacket; i<=mLastPacket; i++)
+				{
+					memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
+					offset += mPackets[i]->mSize;
+				}
+				// NOTE: setData releases current data
+				mFormattedImage->setData(buffer, buffer_size);
+			}
+			mLoadedDiscard = mRequestedDiscard;
+			return true;
+		}
+	}
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
+										   const LLIOPipe::buffer_ptr_t& buffer,
+										   bool partial, bool success)
+{
+	S32 data_size = 0 ;
+
+	LLMutexLock lock(&mWorkMutex);
+
+	if (mState != WAIT_HTTP_REQ)
+	{
+		llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
+				<< " req=" << mSentRequest << " state= " << mState << llendl;
+		return data_size;
+	}
+	if (mLoaded)
+	{
+		llwarns << "Duplicate callback for " << mID.asString() << llendl;
+		return data_size ; // ignore duplicate callback
+	}
+	if (success)
+	{
+		// get length of stream:
+		data_size = buffer->countAfter(channels.in(), NULL);		
+	
+		LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
+		if (data_size > 0)
+		{
+			// *TODO: set the formatted image data here directly to avoid the copy
+			mBuffer = new U8[data_size];
+			buffer->readAfter(channels.in(), NULL, mBuffer, data_size);
+			mBufferSize += data_size;
+			if (data_size < mRequestedSize && mRequestedDiscard == 0)
+			{
+				mHaveAllData = TRUE;
+			}
+			else if (data_size > mRequestedSize)
+			{
+				// *TODO: This shouldn't be happening any more
+				llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
+				mHaveAllData = TRUE;
+				llassert_always(mDecodeHandle == 0);
+				mFormattedImage = NULL; // discard any previous data we had
+				mBufferSize = data_size;
+			}
+		}
+		else
+		{
+			// We requested data but received none (and no error),
+			// so presumably we have all of it
+			mHaveAllData = TRUE;
+		}
+		mRequestedSize = data_size;
+	}
+	else
+	{
+		mRequestedSize = -1; // error
+	}
+	mLoaded = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+
+	return data_size ;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image,
+											 S32 imagesize, BOOL islocal)
+{
+	LLMutexLock lock(&mWorkMutex);
+	if (mState != LOAD_FROM_TEXTURE_CACHE)
+	{
+// 		llwarns << "Read callback for " << mID << " with state = " << mState << llendl;
+		return;
+	}
+	if (success)
+	{
+		llassert_always(imagesize >= 0);
+		mFileSize = imagesize;
+		mFormattedImage = image;
+		mImageCodec = image->getCodec();
+		mInLocalCache = islocal;
+		if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize)
+		{
+			mHaveAllData = TRUE;
+		}
+	}
+	mLoaded = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+void LLTextureFetchWorker::callbackCacheWrite(bool success)
+{
+	LLMutexLock lock(&mWorkMutex);
+	if (mState != WAIT_ON_WRITE)
+	{
+// 		llwarns << "Write callback for " << mID << " with state = " << mState << llendl;
+		return;
+	}
+	mWritten = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux)
+{
+	LLMutexLock lock(&mWorkMutex);
+	if (mDecodeHandle == 0)
+	{
+		return; // aborted, ignore
+	}
+	if (mState != DECODE_IMAGE_UPDATE)
+	{
+// 		llwarns << "Decode callback for " << mID << " with state = " << mState << llendl;
+		mDecodeHandle = 0;
+		return;
+	}
+	llassert_always(mFormattedImage.notNull());
+	
+	mDecodeHandle = 0;
+	if (success)
+	{
+		llassert_always(raw);
+		mRawImage = raw;
+		mAuxImage = aux;
+		mDecodedDiscard = mFormattedImage->getDiscardLevel();
+ 		LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard
+							 << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
+	}
+	else
+	{
+		llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
+		removeFromCache();
+		mDecodedDiscard = -1; // Redundant, here for clarity and paranoia
+	}
+	mDecoded = TRUE;
+// 	llinfos << mID << " : DECODE COMPLETE " << llendl;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::writeToCacheComplete()
+{
+	// Complete write to cache
+	if (mCacheWriteHandle != LLTextureCache::nullHandle())
+	{
+		if (!mWritten)
+		{
+			return false;
+		}
+		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
+		{
+			mCacheWriteHandle = LLTextureCache::nullHandle();
+		}
+		else
+		{
+			return false;
+		}
+	}
+	return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+// public
+
+LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode)
+	: LLWorkerThread("TextureFetch", threaded),
+	  mDebugCount(0),
+	  mDebugPause(FALSE),
+	  mPacketCount(0),
+	  mBadPacketCount(0),
+	  mQueueMutex(getAPRPool()),
+	  mNetworkQueueMutex(getAPRPool()),
+	  mTextureCache(cache),
+	  mImageDecodeThread(imagedecodethread),
+	  mTextureBandwidth(0),
+	  mHTTPTextureBits(0),
+	  mTotalHTTPRequests(0),
+	  mCurlGetRequest(NULL),
+	  mQAMode(qa_mode)
+{
+	mCurlPOSTRequestCount = 0;
+	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
+	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
+}
+
+LLTextureFetch::~LLTextureFetch()
+{
+	clearDeleteList() ;
+
+	while (! mCommands.empty())
+	{
+		TFRequest * req(mCommands.front());
+		mCommands.erase(mCommands.begin());
+		delete req;
+	}
+	
+	// ~LLQueuedThread() called here
+}
+
+bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
+								   S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http)
+{
+	if (mDebugPause)
+	{
+		return false;
+	}
+	
+	LLTextureFetchWorker* worker = getWorker(id) ;
+	if (worker)
+	{
+		if (worker->mHost != host)
+		{
+			llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts: "
+					<< host << " != " << worker->mHost << llendl;
+			removeRequest(worker, true);
+			worker = NULL;
+			return false;
+		}
+	}
+
+	S32 desired_size;
+	std::string exten = gDirUtilp->getExtension(url);
+	if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
+	{
+		// Only do partial requests for J2C at the moment
+		desired_size = MAX_IMAGE_DATA_SIZE;
+		desired_discard = 0;
+	}
+	else if (desired_discard == 0)
+	{
+		// if we want the entire image, and we know its size, then get it all
+		// (calcDataSizeJ2C() below makes assumptions about how the image
+		// was compressed - this code ensures that when we request the entire image,
+		// we really do get it.)
+		desired_size = MAX_IMAGE_DATA_SIZE;
+	}
+	else if (w*h*c > 0)
+	{
+		// If the requester knows the dimensions of the image,
+		// this will calculate how much data we need without having to parse the header
+
+		desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard);
+	}
+	else
+	{
+		desired_size = TEXTURE_CACHE_ENTRY_SIZE;
+		desired_discard = MAX_DISCARD_LEVEL;
+	}
+
+	
+	if (worker)
+	{
+		if (worker->wasAborted())
+		{
+			return false; // need to wait for previous aborted request to complete
+		}
+		worker->lockWorkMutex();
+		worker->mActiveCount++;
+		worker->mNeedsAux = needs_aux;
+		worker->setImagePriority(priority);
+		worker->setDesiredDiscard(desired_discard, desired_size);
+		worker->setCanUseHTTP(can_use_http) ;
+		if (!worker->haveWork())
+		{
+			worker->mState = LLTextureFetchWorker::INIT;
+			worker->unlockWorkMutex();
+
+			worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+		}
+		else
+		{
+			worker->unlockWorkMutex();
+		}
+	}
+	else
+	{
+		worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size);
+		lockQueue() ;
+		mRequestMap[id] = worker;
+		unlockQueue() ;
+
+		worker->lockWorkMutex();
+		worker->mActiveCount++;
+		worker->mNeedsAux = needs_aux;
+		worker->setCanUseHTTP(can_use_http) ;
+		worker->unlockWorkMutex();
+	}
+	
+// 	llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl;
+	return true;
+}
+
+// protected
+void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
+{
+	lockQueue() ;
+	bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ;
+	unlockQueue() ;
+
+	LLMutexLock lock(&mNetworkQueueMutex);
+	if (in_request_map)
+	{
+		// only add to the queue if in the request map
+		// i.e. a delete has not been requested
+		mNetworkQueue.insert(worker->mID);
+	}
+	for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
+		 iter1 != mCancelQueue.end(); ++iter1)
+	{
+		iter1->second.erase(worker->mID);
+	}
+}
+
+void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel)
+{
+	LLMutexLock lock(&mNetworkQueueMutex);
+	size_t erased = mNetworkQueue.erase(worker->mID);
+	if (cancel && erased > 0)
+	{
+		mCancelQueue[worker->mHost].insert(worker->mID);
+	}
+}
+
+// protected
+void LLTextureFetch::addToHTTPQueue(const LLUUID& id)
+{
+	LLMutexLock lock(&mNetworkQueueMutex);
+	mHTTPTextureQueue.insert(id);
+	mTotalHTTPRequests++;
+}
+
+void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size)
+{
+	LLMutexLock lock(&mNetworkQueueMutex);
+	mHTTPTextureQueue.erase(id);
+	mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits	
+}
+
+void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
+{
+	lockQueue() ;
+	LLTextureFetchWorker* worker = getWorkerAfterLock(id);
+	if (worker)
+	{		
+		size_t erased_1 = mRequestMap.erase(worker->mID);
+		unlockQueue() ;
+
+		llassert_always(erased_1 > 0) ;
+
+		removeFromNetworkQueue(worker, cancel);
+		llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
+
+		worker->scheduleDelete();	
+	}
+	else
+	{
+		unlockQueue() ;
+	}
+}
+
+void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel)
+{
+	lockQueue() ;
+	size_t erased_1 = mRequestMap.erase(worker->mID);
+	unlockQueue() ;
+
+	llassert_always(erased_1 > 0) ;
+	removeFromNetworkQueue(worker, cancel);
+	llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
+
+	worker->scheduleDelete();	
+}
+
+S32 LLTextureFetch::getNumRequests() 
+{ 
+	lockQueue() ;
+	S32 size = (S32)mRequestMap.size(); 
+	unlockQueue() ;
+
+	return size ;
+}
+
+S32 LLTextureFetch::getNumHTTPRequests() 
+{ 
+	mNetworkQueueMutex.lock() ;
+	S32 size = (S32)mHTTPTextureQueue.size(); 
+	mNetworkQueueMutex.unlock() ;
+
+	return size ;
+}
+
+U32 LLTextureFetch::getTotalNumHTTPRequests()
+{
+	mNetworkQueueMutex.lock() ;
+	U32 size = mTotalHTTPRequests ;
+	mNetworkQueueMutex.unlock() ;
+
+	return size ;
+}
+
+// call lockQueue() first!
+LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id)
+{
+	LLTextureFetchWorker* res = NULL;
+	map_t::iterator iter = mRequestMap.find(id);
+	if (iter != mRequestMap.end())
+	{
+		res = iter->second;
+	}
+	return res;
+}
+
+LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
+{
+	LLMutexLock lock(&mQueueMutex) ;
+
+	return getWorkerAfterLock(id) ;
+}
+
+
+bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
+										LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux)
+{
+	bool res = false;
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker)
+	{
+		if (worker->wasAborted())
+		{
+			res = true;
+		}
+		else if (!worker->haveWork())
+		{
+			// Should only happen if we set mDebugPause...
+			if (!mDebugPause)
+			{
+// 				llwarns << "Adding work for inactive worker: " << id << llendl;
+				worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+			}
+		}
+		else if (worker->checkWork())
+		{
+			worker->lockWorkMutex();
+			discard_level = worker->mDecodedDiscard;
+			raw = worker->mRawImage;
+			aux = worker->mAuxImage;
+			res = true;
+			LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
+			worker->unlockWorkMutex();
+		}
+		else
+		{
+			worker->lockWorkMutex();
+			if ((worker->mDecodedDiscard >= 0) &&
+				(worker->mDecodedDiscard < discard_level || discard_level < 0) &&
+				(worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE))
+			{
+				// Not finished, but data is ready
+				discard_level = worker->mDecodedDiscard;
+				raw = worker->mRawImage;
+				aux = worker->mAuxImage;
+			}
+			worker->unlockWorkMutex();
+		}
+	}
+	else
+	{
+		res = true;
+	}
+	return res;
+}
+
+bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
+{
+	bool res = false;
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker)
+	{
+		worker->lockWorkMutex();
+		worker->setImagePriority(priority);
+		worker->unlockWorkMutex();
+		res = true;
+	}
+	return res;
+}
+
+// Replicates and expands upon the base class's
+// getPending() implementation.  getPending() and
+// runCondition() replicate one another's logic to
+// an extent and are sometimes used for the same
+// function (deciding whether or not to sleep/pause
+// a thread).  So the implementations need to stay
+// in step, at least until this can be refactored and
+// the redundancy eliminated.
+//
+// May be called from any thread
+
+//virtual
+S32 LLTextureFetch::getPending()
+{
+	S32 res;
+	lockData();
+    {
+        LLMutexLock lock(&mQueueMutex);
+        
+        res = mRequestQueue.size();
+        res += mCurlPOSTRequestCount;
+        res += mCommands.size();
+    }
+	unlockData();
+	return res;
+}
+
+// virtual
+bool LLTextureFetch::runCondition()
+{
+	// Caller is holding the lock on LLThread's condition variable.
+	
+	// LLQueuedThread, unlike its base class LLThread, makes this a
+	// private method which is unfortunate.  I want to use it directly
+	// but I'm going to have to re-implement the logic here (or change
+	// declarations, which I don't want to do right now).
+	//
+	// Changes here may need to be reflected in getPending().
+	
+	bool have_no_commands(false);
+	{
+		LLMutexLock lock(&mQueueMutex);
+		
+		have_no_commands = mCommands.empty();
+	}
+	
+    bool have_no_curl_requests(0 == mCurlPOSTRequestCount);
+	
+	return ! (have_no_commands
+			  && have_no_curl_requests
+			  && (mRequestQueue.empty() && mIdleThread));		// From base class
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
+void LLTextureFetch::commonUpdate()
+{
+	// Run a cross-thread command, if any.
+	cmdDoWork();
+	
+	// Update Curl on same thread as mCurlGetRequest was constructed
+	S32 processed = mCurlGetRequest->process();
+	if (processed > 0)
+	{
+		lldebugs << "processed: " << processed << " messages." << llendl;
+	}
+}
+
+
+// MAIN THREAD
+//virtual
+S32 LLTextureFetch::update(U32 max_time_ms)
+{
+	static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS");
+
+	{
+		mNetworkQueueMutex.lock() ;
+		mMaxBandwidth = band_width ;
+
+		gTextureList.sTextureBits += mHTTPTextureBits ;
+		mHTTPTextureBits = 0 ;
+
+		mNetworkQueueMutex.unlock() ;
+	}
+
+	S32 res = LLWorkerThread::update(max_time_ms);
+	
+	if (!mDebugPause)
+	{
+		sendRequestListToSimulators();
+	}
+
+	if (!mThreaded)
+	{
+		commonUpdate();
+	}
+
+	return res;
+}
+
+//called in the MAIN thread after the TextureCacheThread shuts down.
+void LLTextureFetch::shutDownTextureCacheThread() 
+{
+	if(mTextureCache)
+	{
+		llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ;
+		mTextureCache = NULL ;
+	}
+}
+	
+//called in the MAIN thread after the ImageDecodeThread shuts down.
+void LLTextureFetch::shutDownImageDecodeThread() 
+{
+	if(mImageDecodeThread)
+	{
+		llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ;
+		mImageDecodeThread = NULL ;
+	}
+}
+
+// WORKER THREAD
+void LLTextureFetch::startThread()
+{
+	// Construct mCurlGetRequest from Worker Thread
+	mCurlGetRequest = new LLCurlRequest();
+}
+
+// WORKER THREAD
+void LLTextureFetch::endThread()
+{
+	// Destroy mCurlGetRequest from Worker Thread
+	delete mCurlGetRequest;
+	mCurlGetRequest = NULL;
+}
+
+// WORKER THREAD
+void LLTextureFetch::threadedUpdate()
+{
+	llassert_always(mCurlGetRequest);
+	
+	// Limit update frequency
+	const F32 PROCESS_TIME = 0.05f; 
+	static LLFrameTimer process_timer;
+	if (process_timer.getElapsedTimeF32() < PROCESS_TIME)
+	{
+		return;
+	}
+	process_timer.reset();
+	
+	commonUpdate();
+
+#if 0
+	const F32 INFO_TIME = 1.0f; 
+	static LLFrameTimer info_timer;
+	if (info_timer.getElapsedTimeF32() >= INFO_TIME)
+	{
+		S32 q = mCurlGetRequest->getQueued();
+		if (q > 0)
+		{
+			llinfos << "Queued gets: " << q << llendl;
+			info_timer.reset();
+		}
+	}
+#endif
+	
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetch::sendRequestListToSimulators()
+{
+	// All requests
+	const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps
+	
+	// Sim requests
+	const S32 IMAGES_PER_REQUEST = 50;
+	const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp
+	const F32 MIN_REQUEST_TIME = 1.0f;
+	const F32 MIN_DELTA_PRIORITY = 1000.f;
+
+	// Periodically, gather the list of textures that need data from the network
+	// And send the requests out to the simulators
+	static LLFrameTimer timer;
+	if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME)
+	{
+		return;
+	}
+	timer.reset();
+	
+	// Send requests
+	typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t;
+	typedef std::map< LLHost, request_list_t > work_request_map_t;
+	work_request_map_t requests;
+	{
+	LLMutexLock lock2(&mNetworkQueueMutex);
+	for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); )
+	{
+		queue_t::iterator curiter = iter++;
+		LLTextureFetchWorker* req = getWorker(*curiter);
+		if (!req)
+		{
+			mNetworkQueue.erase(curiter);
+			continue; // paranoia
+		}
+		if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) &&
+			(req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR))
+		{
+			// We already received our URL, remove from the queue
+			llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl;
+			mNetworkQueue.erase(curiter);
+			continue;
+		}
+		if (req->mID == mDebugID)
+		{
+			mDebugCount++; // for setting breakpoints
+		}
+		if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM &&
+			req->mTotalPackets > 0 &&
+			req->mLastPacket >= req->mTotalPackets-1)
+		{
+			// We have all the packets... make sure this is high priority
+// 			req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority);
+			continue;
+		}
+		F32 elapsed = req->mRequestedTimer.getElapsedTimeF32();
+		{
+			F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
+			if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
+				(delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
+				(elapsed >= SIM_LAZY_FLUSH_TIMEOUT))
+			{
+				requests[req->mHost].insert(req);
+			}
+		}
+	}
+	}
+
+	for (work_request_map_t::iterator iter1 = requests.begin();
+		 iter1 != requests.end(); ++iter1)
+	{
+		LLHost host = iter1->first;
+		// invalid host = use agent host
+		if (host == LLHost::invalid)
+		{
+			host = gAgent.getRegionHost();
+		}
+
+		S32 sim_request_count = 0;
+		
+		for (request_list_t::iterator iter2 = iter1->second.begin();
+			 iter2 != iter1->second.end(); ++iter2)
+		{
+			LLTextureFetchWorker* req = *iter2;
+			if (gMessageSystem)
+			{
+				if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM)
+				{
+					// Initialize packet data based on data read from cache
+					req->lockWorkMutex();
+					req->setupPacketData();
+					req->unlockWorkMutex();
+				}
+				if (0 == sim_request_count)
+				{
+					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+					gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+				}
+				S32 packet = req->mLastPacket + 1;
+				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+				gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
+				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mDesiredDiscard);
+				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority);
+				gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
+				gMessageSystem->addU8Fast(_PREHASH_Type, req->mType);
+// 				llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard
+// 						<< " Packet: " << packet << " Priority: " << req->mImagePriority << llendl;
+
+				static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
+				static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
+				if (log_to_viewer_log || log_to_sim)
+				{
+					mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime());
+					mTextureInfo.setRequestOffset(req->mID, 0);
+					mTextureInfo.setRequestSize(req->mID, 0);
+					mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP);
+				}
+
+				req->lockWorkMutex();
+				req->mSentRequest = LLTextureFetchWorker::SENT_SIM;
+				req->mSimRequestedDiscard = req->mDesiredDiscard;
+				req->mRequestedPriority = req->mImagePriority;
+				req->mRequestedTimer.reset();
+				req->unlockWorkMutex();
+				sim_request_count++;
+				if (sim_request_count >= IMAGES_PER_REQUEST)
+				{
+// 					llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+
+					gMessageSystem->sendSemiReliable(host, NULL, NULL);
+					sim_request_count = 0;
+				}
+			}
+		}
+		if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST)
+		{
+// 			llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+			gMessageSystem->sendSemiReliable(host, NULL, NULL);
+			sim_request_count = 0;
+		}
+	}
+	
+	// Send cancelations
+	{
+	LLMutexLock lock2(&mNetworkQueueMutex);
+	if (gMessageSystem && !mCancelQueue.empty())
+	{
+		for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
+			 iter1 != mCancelQueue.end(); ++iter1)
+		{
+			LLHost host = iter1->first;
+			if (host == LLHost::invalid)
+			{
+				host = gAgent.getRegionHost();
+			}
+			S32 request_count = 0;
+			for (queue_t::iterator iter2 = iter1->second.begin();
+				 iter2 != iter1->second.end(); ++iter2)
+			{
+				if (0 == request_count)
+				{
+					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+					gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+				}
+				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+				gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2);
+				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1);
+				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0);
+				gMessageSystem->addU32Fast(_PREHASH_Packet, 0);
+				gMessageSystem->addU8Fast(_PREHASH_Type, 0);
+// 				llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl;
+
+				request_count++;
+				if (request_count >= IMAGES_PER_REQUEST)
+				{
+					gMessageSystem->sendSemiReliable(host, NULL, NULL);
+					request_count = 0;
+				}
+			}
+			if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
+			{
+				gMessageSystem->sendSemiReliable(host, NULL, NULL);
+			}
+		}
+		mCancelQueue.clear();
+	}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
+{
+	mRequestedTimer.reset();
+	if (index >= mTotalPackets)
+	{
+// 		llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << llendl;
+		return false;
+	}
+	if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE)
+	{
+// 		llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << llendl;
+		return false;
+	}
+	
+	if (index >= (S32)mPackets.size())
+	{
+		mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
+	}
+	else if (mPackets[index] != NULL)
+	{
+// 		llwarns << "Received duplicate packet: " << index << " for image: " << mID << llendl;
+		return false;
+	}
+
+	mPackets[index] = new PacketData(data, size);
+	while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
+	{
+		++mLastPacket;
+	}
+	return true;
+}
+
+bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes,
+										U16 data_size, U8* data)
+{
+	LLTextureFetchWorker* worker = getWorker(id);
+	bool res = true;
+
+	++mPacketCount;
+	
+	if (!worker)
+	{
+// 		llwarns << "Received header for non active worker: " << id << llendl;
+		res = false;
+	}
+	else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK ||
+			 worker->mSentRequest != LLTextureFetchWorker::SENT_SIM)
+	{
+// 		llwarns << "receiveImageHeader for worker: " << id
+// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState]
+// 				<< " sent: " << worker->mSentRequest << llendl;
+		res = false;
+	}
+	else if (worker->mLastPacket != -1)
+	{
+		// check to see if we've gotten this packet before
+// 		llwarns << "Received duplicate header for: " << id << llendl;
+		res = false;
+	}
+	else if (!data_size)
+	{
+// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
+		res = false;
+	}
+	if (!res)
+	{
+		++mBadPacketCount;
+		mNetworkQueueMutex.lock() ;
+		mCancelQueue[host].insert(id);
+		mNetworkQueueMutex.unlock() ;
+		return false;
+	}
+
+	worker->lockWorkMutex();
+
+	//	Copy header data into image object
+	worker->mImageCodec = codec;
+	worker->mTotalPackets = packets;
+	worker->mFileSize = (S32)totalbytes;	
+	llassert_always(totalbytes > 0);
+	llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
+	res = worker->insertPacket(0, data, data_size);
+	worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+	worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
+	worker->unlockWorkMutex();
+	return res;
+}
+
+bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data)
+{
+	LLTextureFetchWorker* worker = getWorker(id);
+	bool res = true;
+
+	++mPacketCount;
+	
+	if (!worker)
+	{
+// 		llwarns << "Received packet " << packet_num << " for non active worker: " << id << llendl;
+		res = false;
+	}
+	else if (worker->mLastPacket == -1)
+	{
+// 		llwarns << "Received packet " << packet_num << " before header for: " << id << llendl;
+		res = false;
+	}
+	else if (!data_size)
+	{
+// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
+		res = false;
+	}
+	if (!res)
+	{
+		++mBadPacketCount;
+		mNetworkQueueMutex.lock() ;
+		mCancelQueue[host].insert(id);
+		mNetworkQueueMutex.unlock() ;
+		return false;
+	}
+
+	worker->lockWorkMutex();
+	
+	res = worker->insertPacket(packet_num, data, data_size);
+	
+	if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) ||
+		(worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK))
+	{
+		worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+		worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
+	}
+	else
+	{
+// 		llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id
+// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl;
+		removeFromNetworkQueue(worker, true); // failsafe
+	}
+
+	if(packet_num >= (worker->mTotalPackets - 1))
+	{
+		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
+		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
+
+		if (log_to_viewer_log || log_to_sim)
+		{
+			U64 timeNow = LLTimer::getTotalTime();
+			mTextureInfo.setRequestSize(id, worker->mFileSize);
+			mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow);
+		}
+	}
+	worker->unlockWorkMutex();
+
+	return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id)
+{
+	BOOL from_cache = FALSE ;
+
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker)
+	{
+		worker->lockWorkMutex() ;
+		from_cache = worker->mInLocalCache ;
+		worker->unlockWorkMutex() ;
+	}
+
+	return from_cache ;
+}
+
+S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p,
+								  U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http)
+{
+	S32 state = LLTextureFetchWorker::INVALID;
+	F32 data_progress = 0.0f;
+	F32 requested_priority = 0.0f;
+	F32 fetch_dtime = 999999.f;
+	F32 request_dtime = 999999.f;
+	U32 fetch_priority = 0;
+	
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker && worker->haveWork())
+	{
+		worker->lockWorkMutex();
+		state = worker->mState;
+		fetch_dtime = worker->mFetchTimer.getElapsedTimeF32();
+		request_dtime = worker->mRequestedTimer.getElapsedTimeF32();
+		if (worker->mFileSize > 0)
+		{
+			if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR)
+			{
+				S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE;
+				data_size = llmax(data_size, 0);
+				data_progress = (F32)data_size / (F32)worker->mFileSize;
+			}
+			else if (worker->mFormattedImage.notNull())
+			{
+				data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize;
+			}
+		}
+		if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ)
+		{
+			requested_priority = worker->mRequestedPriority;
+		}
+		else
+		{
+			requested_priority = worker->mImagePriority;
+		}
+		fetch_priority = worker->getPriority();
+		can_use_http = worker->getCanUseHTTP() ;
+		worker->unlockWorkMutex();
+	}
+	data_progress_p = data_progress;
+	requested_priority_p = requested_priority;
+	fetch_priority_p = fetch_priority;
+	fetch_dtime_p = fetch_dtime;
+	request_dtime_p = request_dtime;
+	return state;
+}
+
+void LLTextureFetch::dump()
+{
+	llinfos << "LLTextureFetch REQUESTS:" << llendl;
+	for (request_queue_t::iterator iter = mRequestQueue.begin();
+		 iter != mRequestQueue.end(); ++iter)
+	{
+		LLQueuedThread::QueuedRequest* qreq = *iter;
+		LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq;
+		LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass();
+		llinfos << " ID: " << worker->mID
+				<< " PRI: " << llformat("0x%08x",wreq->getPriority())
+				<< " STATE: " << worker->sStateDescs[worker->mState]
+				<< llendl;
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// cross-thread command methods
+
+void LLTextureFetch::commandSetRegion(U64 region_handle)
+{
+	TFReqSetRegion * req = new TFReqSetRegion(region_handle);
+
+	cmdEnqueue(req);
+}
+
+void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
+										const LLUUID & session_id,
+										const LLUUID & agent_id,
+										LLViewerAssetStats * main_stats)
+{
+	TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
+
+	cmdEnqueue(req);
+}
+
+void LLTextureFetch::commandDataBreak()
+{
+	// The pedantically correct way to implement this is to create a command
+	// request object in the above fashion and enqueue it.  However, this is
+	// simple data of an advisorial not operational nature and this case
+	// of shared-write access is tolerable.
+
+	LLTextureFetch::svMetricsDataBreak = true;
+}
+
+void LLTextureFetch::cmdEnqueue(TFRequest * req)
+{
+	lockQueue();
+	mCommands.push_back(req);
+	unlockQueue();
+
+	unpause();
+}
+
+LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
+{
+	TFRequest * ret = 0;
+	
+	lockQueue();
+	if (! mCommands.empty())
+	{
+		ret = mCommands.front();
+		mCommands.erase(mCommands.begin());
+	}
+	unlockQueue();
+
+	return ret;
+}
+
+void LLTextureFetch::cmdDoWork()
+{
+	if (mDebugPause)
+	{
+		return;  // debug: don't do any work
+	}
+
+	TFRequest * req = cmdDequeue();
+	if (req)
+	{
+		// One request per pass should really be enough for this.
+		req->doWork(this);
+		delete req;
+	}
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Private (anonymous) class methods implementing the command scheme.
+
+namespace
+{
+
+/**
+ * Implements the 'Set Region' command.
+ *
+ * Thread:  Thread1 (TextureFetch)
+ */
+bool
+TFReqSetRegion::doWork(LLTextureFetch *)
+{
+	LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
+
+	return true;
+}
+
+
+TFReqSendMetrics::~TFReqSendMetrics()
+{
+	delete mMainStats;
+	mMainStats = 0;
+}
+
+
+/**
+ * Implements the 'Send Metrics' command.  Takes over
+ * ownership of the passed LLViewerAssetStats pointer.
+ *
+ * Thread:  Thread1 (TextureFetch)
+ */
+bool
+TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
+{
+	/*
+	 * HTTP POST responder.  Doesn't do much but tries to
+	 * detect simple breaks in recording the metrics stream.
+	 *
+	 * The 'volatile' modifiers don't indicate signals,
+	 * mmap'd memory or threads, really.  They indicate that
+	 * the referenced data is part of a pseudo-closure for
+	 * this responder rather than being required for correct
+	 * operation.
+     *
+     * We don't try very hard with the POST request.  We give
+     * it one shot and that's more-or-less it.  With a proper
+     * refactoring of the LLQueuedThread usage, these POSTs
+     * could be put in a request object and made more reliable.
+	 */
+	class lcl_responder : public LLCurl::Responder
+	{
+	public:
+		lcl_responder(LLTextureFetch * fetcher,
+					  S32 expected_sequence,
+                      volatile const S32 & live_sequence,
+                      volatile bool & reporting_break,
+					  volatile bool & reporting_started)
+			: LLCurl::Responder(),
+			  mFetcher(fetcher),
+              mExpectedSequence(expected_sequence),
+              mLiveSequence(live_sequence),
+			  mReportingBreak(reporting_break),
+			  mReportingStarted(reporting_started)
+			{
+                mFetcher->incrCurlPOSTCount();
+            }
+        
+        ~lcl_responder()
+            {
+                mFetcher->decrCurlPOSTCount();
+            }
+
+		// virtual
+		void error(U32 status_num, const std::string & reason)
+			{
+                if (mLiveSequence == mExpectedSequence)
+                {
+                    mReportingBreak = true;
+                }
+				LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service.  Reason:  "
+									<< reason << LL_ENDL;
+			}
+
+		// virtual
+		void result(const LLSD & content)
+			{
+                if (mLiveSequence == mExpectedSequence)
+                {
+                    mReportingBreak = false;
+                    mReportingStarted = true;
+                }
+			}
+
+	private:
+		LLTextureFetch * mFetcher;
+        S32 mExpectedSequence;
+        volatile const S32 & mLiveSequence;
+		volatile bool & mReportingBreak;
+		volatile bool & mReportingStarted;
+
+	}; // class lcl_responder
+	
+	if (! gViewerAssetStatsThread1)
+		return true;
+
+	static volatile bool reporting_started(false);
+	static volatile S32 report_sequence(0);
+    
+	// We've taken over ownership of the stats copy at this
+	// point.  Get a working reference to it for merging here
+	// but leave it in 'this'.  Destructor will rid us of it.
+	LLViewerAssetStats & main_stats = *mMainStats;
+
+	// Merge existing stats into those from main, convert to LLSD
+	main_stats.merge(*gViewerAssetStatsThread1);
+	LLSD merged_llsd = main_stats.asLLSD(true);
+
+	// Add some additional meta fields to the content
+	merged_llsd["session_id"] = mSessionID;
+	merged_llsd["agent_id"] = mAgentID;
+	merged_llsd["message"] = "ViewerAssetMetrics";					// Identifies the type of metrics
+	merged_llsd["sequence"] = report_sequence;						// Sequence number
+	merged_llsd["initial"] = ! reporting_started;					// Initial data from viewer
+	merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak;		// Break in data prior to this report
+		
+	// Update sequence number
+	if (S32_MAX == ++report_sequence)
+		report_sequence = 0;
+
+	// Limit the size of the stats report if necessary.
+	merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
+
+	if (! mCapsURL.empty())
+	{
+		LLCurlRequest::headers_t headers;
+		fetcher->getCurlRequest().post(mCapsURL,
+									   headers,
+									   merged_llsd,
+									   new lcl_responder(fetcher,
+														 report_sequence,
+                                                         report_sequence,
+                                                         LLTextureFetch::svMetricsDataBreak,
+														 reporting_started));
+	}
+	else
+	{
+		LLTextureFetch::svMetricsDataBreak = true;
+	}
+
+	// In QA mode, Metrics submode, log the result for ease of testing
+	if (fetcher->isQAMode())
+	{
+		LL_INFOS("Textures") << merged_llsd << LL_ENDL;
+	}
+
+	gViewerAssetStatsThread1->reset();
+
+	return true;
+}
+
+
+bool
+truncate_viewer_metrics(int max_regions, LLSD & metrics)
+{
+	static const LLSD::String reg_tag("regions");
+	static const LLSD::String duration_tag("duration");
+	
+	LLSD & reg_map(metrics[reg_tag]);
+	if (reg_map.size() <= max_regions)
+	{
+		return false;
+	}
+
+	// Build map of region hashes ordered by duration
+	typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
+	reg_ordered_list_t regions_by_duration;
+
+	int ind(0);
+	LLSD::array_const_iterator it_end(reg_map.endArray());
+	for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
+	{
+		LLSD::Real duration = (*it)[duration_tag].asReal();
+		regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
+	}
+
+	// Build a replacement regions array with the longest-persistence regions
+	LLSD new_region(LLSD::emptyArray());
+	reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
+	reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
+	for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
+	{
+		new_region.append(reg_map[it2->second]);
+	}
+	reg_map = new_region;
+	
+	return true;
+}
+
+} // end of anonymous namespace
+
+
+
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index ad00a7ea36fdb6aec9d13e549e1f26b8bcb25f47..d101da1f4be1697e6221825a3c6cd72d8a7ca150 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -79,6 +79,7 @@ class LLTextureFetch : public LLWorkerThread
 	void dump();
 	S32 getNumRequests() ;
 	S32 getNumHTTPRequests() ;
+	U32 getTotalNumHTTPRequests() ;
 	
 	// Public for access by callbacks
     S32 getPending();
@@ -183,6 +184,9 @@ class LLTextureFetch : public LLWorkerThread
 
 	U32 mHTTPTextureBits;
 
+	//debug use
+	U32 mTotalHTTPRequests ;
+
 	// Out-of-band cross-thread command queue.  This command queue
 	// is logically tied to LLQueuedThread's list of
 	// QueuedRequest instances and so must be covered by the
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index b9a15fd1f4dd836c757b739118563ef81efb9e22..0115115a230fde1d5a2c5030a8649dd37af0fa02 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -516,6 +516,7 @@ void LLGLTexMemBar::draw()
 	S32 v_offset = (S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f);
 	F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024);
 	F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024);
+	U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests() ;
 	//----------------------------------------------------------------------------
 	LLGLSUIDefault gls_ui;
 	LLColor4 text_color(1.f, 1.f, 1.f, 0.75f);
@@ -526,13 +527,13 @@ void LLGLTexMemBar::draw()
 	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
 
-	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB",
+	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d",
 					total_mem,
 					max_total_mem,
 					bound_mem,
 					max_bound_mem,
 					LLImageRaw::sGlobalRawMemory >> 20,	discard_bias,
-					cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded);
+					cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded, total_http_requests);
 	//, cache_entries, cache_max_entries
 
 	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 5849ab4307c016364537d3bac79839daf0f7f768..970cc2e2a784837f282350e5c489cd2bab83e352 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -160,19 +160,13 @@ U64 LLViewerObjectList::getIndex(const U32 local_id,
 	return (((U64)index) << 32) | (U64)local_id;
 }
 
-BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object)
+BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject* objectp)
 {
-	if(object.getRegion())
+	if(objectp && objectp->getRegion())
 	{
-		U32 local_id = object.mLocalID;
-		LLHost region_host = object.getRegion()->getHost();
-		if(!region_host.isOk())
-		{
-			return FALSE ;
-		}
-
-		U32 ip = region_host.getAddress();
-		U32 port = region_host.getPort();
+		U32 local_id = objectp->mLocalID;		
+		U32 ip = objectp->getRegion()->getHost().getAddress();
+		U32 port = objectp->getRegion()->getHost().getPort();
 		U64 ipport = (((U64)ip) << 32) | (U64)port;
 		U32 index = sIPAndPortToIndex[ipport];
 		
@@ -187,7 +181,7 @@ BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object)
 		}
 		
 		// Found existing entry
-		if (iter->second == object.getID())
+		if (iter->second == objectp->getID())
 		{   // Full UUIDs match, so remove the entry
 			sIndexAndLocalIDToUUID.erase(iter);
 			return TRUE;
@@ -477,7 +471,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 			//			<< ", regionp " << (U32) regionp << ", object region " << (U32) objectp->getRegion()
 			//			<< llendl;
 			//}
-			removeFromLocalIDTable(*objectp);
+			removeFromLocalIDTable(objectp);
 			setUUIDAndLocal(fullid,
 							local_id,
 							gMessageSystem->getSenderIP(),
@@ -910,7 +904,7 @@ void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
 	//				<< objectp->getRegion()->getHost().getPort() << llendl;
 	//}	
 	
-	removeFromLocalIDTable(*objectp);
+	removeFromLocalIDTable(objectp);
 
 	if (objectp->onActiveList())
 	{
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index 605bac8e89f29fa0bc855bf6859950159e46c696..fda3d6899d577bcd4103c89d8cb2b144d9a54f8b 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -160,7 +160,7 @@ class LLViewerObjectList
 								const U32 ip,
 								const U32 port); // Requires knowledge of message system info!
 
-	static BOOL removeFromLocalIDTable(const LLViewerObject &object);
+	static BOOL removeFromLocalIDTable(const LLViewerObject* objectp);
 	// Used ONLY by the orphaned object code.
 	static U64 getIndex(const U32 local_id, const U32 ip, const U32 port);
 
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index c7649001c5479563dac58a045907b465917fbc55..23b7b921b8ce1ca94a74b0c2953d8ac4cc8ae67d 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -322,6 +322,12 @@ LLViewerRegion::~LLViewerRegion()
 	std::for_each(mObjectPartition.begin(), mObjectPartition.end(), DeletePointer());
 }
 
+/*virtual*/ 
+const LLHost&	LLViewerRegion::getHost() const				
+{ 
+	return mHost; 
+}
+
 void LLViewerRegion::loadObjectCache()
 {
 	if (mCacheLoaded)
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 7c6559203e5fa7606685f67a5cc8b328e1ffdf4a..dd40b876cd4c24173ab0104d8f9374946ff50981 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -245,7 +245,7 @@ class LLViewerRegion: public LLCapabilityProvider // implements this interface
     LLEventPump& getCapAPI() { return mCapabilityListener.getCapAPI(); }
 
     /// implements LLCapabilityProvider
-	virtual LLHost	getHost() const				{ return mHost; }
+	/*virtual*/ const LLHost& getHost() const;
 	const U64 		&getHandle() const 			{ return mHandle; }
 
 	LLSurface		&getLand() const			{ return *mLandp; }
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 0c05a301e6be6e5bda26c09c497a4ca01017c0a9..cd16b15e3ef56cc38e4ef5fb8d2aff053eba8d7b 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -1214,12 +1214,15 @@ void LLViewerFetchedTexture::cleanup()
 
 void LLViewerFetchedTexture::setForSculpt()
 {
+	static const S32 MAX_INTERVAL = 8 ; //frames
+
 	mForSculpt = TRUE ;
 	if(isForSculptOnly() && !getBoundRecently())
 	{
 		destroyGLTexture() ; //sculpt image does not need gl texture.
 	}
 	checkCachedRawSculptImage() ;
+	setMaxVirtualSizeResetInterval(MAX_INTERVAL) ;
 }
 
 BOOL LLViewerFetchedTexture::isForSculptOnly() const
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index c812fcf2daf512d6f8155da23d972ec5b87556d8..166b11041279a1d55109971ab61e8a4d59e77bf5 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -296,13 +296,15 @@ class LLDebugText
 	line_list_t mLineList;
 	LLColor4 mTextColor;
 	
-public:
-	LLDebugText(LLViewerWindow* window) : mWindow(window) {}
-	
 	void addText(S32 x, S32 y, const std::string &text) 
 	{
 		mLineList.push_back(Line(text, x, y));
 	}
+	
+	void clearText() { mLineList.clear(); }
+	
+public:
+	LLDebugText(LLViewerWindow* window) : mWindow(window) {}
 
 	void update()
 	{
@@ -323,6 +325,8 @@ class LLDebugText
 		U32 ypos = 64;
 		const U32 y_inc = 20;
 
+		clearText();
+		
 		if (gSavedSettings.getBOOL("DebugShowTime"))
 		{
 			const U32 y_inc2 = 15;
@@ -601,6 +605,50 @@ class LLDebugText
 				ypos += y_inc;
 			}
 		}
+		
+		if (gSavedSettings.getBOOL("DebugShowTextureInfo"))
+		{
+			LLViewerObject* objectp = NULL ;
+			//objectp = = gAgentCamera.getFocusObject();
+			
+			LLSelectNode* nodep = LLSelectMgr::instance().getHoverNode();
+			if (nodep)
+			{
+				objectp = nodep->getObject();			
+			}
+			if (objectp && !objectp->isDead())
+			{
+				S32 num_faces = objectp->mDrawable->getNumFaces() ;
+				
+				for(S32 i = 0 ; i < num_faces; i++)
+				{
+					LLFace* facep = objectp->mDrawable->getFace(i) ;
+					if(facep)
+					{
+						//addText(xpos, ypos, llformat("ts_min: %.3f ts_max: %.3f tt_min: %.3f tt_max: %.3f", facep->mTexExtents[0].mV[0], facep->mTexExtents[1].mV[0],
+						//		facep->mTexExtents[0].mV[1], facep->mTexExtents[1].mV[1]));
+						//ypos += y_inc;
+						
+						addText(xpos, ypos, llformat("v_size: %.3f:  p_size: %.3f", facep->getVirtualSize(), facep->getPixelArea()));
+						ypos += y_inc;
+						
+						//const LLTextureEntry *tep = facep->getTextureEntry();
+						//if(tep)
+						//{
+						//	addText(xpos, ypos, llformat("scale_s: %.3f:  scale_t: %.3f", tep->mScaleS, tep->mScaleT)) ;
+						//	ypos += y_inc;
+						//}
+						
+						LLViewerTexture* tex = facep->getTexture() ;
+						if(tex)
+						{
+							addText(xpos, ypos, llformat("ID: %s v_size: %.3f", tex->getID().asString().c_str(), tex->getMaxVirtualSize()));
+							ypos += y_inc;
+						}
+					}
+				}
+			}
+		}
 	}
 
 	void draw()
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index c26008d6406dcb71d9b313267576689b463cfea7..1e2736f7d936a9598f567a03591fc983d4abda17 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -225,8 +225,9 @@ BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
 // Format string used to construct filename for the object cache
 static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
 
-// Throw out 1/20 (5%) of our cache entries if we run out of room.
-const U32 ENTRIES_PURGE_FACTOR = 20;
+const U32 MAX_NUM_OBJECT_ENTRIES = 128 ;
+const U32 MIN_ENTRIES_TO_PURGE = 16 ;
+const U32 INVALID_TIME = 0 ;
 const char* object_cache_dirname = "objectcache";
 const char* header_filename = "object.cache";
 
@@ -261,6 +262,7 @@ void LLVOCache::destroyClass()
 LLVOCache::LLVOCache():
 	mInitialized(FALSE),
 	mReadOnly(TRUE),
+	mNumEntries(0),
 	mCacheSize(1)
 {
 	mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
@@ -298,17 +300,16 @@ void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)
 		llwarns << "Cache already initialized." << llendl;
 		return ;
 	}
+	mInitialized = TRUE ;
 
 	setDirNames(location);
 	if (!mReadOnly)
 	{
 		LLFile::mkdir(mObjectCacheDirName);
 	}
-
-	mCacheSize = size;
-
-	readCacheHeader();
-	mInitialized = TRUE ;
+	mCacheSize = llclamp(size, MIN_ENTRIES_TO_PURGE, MAX_NUM_OBJECT_ENTRIES);
+	mMetaInfo.mVersion = cache_version;
+	readCacheHeader();	
 
 	if(mMetaInfo.mVersion != cache_version) 
 	{
@@ -332,6 +333,8 @@ void LLVOCache::removeCache(ELLPath location)
 		return ;
 	}
 
+	llinfos << "about to remove the object cache due to settings." << llendl ;
+
 	std::string delem = gDirUtilp->getDirDelimiter();
 	std::string mask = delem + "*";
 	std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
@@ -352,6 +355,8 @@ void LLVOCache::removeCache()
 		return ;
 	}
 
+	llinfos << "about to remove the object cache due to some error." << llendl ;
+
 	std::string delem = gDirUtilp->getDirDelimiter();
 	std::string mask = delem + "*";
 	llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
@@ -361,62 +366,80 @@ void LLVOCache::removeCache()
 	writeCacheHeader();
 }
 
-void LLVOCache::clearCacheInMemory()
-{
-	std::for_each(mHandleEntryMap.begin(), mHandleEntryMap.end(), DeletePairedPointer());
-	mHandleEntryMap.clear();
-}
-
-void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename) 
+void LLVOCache::removeEntry(HeaderEntryInfo* entry) 
 {
-	U32 region_x, region_y;
+	llassert_always(mInitialized) ;
+	if(mReadOnly)
+	{
+		return ;
+	}
+	if(!entry)
+	{
+		return ;
+	}
 
-	grid_from_region_handle(handle, &region_x, &region_y);
-	filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, object_cache_dirname,
-			   llformat(OBJECT_CACHE_FILENAME, region_x, region_y));
+	header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry) ;
+	if(iter != mHeaderEntryQueue.end())
+	{		
+		mHandleEntryMap.erase(entry->mHandle) ;		
+		mHeaderEntryQueue.erase(iter) ;
+		removeFromCache(entry) ;
+		delete entry ;
 
-	return ;
+		mNumEntries = mHandleEntryMap.size() ;
+	}
 }
 
-void LLVOCache::removeFromCache(U64 handle)
+void LLVOCache::removeEntry(U64 handle) 
 {
-	if(mReadOnly)
+	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+	if(iter == mHandleEntryMap.end()) //no cache
 	{
-		llwarns << "Not removing cache for handle " << handle << ": Cache is currently in read-only mode." << llendl;
 		return ;
 	}
-
-	std::string filename;
-	getObjectCacheFilename(handle, filename);
-	LLAPRFile::remove(filename, mLocalAPRFilePoolp);	
+	HeaderEntryInfo* entry = iter->second ;
+	removeEntry(entry) ;
 }
 
-BOOL LLVOCache::checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error)
+void LLVOCache::clearCacheInMemory()
 {
-	if(!check_read(apr_file, src, n_bytes))
+	if(!mHeaderEntryQueue.empty()) 
 	{
-		if (remove_cache_on_error)
+		for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin(); iter != mHeaderEntryQueue.end(); ++iter)
 		{
-			removeCache() ;
+			delete *iter ;
 		}
-		return FALSE ;
+		mHeaderEntryQueue.clear();
+		mHandleEntryMap.clear();
+		mNumEntries = 0 ;
 	}
 
-	return TRUE ;
 }
 
-BOOL LLVOCache::checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error) 
+void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename) 
+{
+	U32 region_x, region_y;
+
+	grid_from_region_handle(handle, &region_x, &region_y);
+	filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, object_cache_dirname,
+			   llformat(OBJECT_CACHE_FILENAME, region_x, region_y));
+
+	return ;
+}
+
+void LLVOCache::removeFromCache(HeaderEntryInfo* entry)
 {
-	if(!check_write(apr_file, src, n_bytes))
+	if(mReadOnly)
 	{
-		if (remove_cache_on_error)
-		{
-			removeCache() ;
-		}
-		return FALSE ;
+		llwarns << "Not removing cache for handle " << entry->mHandle << ": Cache is currently in read-only mode." << llendl;
+		return ;
 	}
 
-	return TRUE ;
+	std::string filename;
+	getObjectCacheFilename(entry->mHandle, filename);
+	LLAPRFile::remove(filename, mLocalAPRFilePoolp);
+	entry->mTime = INVALID_TIME ;
+	updateEntry(entry) ; //update the head file.
 }
 
 void LLVOCache::readCacheHeader()
@@ -430,45 +453,71 @@ void LLVOCache::readCacheHeader()
 	//clear stale info.
 	clearCacheInMemory();	
 
+	bool success = true ;
 	if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
 	{
-		LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_READ|APR_FOPEN_BINARY, mLocalAPRFilePoolp);		
+		LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);		
 		
 		//read the meta element
-		bool remove_cache_on_error = false;
-		if(!checkRead(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo), remove_cache_on_error))
-		{
-			llwarns << "Error reading meta information from cache header." << llendl;
-			delete apr_file;
-			return;
-		}
-
-		HeaderEntryInfo* entry ;
-		for(U32 entry_index = 0; entry_index < mCacheSize; ++entry_index)
+		success = check_read(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
+		
+		if(success)
 		{
-			entry = new HeaderEntryInfo() ;
-			if(!checkRead(apr_file, entry, sizeof(HeaderEntryInfo), remove_cache_on_error))
+			HeaderEntryInfo* entry = NULL ;
+			mNumEntries = 0 ;
+			U32 num_read = 0 ;
+			while(num_read++ < MAX_NUM_OBJECT_ENTRIES)
 			{
-				llwarns << "Error reading cache header entry. (entry_index=" << entry_index << ")" << llendl;
-				delete entry ;			
-				break;
+				if(!entry)
+				{
+					entry = new HeaderEntryInfo() ;
+				}
+				success = check_read(&apr_file, entry, sizeof(HeaderEntryInfo));
+								
+				if(!success) //failed
+				{
+					llwarns << "Error reading cache header entry. (entry_index=" << mNumEntries << ")" << llendl;
+					delete entry ;
+					entry = NULL ;
+					break ;
+				}
+				else if(entry->mTime == INVALID_TIME)
+				{
+					continue ; //an empty entry
+				}
+
+				entry->mIndex = mNumEntries++ ;
+				mHeaderEntryQueue.insert(entry) ;
+				mHandleEntryMap[entry->mHandle] = entry ;
+				entry = NULL ;
 			}
-			else if(!entry->mTime) //end of the cache.
+			if(entry)
 			{
 				delete entry ;
-				break;
 			}
-
-			entry->mIndex = entry_index;
-			mHandleEntryMap[entry->mHandle] = entry;
 		}
 
-		delete apr_file ;
+		//---------
+		//debug code
+		//----------
+		//std::string name ;
+		//for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
+		//{
+		//	getObjectCacheFilename((*iter)->mHandle, name) ;
+		//	llinfos << name << llendl ;
+		//}
+		//-----------
 	}
 	else
 	{
 		writeCacheHeader() ;
 	}
+
+	if(!success)
+	{
+		removeCache() ; //failed to read header, clear the cache
+	}
+	return ;
 }
 
 void LLVOCache::writeCacheHeader()
@@ -485,60 +534,50 @@ void LLVOCache::writeCacheHeader()
 		return;
 	}
 
-	LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp);
-
-	//write the meta element
-	if(!checkWrite(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)))
+	bool success = true ;
 	{
-		llwarns << "Error writing meta information to cache header." << llendl;
-		delete apr_file;
-		return;
-	}
+		LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
 
-	U32 entry_index = 0;
-	handle_entry_map_t::iterator iter_end = mHandleEntryMap.end();
-	for(handle_entry_map_t::iterator iter = mHandleEntryMap.begin();
-		iter != iter_end;
-		++iter)
-	{
-		HeaderEntryInfo* entry = iter->second;
-		entry->mIndex = entry_index++;
-		if(!checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)))
+		//write the meta element
+		success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
+
+
+		mNumEntries = 0 ;	
+		for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
 		{
-			llwarns << "Failed to write cache header for entry " << entry->mHandle << " (entry_index = " << entry_index << ")" << llendl;
-			delete apr_file;
-			return;
+			(*iter)->mIndex = mNumEntries++ ;
+			success = check_write(&apr_file, (void*)*iter, sizeof(HeaderEntryInfo));
 		}
-	}
-
-	// Why do we need to fill the cache header with default entries?  DK 2010-12-14
-	// It looks like we currently rely on the file being pre-allocated so we can seek during updateEntry().
-	if(entry_index < mCacheSize)
-	{
-		HeaderEntryInfo* entry = new HeaderEntryInfo() ;
-		for(; entry_index < mCacheSize; ++entry_index)
+	
+		mNumEntries = mHeaderEntryQueue.size() ;
+		if(success && mNumEntries < MAX_NUM_OBJECT_ENTRIES)
 		{
-			//fill the cache with the default entry.
-			if(!checkWrite(apr_file, entry, sizeof(HeaderEntryInfo)))
+			HeaderEntryInfo* entry = new HeaderEntryInfo() ;
+			entry->mTime = INVALID_TIME ;
+			for(S32 i = mNumEntries ; success && i < MAX_NUM_OBJECT_ENTRIES ; i++)
 			{
-				llwarns << "Failed to fill cache header with default entries (entry_index = " << entry_index << ").  Switching to read-only mode." << llendl;
-				mReadOnly = TRUE ; //disable the cache.
-				break;
+				//fill the cache with the default entry.
+				success = check_write(&apr_file, entry, sizeof(HeaderEntryInfo)) ;			
+
 			}
+			delete entry ;
 		}
-		delete entry ;
 	}
-	delete apr_file ;
+
+	if(!success)
+	{
+		clearCacheInMemory() ;
+		mReadOnly = TRUE ; //disable the cache.
+	}
+	return ;
 }
 
 BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
 {
-	LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_WRITE|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
-	apr_file->seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
+	LLAPRFile apr_file(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+	apr_file.seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
 
-	BOOL result = checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
-	delete apr_file;
-	return result;
+	return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
 }
 
 void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) 
@@ -557,76 +596,66 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
 		return ;
 	}
 
-	std::string filename;
-	getObjectCacheFilename(handle, filename);
-	LLAPRFile* apr_file = new LLAPRFile(filename, APR_FOPEN_READ|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
-
-	LLUUID cache_id ;
-	if(!checkRead(apr_file, cache_id.mData, UUID_BYTES))
+	bool success = true ;
 	{
-		llwarns << "Error reading cache_id from " << filename << llendl;
-		delete apr_file;
-		return ;
-	}
-	if(cache_id != id)
-	{
-		llwarns << "Cache ID (" << cache_id << ") doesn't match id for this region (" << id << "), discarding.  handle = " << handle << llendl;
-		delete apr_file ;
-		return ;
-	}
+		std::string filename;
+		getObjectCacheFilename(handle, filename);
+		LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
+	
+		LLUUID cache_id ;
+		success = check_read(&apr_file, cache_id.mData, UUID_BYTES) ;
+	
+		if(success)
+		{		
+			if(cache_id != id)
+			{
+				llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
+				success = false ;
+			}
 
-	S32 num_entries;
-	if(!checkRead(apr_file, &num_entries, sizeof(S32)))
-	{
-		llwarns << "Error reading num_entries from " << filename << llendl;
-		delete apr_file;
-		return ;
+			if(success)
+			{
+				S32 num_entries;
+				success = check_read(&apr_file, &num_entries, sizeof(S32)) ;
+	
+				for (S32 i = 0; success && i < num_entries; i++)
+				{
+					LLVOCacheEntry* entry = new LLVOCacheEntry(&apr_file);
+					if (!entry->getLocalID())
+					{
+						llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
+						delete entry ;
+						success = false ;
+					}
+					cache_entry_map[entry->getLocalID()] = entry;
+				}
+			}
+		}		
 	}
 	
-	for (S32 i = 0; i < num_entries; i++)
+	if(!success)
 	{
-		LLVOCacheEntry* entry = new LLVOCacheEntry(apr_file);
-		if (!entry->getLocalID())
+		if(cache_entry_map.empty())
 		{
-			llwarns << "Aborting cache file load for " << filename << ", cache file corruption! (entry number = " << i << ")" << llendl;
-			delete entry ;
-			break;
+			removeEntry(iter->second) ;
 		}
-		cache_entry_map[entry->getLocalID()] = entry;
 	}
 
-	delete apr_file ;
 	return ;
 }
 	
 void LLVOCache::purgeEntries()
 {
-	U32 limit = mCacheSize - (mCacheSize / ENTRIES_PURGE_FACTOR);
-	limit = llclamp(limit, (U32)1, mCacheSize);
-	// Construct a vector of entries out of the map so we can sort by time.
-	std::vector<HeaderEntryInfo*> header_vector;
-	handle_entry_map_t::iterator iter_end = mHandleEntryMap.end();
-	for (handle_entry_map_t::iterator iter = mHandleEntryMap.begin();
-		iter != iter_end;
-		++iter)
-	{
-		header_vector.push_back(iter->second);
-	}
-	// Sort by time, oldest first.
-	std::sort(header_vector.begin(), header_vector.end(), header_entry_less());
-	while(header_vector.size() > limit)
-	{
-		HeaderEntryInfo* entry = header_vector.front();
-		
-		removeFromCache(entry->mHandle);
+	while(mHeaderEntryQueue.size() >= mCacheSize)
+	{
+		header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
+		HeaderEntryInfo* entry = *iter ;			
 		mHandleEntryMap.erase(entry->mHandle);
-		header_vector.erase(header_vector.begin());
+		mHeaderEntryQueue.erase(iter) ;
+		removeFromCache(entry) ;
 		delete entry;
 	}
-
-	writeCacheHeader() ;
-	// *TODO: Verify that we can avoid re-reading the cache header.  DK 2010-12-14
-	readCacheHeader() ;
+	mNumEntries = mHandleEntryMap.size() ;
 }
 
 void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache) 
@@ -643,30 +672,32 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
 		llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;
 		return ;
 	}
+	if(mNumEntries >= mCacheSize)
+	{
+		purgeEntries() ;
+	}
 
-	U32 num_handle_entries = mHandleEntryMap.size();
-	
 	HeaderEntryInfo* entry;
 	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
 	if(iter == mHandleEntryMap.end()) //new entry
-	{
-		if(num_handle_entries >= mCacheSize)
-		{
-			purgeEntries() ;
-			num_handle_entries = mHandleEntryMap.size();
-		}
-		
+	{				
 		entry = new HeaderEntryInfo();
 		entry->mHandle = handle ;
 		entry->mTime = time(NULL) ;
-		entry->mIndex = num_handle_entries++;
+		entry->mIndex = mNumEntries++;
+		mHeaderEntryQueue.insert(entry) ;
 		mHandleEntryMap[handle] = entry ;
 	}
 	else
 	{
 		// Update access time.
-		entry = iter->second ;
+		entry = iter->second ;		
+
+		//resort
+		mHeaderEntryQueue.erase(entry) ;
+		
 		entry->mTime = time(NULL) ;
+		mHeaderEntryQueue.insert(entry) ;
 	}
 
 	//update cache header
@@ -683,37 +714,33 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
 	}
 
 	//write to cache file
-	std::string filename;
-	getObjectCacheFilename(handle, filename);
-	LLAPRFile* apr_file = new LLAPRFile(filename, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp);
-	
-	if(!checkWrite(apr_file, (void*)id.mData, UUID_BYTES))
+	bool success = true ;
 	{
-		llwarns << "Error writing id to " << filename << llendl;
-		delete apr_file;
-		return ;
-	}
+		std::string filename;
+		getObjectCacheFilename(handle, filename);
+		LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+	
+		success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ;
 
-	S32 num_entries = cache_entry_map.size() ;
-	if(!checkWrite(apr_file, &num_entries, sizeof(S32)))
-	{
-		llwarns << "Error writing num_entries to " << filename << llendl;
-		delete apr_file;
-		return ;
+	
+		if(success)
+		{
+			S32 num_entries = cache_entry_map.size() ;
+			success = check_write(&apr_file, &num_entries, sizeof(S32));
+	
+			for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter)
+			{
+				success = iter->second->writeToFile(&apr_file) ;
+			}
+		}
 	}
 
-	for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); iter != cache_entry_map.end(); ++iter)
+	if(!success)
 	{
-		if(!iter->second->writeToFile(apr_file))
-		{
-			llwarns << "Aborting cache file write for " << filename << ", error writing to file!" << llendl;
-			//failed
-			removeCache() ;
-			break;
-		}
+		removeEntry(entry) ;
+
 	}
 
-	delete apr_file ;
 	return ;
 }
 
diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h
index e1030079793a01a91e36c9c9cee9f670ae69dc57..1070fcaae978d6c161d05c4559330f14415839eb 100644
--- a/indra/newview/llvocache.h
+++ b/indra/newview/llvocache.h
@@ -95,13 +95,15 @@ class LLVOCache
 	{
 		bool operator()(const HeaderEntryInfo* lhs, const HeaderEntryInfo* rhs) const
 		{
-			if (lhs->mTime == rhs->mTime)
+			if(lhs->mTime == rhs->mTime) 
 			{
-				return lhs->mHandle < rhs->mHandle;
+				return lhs < rhs ;
 			}
-			return lhs->mTime < rhs->mTime; // older entry in front
+			
+			return lhs->mTime < rhs->mTime ; // older entry in front of queue (set)
 		}
 	};
+	typedef std::set<HeaderEntryInfo*, header_entry_less> header_entry_queue_t;
 	typedef std::map<U64, HeaderEntryInfo*> handle_entry_map_t;
 private:
 	LLVOCache() ;
@@ -114,6 +116,7 @@ class LLVOCache
 
 	void readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) ;
 	void writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache) ;
+	void removeEntry(U64 handle) ;
 
 	void setReadOnly(BOOL read_only) {mReadOnly = read_only;} 
 
@@ -121,15 +124,14 @@ class LLVOCache
 	void setDirNames(ELLPath location);	
 	// determine the cache filename for the region from the region handle	
 	void getObjectCacheFilename(U64 handle, std::string& filename);
-	void removeFromCache(U64 handle);
+	void removeFromCache(HeaderEntryInfo* entry);
 	void readCacheHeader();
 	void writeCacheHeader();
 	void clearCacheInMemory();
 	void removeCache() ;
+	void removeEntry(HeaderEntryInfo* entry) ;
 	void purgeEntries();
 	BOOL updateEntry(const HeaderEntryInfo* entry);
-	BOOL checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error = true) ;
-	BOOL checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error = true) ;
 	
 private:
 	BOOL                 mEnabled;
@@ -137,9 +139,11 @@ class LLVOCache
 	BOOL                 mReadOnly ;
 	HeaderMetaInfo       mMetaInfo;
 	U32                  mCacheSize;
+	U32                  mNumEntries;
 	std::string          mHeaderFileName ;
 	std::string          mObjectCacheDirName;
 	LLVolatileAPRPool*   mLocalAPRFilePoolp ; 	
+	header_entry_queue_t mHeaderEntryQueue;
 	handle_entry_map_t   mHandleEntryMap;	
 
 	static LLVOCache* sInstance ;
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 761e12020bb3ed36776c6acb351a08a4a32d0fa2..f67e3a97700aecc9f24a82028a94ecff775bee3c 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -388,10 +388,12 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
 				// There's something bogus in the data that we're unpacking.
 				dp->dumpBufferToLog();
 				llwarns << "Flushing cache files" << llendl;
-				std::string mask;
-				mask = gDirUtilp->getDirDelimiter() + "*.slc";
-				gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), mask);
-// 				llerrs << "Bogus TE data in " << getID() << ", crashing!" << llendl;
+
+				if(LLVOCache::hasInstance() && getRegion())
+				{
+					LLVOCache::getInstance()->removeEntry(getRegion()->getHandle()) ;
+				}
+				
 				llwarns << "Bogus TE data in " << getID() << llendl;
 			}
 			else 
@@ -667,11 +669,32 @@ void LLVOVolume::updateTextures()
 	}
 }
 
+BOOL LLVOVolume::isVisible() const 
+{
+	if(mDrawable.notNull() && mDrawable->isVisible())
+	{
+		return TRUE ;
+	}
+
+	if(isAttachment())
+	{
+		LLViewerObject* objp = (LLViewerObject*)getParent() ;
+		while(objp && !objp->isAvatar())
+		{
+			objp = (LLViewerObject*)objp->getParent() ;
+		}
+
+		return objp && objp->mDrawable.notNull() && objp->mDrawable->isVisible() ;
+	}
+
+	return FALSE ;
+}
+
 void LLVOVolume::updateTextureVirtualSize()
 {
 	// Update the pixel area of all faces
 
-	if(mDrawable.isNull() || !mDrawable->isVisible())
+	if(!isVisible())
 	{
 		return ;
 	}
@@ -2738,14 +2761,7 @@ void LLVOVolume::updateRadius()
 
 BOOL LLVOVolume::isAttachment() const
 {
-	if (mState == 0)
-	{
-		return FALSE;
-	}
-	else
-	{
-		return TRUE;
-	}
+	return mState != 0 ;
 }
 
 BOOL LLVOVolume::isHUDAttachment() const
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 1e9b9737b166ee810552297384f37d0c9a5e121a..8b68e7c78a109cfa216b0dd54cd72cea30026539 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -102,6 +102,7 @@ class LLVOVolume : public LLViewerObject
 				void	animateTextures();
 	/*virtual*/ BOOL	idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 
+	            BOOL    isVisible() const ;
 	/*virtual*/ BOOL	isActive() const;
 	/*virtual*/ BOOL	isAttachment() const;
 	/*virtual*/ BOOL	isRootEdit() const; // overridden for sake of attachments treating themselves as a root object
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 15477e0a803eff1aec8b0d5f33e6449a13231e2f..59b526059b367b06c948977cbbc7114db09440e1 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -1967,12 +1967,12 @@ void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera)
 
 	if(drawablep && !drawablep->isDead())
 	{
-	if (drawablep->isSpatialBridge())
-	{
+		if (drawablep->isSpatialBridge())
+		{
 			const LLDrawable* root = ((LLSpatialBridge*) drawablep)->mDrawable;
 			llassert(root); // trying to catch a bad assumption
 			if (root && //  // this test may not be needed, see above
-			    root->getVObj()->isAttachment())
+					root->getVObj()->isAttachment())
 			{
 				LLDrawable* rootparent = root->getParent();
 				if (rootparent) // this IS sometimes NULL
@@ -1980,24 +1980,24 @@ void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera)
 					LLViewerObject *vobj = rootparent->getVObj();
 					llassert(vobj); // trying to catch a bad assumption
 					if (vobj) // this test may not be needed, see above
-		{
+					{
 						const LLVOAvatar* av = vobj->asAvatar();
-			if (av && av->isImpostor())
-			{
-				return;
-			}
-		}
+						if (av && av->isImpostor())
+						{
+							return;
+						}
+					}
 				}
 			}
-		sCull->pushBridge((LLSpatialBridge*) drawablep);
-	}
-	else
-	{
-		sCull->pushDrawable(drawablep);
-	}
+			sCull->pushBridge((LLSpatialBridge*) drawablep);
+		}
+		else
+		{
+			sCull->pushDrawable(drawablep);
+		}
 
-	drawablep->setVisible(camera);
-}
+		drawablep->setVisible(camera);
+	}
 }
 
 void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion)
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 2207e418c8dff036b8e58a3823378bb4cd10abd8..c2735c85e47fc5249f1c5d12f9c29870f1a3e7eb 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1959,6 +1959,16 @@
                 <menu_item_check.on_click
                  function="ToggleControl"
                  parameter="DebugShowRenderInfo" />
+            </menu_item_check>
+			<menu_item_check
+             label="Show Texture Info"
+             name="Show Texture Info">
+                <menu_item_check.on_check
+                 function="CheckControl"
+                 parameter="DebugShowTextureInfo" />
+                <menu_item_check.on_click
+                 function="ToggleControl"
+                 parameter="DebugShowTextureInfo" />
             </menu_item_check>
             <menu_item_check
              label="Show Matrices"