diff --git a/.hgignore b/.hgignore
index bc3020eee4758fe74905091c141492698899e542..501f9e6abe6fbe80f07c8688e75e09f855c29104 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,5 +1,6 @@
 syntax: glob
 
+
 # WinMerge temp files
 *.bak
 # Compiled python bytecode
diff --git a/.hgtags b/.hgtags
old mode 100644
new mode 100755
diff --git a/BuildParams b/BuildParams
index d24666aff9a79170b6aa5da747ddbc130c6a5a7c..dccca834f1de53ca1d1d441a1f5a264d872ab929 100644
--- a/BuildParams
+++ b/BuildParams
@@ -181,4 +181,35 @@ simon_viewer-dev-private.email_status_this_is_os = false
 vir-project-1.viewer_channel = "Second Life Release"
 vir-project-1.login_channel = "Second Life Release"
 
+# ========================================
+# THX-1138 / Runway projects
+# ========================================
+viewer-thx1138-runway-shared.viewer_channel = "Project Viewer - THX-1138 Runway"
+viewer-thx1138-runway-shared.login_channel = "Project Viewer - THX-1138 Runway"
+viewer-thx1138-runway-shared.viewer_grid = uma
+viewer-thx1138-runway-shared.build_debug_release_separately = true
+viewer-thx1138-runway-shared.build_CYGWIN_Debug = false
+viewer-thx1138-runway-shared.build_viewer_update_version_manager = false
+
+viewer-thx1138.viewer_channel = "Project Viewer - THX-1138"
+viewer-thx1138.login_channel = "Project Viewer - THX-1138"
+viewer-thx1138.viewer_grid = uma
+viewer-thx1138.build_debug_release_separately = true
+viewer-thx1138.build_CYGWIN_Debug = false
+viewer-thx1138.build_viewer_update_version_manager = false
+
+runway-merge.viewer_channel = "Project Viewer - Runway Merge"
+runway-merge.login_channel = "Project Viewer - Runway Merge"
+runway-merge.viewer_grid = agni
+runway-merge.build_debug_release_separately = true
+runway-merge.build_CYGWIN_Debug = false
+runway-merge.build_viewer_update_version_manager = false
+
+runway.viewer_channel = "Project Viewer - Runway"
+runway.login_channel = "Project Viewer - Runway"
+runway.viewer_grid = agni
+runway.build_debug_release_separately = true
+runway.build_CYGWIN_Debug = false
+runway.build_viewer_update_version_manager = false
+
 # eof
diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
index 48e876429dfaac10b7e3085c3d4eb1324aa3b7ec..36c5b6782618b63a5c35d2ba0174caa898071712 100644
--- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
+++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
@@ -54,6 +54,11 @@ static const char USAGE[] = "\n"
 " -o, --output <file1 .. file2> OR <type>\n"
 "        List of image files to create (assumes same order as for input files)\n"
 "        OR 3 letters file type extension to convert each input file into.\n"
+" -load, --load_size <n>\n"
+"        Portion of the input file to load, in bytes."
+"        If (load == 0), it will load the whole file."
+"        If (load == -1), it will load the size relevant to reach the requested discard level (see -d)."
+"        Only valid for j2c images. Default is 0 (load whole file).\n"
 " -r, --region <x0, y0, x1, y1>\n"
 "        Crop region applied to the input files in pixels.\n"
 "        Only used for j2c images. Default is no region cropping.\n"
@@ -104,22 +109,52 @@ void output_image_stats(LLPointer<LLImageFormatted> image, const std::string &fi
 	// Print out some statistical data on the image
 	std::cout << "Image stats for : " << filename << ", extension : " << image->getExtension() << std::endl;
 
-	std::cout << "    with : " << (int)(image->getWidth())       << ", height : " << (int)(image->getHeight())       << std::endl;
-	std::cout << "    comp : " << (int)(image->getComponents())  << ", levels : " << (int)(image->getDiscardLevel()) << std::endl;
-	std::cout << "    head : " << (int)(image->calcHeaderSize()) << ",   data : " << (int)(image->getDataSize())     << std::endl;
+	std::cout << "    with : " << (int)(image->getWidth())       << ", height : " << (int)(image->getHeight())   << std::endl;
+	std::cout << "    comp : " << (int)(image->getComponents())  << ", levels : " << (int)(image->getLevels())   << std::endl;
+	std::cout << "    head : " << (int)(image->calcHeaderSize()) << ",   data : " << (int)(image->getDataSize()) << std::endl;
 
 	return;
 }
 
 // Load an image from file and return a raw (decompressed) instance of its data
-LLPointer<LLImageRaw> load_image(const std::string &src_filename, int discard_level, int* region, bool output_stats)
+LLPointer<LLImageRaw> load_image(const std::string &src_filename, int discard_level, int* region, int load_size, bool output_stats)
 {
 	LLPointer<LLImageFormatted> image = create_image(src_filename);
 	
-	// This just loads the image file stream into a buffer. No decoding done.
-	if (!image->load(src_filename))
+	// We support partial loading only for j2c images
+	if (image->getCodec() == IMG_CODEC_J2C)
 	{
-		return NULL;
+		// Load the header
+		if (!image->load(src_filename, 600))
+		{
+			return NULL;
+		}
+		S32 h = ((LLImageJ2C*)(image.get()))->calcHeaderSize();
+		S32 d = (load_size > 0 ? ((LLImageJ2C*)(image.get()))->calcDiscardLevelBytes(load_size) : 0);
+		S8  r = ((LLImageJ2C*)(image.get()))->getRawDiscardLevel();
+		std::cout << "Merov debug : header = " << h << ", load_size = " << load_size << ", discard level = " << d << ", raw discard level = " << r << std::endl;
+		for (d = 0; d < MAX_DISCARD_LEVEL; d++)
+		{
+			S32 data_size = ((LLImageJ2C*)(image.get()))->calcDataSize(d);
+			std::cout << "Merov debug : discard_level = " << d << ", data_size = " << data_size << std::endl;
+		}
+		if (load_size < 0)
+		{
+			load_size = (discard_level != -1 ? ((LLImageJ2C*)(image.get()))->calcDataSize(discard_level) : 0);
+		}
+		// Load the requested byte range
+		if (!image->load(src_filename, load_size))
+		{
+			return NULL;
+		}
+	}
+	else 
+	{
+		// This just loads the image file stream into a buffer. No decoding done.
+		if (!image->load(src_filename))
+		{
+			return NULL;
+		}
 	}
 	
 	if(	(image->getComponents() != 3) && (image->getComponents() != 4) )
@@ -310,6 +345,7 @@ int main(int argc, char** argv)
 	bool image_stats = false;
 	int* region = NULL;
 	int discard_level = -1;
+	int load_size = 0;
 	int precincts_size = -1;
 	int blocks_size = -1;
 	int levels = 0;
@@ -396,6 +432,22 @@ int main(int argc, char** argv)
 				discard_level = llclamp(discard_level,0,5);
 			}
 		}
+		else if (!strcmp(argv[arg], "--load_size") || !strcmp(argv[arg], "-load"))
+		{
+			std::string value_str;
+			if ((arg + 1) < argc)
+			{
+				value_str = argv[arg+1];
+			}
+			if (((arg + 1) >= argc) || (value_str[0] == '-'))
+			{
+				std::cout << "No valid --load_size argument given, load_size ignored" << std::endl;
+			}
+			else
+			{
+				load_size = atoi(value_str.c_str());
+			}
+		}
 		else if (!strcmp(argv[arg], "--precincts") || !strcmp(argv[arg], "-p"))
 		{
 			std::string value_str;
@@ -510,7 +562,7 @@ int main(int argc, char** argv)
 	for (; in_file != in_end; ++in_file, ++out_file)
 	{
 		// Load file
-		LLPointer<LLImageRaw> raw_image = load_image(*in_file, discard_level, region, image_stats);
+		LLPointer<LLImageRaw> raw_image = load_image(*in_file, discard_level, region, load_size, image_stats);
 		if (!raw_image)
 		{
 			std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 56e01ac851e7a067bf740f3bb0a29440e003691a..6775b005f4f179e8beb94b57fd876ca6a81c26f7 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -48,11 +48,15 @@
 //static
 std::string LLImage::sLastErrorMessage;
 LLMutex* LLImage::sMutex = NULL;
+bool LLImage::sUseNewByteRange = false;
+S32  LLImage::sMinimalReverseByteRangePercent = 75;
 LLPrivateMemoryPool* LLImageBase::sPrivatePoolp = NULL ;
 
 //static
-void LLImage::initClass()
+void LLImage::initClass(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent)
 {
+	sUseNewByteRange = use_new_byte_range;
+    sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent;
 	sMutex = new LLMutex(NULL);
 
 	LLImageBase::createPrivatePool() ;
@@ -1334,7 +1338,8 @@ LLImageFormatted::LLImageFormatted(S8 codec)
 	  mCodec(codec),
 	  mDecoding(0),
 	  mDecoded(0),
-	  mDiscardLevel(-1)
+	  mDiscardLevel(-1),
+	  mLevels(0)
 {
 	mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
 }
@@ -1561,7 +1566,7 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
 
 //----------------------------------------------------------------------------
 
-BOOL LLImageFormatted::load(const std::string &filename)
+BOOL LLImageFormatted::load(const std::string &filename, int load_size)
 {
 	resetLastError();
 
@@ -1580,14 +1585,19 @@ BOOL LLImageFormatted::load(const std::string &filename)
 		return FALSE;
 	}
 
+	// Constrain the load size to acceptable values
+	if ((load_size == 0) || (load_size > file_size))
+	{
+		load_size = file_size;
+	}
 	BOOL res;
-	U8 *data = allocateData(file_size);
-	apr_size_t bytes_read = file_size;
+	U8 *data = allocateData(load_size);
+	apr_size_t bytes_read = load_size;
 	apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
-	if (s != APR_SUCCESS || (S32) bytes_read != file_size)
+	if (s != APR_SUCCESS || (S32) bytes_read != load_size)
 	{
 		deleteData();
-		setLastError("Unable to read entire file",filename);
+		setLastError("Unable to read file",filename);
 		res = FALSE;
 	}
 	else
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 4469c9e86025e628d2b1100c6d8c85baaa604c93..46e6d1a901315fb622382dd20f0461c5a1116516 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -48,6 +48,8 @@ const S32 MAX_PRECINCT_SIZE = 2048;			// No reason to be bigger than MAX_IMAGE_S
 const S32 MIN_PRECINCT_SIZE = 4;			// Can't be smaller than MIN_BLOCK_SIZE
 const S32 MAX_BLOCK_SIZE = 64;				// Max total block size is 4096, hence 64x64 when using square blocks
 const S32 MIN_BLOCK_SIZE = 4;				// Min block dim is 4 according to jpeg2000 spec
+const S32 MIN_LAYER_SIZE = 2000;			// Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!!
+const S32 MAX_NB_LAYERS = 64;				// Max number of layers we'll entertain in SL (practical limit)
 
 const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2
 const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 2048
@@ -60,6 +62,7 @@ const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 *
 // *TODO: change both to 1024 when SIM texture fetching is deprecated
 const S32 FIRST_PACKET_SIZE = 600;
 const S32 MAX_IMG_PACKET_SIZE = 1000;
+const S32 HTTP_PACKET_SIZE = 1496;
 
 // Base classes for images.
 // There are two major parts for the image:
@@ -89,15 +92,20 @@ typedef enum e_image_codec
 class LLImage
 {
 public:
-	static void initClass();
+	static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75);
 	static void cleanupClass();
 
 	static const std::string& getLastError();
 	static void setLastError(const std::string& message);
 	
+	static bool useNewByteRange() { return sUseNewByteRange; }
+    static S32  getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; }
+	
 protected:
 	static LLMutex* sMutex;
 	static std::string sLastErrorMessage;
+	static bool sUseNewByteRange;
+    static S32  sMinimalReverseByteRangePercent;
 };
 
 //============================================================================
@@ -294,7 +302,7 @@ class LLImageFormatted : public LLImageBase
 	// getRawDiscardLevel() by default returns mDiscardLevel, but may be overridden (LLImageJ2C)
 	virtual S8  getRawDiscardLevel() { return mDiscardLevel; }
 	
-	BOOL load(const std::string& filename);
+	BOOL load(const std::string& filename, int load_size = 0);
 	BOOL save(const std::string& filename);
 
 	virtual BOOL updateData() = 0; // pure virtual
@@ -313,6 +321,8 @@ class LLImageFormatted : public LLImageBase
 	BOOL isDecoded()  const { return mDecoded ? TRUE : FALSE; }
 	void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
 	S8 getDiscardLevel() const { return mDiscardLevel; }
+	S8 getLevels() const { return mLevels; }
+	void setLevels(S8 nlevels) { mLevels = nlevels; }
 
 	// setLastError needs to be deferred for J2C images since it may be called from a DLL
 	virtual void resetLastError();
@@ -325,7 +335,8 @@ class LLImageFormatted : public LLImageBase
 	S8 mCodec;
 	S8 mDecoding;
 	S8 mDecoded;  // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
-	S8 mDiscardLevel;
+	S8 mDiscardLevel;	// Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc...
+	S8 mLevels;			// Number of resolution levels in that image. Min is 1. 0 means unknown.
 	
 public:
 	static S32 sGlobalFormattedMemory;
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
old mode 100644
new mode 100755
index 8241746a74561b514bbe762ba9331a7b1ab6e37c..452aad25cb9f462d417e2f7cb8427a45e40a2adc
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -56,7 +56,7 @@ std::string LLImageJ2C::getEngineInfo()
 LLImageJ2C::LLImageJ2C() : 	LLImageFormatted(IMG_CODEC_J2C),
 							mMaxBytes(0),
 							mRawDiscardLevel(-1),
-							mRate(0.0f),
+							mRate(DEFAULT_COMPRESSION_RATE),
 							mReversible(FALSE),
 							mAreaUsedForDataSizeCalcs(0)
 {
@@ -142,6 +142,7 @@ BOOL LLImageJ2C::updateData()
 
 BOOL LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region)
 {
+	setDiscardLevel(discard_level != -1 ? discard_level : 0);
 	return mImpl->initDecode(*this,raw_image,discard_level,region);
 }
 
@@ -261,19 +262,34 @@ S32 LLImageJ2C::calcHeaderSizeJ2C()
 //static
 S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
 {
-	// Note: this only provides an *estimate* of the size in bytes of an image level
-	// *TODO: find a way to read the true size (when available) and convey the fact
-	// that the result is an estimate in the other cases
-	if (rate <= 0.f) rate = .125f;
-	while (discard_level > 0)
+	// Note: This provides an estimation for the first to last quality layer of a given discard level
+	// This is however an efficient approximation, as the true discard level boundary would be
+	// in general too big for fast fetching.
+	// For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
+
+	// Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl().
+	S32 nb_layers = 1;
+	S32 surface = w*h;
+	S32 s = 64*64;
+	while (surface > s)
 	{
-		if (w < 1 || h < 1)
-			break;
-		w >>= 1;
-		h >>= 1;
-		discard_level--;
+		nb_layers++;
+		s *= 4;
 	}
-	S32 bytes = (S32)((F32)(w*h*comp)*rate);
+	F32 layer_factor =  3.0f * (7 - llclamp(nb_layers,1,6));
+	
+	// Compute w/pow(2,discard_level) and h/pow(2,discard_level)
+	w >>= discard_level;
+	h >>= discard_level;
+	w = llmax(w, 1);
+	h = llmax(h, 1);
+
+	// Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange 
+	// *TODO: Take the old code out once we have enough tests done
+	S32 bytes;
+	S32 new_bytes = (S32) (sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor);
+	S32 old_bytes = (S32)((F32)(w*h*comp)*rate);
+	bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes);
 	bytes = llmax(bytes, calcHeaderSizeJ2C());
 	return bytes;
 }
@@ -283,15 +299,12 @@ S32 LLImageJ2C::calcHeaderSize()
 	return calcHeaderSizeJ2C();
 }
 
-
-// calcDataSize() returns how many bytes to read 
-// to load discard_level (including header and higher discard levels)
+// calcDataSize() returns how many bytes to read to load discard_level (including header)
 S32 LLImageJ2C::calcDataSize(S32 discard_level)
 {
 	discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL);
-
 	if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) 
-		|| mDataSizes[0] == 0)
+		|| (mDataSizes[0] == 0))
 	{
 		mAreaUsedForDataSizeCalcs = getHeight() * getWidth();
 		
@@ -301,25 +314,6 @@ S32 LLImageJ2C::calcDataSize(S32 discard_level)
 			mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
 			level--;
 		}
-
-		/* This is technically a more correct way to calculate the size required
-		   for each discard level, since they should include the size needed for
-		   lower levels.   Unfortunately, this doesn't work well and will lead to 
-		   download stalls.  The true correct way is to parse the header.  This will
-		   all go away with http textures at some point.
-
-		// Calculate the size for each discard level.   Lower levels (higher quality)
-		// contain the cumulative size of higher levels		
-		S32 total_size = calcHeaderSizeJ2C();
-
-		S32 level = MAX_DISCARD_LEVEL;	// Start at the highest discard
-		while ( level >= 0 )
-		{	// Add in this discard level and all before it
-			total_size += calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
-			mDataSizes[level] = total_size;
-			level--;
-		}
-		*/
 	}
 	return mDataSizes[discard_level];
 }
@@ -334,8 +328,9 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
 	}
 	while (1)
 	{
-		S32 bytes_needed = calcDataSize(discard_level); // virtual
-		if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes
+		S32 bytes_needed = calcDataSize(discard_level);
+		// Use TextureReverseByteRange percent (see settings.xml) of the optimal size to qualify as correct rendering for the given discard level
+		if (bytes >= (bytes_needed*LLImage::getReverseByteRangePercent()/100))
 		{
 			break;
 		}
@@ -348,11 +343,6 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
 	return discard_level;
 }
 
-void LLImageJ2C::setRate(F32 rate)
-{
-	mRate = rate;
-}
-
 void LLImageJ2C::setMaxBytes(S32 max_bytes)
 {
 	mMaxBytes = max_bytes;
@@ -474,6 +464,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester
 	addMetric("Perf Compression (kB/s)");
 
 	mRunBytesInDecompression = 0;
+	mRunBytesOutDecompression = 0;
 	mRunBytesInCompression = 0;
 
 	mTotalBytesInDecompression = 0;
@@ -483,6 +474,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester
 
 	mTotalTimeDecompression = 0.0f;
 	mTotalTimeCompression = 0.0f;
+	mRunTimeDecompression = 0.0f;
 }
 
 LLImageCompressionTester::~LLImageCompressionTester()
@@ -565,12 +557,17 @@ void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const
 	mTotalBytesInDecompression += bytesIn;
 	mRunBytesInDecompression += bytesIn;
 	mTotalBytesOutDecompression += bytesOut;
-	if (mRunBytesInDecompression > (1000000))
+	mRunBytesOutDecompression += bytesOut;
+	//if (mRunBytesInDecompression > (1000000))
+	if (mRunBytesOutDecompression > (10000000))
+	//if ((mTotalTimeDecompression - mRunTimeDecompression) >= (5.0f))
 	{
 		// Output everything
 		outputTestResults();
 		// Reset the decompression data of the run
 		mRunBytesInDecompression = 0;
+		mRunBytesOutDecompression = 0;
+		mRunTimeDecompression = mTotalTimeDecompression;
 	}
 }
 
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index 914174fc57fd092ddfdd2e436e79ebdebd87b4fa..ce8195940d7c43683bad9380c1b40f903a1aff73 100644
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -31,6 +31,9 @@
 #include "llassettype.h"
 #include "llmetricperformancetester.h"
 
+// JPEG2000 : compression rate used in j2c conversion.
+const F32 DEFAULT_COMPRESSION_RATE = 1.f/8.f;
+
 class LLImageJ2CImpl;
 class LLImageCompressionTester ;
 
@@ -67,12 +70,11 @@ class LLImageJ2C : public LLImageFormatted
 
 	// Encode accessors
 	void setReversible(const BOOL reversible); // Use non-lossy?
-	void setRate(F32 rate);
 	void setMaxBytes(S32 max_bytes);
 	S32 getMaxBytes() const { return mMaxBytes; }
 
 	static S32 calcHeaderSizeJ2C();
-	static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = 0.f);
+	static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE);
 
 	static std::string getEngineInfo();
 
@@ -154,13 +156,15 @@ class LLImageCompressionTester : public LLMetricPerformanceTesterBasic
         U32 mTotalBytesOutDecompression;    // Total bytes produced by decompressor
         U32 mTotalBytesInCompression;       // Total bytes fed to compressor
         U32 mTotalBytesOutCompression;      // Total bytes produced by compressor
-		U32 mRunBytesInDecompression;		// Bytes fed to decompressor in this run
+        U32 mRunBytesInDecompression;		// Bytes fed to decompressor in this run
+        U32 mRunBytesOutDecompression;		// Bytes produced by the decompressor in this run
 		U32 mRunBytesInCompression;			// Bytes fed to compressor in this run
         //
         // Time
         //
         F32 mTotalTimeDecompression;        // Total time spent in computing decompression
         F32 mTotalTimeCompression;          // Total time spent in computing compression
+        F32 mRunTimeDecompression;          // Time in this run (we output every 5 sec in decompress)
     };
 
 #endif
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index c156ed0cef40411d153e9d825b27ff1e26a04b59..cf88de12b49ab8ff05512ccc98cd31fb592536dc 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -32,6 +32,7 @@
 #include "llmath.h"
 #include "llkdumem.h"
 
+#include "kdu_block_coding.h"
 
 class kdc_flow_control {
 	
@@ -244,7 +245,9 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod
 	mCodeStreamp->create(mInputp);
 
 	// Set the maximum number of bytes to use from the codestream
-	mCodeStreamp->set_max_bytes(max_bytes);
+	// *TODO: This seems to be wrong. The base class should have no idea of how j2c compression works so no
+	// good way of computing what's the byte range to be used.
+	mCodeStreamp->set_max_bytes(max_bytes,true);
 
 	//	If you want to flip or rotate the image for some reason, change
 	// the resolution, or identify a restricted region of interest, this is
@@ -291,8 +294,13 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod
 		}
 	}
 
+	// Get the number of resolution levels in that image
+	mLevels = mCodeStreamp->get_min_dwt_levels();
+	
+	// Set the base dimensions
 	base.setSize(dims.size.x, dims.size.y, components);
-
+	base.setLevels(mLevels);
+	
 	if (!keep_codestream)
 	{
 		mCodeStreamp->destroy();
@@ -351,7 +359,8 @@ BOOL LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc
 	mLevels = levels;
 	if (mLevels != 0)
 	{
-		mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MIN_DECOMPOSITION_LEVELS);		
+		mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MAX_DECOMPOSITION_LEVELS);
+		base.setLevels(mLevels);
 	}
 	return TRUE;
 }
@@ -364,6 +373,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 	// To regain control, we throw an exception, and catch it here.
 	try
 	{
+		// Merov : Test!! DO NOT COMMIT!!
+		//findDiscardLevelsBoundaries(base);
+
 		base.updateRawDiscardLevel();
 		setupCodeStream(base, TRUE, mode);
 
@@ -381,7 +393,7 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 			region_kdu->size.y = region[3] - region[1];
 		}
 		int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel());
-		
+		//llinfos << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << llendl;
 		// Apply loading restrictions
 		mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu);
 		
@@ -394,12 +406,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 
 		// Resize raw_image according to the image to be decoded
 		kdu_dims dims; mCodeStreamp->get_dims(0,dims);
-		// *TODO: Use the real number of levels read from the file throughout the code instead of relying on an infered value from dimensions
-		//S32 levels = mCodeStreamp->get_min_dwt_levels();
 		S32 channels = base.getComponents() - first_channel;
 		channels = llmin(channels,max_channel_count);
 		raw_image.resize(dims.size.x, dims.size.y, channels);
-		//llinfos << "j2c image dimension: width = " << dims.size.x << ", height = " << dims.size.y << ", channels = " << channels << ", levels = " << levels << llendl;
 
 		if (!mTileIndicesp)
 		{
@@ -583,12 +592,6 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 			comment.put_text(comment_text);
 		}
 
-		// Set codestream options
-		int num_layer_specs = 0;
-
-		kdu_long layer_bytes[64];
-		U32 max_bytes = 0;
-
 		if (num_components >= 3)
 		{
 			// Note that we always use YCC and not YUV
@@ -596,66 +599,51 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 			set_default_colour_weights(codestream.access_siz());
 		}
 
-		if (reversible)
+		// Set codestream options
+		int nb_layers = 0;
+		kdu_long layer_bytes[MAX_NB_LAYERS];
+		U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents());
+
+		// Rate is the argument passed into the LLImageJ2C which specifies the target compression rate. The default is 8:1.
+		// *TODO: mRate is actually always 8:1 in the viewer. Test different values.
+		llassert (base.mRate > 0.f);
+		max_bytes = (U32)((F32)(max_bytes) * base.mRate);
+		
+		// This code is where we specify the target number of bytes for each quality layer.
+		// We're using a logarithmic spacing rule that fits with our way of fetching texture data.
+		// Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h
+		layer_bytes[nb_layers++] = FIRST_PACKET_SIZE;
+		U32 i = MIN_LAYER_SIZE;
+		while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1)))
 		{
-			codestream.access_siz()->parse_string("Creversible=yes");
-			// *TODO: we should use yuv in reversible mode and one level since those images are small. 
-			// Don't turn this on now though as both create problems on decoding for the moment
-			//codestream.access_siz()->parse_string("Clevels=1");
-			//codestream.access_siz()->parse_string("Cycc=no");
-			// If we're doing reversible (i.e. lossless compression), assumes we're not using quality layers.
-			// *TODO: this is incorrect and unecessary. Try using the regular layer setting.
-			codestream.access_siz()->parse_string("Clayers=1");
-			num_layer_specs = 1;
-			layer_bytes[0] = 0;
+			layer_bytes[nb_layers++] = i;
+			i *= 4;
 		}
-		else
+		// Note: for small images, we can have (max_bytes < FIRST_PACKET_SIZE), hence the test
+		if (layer_bytes[nb_layers-1] < max_bytes)
 		{
-			// Rate is the argument passed into the LLImageJ2C which
-			// specifies the target compression rate.  The default is 8:1.
-			// Possibly if max_bytes < 500, we should just use the default setting?
-			// *TODO: mRate is actually always 8:1 in the viewer. Test different values. Also force to reversible for small (< 500 bytes) textures.
-			if (base.mRate != 0.f)
-			{
-				max_bytes = (U32)(base.mRate*base.getWidth()*base.getHeight()*base.getComponents());
-			}
-			else
-			{
-				max_bytes = (U32)(base.getWidth()*base.getHeight()*base.getComponents()*0.125);
-			}
-
-			const U32 min_bytes = FIRST_PACKET_SIZE;
-			if (max_bytes > min_bytes)
-			{
-				U32 i;
-				// This code is where we specify the target number of bytes for
-				// each layer.  Not sure if we should do this for small images
-				// or not.  The goal is to have this roughly align with
-				// different quality levels that we decode at.
-				for (i = min_bytes; i < max_bytes; i*=4)
-				{
-					if (i == min_bytes * 4)
-					{
-						i = 2000;
-					}
-					layer_bytes[num_layer_specs] = i;
-					num_layer_specs++;
-				}
-				layer_bytes[num_layer_specs] = max_bytes;
-				num_layer_specs++;
+			// Set the last quality layer so to fit the preset compression ratio
+			layer_bytes[nb_layers++] = max_bytes;
+		}
 
-				std::string layer_string = llformat("Clayers=%d",num_layer_specs);
-				codestream.access_siz()->parse_string(layer_string.c_str());
-			}
-			else
+		if (reversible)
+		{
+			// Use 0 for a last quality layer for reversible images so all remaining code blocks will be flushed
+			// Hack: KDU encoding for reversible images has a bug for small images that leads to j2c images that 
+			// cannot be open or are very blurry. Avoiding that last layer prevents the problem to happen.
+			if ((base.getWidth() >= 32) || (base.getHeight() >= 32))
 			{
-				layer_bytes[0] = min_bytes;
-				num_layer_specs = 1;
-				std::string layer_string = llformat("Clayers=%d",num_layer_specs);
-				codestream.access_siz()->parse_string(layer_string.c_str());
+				layer_bytes[nb_layers++] = 0;
 			}
+			codestream.access_siz()->parse_string("Creversible=yes");
+			// *TODO: we should use yuv in reversible mode
+			// Don't turn this on now though as it creates problems on decoding for the moment
+			//codestream.access_siz()->parse_string("Cycc=no");
 		}
 		
+		std::string layer_string = llformat("Clayers=%d",nb_layers);
+		codestream.access_siz()->parse_string(layer_string.c_str());
+		
 		// Set up data ordering, markers, etc... if precincts or blocks specified
 		if ((mBlocksSize != -1) || (mPrecinctsSize != -1))
 		{
@@ -669,23 +657,26 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 				std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize);
 				codestream.access_siz()->parse_string(blocks_string.c_str());
 			}
-			std::string ordering_string = llformat("Corder=RPCL");
+			std::string ordering_string = llformat("Corder=LRCP");
 			codestream.access_siz()->parse_string(ordering_string.c_str());
 			std::string PLT_string = llformat("ORGgen_plt=yes");
 			codestream.access_siz()->parse_string(PLT_string.c_str());
 			std::string Parts_string = llformat("ORGtparts=R");
 			codestream.access_siz()->parse_string(Parts_string.c_str());
 		}
+		
+		// Set the number of wavelets subresolutions (aka levels) 
 		if (mLevels != 0)
 		{
 			std::string levels_string = llformat("Clevels=%d",mLevels);
 			codestream.access_siz()->parse_string(levels_string.c_str());
 		}
 		
+		// Complete the encode settings
 		codestream.access_siz()->finalize_all();
 		codestream.change_appearance(transpose,vflip,hflip);
 
-		// Now we are ready for sample data processing.
+		// Now we are ready for sample data processing
 		kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream);
 		bool done = false;
 		while (!done)
@@ -702,7 +693,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 		}
 
 		// Produce the compressed output
-		codestream.flush(layer_bytes,num_layer_specs);
+		codestream.flush(layer_bytes,nb_layers);
 
 		// Cleanup
 		delete tile;
@@ -750,6 +741,207 @@ BOOL LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
 	}
 }
 
+/*****************************************************************************/
+/* STATIC                        copy_block                                  */
+/*****************************************************************************/
+
+static void copy_block(kdu_block *in, kdu_block *out)
+{
+	if (in->K_max_prime != out->K_max_prime)
+    { 
+		std::cout << "Cannot copy blocks belonging to subbands with different quantization parameters." << std::endl; 
+		return;
+	}
+	if ((in->size.x != out->size.x) || (in->size.y != out->size.y))  
+    { 
+		std::cout << "Cannot copy code-blocks with different dimensions." << std::endl; 
+		return;
+	}
+	out->missing_msbs = in->missing_msbs;
+	if (out->max_passes < (in->num_passes+2))        // Gives us enough to round up
+		out->set_max_passes(in->num_passes+2,false); // to the next whole bit-plane
+	out->num_passes = in->num_passes;
+	int num_bytes = 0;
+	for (int z=0; z < in->num_passes; z++)
+    {
+		num_bytes += (out->pass_lengths[z] = in->pass_lengths[z]);
+		out->pass_slopes[z] = in->pass_slopes[z];
+    }
+	
+    // Just copy compressed code-bytes. Block transcoding not supported.
+	if (out->max_bytes < num_bytes)
+		out->set_max_bytes(num_bytes,false);
+	memcpy(out->byte_buffer,in->byte_buffer,(size_t) num_bytes);
+}
+
+/*****************************************************************************/
+/* STATIC                        copy_tile                                   */
+/*****************************************************************************/
+
+static void
+copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out,
+		  kdu_params *siz_in, kdu_params *siz_out, int skip_components,
+		  int &num_blocks)
+{
+	int num_components = tile_out.get_num_components();
+	int new_tpart=0, next_tpart = 1;
+	
+	for (int c=0; c < num_components; c++)
+    {
+		kdu_tile_comp comp_in, comp_out;
+		comp_in = tile_in.access_component(c);
+		comp_out = tile_out.access_component(c);
+		int num_resolutions = comp_out.get_num_resolutions();
+		//std::cout << "    Copying tile : num_resolutions = " << num_resolutions << std::endl;
+		for (int r=0; r < num_resolutions; r++)
+        {
+			kdu_resolution res_in;  res_in = comp_in.access_resolution(r);
+			kdu_resolution res_out; res_out = comp_out.access_resolution(r);
+			int b, min_band;
+			int num_bands = res_in.get_valid_band_indices(min_band);
+			std::cout << "        Copying tile : num_bands = " << num_bands << std::endl;
+			for (b=min_band; num_bands > 0; num_bands--, b++)
+            {
+				kdu_subband band_in;  band_in = res_in.access_subband(b);
+				kdu_subband band_out; band_out = res_out.access_subband(b);
+				kdu_dims blocks_in;  band_in.get_valid_blocks(blocks_in);
+				kdu_dims blocks_out; band_out.get_valid_blocks(blocks_out);
+				if ((blocks_in.size.x != blocks_out.size.x) ||
+					(blocks_in.size.y != blocks_out.size.y))
+                { 
+					std::cout << "Transcoding operation cannot proceed: Code-block partitions for the input and output code-streams do not agree." << std::endl;
+					return;
+				}
+				kdu_coords idx;
+				//std::cout << "            Copying tile : block indices, x = " << blocks_out.size.x << " and y = " << blocks_out.size.y << std::endl;
+				for (idx.y=0; idx.y < blocks_out.size.y; idx.y++)
+				{
+					for (idx.x=0; idx.x < blocks_out.size.x; idx.x++)
+					{
+						kdu_block *in =
+						band_in.open_block(idx+blocks_in.pos,&new_tpart);
+						for (; next_tpart <= new_tpart; next_tpart++)
+							siz_out->copy_from(siz_in,tnum_in,tnum_out,next_tpart,
+											   skip_components);
+						kdu_block *out = band_out.open_block(idx+blocks_out.pos);
+						copy_block(in,out);
+						band_in.close_block(in);
+						band_out.close_block(out);
+						num_blocks++;
+					}
+				}
+            }
+        }
+    }
+}
+
+// Find the block boundary for each discard level in the input image.
+// We parse the input blocks and copy them in a temporary output stream.
+// For the moment, we do nothing more that parsing the raw list of blocks and outputing result.
+void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base)
+{
+	// We need the number of levels in that image before starting.
+	getMetadata(base);
+	
+	for (int discard_level = 0; discard_level < mLevels; discard_level++)
+	{
+		//std::cout << "Parsing discard level = " << discard_level << std::endl;
+		// Create the input codestream object.
+		setupCodeStream(base, TRUE, MODE_FAST);
+		mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL);
+		mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true);
+		siz_params *siz_in = mCodeStreamp->access_siz();
+	
+		// Create the output codestream object.
+		siz_params siz;
+		siz.copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
+		siz.set(Scomponents,0,0,mCodeStreamp->get_num_components());
+	
+		U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents();
+		max_output_size = (max_output_size < 1000 ? 1000 : max_output_size);
+		U8 *output_buffer = new U8[max_output_size];
+		U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size
+		LLKDUMemTarget output(output_buffer, output_size, max_output_size);
+		kdu_codestream codestream_out; 
+		codestream_out.create(&siz,&output);
+		//codestream_out.share_buffering(*mCodeStreamp);
+		siz_params *siz_out = codestream_out.access_siz();
+		siz_out->copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
+		codestream_out.access_siz()->finalize_all(-1);
+	
+		// Set up rate control variables
+		kdu_long max_bytes = KDU_LONG_MAX;
+		kdu_params *cod = siz_out->access_cluster(COD_params);
+		int total_layers;  cod->get(Clayers,0,0,total_layers);
+		kdu_long *layer_bytes = new kdu_long[total_layers];
+		int nel, non_empty_layers = 0;
+	
+		// Now ready to perform the transfer of compressed data between streams
+		int flush_counter = INT_MAX;
+		kdu_dims tile_indices_in;  
+		mCodeStreamp->get_valid_tiles(tile_indices_in);
+		kdu_dims tile_indices_out; 
+		codestream_out.get_valid_tiles(tile_indices_out);
+		assert((tile_indices_in.size.x == tile_indices_out.size.x) &&
+			   (tile_indices_in.size.y == tile_indices_out.size.y));
+		int num_blocks=0;
+	
+		kdu_coords idx;
+		//std::cout << "Parsing tiles : x = " << tile_indices_out.size.x << " to y = " << tile_indices_out.size.y << std::endl;
+		for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++)
+		{
+			for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++)
+			{
+				kdu_tile tile_in = mCodeStreamp->open_tile(idx+tile_indices_in.pos);
+				int tnum_in = tile_in.get_tnum();
+				int tnum_out = idx.x + idx.y*tile_indices_out.size.x;
+				siz_out->copy_from(siz_in,tnum_in,tnum_out,0,0,discard_level,false,false,false);
+				siz_out->finalize_all(tnum_out);
+				// Note: do not open the output tile without first copying any tile-specific code-stream parameters
+				kdu_tile tile_out = codestream_out.open_tile(idx+tile_indices_out.pos);
+				assert(tnum_out == tile_out.get_tnum());
+				copy_tile(tile_in,tile_out,tnum_in,tnum_out,siz_in,siz_out,0,num_blocks);
+				tile_in.close();
+				tile_out.close();
+				flush_counter--;
+				if ((flush_counter <= 0) && codestream_out.ready_for_flush())
+				{
+					flush_counter = INT_MAX;
+					nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
+					non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
+				}
+			}
+		}
+	
+		// Generate the output code-stream
+		if (codestream_out.ready_for_flush())
+		{
+			nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
+			non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
+		}
+		if (non_empty_layers > total_layers)
+			non_empty_layers = total_layers; // Can happen if a tile has more layers
+	
+		// Print out stats
+		std::cout << "Code stream parsing for discard level = " << discard_level << std::endl;
+		std::cout << "    Total compressed memory in  = " << mCodeStreamp->get_compressed_data_memory() << " bytes" << std::endl;
+		std::cout << "    Total compressed memory out = " << codestream_out.get_compressed_data_memory() << " bytes" << std::endl;
+		//std::cout << "    Output contains " << total_layers << " quality layers" << std::endl;		
+		std::cout << "    Transferred " << num_blocks << " code-blocks from in to out" << std::endl;
+		//std::cout << "    Read " << mCodeStreamp->get_num_tparts() << " tile-part(s) from a total of " << (int) tile_indices_in.area() << " tile(s)" << std::endl;
+		std::cout << "    Total bytes read = " << mCodeStreamp->get_total_bytes() << std::endl;
+		//std::cout << "    Wrote " << codestream_out.get_num_tparts() << " tile-part(s) in a total of " << (int) tile_indices_out.area() << " tile(s)" << std::endl;
+		std::cout << "    Total bytes written = " << codestream_out.get_total_bytes() << std::endl;
+		std::cout << "-------------" << std::endl;
+	
+		// Clean-up
+		cleanupCodeStream();
+		codestream_out.destroy();
+		delete[] output_buffer;	
+	}
+	return;
+}
+
 void set_default_colour_weights(kdu_params *siz)
 {
 	kdu_params *cod = siz->access_cluster(COD_params);
diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h
index 1489dbf7043ba08a21a30152ebc0fb121a535233..9ab0b9e4a7bcfe9402ebb81956c3cb9b8e50a83a 100644
--- a/indra/llkdu/llimagej2ckdu.h
+++ b/indra/llkdu/llimagej2ckdu.h
@@ -60,6 +60,7 @@ class LLImageJ2CKDU : public LLImageJ2CImpl
 								BOOL reversible=FALSE);
 	/*virtual*/ BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL);
 	/*virtual*/ BOOL initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0);
+	void findDiscardLevelsBoundaries(LLImageJ2C &base);
 
 private:
 	BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL);
diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp
index ab60ab6d502d05ee3bb0dcb21d43bf4eac151095..beee99a522967af6159b9241c898b076dad827a5 100644
--- a/indra/llkdu/tests/llimagej2ckdu_test.cpp
+++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp
@@ -29,6 +29,7 @@
 // Class to test 
 #include "llimagej2ckdu.h"
 #include "llkdumem.h"
+#include "kdu_block_coding.h"
 // Tut header
 #include "lltut.h"
 
@@ -86,7 +87,7 @@ void LLImageFormatted::resetLastError() { }
 void LLImageFormatted::sanityCheck() { }
 void LLImageFormatted::setLastError(const std::string& , const std::string& ) { }
 
-LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C) { }
+LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), mRate(DEFAULT_COMPRESSION_RATE) { }
 LLImageJ2C::~LLImageJ2C() { }
 S32 LLImageJ2C::calcDataSize(S32 ) { return 0; }
 S32 LLImageJ2C::calcDiscardLevelBytes(S32 ) { return 0; }
@@ -107,16 +108,25 @@ bool LLKDUMemIn::get(int, kdu_line_buf&, int) { return false; }
 
 // Stub Kakadu Library calls
 kdu_tile_comp kdu_tile::access_component(int ) { kdu_tile_comp a; return a; }
+kdu_block_encoder::kdu_block_encoder() { }
+kdu_block_decoder::kdu_block_decoder() { }
+void kdu_block::set_max_passes(int , bool ) { }
+void kdu_block::set_max_bytes(int , bool ) { }
+void kdu_block::set_max_samples(int ) { }
 void kdu_tile::close(kdu_thread_env* ) { }
 int kdu_tile::get_num_components() { return 0; }
 bool kdu_tile::get_ycc() { return false; }
 void kdu_tile::set_components_of_interest(int , const int* ) { }
+int kdu_tile::get_tnum() { return 0; }
 kdu_resolution kdu_tile_comp::access_resolution() { kdu_resolution a; return a; }
+kdu_resolution kdu_tile_comp::access_resolution(int ) { kdu_resolution a; return a; }
 int kdu_tile_comp::get_bit_depth(bool ) { return 8; }
 bool kdu_tile_comp::get_reversible() { return false; }
+int kdu_tile_comp::get_num_resolutions() { return 1; }
 kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; }
 void kdu_resolution::get_dims(kdu_dims& ) { }
 int kdu_resolution::which() { return 0; }
+int kdu_resolution::get_valid_band_indices(int &) { return 1; }
 kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { }
 kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { }
 kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { }
@@ -124,6 +134,7 @@ kdu_params::~kdu_params() { }
 void kdu_params::set(const char* , int , int , bool ) { }
 void kdu_params::set(const char* , int , int , int ) { }
 void kdu_params::finalize_all(bool ) { }
+void kdu_params::finalize_all(int, bool ) { }
 void kdu_params::copy_from(kdu_params*, int, int, int, int, int, bool, bool, bool) { }
 bool kdu_params::parse_string(const char*) { return false; }
 bool kdu_params::get(const char*, int, int, bool&, bool, bool, bool) { return false; }
@@ -135,6 +146,7 @@ void kdu_codestream::set_fast() { }
 void kdu_codestream::set_fussy() { }
 void kdu_codestream::get_dims(int, kdu_dims&, bool ) { }
 int kdu_codestream::get_min_dwt_levels() { return 5; }
+int kdu_codestream::get_max_tile_layers() { return 1; }
 void kdu_codestream::change_appearance(bool, bool, bool) { }
 void kdu_codestream::get_tile_dims(kdu_coords, int, kdu_dims&, bool ) { }
 void kdu_codestream::destroy() { }
@@ -148,9 +160,18 @@ void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { }
 void kdu_codestream::flush(kdu_long *, int , kdu_uint16 *, bool, bool, double, kdu_thread_env*) { }
 void kdu_codestream::set_resilient(bool ) { }
 int kdu_codestream::get_num_components(bool ) { return 0; }
+kdu_long kdu_codestream::get_total_bytes(bool ) { return 0; }
+kdu_long kdu_codestream::get_compressed_data_memory(bool ) {return 0; }
+void kdu_codestream::share_buffering(kdu_codestream ) { }
+int kdu_codestream::get_num_tparts() { return 0; }
+int kdu_codestream::trans_out(kdu_long, kdu_long*, int, bool, kdu_thread_env* ) { return 0; }
+bool kdu_codestream::ready_for_flush(kdu_thread_env*) { return false; }
 siz_params* kdu_codestream::access_siz() { return NULL; }
 kdu_tile kdu_codestream::open_tile(kdu_coords , kdu_thread_env* ) { kdu_tile a; return a; }
 kdu_codestream_comment kdu_codestream::add_comment() { kdu_codestream_comment a; return a; }
+void kdu_subband::close_block(kdu_block*, kdu_thread_env*) { }
+void kdu_subband::get_valid_blocks(kdu_dims &indices) { }
+kdu_block* kdu_subband::open_block(kdu_coords, int*, kdu_thread_env*) { return NULL; }
 bool kdu_codestream_comment::put_text(const char*) { return false; }
 void kdu_customize_warnings(kdu_message*) { }
 void kdu_customize_errors(kdu_message*) { }
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index b93d429feb492070221bf85168214b38c439f92b..8ba965e7ed91134101c3a3b39641b98775fb07a8 100644
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -1506,7 +1506,8 @@ void LLCurl::cleanupClass()
 	delete sHandleMutexp ;
 	sHandleMutexp = NULL ;
 
-	llassert(Easy::sActiveHandles.empty());
+	// removed as per https://jira.secondlife.com/browse/SH-3115
+	//llassert(Easy::sActiveHandles.empty());
 }
 
 //static 
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
old mode 100644
new mode 100755
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
old mode 100644
new mode 100755
index 78591ddd3849facc642adb7b31b61eca3d9f350d..2c13fead9792875737c789e5ae7a968f6b02578e
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -43,7 +43,6 @@
 const F32 MIN_TEXTURE_LIFETIME = 10.f;
 
 //statics
-LLGLuint LLImageGL::sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS] = { 0 };
 
 U32 LLImageGL::sUniqueCount				= 0;
 U32 LLImageGL::sBindCount				= 0;
@@ -65,19 +64,10 @@ std::set<LLImageGL*> LLImageGL::sImageList;
 //****************************************************************************************************
 //-----------------------
 //debug use
-BOOL gAuditTexture = FALSE ;
-#define MAX_TEXTURE_LOG_SIZE 22 //2048 * 2048
-std::vector<S32> LLImageGL::sTextureLoadedCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
-std::vector<S32> LLImageGL::sTextureBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
-std::vector<S32> LLImageGL::sTextureCurBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
 S32 LLImageGL::sCurTexSizeBar = -1 ;
 S32 LLImageGL::sCurTexPickSize = -1 ;
-LLPointer<LLImageGL> LLImageGL::sHighlightTexturep = NULL;
-S32 LLImageGL::sMaxCatagories = 1 ;
+S32 LLImageGL::sMaxCategories = 1 ;
 
-std::vector<S32> LLImageGL::sTextureMemByCategory;
-std::vector<S32> LLImageGL::sTextureMemByCategoryBound ;
-std::vector<S32> LLImageGL::sTextureCurMemByCategoryBound ;
 //------------------------
 //****************************************************************************************************
 //End for texture auditing use only
@@ -175,49 +165,11 @@ BOOL is_little_endian()
 //static 
 void LLImageGL::initClass(S32 num_catagories) 
 {
-	sMaxCatagories = num_catagories ;
-
-	sTextureMemByCategory.resize(sMaxCatagories);
-	sTextureMemByCategoryBound.resize(sMaxCatagories) ;
-	sTextureCurMemByCategoryBound.resize(sMaxCatagories) ;
 }
 
 //static 
 void LLImageGL::cleanupClass() 
 {	
-	sTextureMemByCategory.clear() ;
-	sTextureMemByCategoryBound.clear() ;
-	sTextureCurMemByCategoryBound.clear() ;
-}
-
-//static 
-void LLImageGL::setHighlightTexture(S32 category) 
-{
-	const S32 dim = 128;
-	sHighlightTexturep = new LLImageGL() ;
-	LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
-	U8* data = image_raw->getData();
-	for (S32 i = 0; i<dim; i++)
-	{
-		for (S32 j = 0; j<dim; j++)
-		{
-			const S32 border = 2;
-			if (i<border || j<border || i>=(dim-border) || j>=(dim-border))
-			{
-				*data++ = 0xff;
-				*data++ = 0xff;
-				*data++ = 0xff;
-			}
-			else
-			{
-				*data++ = 0xff;
-				*data++ = 0xff;
-				*data++ = 0x00;
-			}
-		}
-	}
-	sHighlightTexturep->createGLTexture(0, image_raw, 0, TRUE, category);
-	image_raw = NULL;
 }
 
 //static
@@ -285,31 +237,11 @@ void LLImageGL::updateStats(F32 current_time)
 	sLastFrameTime = current_time;
 	sBoundTextureMemoryInBytes = sCurBoundTextureMemory;
 	sCurBoundTextureMemory = 0;
-
-	if(gAuditTexture)
-	{
-		for(U32 i = 0 ; i < sTextureCurBoundCounter.size() ; i++)
-		{
-			sTextureBoundCounter[i] = sTextureCurBoundCounter[i] ;
-			sTextureCurBoundCounter[i] = 0 ;
-		}
-		for(U32 i = 0 ; i < sTextureCurMemByCategoryBound.size() ; i++)
-		{
-			sTextureMemByCategoryBound[i] = sTextureCurMemByCategoryBound[i] ;
-			sTextureCurMemByCategoryBound[i] = 0 ;
-		}
-	}
 }
 
 //static
 S32 LLImageGL::updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category)
 {
-	if(gAuditTexture && ncomponents > 0 && category > -1)
-	{
-		sTextureCurBoundCounter[getTextureCounterIndex(mem / ncomponents)]++ ;
-		sTextureCurMemByCategoryBound[category] += mem ;
-	}
-	
 	LLImageGL::sCurBoundTextureMemory += mem ;
 	return LLImageGL::sCurBoundTextureMemory;
 }
@@ -1284,7 +1216,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
 		return TRUE ;
 	}
 
-	setCategory(category) ;
+	setCategory(category);
  	const U8* rawdata = imageraw->getData();
 	return createGLTexture(discard_level, rawdata, FALSE, usename);
 }
@@ -1362,11 +1294,6 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
 	{
 		sGlobalTextureMemoryInBytes -= mTextureMemory;
 
-		if(gAuditTexture)
-		{
-			decTextureCounter(mTextureMemory, mComponents, mCategory) ;
-		}
-
 		LLImageGL::deleteTextures(1, &old_name);
 
 		stop_glerror();
@@ -1376,10 +1303,6 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
 	sGlobalTextureMemoryInBytes += mTextureMemory;
 	mTexelsInGLTexture = getWidth() * getHeight() ;
 
-	if(gAuditTexture)
-	{
-		incTextureCounter(mTextureMemory, mComponents, mCategory) ;
-	}
 	// mark this as bound at this point, so we don't throw it out immediately
 	mLastBindTime = sLastFrameTime;
 	return TRUE;
@@ -1536,22 +1459,29 @@ void LLImageGL::destroyGLTexture()
 	{
 		if(mTextureMemory)
 		{
-			if(gAuditTexture)
-			{
-				decTextureCounter(mTextureMemory, mComponents, mCategory) ;
-			}
 			sGlobalTextureMemoryInBytes -= mTextureMemory;
 			mTextureMemory = 0;
 		}
 		
 		LLImageGL::deleteTextures(1, &mTexName);			
-		mTexName = 0;
 		mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
+		mTexName = 0;		
 		mGLTextureCreated = FALSE ;
-	}
+	}	
 }
 
-
+//force to invalidate the gl texture, most likely a sculpty texture
+void LLImageGL::forceToInvalidateGLTexture()
+{
+	if (mTexName != 0)
+	{
+		destroyGLTexture();
+	}
+	else
+	{
+		mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
+	}
+}
 
 //----------------------------------------------------------------------------
 
@@ -1969,70 +1899,6 @@ BOOL LLImageGL::getMask(const LLVector2 &tc)
 	return res;
 }
 
-void LLImageGL::setCategory(S32 category) 
-{
-#if 0 //turn this off temporarily because it is not in use now.
-	if(!gAuditTexture)
-	{
-		return ;
-	}
-	if(mCategory != category)
-	{		
-		if(mCategory > -1)
-		{
-			sTextureMemByCategory[mCategory] -= mTextureMemory ;
-		}
-		if(category > -1 && category < sMaxCatagories)
-		{
-			sTextureMemByCategory[category] += mTextureMemory ;		
-			mCategory = category;
-		}
-		else
-		{
-			mCategory = -1 ;
-		}
-	}
-#endif
-}
-
-//for debug use 
-//val is a "power of two" number
-S32 LLImageGL::getTextureCounterIndex(U32 val) 
-{
-	//index range is [0, MAX_TEXTURE_LOG_SIZE].
-	if(val < 2)
-	{
-		return 0 ;
-	}
-	else if(val >= (1 << MAX_TEXTURE_LOG_SIZE))
-	{
-		return MAX_TEXTURE_LOG_SIZE ;
-	}
-	else
-	{
-		S32 ret = 0 ;
-		while(val >>= 1)
-		{
-			++ret;
-		}
-		return ret ;
-	}
-}
-
-//static
-void LLImageGL::incTextureCounter(U32 val, S32 ncomponents, S32 category) 
-{
-	sTextureLoadedCounter[getTextureCounterIndex(val)]++ ;
-	sTextureMemByCategory[category] += (S32)val * ncomponents ;
-}
-
-//static
-void LLImageGL::decTextureCounter(U32 val, S32 ncomponents, S32 category) 
-{
-	sTextureLoadedCounter[getTextureCounterIndex(val)]-- ;
-	sTextureMemByCategory[category] += (S32)val * ncomponents ;
-}
-
 void LLImageGL::setCurTexSizebar(S32 index, BOOL set_pick_size)
 {
 	sCurTexSizeBar = index ;
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
old mode 100644
new mode 100755
index 2cfb15b0d9d4d03c30efa2e8de9f1be2fcafedf4..e515d6b324aa09c74a2c877c3495ebc776de761c
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -102,8 +102,8 @@ class LLImageGL : public LLRefCount
 	static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels);
 
 	BOOL createGLTexture() ;
-	BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, 
-		S32 category = sMaxCatagories - 1);
+	BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE,
+		S32 category = sMaxCategories-1);
 	BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0);
 	void setImage(const LLImageRaw* imageraw);
 	void setImage(const U8* data_in, BOOL data_hasmips = FALSE);
@@ -114,6 +114,7 @@ class LLImageGL : public LLRefCount
 	// Read back a raw image for this discard level, if it exists
 	BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const;
 	void destroyGLTexture();
+	void forceToInvalidateGLTexture();
 
 	void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE);
 	void setComponents(S8 ncomponents) { mComponents = ncomponents; }
@@ -234,8 +235,6 @@ class LLImageGL : public LLRefCount
 	static S32 sCount;
 	
 	static F32 sLastFrameTime;
-	
-	static LLGLuint sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS]; // Currently bound texture ID
 
 	// Global memory statistics
 	static S32 sGlobalTextureMemoryInBytes;		// Tracks main memory texmem
@@ -257,9 +256,10 @@ class LLImageGL : public LLRefCount
 public:
 	static void initClass(S32 num_catagories) ;
 	static void cleanupClass() ;
-private:
-	static S32 sMaxCatagories ;
 
+private:
+	static S32 sMaxCategories;
+	
 	//the flag to allow to call readBackRaw(...).
 	//can be removed if we do not use that function at all.
 	static BOOL sAllowReadBackRaw ;
@@ -269,39 +269,22 @@ class LLImageGL : public LLRefCount
 //****************************************************************************************************
 private:
 	S32 mCategory ;
-public:		
-	void setCategory(S32 category) ;
-	S32  getCategory()const {return mCategory ;}
-
+public:
+	void setCategory(S32 category) {mCategory = category;}
+	S32  getCategory()const {return mCategory;}
+	
 	//for debug use: show texture size distribution 
 	//----------------------------------------
-	static LLPointer<LLImageGL> sHighlightTexturep; //default texture to replace normal textures
-	static std::vector<S32> sTextureLoadedCounter ;
-	static std::vector<S32> sTextureBoundCounter ;
-	static std::vector<S32> sTextureCurBoundCounter ;
 	static S32 sCurTexSizeBar ;
 	static S32 sCurTexPickSize ;
 
-	static void setHighlightTexture(S32 category) ;
-	static S32 getTextureCounterIndex(U32 val) ;
-	static void incTextureCounter(U32 val, S32 ncomponents, S32 category) ;
-	static void decTextureCounter(U32 val, S32 ncomponents, S32 category) ;
 	static void setCurTexSizebar(S32 index, BOOL set_pick_size = TRUE) ;
 	static void resetCurTexSizebar();
-	//----------------------------------------
 
-	//for debug use: show texture category distribution 
-	//----------------------------------------		
-	
-	static std::vector<S32> sTextureMemByCategory;
-	static std::vector<S32> sTextureMemByCategoryBound ;
-	static std::vector<S32> sTextureCurMemByCategoryBound ;
-	//----------------------------------------	
 //****************************************************************************************************
 //End of definitions for texture auditing use only
 //****************************************************************************************************
 
 };
 
-extern BOOL gAuditTexture;
 #endif // LL_LLIMAGEGL_H
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index b0ddacbb05c8dd872ffbfde26501f61639dc0f45..93bac4c779e765bc4142eb0ed525833a5b1c9d83 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -246,14 +246,6 @@ bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
 	}
 
 	//in audit, replace the selected texture by the default one.
-	if(gAuditTexture && for_rendering && LLImageGL::sCurTexPickSize > 0)
-	{
-		if(texture->getWidth() * texture->getHeight() == LLImageGL::sCurTexPickSize)
-		{
-			gl_tex->updateBindStats(gl_tex->mTextureMemory);
-			return bind(LLImageGL::sHighlightTexturep.get());
-		}
-	}
 	if ((mCurrTexture != gl_tex->getTexName()) || forceBind)
 	{
 		activate();
diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp
old mode 100644
new mode 100755
index e01e331acf3f8e5eb2d88467653e457dbf2e84f0..e08ccb0b78c36e1b376d04c87f0a86b6c6444b62
--- a/indra/llui/llcontainerview.cpp
+++ b/indra/llui/llcontainerview.cpp
@@ -196,24 +196,24 @@ void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent)
 	if (total_height < height)
 		total_height = height;
 	
+	LLRect my_rect = getRect();
 	if (followsTop())
 	{
-		// HACK: casting away const. Should use setRect or some helper function instead.
-		const_cast<LLRect&>(getRect()).mBottom = getRect().mTop - total_height;
+		my_rect.mBottom = my_rect.mTop - total_height;
 	}
 	else
 	{
-		// HACK: casting away const. Should use setRect or some helper function instead.
-		const_cast<LLRect&>(getRect()).mTop = getRect().mBottom + total_height;
+		my_rect.mTop = my_rect.mBottom + total_height;
 	}
-	// HACK: casting away const. Should use setRect or some helper function instead.
-		const_cast<LLRect&>(getRect()).mRight = getRect().mLeft + width;
+
+	my_rect.mRight = my_rect.mLeft + width;
+	setRect(my_rect);
 
 	top = total_height;
 	if (mShowLabel)
-		{
-			top -= 20;
-		}
+	{
+		top -= 20;
+	}
 	
 	bottom = top;
 
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index ec4db14790521de961a946989b71761972d37901..04cce7878e4611170a8164aeecef09afce4f5791 100644
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -272,7 +272,7 @@ LLRect LLStatBar::getRequiredRect()
 	{
 		if (mDisplayHistory)
 		{
-			rect.mTop = 67;
+			rect.mTop = 35 + mStatp->getNumBins();
 		}
 		else
 		{
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
old mode 100644
new mode 100755
index 9aaefa9c6ae8e1b74c49c2a069cce26cd3468bcf..4e8b6fc39d0d63d2df1ed75405ee672991aa5bab
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -237,6 +237,7 @@ set(viewer_SOURCE_FILES
     llfloatertelehub.cpp
     llfloatertestinspectors.cpp
     llfloatertestlistview.cpp
+    llfloatertexturefetchdebugger.cpp
     llfloatertools.cpp
     llfloatertopobjects.cpp
     llfloatertos.cpp
@@ -793,6 +794,7 @@ set(viewer_HEADER_FILES
     llfloatertelehub.h
     llfloatertestinspectors.h
     llfloatertestlistview.h
+    llfloatertexturefetchdebugger.h
     llfloatertools.h
     llfloatertopobjects.h
     llfloatertos.h
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
old mode 100644
new mode 100755
index a76eb3cd372d9dcf1875b042b0200ff0bae9b7db..64122bbb6ce9ac782faf15ba47ad80ad2e57583d
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -20,7 +20,7 @@
 					<key>tags</key>
 						<array>
 							<string>AppInit</string>
-              <string>Capabilities</string>
+							<string>Capabilities</string>
 							<string>SystemInfo</string>
 							<string>TextureCache</string>
 							<string>AppCache</string>
@@ -42,8 +42,10 @@
 						</array>
 					<key>tags</key>
 						<array>
-							<!-- sample entry for debugging a specific item	-->
-<!--						<string>Voice</string>		-->
+						<!-- sample entry for debugging specific items	
+						     <string>Avatar</string>
+						     <string>Voice</string>		
+						-->
 						</array>
 				</map>
 			</array>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
old mode 100644
new mode 100755
index 05c05b9393e6ebe87e8a3a2a8f2f3ec458a2926d..d5c43a67f24109e125042131e26a01d1ac75139f
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -335,17 +335,6 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
-  <key>AuditTexture</key>
-  <map>
-    <key>Comment</key>
-    <string>Enable texture auditting.</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Boolean</string>
-    <key>Value</key>
-    <integer>0</integer>
-  </map>
   <key>AutoAcceptNewInventory</key>
     <map>
       <key>Comment</key>
@@ -1936,7 +1925,7 @@
     <key>Type</key>
     <string>Boolean</string>
     <key>Value</key>
-    <integer>0</integer>
+    <integer>1</integer>
   </map>
     <key>DebugBeaconLineWidth</key>
     <map>
@@ -7183,6 +7172,17 @@
       <key>Value</key>
       <integer>-1</integer>
     </map>
+    <key>QAModeMetrics</key>
+    <map>
+      <key>Comment</key>
+      <string>"Enables QA features (logging, faster cycling) for metrics collector"</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
     <key>QuietSnapshotsToDisk</key>
     <map>
       <key>Comment</key>
@@ -10664,6 +10664,39 @@
       <key>Value</key>
       <real>20.0</real>
     </map>
+    <key>TexelPixelRatio</key>
+    <map>
+      <key>Comment</key>
+      <string>texel pixel ratio = texel / pixel</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>1.0</real>
+    </map>
+    <key>TextureCameraMotionThreshold</key>
+    <map>
+      <key>Comment</key>
+      <string>If the overall motion is lower than this value, textures will be loaded faster</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>0.2</real>
+    </map>
+    <key>TextureCameraMotionBoost</key>
+    <map>
+      <key>Comment</key>
+      <string>Progressive discard level decrement when the camera is still</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>3</integer>
+    </map>
     <key>TextureDecodeDisabled</key>
     <map>
       <key>Comment</key>
@@ -10697,6 +10730,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>TextureFetchDebuggerEnabled</key>
+    <map>
+      <key>Comment</key>
+      <string>Enable the texture fetching debugger if set</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
     <key>TextureLoadFullRes</key>
     <map>
       <key>Comment</key>
@@ -10719,6 +10763,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>TextureNewByteRange</key>
+    <map>
+      <key>Comment</key>
+      <string>Use the new more accurate byte range computation for j2c discard levels</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
     <key>TexturePickerShowFolders</key>
     <map>
       <key>Comment</key>
@@ -10741,6 +10796,17 @@
       <key>Value</key>
       <integer>2</integer>
     </map>
+    <key>TextureReverseByteRange</key>
+    <map>
+      <key>Comment</key>
+      <string>Minimal percent of the optimal byte range allowed to render a given discard level</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>50</integer>
+    </map>
     <key>ThrottleBandwidthKBPS</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 3870a3be2ecc71d3d5e122e1b5f21ff6c196bbd1..336760475354c6f094422367d596295088a49cd3 100755
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -3253,6 +3253,10 @@ void LLAgent::processControlRelease(LLMessageSystem *msg, void **)
 void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
 {
 	gAgentQueryManager.mNumPendingQueries--;
+	if (gAgentQueryManager.mNumPendingQueries == 0)
+	{
+		selfStopPhase("fetch_texture_cache_entries");
+	}
 
 	if (!isAgentAvatarValid() || gAgentAvatarp->isDead())
 	{
@@ -3302,13 +3306,12 @@ void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void *
 					else
 					{
 						// no cache of this bake. request upload.
-						gAgentAvatarp->requestLayerSetUpload(baked_index);
+						gAgentAvatarp->invalidateComposite(gAgentAvatarp->getLayerSet(baked_index),TRUE);
 					}
 				}
 			}
 		}
 	}
-
 	llinfos << "Received cached texture response for " << num_results << " textures." << llendl;
 	gAgentAvatarp->outputRezTiming("Fetched agent wearables textures from cache. Will now load them");
 
@@ -3775,7 +3778,15 @@ void LLAgent::sendAgentSetAppearance()
 		return;
 	}
 
-	llinfos << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << llendl;
+	if (!gAgentWearables.changeInProgress())
+	{
+		// Change is fully resolved, can close some open phases.
+		gAgentAvatarp->getPhases().stopPhase("process_initial_wearables_update");
+		gAgentAvatarp->getPhases().stopPhase("wear_inventory_category");
+	}
+	
+	gAgentAvatarp->sendAppearanceChangeMetrics();
+	LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << LL_ENDL;
 	//dumpAvatarTEs( "sendAgentSetAppearance()" );
 
 	LLMessageSystem* msg = gMessageSystem;
@@ -3822,14 +3833,14 @@ void LLAgent::sendAgentSetAppearance()
 	// only update cache entries if we have all our baked textures
 	if (textures_current)
 	{
-		llinfos << "TAT: Sending cached texture data" << llendl;
+		LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL;
 		for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
 		{
 			BOOL generate_valid_hash = TRUE;
 			if (isAgentAvatarValid() && !gAgentAvatarp->isBakedTextureFinal((LLVOAvatarDefines::EBakedTextureIndex)baked_index))
 			{
 				generate_valid_hash = FALSE;
-				llinfos << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << llendl;
+				LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << LL_ENDL;
 			}
 
 			const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash);
diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp
index a8d2222c030cbf9107d45137b543c7bb389d1365..a4c0b056ac192f5384cb81e41fad73520279fb1d 100644
--- a/indra/newview/llagentlistener.cpp
+++ b/indra/newview/llagentlistener.cpp
@@ -126,6 +126,17 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
 		"[\"obj_uuid\"]: id of object to look at, use this or [\"position\"] to indicate the target\n"
 		"[\"position\"]: region position {x, y, z} where to find closest object or avatar to look at",
         &LLAgentListener::lookAt);
+    add("getGroups",
+        "Send information about the agent's groups on [\"reply\"]:\n"
+        "[\"groups\"]: array of group information\n"
+        "[\"id\"]: group id\n"
+        "[\"name\"]: group name\n"
+        "[\"insignia\"]: group insignia texture id\n"
+        "[\"notices\"]: boolean indicating if this user accepts notices from this group\n"
+        "[\"display\"]: boolean indicating if this group is listed in the user's profile\n"
+        "[\"contrib\"]: user's land contribution to this group\n",
+        &LLAgentListener::getGroups,
+        LLSDMap("reply", LLSD()));
 }
 
 void LLAgentListener::requestTeleport(LLSD const & event_data) const
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
old mode 100644
new mode 100755
index dd02a74a381f764d8ba787c04b2ae311186b3b7c..e441f21f904374bc0530159c901b8080464dd4c2
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -955,6 +955,8 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs
 
 	if (isAgentAvatarValid())
 	{
+		//gAgentAvatarp->clearPhases(); // reset phase timers for outfit loading.
+		gAgentAvatarp->getPhases().startPhase("process_initial_wearables_update");
 		gAgentAvatarp->outputRezTiming("Received initial wearables update");
 	}
 
@@ -1448,7 +1450,16 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 	{
 		gAgentAvatarp->setCompositeUpdatesEnabled(TRUE);
 		gAgentAvatarp->updateVisualParams();
-		gAgentAvatarp->invalidateAll();
+
+		// If we have not yet declouded, we may want to use
+		// baked texture UUIDs sent from the first objectUpdate message
+		// don't overwrite these. If we have already declouded, we've saved
+		// these ids as the last known good textures and can invalidate without
+		// re-clouding.
+		if (!gAgentAvatarp->getIsCloud())
+		{
+			gAgentAvatarp->invalidateAll();
+		}
 	}
 
 	// Start rendering & update the server
@@ -1630,10 +1641,11 @@ void LLAgentWearables::queryWearableCache()
 	{
 		if (isAgentAvatarValid())
 		{
+			selfStartPhase("fetch_texture_cache_entries");
 			gAgentAvatarp->outputRezTiming("Fetching textures from cache");
 		}
 
-		llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl;
+		LL_INFOS("Avatar") << gAgentAvatarp->avString() << "Requesting texture cache entry for " << num_queries << " baked textures" << LL_ENDL;
 		gMessageSystem->sendReliable(gAgent.getRegion()->getHost());
 		gAgentQueryManager.mNumPendingQueries++;
 		gAgentQueryManager.mWearablesCacheQueryID++;
@@ -2081,6 +2093,11 @@ boost::signals2::connection LLAgentWearables::addLoadedCallback(loaded_callback_
 	return mLoadedSignal.connect(cb);
 }
 
+bool LLAgentWearables::changeInProgress() const
+{
+	return mCOFChangeInProgress;
+}
+
 void LLAgentWearables::notifyLoadingStarted()
 {
 	mCOFChangeInProgress = true;
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
old mode 100644
new mode 100755
index 01cae3ffd86b1cb59b611655b2bf89d7c5cb434f..5932be21c639be6626ed70fdf3fcac6d55ef6888
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -233,6 +233,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>
 	typedef boost::signals2::signal<void()>	loaded_signal_t;
 	boost::signals2::connection				addLoadedCallback(loaded_callback_t cb);
 
+	bool									changeInProgress() const;
 	void									notifyLoadingStarted();
 	void									notifyLoadingFinished();
 
diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp
index 8cba54347e3141f744f8dc32970b407e53dc50ca..e2417cdddb3528d17d68897dae5e8f547744e366 100644
--- a/indra/newview/llagentwearablesfetch.cpp
+++ b/indra/newview/llagentwearablesfetch.cpp
@@ -89,6 +89,7 @@ LLInitialWearablesFetch::LLInitialWearablesFetch(const LLUUID& cof_id) :
 {
 	if (isAgentAvatarValid())
 	{
+		gAgentAvatarp->getPhases().startPhase("initial_wearables_fetch");
 		gAgentAvatarp->outputRezTiming("Initial wearables fetch started");
 	}
 }
@@ -107,6 +108,7 @@ void LLInitialWearablesFetch::done()
 	doOnIdleOneTime(boost::bind(&LLInitialWearablesFetch::processContents,this));
 	if (isAgentAvatarValid())
 	{
+		gAgentAvatarp->getPhases().stopPhase("initial_wearables_fetch");
 		gAgentAvatarp->outputRezTiming("Initial wearables fetch done");
 	}
 }
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
old mode 100644
new mode 100755
index 33f5373d7e80627cc3e3ad9581040968b2a89dc0..faadfb4b87bcdf86d4797913205964daecbd62bc
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -50,6 +50,11 @@
 #include "llviewerregion.h"
 #include "llwearablelist.h"
 
+std::string self_av_string()
+{
+	return gAgentAvatarp->avString();
+}
+
 // RAII thingy to guarantee that a variable gets reset when the Setter
 // goes out of scope.  More general utility would be handy - TODO:
 // check boost.
@@ -156,6 +161,10 @@ class LLWearInventoryCategoryCallback : public LLInventoryCallback
 	{
 		mCatID = cat_id;
 		mAppend = append;
+
+		LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
+		
+		selfStartPhase("wear_inventory_category_callback");
 	}
 	void fire(const LLUUID& item_id)
 	{
@@ -167,13 +176,16 @@ class LLWearInventoryCategoryCallback : public LLInventoryCallback
 		 * after the last item has fired the event and dereferenced it -- if all
 		 * the events actually fire!
 		 */
+		LL_DEBUGS("Avatar") << self_av_string() << " fired on copied item, id " << item_id << LL_ENDL;
 	}
 
 protected:
 	~LLWearInventoryCategoryCallback()
 	{
-		llinfos << "done all inventory callbacks" << llendl;
+		LL_INFOS("Avatar") << self_av_string() << "done all inventory callbacks" << LL_ENDL;
 		
+		selfStopPhase("wear_inventory_category_callback");
+
 		// Is the destructor called by ordinary dereference, or because the app's shutting down?
 		// If the inventory callback manager goes away, we're shutting down, no longer want the callback.
 		if( LLInventoryCallbackManager::is_instantiated() )
@@ -182,7 +194,7 @@ class LLWearInventoryCategoryCallback : public LLInventoryCallback
 		}
 		else
 		{
-			llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
+			llwarns << self_av_string() << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
 		}
 	}
 
@@ -212,11 +224,14 @@ LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit
 	mFireCount(0),
 	mUpdateBaseOrder(update_base_outfit_ordering)
 {
+	selfStartPhase("update_appearance_on_destroy");
 }
 
 LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
 {
-	llinfos << "done update appearance on destroy" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL;
+
+	selfStopPhase("update_appearance_on_destroy");
 	
 	if (!LLApp::isExiting())
 	{
@@ -229,7 +244,7 @@ void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
 	LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item);
 	const std::string item_name = item ? item->getName() : "ITEM NOT FOUND";
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << LL_ENDL;
 #endif
 	mFireCount++;
 }
@@ -339,11 +354,16 @@ LLWearableHoldingPattern::LLWearableHoldingPattern():
 			 
 	}
 	sActiveHoldingPatterns.insert(this);
+	selfStartPhase("holding_pattern");
 }
 
 LLWearableHoldingPattern::~LLWearableHoldingPattern()
 {
 	sActiveHoldingPatterns.erase(this);
+	if (isMostRecent())
+	{
+		selfStopPhase("holding_pattern");
+	}
 }
 
 bool LLWearableHoldingPattern::isMostRecent()
@@ -390,9 +410,10 @@ void LLWearableHoldingPattern::checkMissingWearables()
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway why don't we actually skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
-		
+
 	std::vector<S32> found_by_type(LLWearableType::WT_COUNT,0);
 	std::vector<S32> requested_by_type(LLWearableType::WT_COUNT,0);
 	for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
@@ -408,7 +429,7 @@ void LLWearableHoldingPattern::checkMissingWearables()
 	{
 		if (requested_by_type[type] > found_by_type[type])
 		{
-			llwarns << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl;
+			llwarns << self_av_string() << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl;
 		}
 		if (found_by_type[type] > 0)
 			continue;
@@ -425,11 +446,13 @@ void LLWearableHoldingPattern::checkMissingWearables()
 			mTypesToRecover.insert(type);
 			mTypesToLink.insert(type);
 			recoverMissingWearable((LLWearableType::EType)type);
-			llwarns << "need to replace " << type << llendl; 
+			llwarns << self_av_string() << "need to replace " << type << llendl; 
 		}
 	}
 
 	resetTime(60.0F);
+
+	selfStartPhase("get_missing_wearables");
 	if (!pollMissingWearables())
 	{
 		doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
@@ -445,13 +468,14 @@ void LLWearableHoldingPattern::onAllComplete()
 
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway need to skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 
 	// Activate all gestures in this folder
 	if (mGestItems.count() > 0)
 	{
-		llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Activating " << mGestItems.count() << " gestures" << LL_ENDL;
 		
 		LLGestureMgr::instance().activateGestures(mGestItems);
 		
@@ -468,13 +492,13 @@ void LLWearableHoldingPattern::onAllComplete()
 	}
 
 	// Update wearables.
-	llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Updating agent wearables with " << mResolved << " wearable items " << LL_ENDL;
 	LLAppearanceMgr::instance().updateAgentWearables(this, false);
 	
 	// Update attachments to match those requested.
 	if (isAgentAvatarValid())
 	{
-		llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Updating " << mObjItems.count() << " attachments" << LL_ENDL;
 		LLAgentWearables::userUpdateAttachments(mObjItems);
 	}
 
@@ -492,9 +516,12 @@ void LLWearableHoldingPattern::onAllComplete()
 
 void LLWearableHoldingPattern::onFetchCompletion()
 {
+	selfStopPhase("get_wearables");
+		
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 
 	checkMissingWearables();
@@ -505,7 +532,8 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 
 	bool completed = isFetchCompleted();
@@ -514,14 +542,14 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
 
 	if (done)
 	{
-		llinfos << "polling, done status: " << completed << " timed out " << timed_out
-				<< " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
+		LL_INFOS("Avatar") << self_av_string() << "polling, done status: " << completed << " timed out " << timed_out
+				<< " elapsed " << mWaitTime.getElapsedTimeF32() << LL_ENDL;
 
 		mFired = true;
 		
 		if (timed_out)
 		{
-			llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
+			llwarns << self_av_string() << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
 		}
 
 		onFetchCompletion();
@@ -543,6 +571,7 @@ class RecoveredItemLinkCB: public LLInventoryCallback
 		if (!mHolder->isMostRecent())
 		{
 			llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+			// runway skip here?
 		}
 
 		llinfos << "Recovered item link for type " << mType << llendl;
@@ -569,12 +598,12 @@ class RecoveredItemLinkCB: public LLInventoryCallback
 			}
 			else
 			{
-				llwarns << "inventory item not found for recovered wearable" << llendl;
+				llwarns << self_av_string() << "inventory item not found for recovered wearable" << llendl;
 			}
 		}
 		else
 		{
-			llwarns << "inventory link not found for recovered wearable" << llendl;
+			llwarns << self_av_string() << "inventory link not found for recovered wearable" << llendl;
 		}
 	}
 private:
@@ -596,10 +625,11 @@ class RecoveredItemCB: public LLInventoryCallback
 	{
 		if (!mHolder->isMostRecent())
 		{
-			llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+			// runway skip here?
+			llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 		}
 
-		llinfos << "Recovered item for type " << mType << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Recovered item for type " << mType << LL_ENDL;
 		LLViewerInventoryItem *itemp = gInventory.getItem(item_id);
 		mWearable->setItemID(item_id);
 		LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder);
@@ -626,7 +656,8 @@ void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 	
 		// Try to recover by replacing missing wearable with a new one.
@@ -665,7 +696,7 @@ void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
 		if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable))
 		{
 			// Wearable link that was never resolved; remove links to it from COF
-			llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl;
+			LL_INFOS("Avatar") << self_av_string() << "removing link for unresolved item " << data.mItemID.asString() << LL_ENDL;
 			LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
 		}
 	}
@@ -675,7 +706,8 @@ bool LLWearableHoldingPattern::pollMissingWearables()
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 	
 	bool timed_out = isTimedOut();
@@ -684,15 +716,17 @@ bool LLWearableHoldingPattern::pollMissingWearables()
 
 	if (!done)
 	{
-		llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size()
+		LL_INFOS("Avatar") << self_av_string() << "polling missing wearables, waiting for items " << mTypesToRecover.size()
 				<< " links " << mTypesToLink.size()
 				<< " wearables, timed out " << timed_out
 				<< " elapsed " << mWaitTime.getElapsedTimeF32()
-				<< " done " << done << llendl;
+				<< " done " << done << LL_ENDL;
 	}
 
 	if (done)
 	{
+		selfStopPhase("get_missing_wearables");
+
 		gAgentAvatarp->debugWearablesLoaded();
 
 		// BAP - if we don't call clearCOFLinksForMissingWearables()
@@ -722,14 +756,14 @@ void LLWearableHoldingPattern::handleLateArrivals()
 	}
 	if (!isMostRecent())
 	{
-		llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl;
+		llwarns << self_av_string() << "Late arrivals not handled - outfit change no longer valid" << llendl;
 	}
 	if (!mIsAllComplete)
 	{
-		llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl;
+		llwarns << self_av_string() << "Late arrivals not handled - in middle of missing wearables processing" << llendl;
 	}
 
-	llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << LL_ENDL;
 
 	// Update mFoundList using late-arriving wearables.
 	std::set<LLWearableType::EType> replaced_types;
@@ -805,19 +839,19 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 	
 	mResolved += 1;  // just counting callbacks, not successes.
-	llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "resolved " << mResolved << "/" << getFoundList().size() << LL_ENDL;
 	if (!wearable)
 	{
-		llwarns << "no wearable found" << llendl;
+		llwarns << self_av_string() << "no wearable found" << llendl;
 	}
 
 	if (mFired)
 	{
-		llwarns << "called after holder fired" << llendl;
+		llwarns << self_av_string() << "called after holder fired" << llendl;
 		if (wearable)
 		{
 			mLateArrivals.insert(wearable);
@@ -843,7 +877,7 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
 			// Failing this means inventory or asset server are corrupted in a way we don't handle.
 			if ((data.mWearableType >= LLWearableType::WT_COUNT) || (wearable->getType() != data.mWearableType))
 			{
-				llwarns << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl;
+				llwarns << self_av_string() << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl;
 				break;
 			}
 
@@ -1391,8 +1425,8 @@ void LLAppearanceMgr::filterWearableItems(
 
 // Create links to all listed items.
 void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
-								  LLInventoryModel::item_array_t& items,
-								  LLPointer<LLInventoryCallback> cb)
+							  LLInventoryModel::item_array_t& items,
+							  LLPointer<LLInventoryCallback> cb)
 {
 	for (S32 i=0; i<items.count(); i++)
 	{
@@ -1408,7 +1442,7 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
 		const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid);
 		const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND";
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-		llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << LL_ENDL;
 #endif
 	}
 }
@@ -1416,7 +1450,7 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
 void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 {
 	LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
-	llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "starting, cat '" << (pcat ? pcat->getName() : "[UNKNOWN]") << "'" << LL_ENDL;
 
 	const LLUUID cof = getCOF();
 
@@ -1478,26 +1512,26 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 	gInventory.notifyObservers();
 
 	// Create links to new COF contents.
-	llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "creating LLUpdateAppearanceOnDestroy" << LL_ENDL;
 	LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(!append);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking body items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking body items" << LL_ENDL;
 #endif
 	linkAll(cof, body_items, link_waiter);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking wear items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking wear items" << LL_ENDL;
 #endif
 	linkAll(cof, wear_items, link_waiter);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking obj items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking obj items" << LL_ENDL;
 #endif
 	linkAll(cof, obj_items, link_waiter);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking gesture items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking gesture items" << LL_ENDL;
 #endif
 	linkAll(cof, gest_items, link_waiter);
 
@@ -1506,7 +1540,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 	{
 		createBaseOutfitLink(category, link_waiter);
 	}
-	llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "waiting for LLUpdateAppearanceOnDestroy" << LL_ENDL;
 }
 
 void LLAppearanceMgr::updatePanelOutfitName(const std::string& name)
@@ -1663,7 +1697,7 @@ void LLAppearanceMgr::enforceItemRestrictions()
 			 ++it)
 		{
 			LLViewerInventoryItem *item = *it;
-			llinfos << "purging duplicate or excess item " << item->getName() << llendl;
+			LL_DEBUGS("Avatar") << self_av_string() << "purging duplicate or excess item " << item->getName() << LL_ENDL;
 			gInventory.purgeObject(item->getUUID());
 		}
 		gInventory.notifyObservers();
@@ -1678,9 +1712,11 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
 		return;
 	}
 
+	LLVOAvatar::ScopedPhaseSetter(gAgentAvatarp,"update_appearance_from_cof");
+	
 	BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF);
 
-	llinfos << "starting" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
 
 	//checking integrity of the COF in terms of ordering of wearables, 
 	//checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state)
@@ -1772,12 +1808,14 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
 		}
 	}
 
+	selfStartPhase("get_wearables");
+
 	for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin();
 		 it != holder->getFoundList().end(); ++it)
 	{
 		LLFoundData& found = *it;
 
-		lldebugs << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
+		lldebugs << self_av_string() << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
 
 		// Fetch the wearables about to be worn.
 		LLWearableList::instance().getAsset(found.mAssetID,
@@ -1849,11 +1887,15 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
 {
 	if(!category) return;
 
+	selfClearPhases();
+	selfStartPhase("wear_inventory_category");
+
 	gAgentWearables.notifyLoadingStarted();
 
-	llinfos << "wearInventoryCategory( " << category->getName()
-			 << " )" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName()
+			 << " )" << LL_ENDL;
 
+	selfStartPhase("wear_inventory_category_fetch");
 	callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
 														   &LLAppearanceMgr::instance(),
 														   category->getUUID(), copy, append));
@@ -1861,7 +1903,9 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
 
 void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append)
 {
-	llinfos << "starting" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
+
+	selfStopPhase("wear_inventory_category_fetch");
 	
 	// We now have an outfit ready to be copied to agent inventory. Do
 	// it, and wear that outfit normally.
@@ -1944,8 +1988,8 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
 	// wearables being dirty.
 	if(!category) return;
 
-	llinfos << "wearInventoryCategoryOnAvatar( " << category->getName()
-			 << " )" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategoryOnAvatar '" << category->getName()
+			 << "'" << LL_ENDL;
 			 	
 	if (gAgentCamera.cameraCustomizeAvatar())
 	{
@@ -1958,7 +2002,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
 
 void LLAppearanceMgr::wearOutfitByName(const std::string& name)
 {
-	llinfos << "Wearing category " << name << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL;
 	//inc_busy_count();
 
 	LLInventoryModel::cat_array_t cat_array;
@@ -2281,7 +2325,7 @@ const std::string OTHER_GESTURES_FOLDER = "Other Gestures";
 
 void LLAppearanceMgr::copyLibraryGestures()
 {
-	llinfos << "Copying library gestures" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Copying library gestures" << LL_ENDL;
 
 	// Copy gestures
 	LLUUID lib_gesture_cat_id =
@@ -2337,11 +2381,11 @@ void LLAppearanceMgr::copyLibraryGestures()
 		LLUUID cat_id = findDescendentCategoryIDByName(lib_gesture_cat_id,folder_name);
 		if (cat_id.isNull())
 		{
-			llwarns << "failed to find gesture folder for " << folder_name << llendl;
+			llwarns << self_av_string() << "failed to find gesture folder for " << folder_name << llendl;
 		}
 		else
 		{
-			llinfos << "initiating fetch and copy for " << folder_name << " cat_id " << cat_id << llendl;
+			LL_DEBUGS("Avatar") << self_av_string() << "initiating fetch and copy for " << folder_name << " cat_id " << cat_id << LL_ENDL;
 			callAfterCategoryFetch(cat_id,
 								   boost::bind(&LLAppearanceMgr::shallowCopyCategory,
 											   &LLAppearanceMgr::instance(),
@@ -2355,7 +2399,7 @@ void LLAppearanceMgr::autopopulateOutfits()
 	// If this is the very first time the user has logged into viewer2+ (from a legacy viewer, or new account)
 	// then auto-populate outfits from the library into the My Outfits folder.
 
-	llinfos << "avatar fully visible" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "avatar fully visible" << LL_ENDL;
 
 	static bool check_populate_my_outfits = true;
 	if (check_populate_my_outfits && 
@@ -2731,7 +2775,7 @@ void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg)
 }
 
 void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items,
-										const std::string& msg)
+									const std::string& msg)
 {
 	for (S32 i=0; i<items.count(); i++)
 	{
@@ -2742,9 +2786,8 @@ void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items,
 		{
 			asset_id = linked_item->getAssetUUID();
 		}
-		llinfos << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << LL_ENDL;
 	}
-	llinfos << llendl;
 }
 
 LLAppearanceMgr::LLAppearanceMgr():
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 1174d108d2b971b9d43ec40cc20fc3463d8a205c..178b96e42e16a751cf33e2356548260a146f48b2 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -93,6 +93,7 @@
 #include "llsecondlifeurls.h"
 #include "llupdaterservice.h"
 #include "llcallfloater.h"
+#include "llfloatertexturefetchdebugger.h"
 
 // Linden library includes
 #include "llavatarnamecache.h"
@@ -560,7 +561,6 @@ static void settings_modify()
 	LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4]
 	gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession;
 	gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline");
-	gAuditTexture = gSavedSettings.getBOOL("AuditTexture");
 }
 
 class LLFastTimerLogThread : public LLThread
@@ -731,12 +731,12 @@ bool LLAppViewer::init()
 	
 	{
 		// Viewer metrics initialization
-		static LLCachedControl<bool> metrics_submode(gSavedSettings,
-													 "QAModeMetrics",
-													 false,
-													 "Enables QA features (logging, faster cycling) for metrics collector");
+		//static LLCachedControl<bool> metrics_submode(gSavedSettings,
+		//											 "QAModeMetrics",
+		//											 false,
+		//											 "Enables QA features (logging, faster cycling) for metrics collector");
 
-		if (metrics_submode)
+		if (gSavedSettings.getBOOL("QAModeMetrics"))
 		{
 			app_metrics_qa_mode = true;
 			app_metrics_interval = METRICS_INTERVAL_QA;
@@ -1219,7 +1219,7 @@ bool LLAppViewer::mainLoop()
 			if(mem_leak_instance)
 			{
 				mem_leak_instance->idle() ;				
-			}			
+			}							
 
             // canonical per-frame event
             mainloop.post(newFrame);
@@ -1340,13 +1340,11 @@ bool LLAppViewer::mainLoop()
 					ms_sleep(500);
 				}
 
-				static const F64 FRAME_SLOW_THRESHOLD = 0.5; //2 frames per seconds				
 				const F64 max_idle_time = llmin(.005*10.0*gFrameTimeSeconds, 0.005); // 5 ms a second
 				idleTimer.reset();
-				bool is_slow = (frameTimer.getElapsedTimeF64() > FRAME_SLOW_THRESHOLD) ;
 				S32 total_work_pending = 0;
 				S32 total_io_pending = 0;	
-				while(!is_slow)//do not unpause threads if the frame rates are very low.
+				while(1)
 				{
 					S32 work_pending = 0;
 					S32 io_pending = 0;
@@ -1406,6 +1404,17 @@ bool LLAppViewer::mainLoop()
 					LLLFSThread::sLocal->pause(); 
 				}									
 
+				//texture fetching debugger
+				if(LLTextureFetchDebugger::isEnabled())
+				{
+					LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
+						LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
+					if(tex_fetch_debugger_instance)
+					{
+						tex_fetch_debugger_instance->idle() ;				
+					}
+				}
+
 				if ((LLStartUp::getStartupState() >= STATE_CLEANUP) &&
 					(frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD))
 				{
@@ -1951,7 +1960,7 @@ bool LLAppViewer::initThreads()
 	static const bool enable_threads = true;
 #endif
 
-	LLImage::initClass();
+	LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange"));
 
 	LLVFSThread::initClass(enable_threads && false);
 	LLLFSThread::initClass(enable_threads && false);
@@ -4201,6 +4210,7 @@ void LLAppViewer::idle()
 			// The 5-second interval is nice for this purpose.  If the object debug
 			// bit moves or is disabled, please give this a suitable home.
 			LLViewerAssetStatsFF::record_fps_main(gFPSClamped);
+			LLViewerAssetStatsFF::record_avatar_stats();
 		}
 	}
 
@@ -4248,7 +4258,8 @@ void LLAppViewer::idle()
 		static LLTimer report_interval;
 
 		// *TODO:  Add configuration controls for this
-		if (report_interval.getElapsedTimeF32() >= app_metrics_interval)
+		F32 seconds = report_interval.getElapsedTimeF32();
+		if (seconds >= app_metrics_interval)
 		{
 			metricsSend(! gDisconnected);
 			report_interval.reset();
diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp
index b1cd83a1fba65a706b50e09e48290f711b2a6bb4..706bc42ea07cac99f442f40438bfd7fa9bd58e31 100644
--- a/indra/newview/llavatarpropertiesprocessor.cpp
+++ b/indra/newview/llavatarpropertiesprocessor.cpp
@@ -33,6 +33,7 @@
 #include "llagentpicksinfo.h"
 #include "lldateutil.h"
 #include "llviewergenericmessage.h"
+#include "llstartup.h"
 
 // Linden library includes
 #include "llavatarconstants.h"	// AVATAR_TRANSACTED, etc.
@@ -113,6 +114,14 @@ void LLAvatarPropertiesProcessor::sendGenericRequest(const LLUUID& avatar_id, EA
 
 void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequest(const LLUUID& avatar_id)
 {
+	// this is the startup state when send_complete_agent_movement() message is sent.
+	// Before this, the AvatarPropertiesRequest message  
+	// won't work so don't bother trying
+	if (LLStartUp::getStartupState() <= STATE_AGENT_SEND)
+	{
+		return;
+	}
+
 	if (isPendingRequest(avatar_id, APT_PROPERTIES))
 	{
 		// waiting for a response, don't re-request
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
index 7d3170cb76504149753aa9243920a28980fa0230..29b1d23d7df24737563a50305aeba8f86510c506 100644
--- a/indra/newview/lldebugview.cpp
+++ b/indra/newview/lldebugview.cpp
@@ -68,8 +68,6 @@ LLDebugView::~LLDebugView()
 	gDebugView = NULL;
 	gTextureView = NULL;
 	gSceneView = NULL;
-	gTextureSizeView = NULL;
-	gTextureCategoryView = NULL;
 }
 
 void LLDebugView::init()
@@ -117,35 +115,11 @@ void LLDebugView::init()
 	LLTextureView::Params tvp;
 	tvp.name("gTextureView");
 	tvp.rect(r);
-	tvp.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
+	tvp.follows.flags(FOLLOWS_TOP|FOLLOWS_LEFT);
 	tvp.visible(false);
 	gTextureView = LLUICtrlFactory::create<LLTextureView>(tvp);
 	addChild(gTextureView);
 	//gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE);
-
-
-	if(gAuditTexture)
-	{
-		r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100);
-		LLTextureSizeView::Params tsv ;
-		tsv.name("gTextureSizeView");
-		tsv.rect(r);
-		tsv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
-		tsv.visible(false);
-		gTextureSizeView = LLUICtrlFactory::create<LLTextureSizeView>(tsv);
-		addChild(gTextureSizeView);
-		gTextureSizeView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_SIZE) ;
-
-		r.set(150, rect.getHeight() - 50, 900 + LLViewerTexture::getTotalNumOfCategories() * 30, 100);
-		LLTextureSizeView::Params tcv ;
-		tcv.name("gTextureCategoryView");
-		tcv.rect(r);
-		tcv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
-		tcv.visible(false);
-		gTextureCategoryView = LLUICtrlFactory::create<LLTextureSizeView>(tcv);
-		gTextureCategoryView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_CATEGORY);
-		addChild(gTextureCategoryView);
-	}
 }
 
 void LLDebugView::draw()
diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b34b720557148beb70c7c214cf96a3f9f62f790
--- /dev/null
+++ b/indra/newview/llfloatertexturefetchdebugger.cpp
@@ -0,0 +1,390 @@
+/** 
+ * @file llfloatertexturefetchdebugger.cpp
+ * @brief LLFloaterTextureFetchDebugger class definition
+ *
+ * $LicenseInfo:firstyear=2007&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 "llfloatertexturefetchdebugger.h"
+
+#include "lluictrlfactory.h"
+#include "llbutton.h"
+#include "llspinctrl.h"
+#include "llresmgr.h"
+
+#include "llmath.h"
+#include "llviewerwindow.h"
+#include "llappviewer.h"
+#include "lltexturefetch.h"
+#include "llviewercontrol.h"
+
+LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key)
+	: LLFloater(key),
+	mDebugger(NULL)
+{
+	setTitle("Texture Fetching Debugger Floater");
+	
+	mCommitCallbackRegistrar.add("TexFetchDebugger.ChangeTexelPixelRatio",	boost::bind(&LLFloaterTextureFetchDebugger::onChangeTexelPixelRatio, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Start",	boost::bind(&LLFloaterTextureFetchDebugger::onClickStart, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Clear",	boost::bind(&LLFloaterTextureFetchDebugger::onClickClear, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Close",	boost::bind(&LLFloaterTextureFetchDebugger::onClickClose, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.CacheRead",	boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheRead, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.CacheWrite",	boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheWrite, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.HTTPLoad",	boost::bind(&LLFloaterTextureFetchDebugger::onClickHTTPLoad, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Decode",	boost::bind(&LLFloaterTextureFetchDebugger::onClickDecode, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.GLTexture",	boost::bind(&LLFloaterTextureFetchDebugger::onClickGLTexture, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisCache",	boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisCache, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisHTTP",	boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP, this));
+}
+//----------------------------------------------
+
+BOOL LLFloaterTextureFetchDebugger::postBuild(void) 
+{	
+	mDebugger = LLAppViewer::getTextureFetch()->getFetchDebugger();
+
+	//set states for buttons
+	mButtonStateMap["start_btn"] = true;
+	mButtonStateMap["close_btn"] = true;
+	mButtonStateMap["clear_btn"] = true;
+	mButtonStateMap["cacheread_btn"] = false;
+	mButtonStateMap["cachewrite_btn"] = false;
+	mButtonStateMap["http_btn"] = false;
+	mButtonStateMap["decode_btn"] = false;
+	mButtonStateMap["gl_btn"] = false;
+
+	mButtonStateMap["refetchviscache_btn"] = true;
+	mButtonStateMap["refetchvishttp_btn"] = true;
+
+	updateButtons();
+
+	getChild<LLUICtrl>("texel_pixel_ratio")->setValue(gSavedSettings.getF32("TexelPixelRatio"));
+
+	return TRUE ;
+}
+
+LLFloaterTextureFetchDebugger::~LLFloaterTextureFetchDebugger()
+{
+	//stop everything
+	mDebugger->stopDebug();
+}
+
+void LLFloaterTextureFetchDebugger::updateButtons()
+{
+	for(std::map<std::string, bool>::iterator iter = mButtonStateMap.begin(); iter != mButtonStateMap.end(); ++iter)
+	{
+		if(iter->second)
+		{
+			childEnable(iter->first.c_str());
+		}
+		else
+		{
+			childDisable(iter->first.c_str());
+		}
+	}
+}
+
+void LLFloaterTextureFetchDebugger::disableButtons()
+{
+	childDisable("start_btn");
+	childDisable("clear_btn");
+	childDisable("cacheread_btn");
+	childDisable("cachewrite_btn");
+	childDisable("http_btn");
+	childDisable("decode_btn");
+	childDisable("gl_btn");
+	childDisable("refetchviscache_btn");
+	childDisable("refetchvishttp_btn");
+}
+
+void LLFloaterTextureFetchDebugger::idle()
+{	
+	LLTextureFetchDebugger::e_debug_state state = mDebugger->getState();
+	
+	if(mDebugger->update())
+	{
+		switch(state)
+		{
+		case LLTextureFetchDebugger::IDLE:
+			break;
+		case LLTextureFetchDebugger::READ_CACHE:
+			mButtonStateMap["cachewrite_btn"] = true;
+			mButtonStateMap["decode_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::WRITE_CACHE:
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::DECODING:
+			mButtonStateMap["gl_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::HTTP_FETCHING:
+			mButtonStateMap["cacheread_btn"] = true;
+			mButtonStateMap["cachewrite_btn"] = true;
+			mButtonStateMap["decode_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::GL_TEX:
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::REFETCH_VIS_CACHE:
+			updateButtons();
+		case LLTextureFetchDebugger::REFETCH_VIS_HTTP:
+			updateButtons();
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+//----------------------
+void LLFloaterTextureFetchDebugger::onChangeTexelPixelRatio()
+{
+	gSavedSettings.setF32("TexelPixelRatio", getChild<LLUICtrl>("texel_pixel_ratio")->getValue().asReal());
+}
+
+void LLFloaterTextureFetchDebugger::onClickStart()
+{
+	disableButtons();
+
+	mDebugger->startDebug();
+
+	mButtonStateMap["start_btn"] = false;
+	mButtonStateMap["cacheread_btn"] = true;
+	mButtonStateMap["http_btn"] = true;
+	updateButtons();
+}
+
+void LLFloaterTextureFetchDebugger::onClickClose()
+{
+	setVisible(FALSE);
+	
+	//stop everything
+	mDebugger->stopDebug();
+}
+
+void LLFloaterTextureFetchDebugger::onClickClear()
+{
+	mButtonStateMap["start_btn"] = true;
+	mButtonStateMap["close_btn"] = true;
+	mButtonStateMap["clear_btn"] = true;
+	mButtonStateMap["cacheread_btn"] = false;
+	mButtonStateMap["cachewrite_btn"] = false;
+	mButtonStateMap["http_btn"] = false;
+	mButtonStateMap["decode_btn"] = false;
+	mButtonStateMap["gl_btn"] = false;
+	mButtonStateMap["refetchviscache_btn"] = true;
+	mButtonStateMap["refetchvishttp_btn"] = true;
+	updateButtons();
+
+	//stop everything
+	mDebugger->stopDebug();
+	mDebugger->clearHistory();
+}
+
+void LLFloaterTextureFetchDebugger::onClickCacheRead()
+{
+	disableButtons();
+
+	mDebugger->debugCacheRead();
+}
+
+void LLFloaterTextureFetchDebugger::onClickCacheWrite()
+{
+	disableButtons();
+
+	mDebugger->debugCacheWrite();
+}
+
+void LLFloaterTextureFetchDebugger::onClickHTTPLoad()
+{
+	disableButtons();
+
+	mDebugger->debugHTTP();
+}
+
+void LLFloaterTextureFetchDebugger::onClickDecode()
+{
+	disableButtons();
+
+	mDebugger->debugDecoder();
+}
+
+void LLFloaterTextureFetchDebugger::onClickGLTexture()
+{
+	disableButtons();
+
+	mDebugger->debugGLTextureCreation();
+}
+
+void LLFloaterTextureFetchDebugger::onClickRefetchVisCache()
+{
+	disableButtons();
+
+	mDebugger->debugRefetchVisibleFromCache();
+}
+
+void LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP()
+{
+	disableButtons();
+
+	mDebugger->debugRefetchVisibleFromHTTP();
+}
+
+void LLFloaterTextureFetchDebugger::draw()
+{
+	//total number of fetched textures
+	{
+		getChild<LLUICtrl>("total_num_fetched_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumFetchedTextures()));
+	}
+
+	//total number of fetching requests
+	{
+		getChild<LLUICtrl>("total_num_fetching_requests_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumFetchingRequests()));
+	}
+
+	//total number of cache hits
+	{
+		getChild<LLUICtrl>("total_num_cache_hits_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumCacheHits()));
+	}
+
+	//total number of visible textures
+	{
+		getChild<LLUICtrl>("total_num_visible_tex_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumVisibleFetchedTextures()));
+	}
+
+	//total number of visible texture fetching requests
+	{
+		getChild<LLUICtrl>("total_num_visible_tex_fetch_req_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumVisibleFetchingRequests()));
+	}
+
+	//total number of fetched data
+	{
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getFetchedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getDecodedData() >> 10));
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getFetchedPixels() / 1000000.f));
+	}
+
+	//total number of visible fetched data
+	{		
+		getChild<LLUICtrl>("total_fetched_vis_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getVisibleFetchedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_vis_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getVisibleDecodedData() >> 10));
+	}
+
+	//total number of rendered fetched data
+	{
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getRenderedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getRenderedDecodedData() >> 10));
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRenderedPixels() / 1000000.f));
+	}
+
+	//total time on cache readings
+	if(mDebugger->getCacheReadTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_cache_read_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_cache_read_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getCacheReadTime()));
+	}
+
+	//total time on cache writings
+	if(mDebugger->getCacheWriteTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_cache_write_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_cache_write_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getCacheWriteTime()));
+	}
+
+	//total time on decoding
+	if(mDebugger->getDecodeTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_decode_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_decode_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getDecodeTime()));
+	}
+
+	//total time on gl texture creation
+	if(mDebugger->getGLCreationTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_gl_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_gl_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getGLCreationTime()));
+	}
+
+	//total time on HTTP fetching
+	if(mDebugger->getHTTPTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_http_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getHTTPTime()));
+	}
+
+	//total time on entire fetching
+	{
+		getChild<LLUICtrl>("total_time_fetch_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getTotalFetchingTime()));
+	}
+
+	//total time on refetching visible textures from cache
+	if(mDebugger->getRefetchVisCacheTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[TIME]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisCacheTime()));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f));
+	}
+
+	//total time on refetching visible textures from http
+	if(mDebugger->getRefetchVisHTTPTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[TIME]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisHTTPTime()));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f));
+	}
+
+	LLFloater::draw();
+}
diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h
new file mode 100644
index 0000000000000000000000000000000000000000..33012c6a3dcf67978bd3e49df7fc6173df2ae04c
--- /dev/null
+++ b/indra/newview/llfloatertexturefetchdebugger.h
@@ -0,0 +1,71 @@
+/** 
+ * @file llfloatertexturefetchdebugger.h
+ * @brief texture fetching debugger window, debug use only
+ *
+ * $LicenseInfo:firstyear=2004&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$
+ */
+
+#ifndef LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
+#define LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
+
+#include "llfloater.h"
+class LLTextureFetchDebugger;
+
+class LLFloaterTextureFetchDebugger : public LLFloater
+{
+	friend class LLFloaterReg;
+public:
+	/// initialize all the callbacks for the menu
+
+	virtual BOOL postBuild() ;
+	virtual void draw() ;
+	
+	void onChangeTexelPixelRatio();
+	
+	void onClickStart();
+	void onClickClear();
+	void onClickClose();
+
+	void onClickCacheRead();
+	void onClickCacheWrite();
+	void onClickHTTPLoad();
+	void onClickDecode();
+	void onClickGLTexture();
+
+	void onClickRefetchVisCache();
+	void onClickRefetchVisHTTP();
+public:
+	void idle() ;
+
+private:	
+	LLFloaterTextureFetchDebugger(const LLSD& key);
+	virtual ~LLFloaterTextureFetchDebugger();
+
+	void updateButtons();
+	void disableButtons();
+
+private:	
+	LLTextureFetchDebugger* mDebugger;
+	std::map<std::string, bool> mButtonStateMap;
+};
+
+#endif // LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
old mode 100644
new mode 100755
index 85ecb133d07eefea4624693663a7f92dc71a37e1..8092f3bf36bbcfe7cf5898847b4532ca9dbe503a
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1759,6 +1759,7 @@ bool LLInventoryModel::loadSkeleton(
 		update_map_t child_counts;
 		cat_array_t categories;
 		item_array_t items;
+		item_array_t possible_broken_links;
 		cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded.
 		std::string owner_id_str;
 		owner_id.toString(owner_id_str);
@@ -1807,7 +1808,7 @@ bool LLInventoryModel::loadSkeleton(
 				LLViewerInventoryCategory* tcat = *cit;
 				
 				// we can safely ignore anything loaded from file, but
-				// not sent down in the skeleton.
+				// not sent down in the skeleton. Must have been removed from inventory.
 				if(cit == not_cached)
 				{
 					continue;
@@ -1845,6 +1846,8 @@ bool LLInventoryModel::loadSkeleton(
 			// Add all the items loaded which are parented to a
 			// category with a correctly cached parent
 			S32 bad_link_count = 0;
+			S32 good_link_count = 0;
+			S32 recovered_link_count = 0;
 			cat_map_t::iterator unparented = mCategoryMap.end();
 			for(item_array_t::const_iterator item_iter = items.begin();
 				item_iter != items.end();
@@ -1861,26 +1864,56 @@ bool LLInventoryModel::loadSkeleton(
 						// This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache.
 						if (item->getIsBrokenLink())
 						{
-							bad_link_count++;
+							//bad_link_count++;
 							lldebugs << "Attempted to add cached link item without baseobj present ( name: "
 									 << item->getName() << " itemID: " << item->getUUID()
 									 << " assetID: " << item->getAssetUUID()
 									 << " ).  Ignoring and invalidating " << cat->getName() << " . " << llendl;
-							invalid_categories.insert(cit->second);
+							possible_broken_links.push_back(item);
 							continue;
 						}
+						else if (item->getIsLinkType())
+						{
+							good_link_count++;
+						}
 						addItem(item);
 						cached_item_count += 1;
 						++child_counts[cat->getUUID()];
 					}
 				}
 			}
-			if (bad_link_count > 0)
+			if (possible_broken_links.size() > 0)
 			{
-				llinfos << "Attempted to add " << bad_link_count
-						<< " cached link items without baseobj present. "
-						<< "The corresponding categories were invalidated." << llendl;
+				for(item_array_t::const_iterator item_iter = possible_broken_links.begin();
+				    item_iter != possible_broken_links.end();
+				    ++item_iter)
+				{
+					LLViewerInventoryItem *item = (*item_iter).get();
+					const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID());
+					const LLViewerInventoryCategory* cat = cit->second.get();
+					if (item->getIsBrokenLink())
+					{
+						bad_link_count++;
+						invalid_categories.insert(cit->second);
+						//llinfos << "link still broken: " << item->getName() << " in folder " << cat->getName() << llendl;
+					}
+					else
+					{
+						// was marked as broken because of loading order, its actually fine to load
+						addItem(item);
+						cached_item_count += 1;
+						++child_counts[cat->getUUID()];
+						recovered_link_count++;
+					}
+				}
+
+ 				llinfos << "Attempted to add " << bad_link_count
+ 						<< " cached link items without baseobj present. "
+					    << good_link_count << " link items were successfully added. "
+					    << recovered_link_count << " links added in recovery. "
+ 						<< "The corresponding categories were invalidated." << llendl;
 			}
+
 		}
 		else
 		{
@@ -2778,7 +2811,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
 	{
 		LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
 		titem->unpackMessage(msg, _PREHASH_ItemData, i);
-		llinfos << "unpaked item '" << titem->getName() << "' in "
+		llinfos << "unpacked item '" << titem->getName() << "' in "
 				<< titem->getParentUUID() << llendl;
 		U32 callback_id;
 		msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id);
diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp
index b4224e30e688d884104a85403e20e022f98fdfad..c71ea48193d778b6eb29dff6f2799a187e2f0bd1 100644
--- a/indra/newview/llnearbychatbar.cpp
+++ b/indra/newview/llnearbychatbar.cpp
@@ -34,6 +34,7 @@
 
 #include "llfirstuse.h"
 #include "llnearbychatbar.h"
+#include "llnearbychatbarlistener.h"
 #include "llagent.h"
 #include "llgesturemgr.h"
 #include "llmultigesture.h"
@@ -80,6 +81,7 @@ LLNearbyChatBar::LLNearbyChatBar(const LLSD& key)
 	mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT)
 {
 	mSpeakerMgr = LLLocalSpeakerMgr::getInstance();
+	mListener.reset(new LLNearbyChatBarListener(*this));
 }
 
 //virtual
diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h
index 8547cf0bcedbf83b5a05bc61d1fbcce8d4508d92..aa9c3a6a983da75e4519aee61351652accc0ee72 100644
--- a/indra/newview/llnearbychatbar.h
+++ b/indra/newview/llnearbychatbar.h
@@ -35,6 +35,8 @@
 #include "lloutputmonitorctrl.h"
 #include "llspeakers.h"
 
+class LLNearbyChatBarListener;
+
 class LLNearbyChatBar :	public LLFloater
 {
 public:
@@ -92,6 +94,8 @@ class LLNearbyChatBar :	public LLFloater
 	LLLocalSpeakerMgr*		mSpeakerMgr;
 
 	S32 mExpandedHeight;
+
+	boost::shared_ptr<LLNearbyChatBarListener> mListener;
 };
 
 #endif
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 5d196a465f6bc3a9f4f5c2284ce95aa9752305f0..b7a5eea27c0ad7af303578a036652aef8fbfe3d1 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -3647,6 +3647,110 @@ void renderShadowFrusta(LLDrawInfo* params)
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);
 }
 
+void renderTexelDensity(LLDrawable* drawable)
+{
+	if (LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_OFF
+		|| LLViewerTexture::sCheckerBoardImagep.isNull())
+	{
+		return;
+	}
+
+	LLGLEnable _(GL_BLEND);
+	//gObjectFullbrightProgram.bind();
+
+	LLMatrix4 checkerboard_matrix;
+	S32 discard_level = -1;
+
+	for (S32 f = 0; f < drawable->getNumFaces(); f++)
+	{
+		LLFace* facep = drawable->getFace(f);
+		LLVertexBuffer* buffer = facep->getVertexBuffer();
+		LLViewerTexture* texturep = facep->getTexture();
+
+		if (texturep == NULL) continue;
+
+		switch(LLViewerTexture::sDebugTexelsMode)
+		{
+		case LLViewerTexture::DEBUG_TEXELS_CURRENT:
+			discard_level = -1;
+			break;
+		case LLViewerTexture::DEBUG_TEXELS_DESIRED:
+			{
+				LLViewerFetchedTexture* fetched_texturep = dynamic_cast<LLViewerFetchedTexture*>(texturep);
+				discard_level = fetched_texturep ? fetched_texturep->getDesiredDiscardLevel() : -1;
+				break;
+			}
+		default:
+		case LLViewerTexture::DEBUG_TEXELS_FULL:
+			discard_level = 0;
+			break;
+		}
+
+		checkerboard_matrix.initScale(LLVector3(texturep->getWidth(discard_level) / 8, texturep->getHeight(discard_level) / 8, 1.f));
+
+		gGL.getTexUnit(0)->bind(LLViewerTexture::sCheckerBoardImagep, TRUE);
+		gGL.matrixMode(LLRender::MM_TEXTURE);
+		gGL.loadMatrix((GLfloat*)&checkerboard_matrix.mMatrix);
+
+		if (buffer && (facep->getGeomCount() >= 3))
+		{
+			buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
+			U16 start = facep->getGeomStart();
+			U16 end = start + facep->getGeomCount()-1;
+			U32 count = facep->getIndicesCount();
+			U16 offset = facep->getIndicesStart();
+			buffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+		}
+
+		gGL.loadIdentity();
+		gGL.matrixMode(LLRender::MM_MODELVIEW);
+	}
+
+	//S32 num_textures = llmax(1, (S32)params->mTextureList.size());
+
+	//for (S32 i = 0; i < num_textures; i++)
+	//{
+	//	LLViewerTexture* texturep = params->mTextureList.empty() ? params->mTexture.get() : params->mTextureList[i].get();
+	//	if (texturep == NULL) continue;
+
+	//	LLMatrix4 checkboard_matrix;
+	//	S32 discard_level = -1;
+	//	switch(LLViewerTexture::sDebugTexelsMode)
+	//	{
+	//	case LLViewerTexture::DEBUG_TEXELS_CURRENT:
+	//		discard_level = -1;
+	//		break;
+	//	case LLViewerTexture::DEBUG_TEXELS_DESIRED:
+	//		{
+	//			LLViewerFetchedTexture* fetched_texturep = dynamic_cast<LLViewerFetchedTexture*>(texturep);
+	//			discard_level = fetched_texturep ? fetched_texturep->getDesiredDiscardLevel() : -1;
+	//			break;
+	//		}
+	//	default:
+	//	case LLViewerTexture::DEBUG_TEXELS_FULL:
+	//		discard_level = 0;
+	//		break;
+	//	}
+
+	//	checkboard_matrix.initScale(LLVector3(texturep->getWidth(discard_level) / 8, texturep->getHeight(discard_level) / 8, 1.f));
+	//	gGL.getTexUnit(i)->activate();
+
+	//	glMatrixMode(GL_TEXTURE);
+	//	glPushMatrix();
+	//	glLoadIdentity();
+	//	//gGL.matrixMode(LLRender::MM_TEXTURE);
+	//	glLoadMatrixf((GLfloat*) checkboard_matrix.mMatrix);
+
+	//	gGL.getTexUnit(i)->bind(LLViewerTexture::sCheckerBoardImagep, TRUE);
+
+	//	pushVerts(params, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_NORMAL );
+
+	//	glPopMatrix();
+	//	glMatrixMode(GL_MODELVIEW);
+	//	//gGL.matrixMode(LLRender::MM_MODELVIEW);
+	//}
+}
+
 
 void renderLights(LLDrawable* drawablep)
 {
@@ -4042,6 +4146,10 @@ class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
 			{
 				renderComplexityDisplay(drawable);
 			}
+			if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY))
+			{
+				renderTexelDensity(drawable);
+			}
 
 			LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
 			
@@ -4291,7 +4399,8 @@ void LLSpatialPartition::renderDebug()
 									  LLPipeline::RENDER_DEBUG_AGENT_TARGET |
 									  //LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
 									  LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA |
-									  LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY)) 
+									  LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY |
+									  LLPipeline::RENDER_DEBUG_TEXEL_DENSITY)) 
 	{
 		return;
 	}
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
old mode 100644
new mode 100755
index 0ac8c1fe397d8b2fb0c6fd444a5b9e21cae561af..6b0fc26db7a62d2e9a660591b19105462e60ca4a
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -240,6 +240,7 @@ static bool mLoginStatePastUI = false;
 
 boost::scoped_ptr<LLEventPump> LLStartUp::sStateWatcher(new LLEventStream("StartupState"));
 boost::scoped_ptr<LLStartupListener> LLStartUp::sListener(new LLStartupListener());
+boost::scoped_ptr<LLViewerStats::PhaseMap> LLStartUp::sPhases(new LLViewerStats::PhaseMap);
 
 //
 // local function declaration
@@ -2705,7 +2706,10 @@ void LLStartUp::setStartupState( EStartupState state )
 	LL_INFOS("AppInit") << "Startup state changing from " <<  
 		getStartupStateString() << " to " <<  
 		startupStateToString(state) << LL_ENDL;
+
+	sPhases->stopPhase(getStartupStateString());
 	gStartupState = state;
+	sPhases->startPhase(getStartupStateString());
 	postStartupState();
 }
 
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
old mode 100644
new mode 100755
index 0a18ef1b2d140389e32f8db302669e9f89ca9782..3754aaf966e5fc8503d9fe6e8667e6ba5fa65bf9
--- a/indra/newview/llstartup.h
+++ b/indra/newview/llstartup.h
@@ -34,6 +34,8 @@ class LLEventPump;
 class LLStartupListener;
 class LLSLURL;
 
+#include "llviewerstats.h"
+
 // functions
 bool idle_startup();
 void release_start_screen();
@@ -113,6 +115,7 @@ class LLStartUp
 
 	static bool startLLProxy(); // Initialize the SOCKS 5 proxy
 
+	static LLViewerStats::PhaseMap& getPhases() { return *sPhases; }
 private:
 	static LLSLURL sStartSLURL;
 
@@ -120,6 +123,7 @@ class LLStartUp
 	static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState
 	static boost::scoped_ptr<LLEventPump> sStateWatcher;
 	static boost::scoped_ptr<LLStartupListener> sListener;
+	static boost::scoped_ptr<LLViewerStats::PhaseMap> sPhases;
 };
 
 
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 6f6d5dbf122ea787f6d24f4065209104015ada5d..467115c92849b6ad9bc2c8e7d637c752ba9d6b8e 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -55,6 +55,9 @@ using namespace LLVOAvatarDefines;
 static const S32 BAKE_UPLOAD_ATTEMPTS = 7;
 static const F32 BAKE_UPLOAD_RETRY_DELAY = 2.f; // actual delay grows by power of 2 each attempt
 
+// runway consolidate
+extern std::string self_av_string();
+
 class LLTexLayerInfo
 {
 	friend class LLTexLayer;
@@ -494,7 +497,6 @@ void LLTexLayerSetBuffer::doUpload()
 	}
 	
 	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C;
-	compressedImage->setRate(0.f);
 	const char* comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // writes into baked_color_data. 5 channels (rgb, heightfield/alpha, mask)
 	if (compressedImage->encode(baked_image, comment_text))
 	{
@@ -577,7 +579,7 @@ void LLTexLayerSetBuffer::doUpload()
 					args["BODYREGION"] = mTexLayerSet->getBodyRegionName();
 					args["RESOLUTION"] = lod_str;
 					LLNotificationsUtil::add("AvatarRezSelfBakedTextureUploadNotification",args);
-					llinfos << "Uploading [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << llendl;
+					LL_DEBUGS("Avatar") << self_av_string() << "Uploading [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << LL_ENDL;
 				}
 			}
 			else
@@ -631,7 +633,7 @@ void LLTexLayerSetBuffer::doUpdate()
 		args["BODYREGION"] = mTexLayerSet->getBodyRegionName();
 		args["RESOLUTION"] = lod_str;
 		LLNotificationsUtil::add("AvatarRezSelfBakedTextureUpdateNotification",args);
-		llinfos << "Locally updating [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUpdateTimer.getElapsedTimeF32() << " ]" << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Locally updating [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUpdateTimer.getElapsedTimeF32() << " ]" << LL_ENDL;
 	}
 }
 
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
old mode 100644
new mode 100755
index f18aa8b4e61b05cefaf821af67165f368855ec08..e2af497a7d05470de4b219fdfbb7d494a82f07a8
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -52,12 +52,20 @@
 #include "llviewerstats.h"
 #include "llviewerassetstats.h"
 #include "llworld.h"
+#include "llsdutil.h"
+#include "llstartup.h"
+#include "llviewerstats.h"
+
+bool LLTextureFetchDebugger::sDebuggerEnabled = false ;
+LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128);
+LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128);
 
 //////////////////////////////////////////////////////////////////////////////
 class LLTextureFetchWorker : public LLWorkerClass
 {
 	friend class LLTextureFetch;
 	friend class HTTPGetResponder;
+	friend class LLTextureFetchDebugger;
 	
 private:
 	class CacheReadResponder : public LLTextureCache::ReadResponder
@@ -242,6 +250,8 @@ class LLTextureFetchWorker : public LLWorkerClass
 	S32 mDecodedDiscard;
 	LLFrameTimer mRequestedTimer;
 	LLFrameTimer mFetchTimer;
+	LLTimer			mCacheReadTimer;
+	F32				mCacheReadTime;
 	LLTextureCache::handle_t mCacheReadHandle;
 	LLTextureCache::handle_t mCacheWriteHandle;
 	U8* mBuffer;
@@ -258,6 +268,7 @@ class LLTextureFetchWorker : public LLWorkerClass
 	BOOL mNeedsAux;
 	BOOL mHaveAllData;
 	BOOL mInLocalCache;
+	BOOL mInCache;
 	bool mCanUseHTTP ;
 	bool mCanUseNET ; //can get from asset server.
 	S32 mHTTPFailCount;
@@ -653,6 +664,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
 	  mRequestedDiscard(-1),
 	  mLoadedDiscard(-1),
 	  mDecodedDiscard(-1),
+	  mCacheReadTime(0.f),
 	  mCacheReadHandle(LLTextureCache::nullHandle()),
 	  mCacheWriteHandle(LLTextureCache::nullHandle()),
 	  mBuffer(NULL),
@@ -669,6 +681,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
 	  mNeedsAux(FALSE),
 	  mHaveAllData(FALSE),
 	  mInLocalCache(FALSE),
+	  mInCache(FALSE),
 	  mCanUseHTTP(true),
 	  mHTTPFailCount(0),
 	  mRetryAttempt(0),
@@ -838,6 +851,8 @@ void LLTextureFetchWorker::startWork(S32 param)
 // Called from LLWorkerThread::processRequest()
 bool LLTextureFetchWorker::doWork(S32 param)
 {
+	static const F32 FETCHING_TIMEOUT = 120.f;//seconds
+
 	LLMutexLock lock(&mWorkMutex);
 
 	if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
@@ -896,6 +911,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 		mCacheReadHandle = LLTextureCache::nullHandle();
 		mCacheWriteHandle = LLTextureCache::nullHandle();
 		mState = LOAD_FROM_TEXTURE_CACHE;
+		mInCache = FALSE;
 		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;
@@ -926,6 +942,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
 				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
 																		  offset, size, responder);
+				mCacheReadTimer.reset();
 			}
 			else if (mUrl.empty())
 			{
@@ -934,6 +951,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
 				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
 																		  offset, size, responder);
+				mCacheReadTimer.reset();
 			}
 			else if(mCanUseHTTP)
 			{
@@ -982,11 +1000,12 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			llassert_always(mFormattedImage->getDataSize() > 0);
 			mLoadedDiscard = mDesiredDiscard;
 			mState = DECODE_IMAGE;
+			mInCache = TRUE;
 			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
+			LLTextureFetch::sCacheHitRate.addValue(100.f);
 		}
 		else
 		{
@@ -1002,6 +1021,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				mState = LOAD_FROM_NETWORK;
 			}
 			// fall through
+			LLTextureFetch::sCacheHitRate.addValue(0.f);
 		}
 	}
 
@@ -1177,6 +1197,8 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			bool res = false;
 			if (!mUrl.empty())
 			{
+				mRequestedTimer.reset();
+
 				mLoaded = FALSE;
 				mGetStatus = 0;
 				mGetReason.clear();
@@ -1335,6 +1357,13 @@ bool LLTextureFetchWorker::doWork(S32 param)
 		}
 		else
 		{
+			if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32())
+			{
+				//timeout, abort.
+				mState = DONE;
+				return true;
+			}
+
 			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 			return false;
 		}
@@ -1396,6 +1425,11 @@ bool LLTextureFetchWorker::doWork(S32 param)
 	{
 		if (mDecoded)
 		{
+			if(mFetcher->getFetchDebugger() && !mInLocalCache)
+			{
+				mFetcher->getFetchDebugger()->addHistoryEntry(this);
+			}
+
 			if (mDecodedDiscard < 0)
 			{
 				LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
@@ -1780,6 +1814,7 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag
 	mDecoded = TRUE;
 // 	llinfos << mID << " : DECODE COMPLETE " << llendl;
 	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+	mCacheReadTime = mCacheReadTimer.getElapsedTimeF32();
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1824,11 +1859,18 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	  mHTTPTextureBits(0),
 	  mTotalHTTPRequests(0),
 	  mCurlGetRequest(NULL),
-	  mQAMode(qa_mode)
+	  mQAMode(qa_mode),
+	  mFetchDebugger(NULL)
 {
 	mCurlPOSTRequestCount = 0;
 	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
 	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
+
+	LLTextureFetchDebugger::sDebuggerEnabled = gSavedSettings.getBOOL("TextureFetchDebuggerEnabled");
+	if(LLTextureFetchDebugger::isEnabled())
+	{
+		mFetchDebugger = new LLTextureFetchDebugger(this, cache, imagedecodethread) ;
+	}
 }
 
 LLTextureFetch::~LLTextureFetch()
@@ -1843,11 +1885,17 @@ LLTextureFetch::~LLTextureFetch()
 	}
 	
 	// ~LLQueuedThread() called here
+
+	delete mFetchDebugger;
 }
 
 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(mFetcherLocked)
+	{
+		return false;
+	}
 	if (mDebugPause)
 	{
 		return false;
@@ -2092,6 +2140,11 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
 			discard_level = worker->mDecodedDiscard;
 			raw = worker->mRawImage;
 			aux = worker->mAuxImage;
+			F32 cache_read_time = worker->mCacheReadTime;
+			if (cache_read_time != 0.f)
+			{
+				sCacheReadLatency.addValue(cache_read_time * 1000.f);
+			}
 			res = true;
 			LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
 			worker->unlockWorkMutex();
@@ -2222,7 +2275,13 @@ S32 LLTextureFetch::update(F32 max_time_ms)
 	
 	if (!mDebugPause)
 	{
-		sendRequestListToSimulators();
+		// this is the startup state when send_complete_agent_movement() message is sent.
+		// Before this, the RequestImages message sent by sendRequestListToSimulators 
+		// won't work so don't bother trying
+		if (LLStartUp::getStartupState() > STATE_AGENT_SEND)
+		{
+			sendRequestListToSimulators();
+		}
 	}
 
 	if (!mThreaded)
@@ -2258,6 +2317,11 @@ void LLTextureFetch::startThread()
 {
 	// Construct mCurlGetRequest from Worker Thread
 	mCurlGetRequest = new LLCurlRequest();
+	
+	if(mFetchDebugger)
+	{
+		mFetchDebugger->setCurlGetRequest(mCurlGetRequest);
+	}
 }
 
 // WORKER THREAD
@@ -2266,6 +2330,10 @@ void LLTextureFetch::endThread()
 	// Destroy mCurlGetRequest from Worker Thread
 	delete mCurlGetRequest;
 	mCurlGetRequest = NULL;
+	if(mFetchDebugger)
+	{
+		mFetchDebugger->setCurlGetRequest(NULL);
+	}
 }
 
 // WORKER THREAD
@@ -2803,7 +2871,6 @@ void LLTextureFetch::cmdDoWork()
 	}
 }
 
-
 //////////////////////////////////////////////////////////////////////////////
 
 // Private (anonymous) class methods implementing the command scheme.
@@ -2959,7 +3026,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
 	// In QA mode, Metrics submode, log the result for ease of testing
 	if (fetcher->isQAMode())
 	{
-		LL_INFOS("Textures") << merged_llsd << LL_ENDL;
+		LL_INFOS("Textures") << ll_pretty_print_sd(merged_llsd) << LL_ENDL;
 	}
 
 	gViewerAssetStatsThread1->reset();
@@ -3007,5 +3074,659 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics)
 
 } // end of anonymous namespace
 
+///////////////////////////////////////////////////////////////////////////////////////////
+//Start LLTextureFetchDebugger
+///////////////////////////////////////////////////////////////////////////////////////////
+//---------------------
+class LLDebuggerCacheReadResponder : public LLTextureCache::ReadResponder
+{
+public:
+	LLDebuggerCacheReadResponder(LLTextureFetchDebugger* debugger, S32 id, LLImageFormatted* image)
+		: mDebugger(debugger), mID(id)
+	{
+		setImage(image);
+	}
+	virtual void completed(bool success)
+	{
+		mDebugger->callbackCacheRead(mID, success, mFormattedImage, mImageSize, mImageLocal);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerCacheWriteResponder : public LLTextureCache::WriteResponder
+{
+public:
+	LLDebuggerCacheWriteResponder(LLTextureFetchDebugger* debugger, S32 id)
+		: mDebugger(debugger), mID(id)
+	{
+	}
+	virtual void completed(bool success)
+	{
+		mDebugger->callbackCacheWrite(mID, success);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerDecodeResponder : public LLImageDecodeThread::Responder
+{
+public:
+	LLDebuggerDecodeResponder(LLTextureFetchDebugger* debugger, S32 id)
+		: mDebugger(debugger), mID(id)
+	{
+	}
+	virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
+	{
+		mDebugger->callbackDecoded(mID, success, raw, aux);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerHTTPResponder : public LLCurl::Responder
+{
+public:
+	LLDebuggerHTTPResponder(LLTextureFetchDebugger* debugger, S32 index)
+	: mDebugger(debugger), mIndex(index)
+	{
+	}
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+	{
+		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)
+		{
+			llinfos << "Fetch Debugger : CURL GET FAILED, index = " << mIndex << ", status:" << status << " reason:" << reason << llendl;
+		}
+		mDebugger->callbackHTTP(mIndex, channels, buffer, partial, success);
+	}
+	virtual bool followRedir()
+	{
+		return true;
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mIndex;
+};
+
+LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) :
+	mFetcher(fetcher),
+	mTextureCache(cache),
+	mImageDecodeThread(imagedecodethread),
+	mCurlGetRequest(NULL)
+{
+	init();
+}
+	
+LLTextureFetchDebugger::~LLTextureFetchDebugger()
+{
+	mFetchingHistory.clear();
+	stopDebug();
+}
+
+void LLTextureFetchDebugger::init()
+{
+	mState = IDLE;
+	
+	mCacheReadTime = -1.f;
+	mCacheWriteTime = -1.f;
+	mDecodingTime = -1.f;
+	mHTTPTime = -1.f;
+	mGLCreationTime = -1.f;
+	mTotalFetchingTime = 0.f;
+	mRefetchVisCacheTime = -1.f;
+	mRefetchVisHTTPTime = -1.f;
+
+	mNumFetchedTextures = 0;
+	mNumCacheHits = 0;
+	mNumVisibleFetchedTextures = 0;
+	mNumVisibleFetchingRequests = 0;
+	mFetchedData = 0;
+	mDecodedData = 0;
+	mVisibleFetchedData = 0;
+	mVisibleDecodedData = 0;
+	mRenderedData = 0;
+	mRenderedDecodedData = 0;
+	mFetchedPixels = 0;
+	mRenderedPixels = 0;
+	mRefetchedData = 0;
+	mRefetchedPixels = 0;
+
+	mFreezeHistory = FALSE;
+}
+
+void LLTextureFetchDebugger::startDebug()
+{
+	//lock the fetcher
+	mFetcher->lockFetcher(true);
+	mFreezeHistory = TRUE;
+
+	//clear the current fetching queue
+	gTextureList.clearFetchingRequests();
+
+	//wait for all works to be done
+	while(1)
+	{
+		S32 pending = 0;
+		pending += LLAppViewer::getTextureCache()->update(1); 
+		pending += LLAppViewer::getImageDecodeThread()->update(1); 
+		pending += LLAppViewer::getTextureFetch()->update(1); 
+		if(!pending)
+		{
+			break;
+		}
+	}
+
+	//collect statistics
+	mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime;
+	
+	std::set<LLUUID> fetched_textures;
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size; i++)
+	{
+		bool in_list = true;
+		if(fetched_textures.find(mFetchingHistory[i].mID) == fetched_textures.end())
+		{
+			fetched_textures.insert(mFetchingHistory[i].mID);
+			in_list = false;
+		}
+		
+		LLViewerFetchedTexture* tex = LLViewerTextureManager::findFetchedTexture(mFetchingHistory[i].mID);
+		if(tex && tex->isJustBound()) //visible
+		{
+			if(!in_list)
+			{
+				mNumVisibleFetchedTextures++;
+			}
+			mNumVisibleFetchingRequests++;
+	
+			mVisibleFetchedData += mFetchingHistory[i].mFetchedSize;
+			mVisibleDecodedData += mFetchingHistory[i].mDecodedSize;
+	
+			if(tex->getDiscardLevel() >= mFetchingHistory[i].mDecodedLevel)
+			{
+				mRenderedData += mFetchingHistory[i].mFetchedSize;
+				mRenderedDecodedData += mFetchingHistory[i].mDecodedSize;
+				mRenderedPixels += tex->getWidth() * tex->getHeight();
+			}
+		}
+	}
+
+	mNumFetchedTextures = fetched_textures.size();
+}
+
+void LLTextureFetchDebugger::stopDebug()
+{
+	//clear the current debug work
+	S32 size = mFetchingHistory.size();
+	switch(mState)
+	{
+	case READ_CACHE:		
+		for(S32 i = 0 ; i < size; i++)
+		{
+			if (mFetchingHistory[i]. mCacheHandle != LLTextureCache::nullHandle())
+			{
+				mTextureCache->readComplete(mFetchingHistory[i].mCacheHandle, true);
+			}
+		}	
+		break;
+	case WRITE_CACHE:
+		for(S32 i = 0 ; i < size; i++)
+		{
+			if (mFetchingHistory[i].mCacheHandle != LLTextureCache::nullHandle())
+			{
+				mTextureCache->writeComplete(mFetchingHistory[i].mCacheHandle, true);
+			}
+		}
+		break;
+	case DECODING:
+		break;
+	case HTTP_FETCHING:
+		break;
+	case GL_TEX:
+		break;
+	default:
+		break;
+	}
+
+	while(1)
+	{
+		if(update())
+		{
+			break;
+		}
+	}
+
+	//unlock the fetcher
+	mFetcher->lockFetcher(false);
+	mFreezeHistory = FALSE;
+	mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset
+}
+
+//called in the main thread and when the fetching queue is empty
+void LLTextureFetchDebugger::clearHistory()
+{
+	mFetchingHistory.clear();
+	init();
+}
+
+void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker)
+{
+	if(mFreezeHistory)
+	{
+		mRefetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight();
+		mRefetchedData += worker->mFormattedImage->getDataSize();
+		return;
+	}
+
+	if(worker->mInCache)
+	{
+		mNumCacheHits++;
+	}
+	mFetchedData += worker->mFormattedImage->getDataSize();
+	mDecodedData += worker->mRawImage->getDataSize();
+	mFetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight();
+
+	mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize()));
+	//mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mHaveAllData ? 0 : worker->mLoadedDiscard, worker->mFormattedImage->getComponents(),
+		//worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize()));
+}
+
+void LLTextureFetchDebugger::lockCache()
+{
+}
+	
+void LLTextureFetchDebugger::unlockCache()
+{
+}
+	
+void LLTextureFetchDebugger::debugCacheRead()
+{
+	lockCache();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = READ_CACHE;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		mFetchingHistory[i].mFormattedImage = NULL;
+		mFetchingHistory[i].mCacheHandle = mTextureCache->readFromCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, 0, mFetchingHistory[i].mFetchedSize, 
+			new LLDebuggerCacheReadResponder(this, i, mFetchingHistory[i].mFormattedImage));
+	}
+}
+	
+void LLTextureFetchDebugger::clearCache()
+{
+	S32 size = mFetchingHistory.size();
+	{
+		std::set<LLUUID> deleted_list;
+		for(S32 i = 0 ; i < size ; i++)
+		{
+			if(deleted_list.find(mFetchingHistory[i].mID) == deleted_list.end())
+			{
+				deleted_list.insert(mFetchingHistory[i].mID);
+				mTextureCache->removeFromCache(mFetchingHistory[i].mID);
+			}
+		}
+	}
+}
+
+void LLTextureFetchDebugger::debugCacheWrite()
+{
+	//remove from cache
+	clearCache();
+
+	lockCache();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = WRITE_CACHE;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		if(mFetchingHistory[i].mFormattedImage.notNull())
+		{
+			mFetchingHistory[i].mCacheHandle = mTextureCache->writeToCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, 
+				mFetchingHistory[i].mFormattedImage->getData(), mFetchingHistory[i].mFetchedSize,
+				mFetchingHistory[i].mDecodedLevel == 0 ? mFetchingHistory[i].mFetchedSize : mFetchingHistory[i].mFetchedSize + 1, 
+				new LLDebuggerCacheWriteResponder(this, i));					
+		}
+	}
+}
+
+void LLTextureFetchDebugger::lockDecoder()
+{
+}
+	
+void LLTextureFetchDebugger::unlockDecoder()
+{
+}
+
+void LLTextureFetchDebugger::debugDecoder()
+{
+	lockDecoder();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = DECODING;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		if(mFetchingHistory[i].mFormattedImage.isNull())
+		{
+			continue;
+		}
+
+		mImageDecodeThread->decodeImage(mFetchingHistory[i].mFormattedImage, LLWorkerThread::PRIORITY_NORMAL, 
+			mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mNeedsAux,
+			new LLDebuggerDecodeResponder(this, i));
+	}
+}
+
+void LLTextureFetchDebugger::debugHTTP()
+{
+	llassert_always(mState == IDLE);
+
+	LLViewerRegion* region = gAgent.getRegion();
+	if (!region)
+	{
+		llinfos << "Fetch Debugger : Current region undefined. Cannot fetch textures through HTTP." << llendl;
+		return;
+	}
+	
+	mHTTPUrl = region->getHttpUrl();
+	if (mHTTPUrl.empty())
+	{
+		llinfos << "Fetch Debugger : Current region URL undefined. Cannot fetch textures through HTTP." << llendl;
+		return;
+	}
+	
+	mTimer.reset();
+	mState = HTTP_FETCHING;
+	
+	S32 size = mFetchingHistory.size();
+	for (S32 i = 0 ; i < size ; i++)
+	{
+		mFetchingHistory[i].mCurlState = FetchEntry::CURL_NOT_DONE;
+		mFetchingHistory[i].mCurlReceivedSize = 0;
+		mFetchingHistory[i].mHTTPFailCount = 0;
+	}
+	mNbCurlRequests = 0;
+	mNbCurlCompleted = 0;
+	
+	fillCurlQueue();
+}
+
+S32 LLTextureFetchDebugger::fillCurlQueue()
+{
+	if (mNbCurlRequests == 24)
+		return mNbCurlRequests;
+	
+	S32 size = mFetchingHistory.size();
+	for (S32 i = 0 ; i < size ; i++)
+	{		
+		if (mFetchingHistory[i].mCurlState != FetchEntry::CURL_NOT_DONE)
+			continue;
+		std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[i].mID.asString().c_str();
+		S32 requestedSize = mFetchingHistory[i].mRequestedSize;
+		// We request the whole file if the size was not set.
+		requestedSize = llmax(0,requestedSize);
+		// We request the whole file if the size was set to an absurdly high value (meaning all file)
+		requestedSize = (requestedSize == 33554432 ? 0 : requestedSize);
+		std::vector<std::string> headers;
+		headers.push_back("Accept: image/x-j2c");
+		bool res = mCurlGetRequest->getByteRange(texture_url, headers, 0, requestedSize, new LLDebuggerHTTPResponder(this, i));
+		if (res)
+		{
+			mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS;
+			mNbCurlRequests++;
+			// Hack
+			if (mNbCurlRequests == 24)
+				break;
+		}
+		else 
+		{
+			break;
+		}
+	}
+	//llinfos << "Fetch Debugger : Having " << mNbCurlRequests << " requests through the curl thread." << llendl;
+	return mNbCurlRequests;
+}
+
+void LLTextureFetchDebugger::debugGLTextureCreation()
+{
+	llassert_always(mState == IDLE);
+	mState = GL_TEX;
+	std::vector<LLViewerFetchedTexture*> tex_list;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{
+		if(mFetchingHistory[i].mRawImage.notNull())
+		{
+			LLViewerFetchedTexture* tex = gTextureList.findImage(mFetchingHistory[i].mID) ;
+			if(tex && !tex->isForSculptOnly())
+			{
+				tex->destroyGLTexture() ;
+				tex_list.push_back(tex);
+			}
+		}
+	}
+
+	mTimer.reset();
+	S32 j = 0 ;
+	S32 size1 = tex_list.size();
+	for(S32 i = 0 ; i < size && j < size1; i++)
+	{
+		if(mFetchingHistory[i].mRawImage.notNull())
+		{
+			if(mFetchingHistory[i].mID == tex_list[j]->getID())
+			{
+				tex_list[j]->createGLTexture(mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mRawImage, 0, TRUE, tex_list[j]->getBoostLevel());
+				j++;
+			}
+		}
+	}
+
+	mGLCreationTime = mTimer.getElapsedTimeF32() ;
+	return;
+}
+
+//clear fetching results of all textures.
+void LLTextureFetchDebugger::clearTextures()
+{
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{
+		LLViewerFetchedTexture* tex = gTextureList.findImage(mFetchingHistory[i].mID) ;
+		if(tex)
+		{
+			tex->clearFetchedResults() ;
+		}
+	}
+}
+
+void LLTextureFetchDebugger::debugRefetchVisibleFromCache()
+{
+	llassert_always(mState == IDLE);
+	mState = REFETCH_VIS_CACHE;
+
+	clearTextures();
+
+	mTimer.reset();
+	mFetcher->lockFetcher(false);
+}
+
+void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP()
+{
+	llassert_always(mState == IDLE);
+	mState = REFETCH_VIS_HTTP;
+
+	clearCache();
+	clearTextures();
+
+	mTimer.reset();
+	mFetcher->lockFetcher(false);
+}
+
+bool LLTextureFetchDebugger::update()
+{
+	switch(mState)
+	{
+	case READ_CACHE:
+		if(!mTextureCache->update(1))
+		{
+			mCacheReadTime = mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockCache();
+		}
+		break;
+	case WRITE_CACHE:
+		if(!mTextureCache->update(1))
+		{
+			mCacheWriteTime = mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockCache();
+		}
+		break;
+	case DECODING:
+		if(!mImageDecodeThread->update(1))
+		{
+			mDecodingTime =  mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockDecoder();
+		}
+		break;
+	case HTTP_FETCHING:
+		mCurlGetRequest->process();
+		LLCurl::getCurlThread()->update(1);
+		if (!fillCurlQueue() && mNbCurlCompleted == mFetchingHistory.size())
+		{
+			mHTTPTime =  mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+		}
+		break;
+	case GL_TEX:
+		mState = IDLE;
+		break;
+	case REFETCH_VIS_CACHE:
+		if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
+		{
+			mRefetchVisCacheTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime;
+			mState = IDLE;
+			mFetcher->lockFetcher(true);
+		}
+		break;
+	case REFETCH_VIS_HTTP:
+		if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
+		{
+			mRefetchVisHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime;
+			mState = IDLE;
+			mFetcher->lockFetcher(true);
+		}
+		break;
+	default:
+		mState = IDLE;
+		break;
+	}
+
+	return mState == IDLE;
+}
+
+void LLTextureFetchDebugger::callbackCacheRead(S32 id, bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal)
+{
+	if (success)
+	{
+		mFetchingHistory[id].mFormattedImage = image;
+	}
+	mTextureCache->readComplete(mFetchingHistory[id].mCacheHandle, false);
+	mFetchingHistory[id].mCacheHandle = LLTextureCache::nullHandle();
+}
+
+void LLTextureFetchDebugger::callbackCacheWrite(S32 id, bool success)
+{
+	mTextureCache->writeComplete(mFetchingHistory[id].mCacheHandle);
+	mFetchingHistory[id].mCacheHandle = LLTextureCache::nullHandle();
+}
+
+void LLTextureFetchDebugger::callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux)
+{
+	if (success)
+	{
+		llassert_always(raw);
+		mFetchingHistory[id].mRawImage = raw;
+	}
+}
+
+void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& channels,
+										  const LLIOPipe::buffer_ptr_t& buffer, 
+										  bool partial, bool success)
+{
+	mNbCurlRequests--;
+	if (success)
+	{
+		mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE;
+		mNbCurlCompleted++;
+
+		S32 data_size = buffer->countAfter(channels.in(), NULL);
+		mFetchingHistory[id].mCurlReceivedSize += data_size;
+		//llinfos << "Fetch Debugger : got results for " << id << ", data_size = " << data_size << ", received = " << mFetchingHistory[id].mCurlReceivedSize << ", requested = " << mFetchingHistory[id].mRequestedSize << ", partial = " << partial << llendl;
+		if ((mFetchingHistory[id].mCurlReceivedSize >= mFetchingHistory[id].mRequestedSize) || !partial || (mFetchingHistory[id].mRequestedSize == 600))
+		{
+			U8* d_buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size);
+			buffer->readAfter(channels.in(), NULL, d_buffer, data_size);
+			
+			llassert_always(mFetchingHistory[id].mFormattedImage.isNull());
+			{
+				// For now, create formatted image based on extension
+				std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[id].mID.asString().c_str();
+				std::string extension = gDirUtilp->getExtension(texture_url);
+				mFetchingHistory[id].mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
+				if (mFetchingHistory[id].mFormattedImage.isNull())
+				{
+					mFetchingHistory[id].mFormattedImage = new LLImageJ2C; // default
+				}
+			}
+						
+			mFetchingHistory[id].mFormattedImage->setData(d_buffer, data_size);	
+		}
+	}
+	else //failed
+	{
+		mFetchingHistory[id].mHTTPFailCount++;
+		if(mFetchingHistory[id].mHTTPFailCount < 5)
+		{
+			// Fetch will have to be redone
+			mFetchingHistory[id].mCurlState = FetchEntry::CURL_NOT_DONE;
+		}
+		else //skip
+		{
+			mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE;
+			mNbCurlCompleted++;
+		}
+	}
+}
+
+
+//---------------------
+///////////////////////////////////////////////////////////////////////////////////////////
+//End LLTextureFetchDebugger
+///////////////////////////////////////////////////////////////////////////////////////////
 
 
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 35df7d816f20b91e28b319c40c9767e1a428c7b6..107e1623b0167dafe7eff655a5fb95b8cfba66f8 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -34,14 +34,17 @@
 #include "llcurl.h"
 #include "lltextureinfo.h"
 #include "llapr.h"
+#include "llimageworker.h"
+//#include "lltexturecache.h"
 
 class LLViewerTexture;
 class LLTextureFetchWorker;
 class HTTPGetResponder;
-class LLTextureCache;
 class LLImageDecodeThread;
 class LLHost;
 class LLViewerAssetStats;
+class LLTextureFetchDebugger;
+class LLTextureCache;
 
 // Interface class
 class LLTextureFetch : public LLWorkerThread
@@ -164,6 +167,9 @@ class LLTextureFetch : public LLWorkerThread
 	LLMutex mQueueMutex;        //to protect mRequestMap and mCommands only
 	LLMutex mNetworkQueueMutex; //to protect mNetworkQueue, mHTTPTextureQueue and mCancelQueue.
 
+	static LLStat sCacheHitRate;
+	static LLStat sCacheReadLatency;
+
 	LLTextureCache* mTextureCache;
 	LLImageDecodeThread* mImageDecodeThread;
 	LLCurlRequest* mCurlGetRequest;
@@ -209,7 +215,195 @@ class LLTextureFetch : public LLWorkerThread
 	// attempt to log metrics follows a break in the metrics stream
 	// reporting due to either startup or a problem POSTing data.
 	static volatile bool svMetricsDataBreak;
+
+private:
+	//debug use
+	LLTextureFetchDebugger* mFetchDebugger;
+	bool mFetcherLocked;
+
+public:
+	//debug use
+	LLTextureFetchDebugger* getFetchDebugger() { return mFetchDebugger;}
+	void lockFetcher(bool lock) { mFetcherLocked = lock;}
 };
 
+//debug use
+class LLTextureFetchDebugger
+{
+	friend class LLTextureFetch;
+public:
+	LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) ;
+	~LLTextureFetchDebugger();
+
+public:
+	enum e_debug_state
+	{
+		IDLE = 0,
+		READ_CACHE,
+		WRITE_CACHE,
+		DECODING,
+		HTTP_FETCHING,
+		GL_TEX,
+		REFETCH_VIS_CACHE,
+		REFETCH_VIS_HTTP,
+		REFETCH_ALL_CACHE,
+		REFETCH_ALL_HTTP,
+		INVALID
+	};
+
+private:	
+	struct FetchEntry
+	{
+		enum e_curl_state
+		{
+			CURL_NOT_DONE = 0,
+			CURL_IN_PROGRESS,
+			CURL_DONE
+		};
+		LLUUID mID;
+		S32 mRequestedSize;
+		S32 mDecodedLevel;
+		S32 mFetchedSize;
+		S32 mDecodedSize;
+		BOOL mNeedsAux;
+		U32 mCacheHandle;
+		LLPointer<LLImageFormatted> mFormattedImage;
+		LLPointer<LLImageRaw> mRawImage;
+		e_curl_state mCurlState;
+		S32 mCurlReceivedSize;
+		S32 mHTTPFailCount;
+
+		FetchEntry() :
+			mDecodedLevel(-1),
+			mFetchedSize(0),
+			mDecodedSize(0)
+			{}
+		FetchEntry(LLUUID& id, S32 r_size, /*S32 f_discard, S32 c,*/ S32 level, S32 f_size, S32 d_size) :
+			mID(id),
+			mRequestedSize(r_size),
+			mDecodedLevel(level),
+			mFetchedSize(f_size),
+			mDecodedSize(d_size),
+			mNeedsAux(false),
+			mHTTPFailCount(0)
+			{}
+	};
+	std::vector<FetchEntry> mFetchingHistory;
+	
+	e_debug_state mState;
+	
+	F32 mCacheReadTime;
+	F32 mCacheWriteTime;
+	F32 mDecodingTime;
+	F32 mHTTPTime;
+	F32 mGLCreationTime;
+
+	F32 mTotalFetchingTime;
+	F32 mRefetchVisCacheTime;
+	F32 mRefetchVisHTTPTime;
+
+	LLTimer mTimer;
+	
+	LLTextureFetch* mFetcher;
+	LLTextureCache* mTextureCache;
+	LLImageDecodeThread* mImageDecodeThread;
+	LLCurlRequest* mCurlGetRequest;
+	
+	S32 mNumFetchedTextures;
+	S32 mNumCacheHits;
+	S32 mNumVisibleFetchedTextures;
+	S32 mNumVisibleFetchingRequests;
+	U32 mFetchedData;
+	U32 mDecodedData;
+	U32 mVisibleFetchedData;
+	U32 mVisibleDecodedData;
+	U32 mRenderedData;
+	U32 mRenderedDecodedData;
+	U32 mFetchedPixels;
+	U32 mRenderedPixels;
+	U32 mRefetchedData;
+	U32 mRefetchedPixels;
+
+	BOOL mFreezeHistory;
+
+	std::string mHTTPUrl;
+	S32 mNbCurlRequests;
+	S32 mNbCurlCompleted;
+
+public:
+	bool update(); //called in the main thread once per frame
+
+	//fetching history
+	void clearHistory();
+	void addHistoryEntry(LLTextureFetchWorker* worker);
+	
+	void setCurlGetRequest(LLCurlRequest* request) { mCurlGetRequest = request;}
+	
+	void startDebug();
+	void stopDebug(); //stop everything
+	void debugCacheRead();
+	void debugCacheWrite();	
+	void debugHTTP();
+	void debugDecoder();
+	void debugGLTextureCreation();
+	void debugRefetchVisibleFromCache();
+	void debugRefetchVisibleFromHTTP();
+
+	void callbackCacheRead(S32 id, bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal);
+	void callbackCacheWrite(S32 id, bool success);
+	void callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux);
+	void callbackHTTP(S32 id, const LLChannelDescriptors& channels,
+					  const LLIOPipe::buffer_ptr_t& buffer, 
+					  bool partial, bool success);
+	
+
+	e_debug_state getState()             {return mState;}
+	S32  getNumFetchedTextures()         {return mNumFetchedTextures;}
+	S32  getNumFetchingRequests()        {return mFetchingHistory.size();}
+	S32  getNumCacheHits()               {return mNumCacheHits;}
+	S32  getNumVisibleFetchedTextures()  {return mNumVisibleFetchedTextures;}
+	S32  getNumVisibleFetchingRequests() {return mNumVisibleFetchingRequests;}
+	U32  getFetchedData()                {return mFetchedData;}
+	U32  getDecodedData()                {return mDecodedData;}
+	U32  getVisibleFetchedData()         {return mVisibleFetchedData;}
+	U32  getVisibleDecodedData()         {return mVisibleDecodedData;}
+	U32  getRenderedData()               {return mRenderedData;}
+	U32  getRenderedDecodedData()        {return mRenderedDecodedData;}
+	U32  getFetchedPixels()              {return mFetchedPixels;}
+	U32  getRenderedPixels()             {return mRenderedPixels;}
+	U32  getRefetchedData()              {return mRefetchedData;}
+	U32  getRefetchedPixels()            {return mRefetchedPixels;}
+
+	F32  getCacheReadTime()     {return mCacheReadTime;}
+	F32  getCacheWriteTime()    {return mCacheWriteTime;}
+	F32  getDecodeTime()        {return mDecodingTime;}
+	F32  getGLCreationTime()    {return mGLCreationTime;}
+	F32  getHTTPTime()          {return mHTTPTime;}
+	F32  getTotalFetchingTime() {return mTotalFetchingTime;}
+	F32  getRefetchVisCacheTime() {return mRefetchVisCacheTime;}
+	F32  getRefetchVisHTTPTime()  {return mRefetchVisHTTPTime;}
+
+private:
+	void init();
+	void clearTextures();//clear fetching results of all textures.
+	void clearCache();
+
+	void lockFetcher();
+	void unlockFetcher();
+
+	void lockCache();
+	void unlockCache();
+
+	void lockDecoder();
+	void unlockDecoder();
+	
+	S32 fillCurlQueue();
+
+private:
+	static bool sDebuggerEnabled;
+public:
+	static bool isEnabled() {return sDebuggerEnabled;}
+};
 #endif // LL_LLTEXTUREFETCH_H
 
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
old mode 100644
new mode 100755
index 5b41a05f2aac01d5f66ad1fcf9b4c849c731e9c6..425bf7ee871f41b5ae8ba863e1fd41c7e9ea2c08
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -57,8 +57,6 @@
 extern F32 texmem_lower_bound_scale;
 
 LLTextureView *gTextureView = NULL;
-LLTextureSizeView *gTextureSizeView = NULL;
-LLTextureSizeView *gTextureCategoryView = NULL;
 
 #define HIGH_PRIORITY 100000000.f
 
@@ -512,8 +510,8 @@ void LLGLTexMemBar::draw()
 	F32 discard_bias = LLViewerTexture::sDesiredDiscardBias;
 	F32 cache_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getUsage()) ;
 	F32 cache_max_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getMaxUsage()) ;
-	S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-	S32 v_offset = (S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f);
+	S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f);
+	S32 v_offset = 0;//(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() ;
@@ -527,80 +525,24 @@ 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 FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d",
+	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB",
 					total_mem,
 					max_total_mem,
 					bound_mem,
 					max_bound_mem,
 					LLRenderTarget::sBytesAllocated/(1024*1024),
 					LLImageRaw::sGlobalRawMemory >> 20,	discard_bias,
-					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,
+					cache_usage, cache_max_usage);
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
 
-	//----------------------------------------------------------------------------
-#if 0
-	S32 bar_left = 400;
-	S32 bar_width = 200;
-	S32 top = line_height*3 - 2 + v_offset;
-	S32 bottom = top - 6;
-	S32 left = bar_left;
-	S32 right = left + bar_width;
-	F32 bar_scale;
-	
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
-	// GL Mem Bar
-		
-	left = bar_left;
-	text = "GL";
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3,
+	text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d",
+					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,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	
-	left = bar_left+20;
-	right = left + bar_width;
-	
-	gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f); // grey
-	gl_rect_2d(left, top, right, bottom);
-
-	bar_scale = (F32)bar_width / (max_total_mem * 1.5f);
-	right = left + llfloor(total_mem * bar_scale);
-	right = llclamp(right, bar_left, bar_left + bar_width);
-	
-	color = (total_mem < llfloor(max_total_mem * texmem_lower_bound_scale)) ? LLColor4::green :
-		  	(total_mem < max_total_mem) ? LLColor4::yellow : LLColor4::red;
-	color[VALPHA] = .75f;
-	gGL.diffuseColor4fv(color.mV);
-	
-	gl_rect_2d(left, top, right, bottom); // red/yellow/green
-
-	//
-	bar_left += bar_width + bar_space;
-	//top = bottom - 2; bottom = top - 6;
-	
-	// Bound Mem Bar
-
-	left = bar_left;
-	text = "GL";
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	left = bar_left + 20;
-	right = left + bar_width;
-	
-	gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f);
-	gl_rect_2d(left, top, right, bottom);
-
-	color = (bound_mem < llfloor(max_bound_mem * texmem_lower_bound_scale)) ? LLColor4::green :
-		  	(bound_mem < max_bound_mem) ? LLColor4::yellow : LLColor4::red;
-	color[VALPHA] = .75f;
-	gGL.diffuseColor4fv(color.mV);
 
-	gl_rect_2d(left, top, right, bottom);
-#else
 	S32 left = 0 ;
-#endif
 	//----------------------------------------------------------------------------
 
 	text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d",
@@ -669,8 +611,7 @@ BOOL LLGLTexMemBar::handleMouseDown(S32 x, S32 y, MASK mask)
 LLRect LLGLTexMemBar::getRequiredRect()
 {
 	LLRect rect;
-	//rect.mTop = 50;
-	rect.mTop = 0;
+	rect.mTop = 50; //LLFontGL::getFontMonospace()->getLineHeight() * 6;
 	return rect;
 }
 
@@ -954,9 +895,11 @@ void LLTextureView::draw()
 		LLRect tmbr;
 		tmbp.name("gl texmem bar");
 		tmbp.rect(tmbr);
+		tmbp.follows.flags = FOLLOWS_LEFT|FOLLOWS_TOP;
 		tmbp.texture_view(this);
 		mGLTexMemBar = LLUICtrlFactory::create<LLGLTexMemBar>(tmbp);
-		addChildInBack(mGLTexMemBar);
+		addChild(mGLTexMemBar);
+		sendChildToFront(mGLTexMemBar);
 
 		LLAvatarTexBar::Params atbp;
 		LLRect atbr;
@@ -965,16 +908,13 @@ void LLTextureView::draw()
 		atbp.rect(atbr);
 		mAvatarTexBar = LLUICtrlFactory::create<LLAvatarTexBar>(atbp);
 		addChild(mAvatarTexBar);
+		sendChildToFront(mAvatarTexBar);
 
 		reshape(getRect().getWidth(), getRect().getHeight(), TRUE);
 
-		/*
-		  count = gTextureList.getNumImages();
-		  std::string info_string;
-		  info_string = llformat("Global Info:\nTexture Count: %d", count);
-		  mInfoTextp->setText(info_string);
-		*/
-
+		LLUI::popMatrix();
+		LLUI::pushMatrix();
+		LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
 
 		for (child_list_const_iter_t child_iter = getChildList()->begin();
 			 child_iter != getChildList()->end(); ++child_iter)
@@ -1049,302 +989,4 @@ BOOL LLTextureView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
 	return FALSE;
 }
 
-//-----------------------------------------------------------------
-LLTextureSizeView::LLTextureSizeView(const LLTextureSizeView::Params& p) : LLContainerView(p)
-{
-	setVisible(FALSE) ;
-
-	mTextureSizeBarWidth = 30 ;
-}
-
-LLTextureSizeView::~LLTextureSizeView()
-{
-	if(mTextureSizeBar.size())
-	{
-		for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-		{
-			delete mTextureSizeBar[i] ;
-		}
-		mTextureSizeBar.clear() ;
-	}
-}
-void LLTextureSizeView::draw()
-{
-	if(mType == TEXTURE_MEM_OVER_SIZE)
-	{
-		drawTextureSizeGraph();
-	}
-	else
-	{
-		drawTextureCategoryGraph() ;
-	}
-	
-	LLView::draw();
-}
-
-BOOL LLTextureSizeView::handleHover(S32 x, S32 y, MASK mask) 
-{
-	if(x > mTextureSizeBarRect.mLeft && x < mTextureSizeBarRect.mRight)
-	{
-		mTextureSizeBar[(x - mTextureSizeBarRect.mLeft) / mTextureSizeBarWidth]->handleHover(x, y, mask, (mType == TEXTURE_MEM_OVER_SIZE)) ;
-	}
-
-	return TRUE ;
-}
-
-//draw real-time texture mem bar over size
-void LLTextureSizeView::drawTextureSizeGraph()
-{
-	if(mTextureSizeBar.size() == 0)
-	{
-		S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-		mTextureSizeBar.resize(LLImageGL::sTextureLoadedCounter.size()) ;
-		mTextureSizeBarRect.set(700, line_height * 2 + 400, 700 + mTextureSizeBar.size() * mTextureSizeBarWidth, line_height * 2) ;
-		
-		for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-		{				
-			mTextureSizeBar[i] = new LLGLTexSizeBar(i, mTextureSizeBarRect.mLeft + i * mTextureSizeBarWidth , 
-				line_height * 2, mTextureSizeBarRect.mLeft + (i + 1) * mTextureSizeBarWidth, line_height) ;				
-		}			
-	}
-
-	F32 size_bar_scale = drawTextureSizeDistributionGraph() ;		
-	for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-	{
-		mTextureSizeBar[i]->setTop(LLImageGL::sTextureLoadedCounter[i], LLImageGL::sTextureBoundCounter[i], size_bar_scale) ;
-		mTextureSizeBar[i]->draw() ;
-	}		
-	LLImageGL::resetCurTexSizebar();
-}
-
-//draw background of texture size bar graph
-F32 LLTextureSizeView::drawTextureSizeDistributionGraph()
-{	
-	//scale
-	F32 scale = 1.0f ;
-	
-	LLGLSUIDefault gls_ui;
-
-	{
-		S32 count = 0 ;
-		for(U32 i = 0 ; i < LLImageGL::sTextureLoadedCounter.size() ; i++)
-		{
-			if(LLImageGL::sTextureLoadedCounter[i] > count)
-			{
-				count = LLImageGL::sTextureLoadedCounter[i] ;
-			}
-		}
-		if(count > mTextureSizeBarRect.getHeight())
-		{
-			scale = (F32)mTextureSizeBarRect.getHeight() / count ;
-		}
-	}
-
-	S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-	S32 left = mTextureSizeBarRect.mLeft ;
-	S32 bottom = mTextureSizeBarRect.mBottom ;
-	S32 right = mTextureSizeBarRect.mRight ;
-	S32 top = mTextureSizeBarRect.mTop ;
-
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-	
-	//background rect
-	gl_rect_2d(left - 25, top + 30, right + 100, bottom - 25, LLColor4(0.0f, 0.0f, 0.0f, 0.25f)) ;
-
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.75f);
-	gl_line_2d(left, bottom, right, bottom) ; //x axis
-	gl_line_2d(left, bottom, left, top) ; //y axis
-
-	//ruler
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.5f);
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		gl_line_2d(left, i, right, i) ;
-	}
-
-	//texts
-	//--------------------------------------------------
-	F32 text_color[] = {1.f, 1.f, 1.f, 0.75f};	
-	std::string text;
-	
-	//-------
-	//x axis: size label
-	text = llformat("%d", 0) ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	for(U32 i = 1 ; i < mTextureSizeBar.size() ; i++)
-	{
-		text = llformat("%d", (1 << (i / 2)) + ((i & 1) ? ((1 << (i / 2)) >> 1) : 0)) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + i * mTextureSizeBarWidth + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-	text = llformat("(w + h)/2") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 10, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	//-------
-
-	//y axis: number label
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		text = llformat("%d", (S32)((i - bottom) / scale)) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 5, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-
-	//--------------------------------------------------
-	F32 loaded_color[] = {1.0f, 0.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 70, top + line_height * 2, left + 90, top + line_height, loaded_color) ;
-	text = llformat("Loaded") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 100, top + line_height * 2,
-									 loaded_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 170, top + line_height * 2, left + 190, top + line_height, bound_color) ;
-	text = llformat("Bound") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 200, top + line_height * 2,
-									 bound_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	//--------------------------------------------------
-
-	//title
-	text = llformat("Texture Size Distribution") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	return scale ;
-}
-
-//draw real-time texture mem bar over category
-void LLTextureSizeView::drawTextureCategoryGraph()
-{
-	if(mTextureSizeBar.size() == 0)
-	{
-		S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-		mTextureSizeBar.resize(LLViewerTexture::getTotalNumOfCategories()) ;
-		mTextureSizeBarRect.set(700, line_height * 2 + 400, 700 + mTextureSizeBar.size() * mTextureSizeBarWidth, line_height * 2) ;
-		
-		for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-		{				
-			mTextureSizeBar[i] = new LLGLTexSizeBar(i, mTextureSizeBarRect.mLeft + i * mTextureSizeBarWidth , 
-				line_height * 2, mTextureSizeBarRect.mLeft + (i + 1) * mTextureSizeBarWidth, line_height) ;				
-		}			
-	}
-
-	F32 size_bar_scale = drawTextureCategoryDistributionGraph() ;		
-	for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-	{
-		U32 k = LLViewerTexture::getIndexFromCategory(i) ;
-		mTextureSizeBar[i]->setTop(LLImageGL::sTextureMemByCategory[k] >> 20, LLImageGL::sTextureMemByCategoryBound[k] >> 20, size_bar_scale) ;
-		mTextureSizeBar[i]->draw() ;
-	}		
-	LLImageGL::resetCurTexSizebar();
-}
-
-//draw background for TEXTURE_MEM_OVER_CATEGORY
-F32 LLTextureSizeView::drawTextureCategoryDistributionGraph() 
-{
-	//scale
-	F32 scale = 4.0f ;
-	
-	LLGLSUIDefault gls_ui;
 
-	{
-		S32 count = 0 ;
-		for(U32 i = 0 ; i < LLImageGL::sTextureMemByCategory.size() ; i++)
-		{
-			S32 tmp = LLImageGL::sTextureMemByCategory[i] >> 20 ;
-			if(tmp > count)
-			{
-				count = tmp ;
-			}
-		}
-		if(count > mTextureSizeBarRect.getHeight() * 0.25f)
-		{
-			scale = (F32)mTextureSizeBarRect.getHeight() * 0.25f / count ;
-		}
-	}
-
-	S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-	S32 left = mTextureSizeBarRect.mLeft ;
-	S32 bottom = mTextureSizeBarRect.mBottom ;
-	S32 right = mTextureSizeBarRect.mRight ;
-	S32 top = mTextureSizeBarRect.mTop ;
-
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-	
-	//background rect
-	gl_rect_2d(left - 25, top + 30, right + 100, bottom - 25, LLColor4(0.0f, 0.0f, 0.0f, 0.25f)) ;
-
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.75f);
-	gl_line_2d(left, bottom, right, bottom) ; //x axis
-	gl_line_2d(left, bottom, left, top) ; //y axis
-
-	//ruler
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.5f);
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		gl_line_2d(left, i, right, i) ;
-	}
-
-	//texts
-	//--------------------------------------------------
-	F32 text_color[] = {1.f, 1.f, 1.f, 0.75f};	
-	std::string text;
-	
-	//-------
-	//x axis: size label			
-	static char category[LLViewerTexture::MAX_GL_IMAGE_CATEGORY][4] = 
-	{"Non", "Bak", "Av", "Cld", "Scp", "Hi", "Trn", "Slt", "Hud", "Bsf", "UI", "Pvw", "Map", "Mvs", "Slf", "Loc", "Scr", "Dyn", "Mdi", "ALT", "Oth" } ;
-
-	text = llformat("%s", category[0]) ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	for(U32 i = 1 ; i < mTextureSizeBar.size() ; i++)
-	{
-		text = llformat("%s", category[i]) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + i * mTextureSizeBarWidth + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-	//-------
-
-	//y axis: number label
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		text = llformat("%d", (S32)((i - bottom) / scale)) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 5, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-
-	text = llformat("MB") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, top + line_height * 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	//--------------------------------------------------
-	F32 loaded_color[] = {1.0f, 0.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 70, top + line_height * 2, left + 90, top + line_height, loaded_color) ;
-	text = llformat("Loaded") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 100, top + line_height * 2,
-									 loaded_color, 
-									 LLFontGL::LEFT, LLFontGL::TOP);
-
-	F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 170, top + line_height * 2, left + 190, top + line_height, bound_color) ;
-	text = llformat("Bound") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 200, top + line_height * 2,
-									 bound_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	//--------------------------------------------------
-
-	//title
-	text = llformat("Texture Category Distribution") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	return scale ;
-}
diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h
index 3723eb737b46751fc3247364831d34b691524396..900b4e17d82ada90e6abdae8f6a8708106a29dcb 100644
--- a/indra/newview/lltextureview.h
+++ b/indra/newview/lltextureview.h
@@ -75,41 +75,6 @@ class LLTextureView : public LLContainerView
 };
 
 class LLGLTexSizeBar;
-class LLTextureSizeView : public LLContainerView
-{
-protected:
-	LLTextureSizeView(const Params&);
-	friend class LLUICtrlFactory;
-public:	
-	~LLTextureSizeView();
-
-	/*virtual*/ void draw();
-	/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) ;
-	
-	void setType(S32 type) {mType = type ;}
-	enum
-	{
-		TEXTURE_MEM_OVER_SIZE,
-		TEXTURE_MEM_OVER_CATEGORY
-	};
-private:
-	//draw background for TEXTURE_MEM_OVER_SIZE
-	F32 drawTextureSizeDistributionGraph() ;
-	//draw real-time texture mem bar over size
-	void drawTextureSizeGraph();
-
-	//draw background for TEXTURE_MEM_OVER_CATEGORY
-	F32 drawTextureCategoryDistributionGraph() ;
-	//draw real-time texture mem bar over category
-	void drawTextureCategoryGraph();
 
-private:
-	std::vector<LLGLTexSizeBar*> mTextureSizeBar ;
-	LLRect mTextureSizeBarRect ;
-	S32    mTextureSizeBarWidth ;	
-	S32    mType ;
-};
 extern LLTextureView *gTextureView;
-extern LLTextureSizeView *gTextureSizeView;
-extern LLTextureSizeView *gTextureCategoryView;
 #endif // LL_TEXTURE_VIEW_H
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp
old mode 100644
new mode 100755
index e621cf647ef777f1354e43fb0dceb9854c7a5304..4c59fd037195925fe656ac3d99a5085061381173
--- a/indra/newview/llviewerassetstats.cpp
+++ b/indra/newview/llviewerassetstats.cpp
@@ -30,6 +30,7 @@
 #include "llregionhandle.h"
 
 #include "stdtypes.h"
+#include "llvoavatar.h"
 
 /*
  * Classes and utility functions for per-thread and per-region
@@ -126,6 +127,8 @@ LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionSta
 		mFPS.merge(src.mFPS);
 	}
 
+	// Avatar stats - data all comes from main thread, so leave alone.
+
 	// Requests
 	for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i)
 	{
@@ -133,6 +136,7 @@ LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionSta
 		mRequests[i].mDequeued.merge(src.mRequests[i].mDequeued);
 		mRequests[i].mResponse.merge(src.mRequests[i].mResponse);
 	}
+
 }
 
 
@@ -156,7 +160,9 @@ LLViewerAssetStats::LLViewerAssetStats()
 
 LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src)
 	: mRegionHandle(src.mRegionHandle),
-	  mResetTimestamp(src.mResetTimestamp)
+	  mResetTimestamp(src.mResetTimestamp),
+	  mPhaseStats(src.mPhaseStats),
+	  mAvatarRezStates(src.mAvatarRezStates)
 {
 	const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
 	for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
@@ -252,6 +258,17 @@ LLViewerAssetStats::recordFPS(F32 fps)
 	mCurRegionStats->mFPS.record(fps);
 }
 
+void
+LLViewerAssetStats::recordAvatarStats()
+{
+	std::vector<S32> rez_counts;
+	LLVOAvatar::getNearbyRezzedStats(rez_counts);
+	mAvatarRezStates = rez_counts;
+	mPhaseStats.clear();
+	mPhaseStats["cloud"] = LLViewerStats::PhaseMap::getPhaseStats("cloud");
+	mPhaseStats["cloud-or-gray"] = LLViewerStats::PhaseMap::getPhaseStats("cloud-or-gray");
+}
+
 LLSD
 LLViewerAssetStats::asLLSD(bool compact_output)
 {
@@ -282,6 +299,11 @@ LLViewerAssetStats::asLLSD(bool compact_output)
 	static const LLSD::String max_tag("max");
 	static const LLSD::String mean_tag("mean");
 
+	// Avatar sub-tags
+	static const LLSD::String avatar_tag("avatar");
+	static const LLSD::String avatar_nearby_tag("nearby");
+	static const LLSD::String avatar_phase_stats_tag("phase_stats");
+	
 	const duration_t now = LLViewerAssetStatsFF::get_timestamp();
 	mCurRegionStats->accumulateTime(now);
 
@@ -329,7 +351,6 @@ LLViewerAssetStats::asLLSD(bool compact_output)
 			slot[max_tag] = LLSD(F64(stats.mFPS.getMax()));
 			slot[mean_tag] = LLSD(F64(stats.mFPS.getMean()));
 		}
-
 		U32 grid_x(0), grid_y(0);
 		grid_from_region_handle(it->first, &grid_x, &grid_y);
 		reg_stat["grid_x"] = LLSD::Integer(grid_x);
@@ -341,6 +362,16 @@ LLViewerAssetStats::asLLSD(bool compact_output)
 	LLSD ret = LLSD::emptyMap();
 	ret["regions"] = regions;
 	ret["duration"] = LLSD::Real((now - mResetTimestamp) * 1.0e-6);
+	LLSD avatar_info;
+	avatar_info[avatar_nearby_tag] = LLSD::emptyArray();
+	for (S32 rez_stat=0; rez_stat < mAvatarRezStates.size(); ++rez_stat)
+	{
+		std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat);
+		avatar_info[avatar_nearby_tag][rez_status_name] = mAvatarRezStates[rez_stat];
+	}
+	avatar_info[avatar_phase_stats_tag]["cloud"] = mPhaseStats["cloud"].getData();
+	avatar_info[avatar_phase_stats_tag]["cloud-or-gray"] = mPhaseStats["cloud-or-gray"].getData();
+	ret[avatar_tag] = avatar_info;
 	
 	return ret;
 }
@@ -439,6 +470,14 @@ record_fps_main(F32 fps)
 	gViewerAssetStatsMain->recordFPS(fps);
 }
 
+void
+record_avatar_stats()
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	gViewerAssetStatsMain->recordAvatarStats();
+}
 
 // 'thread1' - should be for TextureFetch thread
 
diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h
old mode 100644
new mode 100755
index 73ec5974b296928b227a890c3ff2b69bb8a59c46..83197522306f2572ce64bec9e908354d1ab1f255
--- a/indra/newview/llviewerassetstats.h
+++ b/indra/newview/llviewerassetstats.h
@@ -36,6 +36,7 @@
 #include "llviewerassetstorage.h"
 #include "llsimplestat.h"
 #include "llsd.h"
+#include "llvoavatar.h"
 
 /**
  * @class LLViewerAssetStats
@@ -181,6 +182,9 @@ class LLViewerAssetStats
 	// Frames-Per-Second Samples
 	void recordFPS(F32 fps);
 
+	// Avatar-related statistics
+	void recordAvatarStats();
+
 	// Merge a source instance into a destination instance.  This is
 	// conceptually an 'operator+=()' method:
 	// - counts are added
@@ -252,6 +256,10 @@ class LLViewerAssetStats
 
 	// Time of last reset
 	duration_t mResetTimestamp;
+
+	// Nearby avatar stats
+	std::vector<S32> mAvatarRezStates;
+	LLViewerStats::phase_stats_t mPhaseStats;
 };
 
 
@@ -310,6 +318,7 @@ void record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_t
 
 void record_fps_main(F32 fps);
 
+void record_avatar_stats();
 
 /**
  * Region context, event and duration loggers for Thread 1.
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index ab45aae5cc982517e0fd518ef8f57835eb2afe8e..f2712e7590880a38175ddcf9974dcdb13c1d86db 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -87,7 +87,6 @@ std::string gLastRunVersion;
 
 extern BOOL gResizeScreenTexture;
 extern BOOL gDebugGL;
-extern BOOL gAuditTexture;
 ////////////////////////////////////////////////////////////////////////////
 // Listeners
 
@@ -411,12 +410,6 @@ static bool handleRenderUseImpostorsChanged(const LLSD& newvalue)
 	return true;
 }
 
-static bool handleAuditTextureChanged(const LLSD& newvalue)
-{
-	gAuditTexture = newvalue.asBoolean();
-	return true;
-}
-
 static bool handleRenderDebugGLChanged(const LLSD& newvalue)
 {
 	gDebugGL = newvalue.asBoolean() || gDebugSession;
@@ -618,7 +611,6 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("RenderDeferredSSAO")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
 	gSavedSettings.getControl("RenderPerformanceTest")->getSignal()->connect(boost::bind(&handleRenderPerfTestChanged, _2));
 	gSavedSettings.getControl("TextureMemory")->getSignal()->connect(boost::bind(&handleVideoMemoryChanged, _2));
-	gSavedSettings.getControl("AuditTexture")->getSignal()->connect(boost::bind(&handleAuditTextureChanged, _2));
 	gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&handleChatFontSizeChanged, _2));
 	gSavedSettings.getControl("ChatPersistTime")->getSignal()->connect(boost::bind(&handleChatPersistTimeChanged, _2));
 	gSavedSettings.getControl("ConsoleMaxLines")->getSignal()->connect(boost::bind(&handleConsoleMaxLinesChanged, _2));
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 7fdaac68c8753b048e3f36e3a163cfb67e4f3e64..d0e0d0d8264363a772ca97d1cbe6fc01e23ee5dd 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -103,6 +103,7 @@
 #include "llfloatertelehub.h"
 #include "llfloatertestinspectors.h"
 #include "llfloatertestlistview.h"
+#include "llfloatertexturefetchdebugger.h"
 #include "llfloatertools.h"
 #include "llfloatertos.h"
 #include "llfloatertopobjects.h"
@@ -227,6 +228,11 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>);
 	
 	LLFloaterReg::add("mem_leaking", "floater_mem_leaking.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMemLeak>);
+
+	if(gSavedSettings.getBOOL("TextureFetchDebuggerEnabled"))
+	{
+		LLFloaterReg::add("tex_fetch_debugger", "floater_texture_fetch_debugger.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTextureFetchDebugger>);
+	}
 	LLFloaterReg::add("media_settings", "floater_media_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMediaSettings>);	
 	LLFloaterReg::add("message_critical", "floater_critical.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
 	LLFloaterReg::add("message_tos", "floater_tos.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
old mode 100644
new mode 100755
index 45ca23cdfe2d3b19b49dd1f1410d99902731a57b..b47a41c44c9d891acafafd097d90388a8499b550
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -342,8 +342,8 @@ void LLViewerInventoryItem::cloneViewerItem(LLPointer<LLViewerInventoryItem>& ne
 
 void LLViewerInventoryItem::removeFromServer()
 {
-	llinfos << "Removing inventory item " << mUUID << " from server."
-			<< llendl;
+	lldebugs << "Removing inventory item " << mUUID << " from server."
+			 << llendl;
 
 	LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
 	gInventory.accountForUpdate(up);
diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp
old mode 100644
new mode 100755
index e052e373931e81044340f01d32d53bee68a09696..f029ae5302f040785f4390eb5a8c723b6e4f90f9
--- a/indra/newview/llviewerjointmesh.cpp
+++ b/indra/newview/llviewerjointmesh.cpp
@@ -576,7 +576,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 		{
 			old_mode = mTexture->getAddressMode();
 		}
-		gGL.getTexUnit(diffuse_channel)->bind(mTexture.get());
 		gGL.getTexUnit(diffuse_channel)->bind(mTexture);
 		gGL.getTexUnit(diffuse_channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
 	}
diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h
old mode 100644
new mode 100755
index 0191f0cae8f8a6b06719f6ad6dab44746cd39157..dd5dae1dc1e997cd330ffcfbf9c6aa03e0eedbdd
--- a/indra/newview/llviewerjointmesh.h
+++ b/indra/newview/llviewerjointmesh.h
@@ -61,6 +61,7 @@ class LLSkinJoint
 //-----------------------------------------------------------------------------
 class LLViewerJointMesh : public LLViewerJoint
 {
+	friend class LLVOAvatar;
 protected:
 	LLColor4					mColor;			// color value
 // 	LLColor4					mSpecular;		// specular color (always white for now)
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 7481414b5c49771772f91844ba70798f06125183..48e48132052750ae29f20e9663827afd8b696182 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -516,14 +516,6 @@ class LLAdvancedToggleConsole : public view_listener_t
 		{
 			toggle_visibility( (void*)static_cast<LLUICtrl*>(gDebugView->mDebugConsolep));
 		}
-		else if (gTextureSizeView && "texture size" == console_type)
-		{
-			toggle_visibility( (void*)gTextureSizeView );
-		}
-		else if (gTextureCategoryView && "texture category" == console_type)
-		{
-			toggle_visibility( (void*)gTextureCategoryView );
-		}
 		else if ("fast timers" == console_type)
 		{
 			LLFloaterReg::toggleInstance("fast_timers");
@@ -556,14 +548,6 @@ class LLAdvancedCheckConsole : public view_listener_t
 		{
 			new_value = get_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) );
 		}
-		else if (gTextureSizeView && "texture size" == console_type)
-		{
-			new_value = get_visibility( (void*)gTextureSizeView );
-		}
-		else if (gTextureCategoryView && "texture category" == console_type)
-		{
-			new_value = get_visibility( (void*)gTextureCategoryView );
-		}
 		else if ("fast timers" == console_type)
 		{
 			new_value = LLFloaterReg::instanceVisible("fast_timers");
@@ -866,6 +850,73 @@ class LLAdvancedCheckFeature : public view_listener_t
 }
 };
 
+class LLAdvancedCheckDisplayTextureDensity : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		std::string mode = userdata.asString();
+		if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY))
+		{
+			return mode == "none";
+		}
+		if (mode == "current")
+		{
+			return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_CURRENT;
+		}
+		else if (mode == "desired")
+		{
+			return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_DESIRED;
+		}
+		else if (mode == "full")
+		{
+			return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_FULL;
+		}
+		return false;
+	}
+};
+
+class LLAdvancedSetDisplayTextureDensity : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		std::string mode = userdata.asString();
+		if (mode == "none")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == TRUE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
+		}
+		else if (mode == "current")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_CURRENT;
+		}
+		else if (mode == "desired")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			gPipeline.setRenderDebugFeatureControl(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY, true);
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_DESIRED;
+		}
+		else if (mode == "full")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_FULL;
+		}
+		return true;
+	}
+};
+
 
 //////////////////
 // INFO DISPLAY //
@@ -980,6 +1031,10 @@ U32 info_display_from_string(std::string info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_WIND_VECTORS;
 	}
+	else if ("texel density" == info_display)
+	{
+		return LLPipeline::RENDER_DEBUG_TEXEL_DENSITY;
+	}
 	else
 	{
 		return 0;
@@ -2245,6 +2300,14 @@ class LLDevelopSetLoggingLevel : public view_listener_t
 	}
 };
 
+class LLDevelopTextureFetchDebugger : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		return gSavedSettings.getBOOL("TextureFetchDebuggerEnabled");
+	}
+};
+
 //////////////////
 // ADMIN MENU   //
 //////////////////
@@ -8164,6 +8227,10 @@ void initialize_menus()
 	//// Advanced > Render > Features
 	view_listener_t::addMenu(new LLAdvancedToggleFeature(), "Advanced.ToggleFeature");
 	view_listener_t::addMenu(new LLAdvancedCheckFeature(), "Advanced.CheckFeature");
+
+	view_listener_t::addMenu(new LLAdvancedCheckDisplayTextureDensity(), "Advanced.CheckDisplayTextureDensity");
+	view_listener_t::addMenu(new LLAdvancedSetDisplayTextureDensity(), "Advanced.SetDisplayTextureDensity");
+
 	// Advanced > Render > Info Displays
 	view_listener_t::addMenu(new LLAdvancedToggleInfoDisplay(), "Advanced.ToggleInfoDisplay");
 	view_listener_t::addMenu(new LLAdvancedCheckInfoDisplay(), "Advanced.CheckInfoDisplay");
@@ -8295,6 +8362,9 @@ void initialize_menus()
 	// Develop >Set logging level
 	view_listener_t::addMenu(new LLDevelopCheckLoggingLevel(), "Develop.CheckLoggingLevel");
 	view_listener_t::addMenu(new LLDevelopSetLoggingLevel(), "Develop.SetLoggingLevel");
+	
+	//Develop (Texture Fetch Debug Console)
+	view_listener_t::addMenu(new LLDevelopTextureFetchDebugger(), "Develop.SetTexFetchDebugger");
 
 	// Admin >Object
 	view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy");
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 6912faa9ec93bc5420356d13f8aa9aaae56e57b6..54ccfb9aae4d44069429441866238425b30e0094 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -91,8 +91,9 @@ extern LLPipeline	gPipeline;
 
 // Statics for object lookup tables.
 U32						LLViewerObjectList::sSimulatorMachineIndex = 1; // Not zero deliberately, to speed up index check.
-std::map<U64, U32>			LLViewerObjectList::sIPAndPortToIndex;
+std::map<U64, U32>		LLViewerObjectList::sIPAndPortToIndex;
 std::map<U64, LLUUID>	LLViewerObjectList::sIndexAndLocalIDToUUID;
+LLStat					LLViewerObjectList::sCacheHitRate("object_cache_hits", 128);
 
 LLViewerObjectList::LLViewerObjectList()
 {
@@ -542,6 +543,8 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 			}
 			justCreated = TRUE;
 			mNumNewObjects++;
+			sCacheHitRate.addValue(cached ? 100.f : 0.f);
+
 		}
 
 
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index c5f2a2c1eee1a229cfcd23ea291629ee022b9d5d..64925f46aeacea27376e964bd3587eb326d5f1c6 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -192,6 +192,8 @@ class LLViewerObjectList
 	std::vector<OrphanInfo> mOrphanChildren;	// UUID's of orphaned objects
 	S32 mNumOrphans;
 
+	static LLStat sCacheHitRate;
+
 	typedef std::vector<LLPointer<LLViewerObject> > vobj_list_t;
 
 	vobj_list_t mObjects;
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
old mode 100644
new mode 100755
index c88122f22c9078efd0a46fc00a388cb5444e59cf..497e95c5e3712286b376b608cc666fac4a6df5b4
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -860,3 +860,110 @@ void send_stats()
 	LLHTTPClient::post(url, body, new ViewerStatsResponder());
 }
 
+LLFrameTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name)
+{
+	phase_map_t::iterator iter = mPhaseMap.find(phase_name);
+	if (iter == mPhaseMap.end())
+	{
+		LLFrameTimer timer;
+		mPhaseMap[phase_name] = timer;
+	}
+	LLFrameTimer& timer = mPhaseMap[phase_name];
+	return timer;
+}
+
+void LLViewerStats::PhaseMap::startPhase(const std::string& phase_name)
+{
+	LLFrameTimer& timer = getPhaseTimer(phase_name);
+	lldebugs << "startPhase " << phase_name << llendl;
+	timer.unpause();
+}
+
+void LLViewerStats::PhaseMap::stopPhase(const std::string& phase_name)
+{
+	phase_map_t::iterator iter = mPhaseMap.find(phase_name);
+	if (iter != mPhaseMap.end())
+	{
+		if (iter->second.getStarted())
+		{
+			// Going from started to paused state - record stats.
+			recordPhaseStat(phase_name,iter->second.getElapsedTimeF32());
+		}
+		lldebugs << "stopPhase " << phase_name << llendl;
+		iter->second.pause();
+	}
+	else
+	{
+		lldebugs << "stopPhase " << phase_name << " is not started, no-op" << llendl;
+	}
+}
+
+void LLViewerStats::PhaseMap::stopAllPhases()
+{
+	for (phase_map_t::iterator iter = mPhaseMap.begin();
+		 iter != mPhaseMap.end(); ++iter)
+	{
+		const std::string& phase_name = iter->first;
+		if (iter->second.getStarted())
+		{
+			// Going from started to paused state - record stats.
+			recordPhaseStat(phase_name,iter->second.getElapsedTimeF32());
+		}
+		lldebugs << "stopPhase (all) " << phase_name << llendl;
+		iter->second.pause();
+	}
+}
+
+void LLViewerStats::PhaseMap::clearPhases()
+{
+	lldebugs << "clearPhases" << llendl;
+
+	mPhaseMap.clear();
+}
+
+LLSD LLViewerStats::PhaseMap::dumpPhases()
+{
+	LLSD result;
+	for (phase_map_t::iterator iter = mPhaseMap.begin(); iter != mPhaseMap.end(); ++iter)
+	{
+		const std::string& phase_name = iter->first;
+		result[phase_name]["completed"] = !(iter->second.getStarted());
+		result[phase_name]["elapsed"] = iter->second.getElapsedTimeF32();
+#if 0 // global stats for each phase seem like overkill here
+		phase_stats_t::iterator stats_iter = sPhaseStats.find(phase_name);
+		if (stats_iter != sPhaseStats.end())
+		{
+			result[phase_name]["stats"] = stats_iter->second.getData();
+		}
+#endif
+	}
+	return result;
+}
+
+// static initializer
+//static
+LLViewerStats::phase_stats_t LLViewerStats::PhaseMap::sStats;
+
+LLViewerStats::PhaseMap::PhaseMap()
+{
+}
+
+// static
+LLViewerStats::StatsAccumulator& LLViewerStats::PhaseMap::getPhaseStats(const std::string& phase_name)
+{
+	phase_stats_t::iterator it = sStats.find(phase_name);
+	if (it == sStats.end())
+	{
+		LLViewerStats::StatsAccumulator new_stats;
+		sStats[phase_name] = new_stats;
+	}
+	return sStats[phase_name];
+}
+
+// static
+void LLViewerStats::PhaseMap::recordPhaseStat(const std::string& phase_name, F32 value)
+{
+	LLViewerStats::StatsAccumulator& stats = getPhaseStats(phase_name);
+	stats.push(value);
+}
+
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
old mode 100644
new mode 100755
index f91a1241fe584cd5770bf009d19aa7ee67ff16f8..750d963f69e6792310034b5c5fe85e5f4084a616
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -244,7 +244,7 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 		inline F32 getStdDev() const
 		{
 			const F32 mean = getMean();
-			return (mCount == 0) ? 0.f : sqrt( mSumOfSquares/mCount - (mean * mean) );
+			return (mCount < 2) ? 0.f : sqrt(llmax(0.f,mSumOfSquares/mCount - (mean * mean)));
 		}
 		
 		inline U32 getCount() const
@@ -274,7 +274,28 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 	};
 
 	StatsAccumulator mAgentPositionSnaps;
-	
+
+	// Phase tracking (originally put in for avatar rezzing), tracking
+	// progress of active/completed phases for activities like outfit changing.
+	typedef std::map<std::string,LLFrameTimer>	phase_map_t;
+	typedef std::map<std::string,StatsAccumulator>	phase_stats_t;
+	class PhaseMap
+	{
+	private:
+		phase_map_t mPhaseMap;
+		static phase_stats_t sStats;
+	public:
+		PhaseMap();
+		LLFrameTimer& 	getPhaseTimer(const std::string& phase_name);
+		void			startPhase(const std::string& phase_name);
+		void			stopPhase(const std::string& phase_name);
+		void			stopAllPhases();
+		void			clearPhases();
+		LLSD			dumpPhases();
+		static StatsAccumulator& getPhaseStats(const std::string& phase_name);
+		static void recordPhaseStat(const std::string& phase_name, F32 value);
+	};
+
 private:
 	F64	mStats[ST_COUNT];
 
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 61236edc86e0e46ed4ef4c7ca2ebc0484c760a32..ea329f6aaced4a5a7efb54f26e8336927156a934 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -67,6 +67,7 @@
 // statics
 LLPointer<LLViewerTexture>        LLViewerTexture::sNullImagep = NULL;
 LLPointer<LLViewerTexture>        LLViewerTexture::sBlackImagep = NULL;
+LLPointer<LLViewerTexture>        LLViewerTexture::sCheckerBoardImagep = NULL;
 LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sMissingAssetImagep = NULL;
 LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sWhiteImagep = NULL;
 LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultImagep = NULL;
@@ -87,6 +88,7 @@ S32 LLViewerTexture::sMaxBoundTextureMemInMegaBytes = 0;
 S32 LLViewerTexture::sMaxTotalTextureMemInMegaBytes = 0;
 S32 LLViewerTexture::sMaxDesiredTextureMemInBytes = 0 ;
 S8  LLViewerTexture::sCameraMovingDiscardBias = 0 ;
+F32 LLViewerTexture::sCameraMovingBias = 0.0f ;
 S32 LLViewerTexture::sMaxSculptRez = 128 ; //max sculpt image size
 const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64 ;
 const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez ;
@@ -96,6 +98,9 @@ S32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ;
 BOOL LLViewerTexture::sFreezeImageScalingDown = FALSE ;
 F32 LLViewerTexture::sCurrentTime = 0.0f ;
 BOOL LLViewerTexture::sUseTextureAtlas        = FALSE ;
+F32  LLViewerTexture::sTexelPixelRatio = 1.0f;
+
+LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
 
 const F32 desired_discard_bias_min = -2.0f; // -max number of levels to improve image quality by
 const F32 desired_discard_bias_max = (F32)MAX_DISCARD_LEVEL; // max number of levels to reduce image quality by
@@ -175,7 +180,12 @@ LLViewerTexture*  LLViewerTextureManager::findTexture(const LLUUID& id)
 	}
 	return tex ;
 }
-		
+
+LLViewerFetchedTexture*  LLViewerTextureManager::findFetchedTexture(const LLUUID& id) 
+{
+	return gTextureList.findImage(id);
+}
+
 LLViewerMediaTexture* LLViewerTextureManager::findMediaTexture(const LLUUID &media_id)
 {
 	return LLViewerMediaTexture::findMediaTexture(media_id) ;	
@@ -347,6 +357,21 @@ void LLViewerTextureManager::init()
  	LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, TRUE, LLViewerTexture::BOOST_UI);
 	LLViewerFetchedTexture::sSmokeImagep->setNoDelete() ;
 
+	image_raw = new LLImageRaw(32,32,3);
+	data = image_raw->getData();
+
+	for (S32 i = 0; i < (32*32*3); i+=3)
+	{
+		S32 x = (i % (32*3)) / (3*16);
+		S32 y = i / (32*3*16);
+		U8 color = ((x + y) % 2) * 255;
+		data[i] = color;
+		data[i+1] = color;
+		data[i+2] = color;
+	}
+
+	LLViewerTexture::sCheckerBoardImagep = LLViewerTextureManager::getLocalTexture(image_raw.get(), TRUE);
+
 	LLViewerTexture::initClass() ;
 
 	if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName))
@@ -367,6 +392,7 @@ void LLViewerTextureManager::cleanup()
 	LLImageGL::sDefaultGLTexture = NULL ;
 	LLViewerTexture::sNullImagep = NULL;
 	LLViewerTexture::sBlackImagep = NULL;
+	LLViewerTexture::sCheckerBoardImagep = NULL;
 	LLViewerFetchedTexture::sDefaultImagep = NULL;	
 	LLViewerFetchedTexture::sSmokeImagep = NULL;
 	LLViewerFetchedTexture::sMissingAssetImagep = NULL;
@@ -383,11 +409,7 @@ void LLViewerTextureManager::cleanup()
 void LLViewerTexture::initClass()
 {
 	LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ;
-
-	if(gAuditTexture)
-	{
-		LLImageGL::setHighlightTexture(LLViewerTexture::OTHER) ;	
-	}
+	sTexelPixelRatio = gSavedSettings.getF32("TexelPixelRatio");
 }
 
 // static
@@ -534,7 +556,8 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity
 	
 	F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ;
 	F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed();
-	sCameraMovingDiscardBias = (S8)llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1) ;
+	sCameraMovingBias = llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1);
+	sCameraMovingDiscardBias = (S8)(sCameraMovingBias);
 
 	LLViewerTexture::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < 0.75f * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) &&
 				(BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < 0.75f * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ;
@@ -655,10 +678,6 @@ void LLViewerTexture::setBoostLevel(S32 level)
 		{
 			setNoDelete() ;		
 		}
-		if(gAuditTexture)
-		{
-			setCategory(mBoostLevel);
-		}
 	}
 }
 
@@ -712,6 +731,7 @@ void LLViewerTexture::addTextureStats(F32 virtual_size, BOOL needs_gltexture) co
 		mNeedsGLTexture = TRUE ;
 	}
 
+	virtual_size *= sTexelPixelRatio;
 	if(!mMaxVirtualSizeResetCounter)
 	{
 		//flag to reset the values because the old values are used.
@@ -1287,6 +1307,7 @@ void LLViewerFetchedTexture::cleanup()
 	mCachedRawDiscardLevel = -1 ;
 	mCachedRawImageReady = FALSE ;
 	mSavedRawImage = NULL ;
+	mSavedRawDiscardLevel = -1;
 }
 
 void LLViewerFetchedTexture::setForSculpt()
@@ -1880,6 +1901,8 @@ S32 LLViewerFetchedTexture::getCurrentDiscardLevelForFetching()
 bool LLViewerFetchedTexture::updateFetch()
 {
 	static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
+	static LLCachedControl<F32>  sCameraMotionThreshold(gSavedSettings,"TextureCameraMotionThreshold");
+	static LLCachedControl<S32>  sCameraMotionBoost(gSavedSettings,"TextureCameraMotionBoost");
 	if(textures_decode_disabled)
 	{
 		return false ;
@@ -2042,18 +2065,24 @@ bool LLViewerFetchedTexture::updateFetch()
 	//	make_request = false;
 	//}
 	
-	if(make_request)
+	if (make_request)
 	{
-		//load the texture progressively.
+		// Load the texture progressively: we try not to rush to the desired discard too fast.
+		// If the camera is not moving, we do not tweak the discard level notch by notch but go to the desired discard with larger boosted steps
+		// This mitigates the "textures stay blurry" problem when loading while not killing the texture memory while moving around
 		S32 delta_level = (mBoostLevel > LLViewerTexture::BOOST_NONE) ? 2 : 1 ; 
-		if(current_discard < 0)
+		if (current_discard < 0)
 		{
 			desired_discard = llmax(desired_discard, getMaxDiscardLevel() - delta_level);
 		}
-		else
+		else if (LLViewerTexture::sCameraMovingBias < sCameraMotionThreshold)
 		{
-			desired_discard = llmax(desired_discard, current_discard - delta_level);
+			desired_discard = llmax(desired_discard, current_discard - sCameraMotionBoost);
 		}
+        else
+        {
+			desired_discard = llmax(desired_discard, current_discard - delta_level);
+        }
 
 		if (mIsFetching)
 		{
@@ -2121,6 +2150,30 @@ bool LLViewerFetchedTexture::updateFetch()
 	return mIsFetching ? true : false;
 }
 
+void LLViewerFetchedTexture::clearFetchedResults()
+{
+	llassert_always(!mNeedsCreateTexture && !mIsFetching);
+	
+	cleanup();
+	destroyGLTexture();
+
+	if(getDiscardLevel() >= 0) //sculpty texture, force to invalidate
+	{
+		mGLTexturep->forceToInvalidateGLTexture();
+	}
+}
+
+void LLViewerFetchedTexture::forceToDeleteRequest()
+{
+	if (mHasFetcher)
+	{
+		LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
+		mHasFetcher = FALSE;
+		mIsFetching = FALSE ;
+		resetTextureStats();
+	}
+}
+
 void LLViewerFetchedTexture::setIsMissingAsset()
 {
 	if (mUrl.empty())
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index b96441127dc5f9c49ccd62255230956e465b972a..41bf625225155f3973fa119791a723fef7e72dfc 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -139,6 +139,7 @@ class LLViewerTexture : public LLTexture
 		OTHER,
 		MAX_GL_IMAGE_CATEGORY
 	};
+
 	static S32 getTotalNumOfCategories() ;
 	static S32 getIndexFromCategory(S32 category) ;
 	static S32 getCategoryFromIndex(S32 index) ;
@@ -309,6 +310,7 @@ class LLViewerTexture : public LLTexture
 	} LLGLTextureState;
 	LLGLTextureState  mTextureState ;
 
+	static F32 sTexelPixelRatio;
 public:
 	static const U32 sCurrentFileVersion;	
 	static S32 sImageCount;
@@ -323,6 +325,7 @@ class LLViewerTexture : public LLTexture
 	static S32 sMaxTotalTextureMemInMegaBytes;
 	static S32 sMaxDesiredTextureMemInBytes ;
 	static S8  sCameraMovingDiscardBias;
+	static F32 sCameraMovingBias;
 	static S32 sMaxSculptRez ;
 	static S32 sMinLargeImageSize ;
 	static S32 sMaxSmallImageSize ;
@@ -330,8 +333,19 @@ class LLViewerTexture : public LLTexture
 	static F32  sCurrentTime ;
 	static BOOL sUseTextureAtlas ;
 
+	enum EDebugTexels
+	{
+		DEBUG_TEXELS_OFF,
+		DEBUG_TEXELS_CURRENT,
+		DEBUG_TEXELS_DESIRED,
+		DEBUG_TEXELS_FULL
+	};
+
+	static EDebugTexels sDebugTexelsMode;
+
 	static LLPointer<LLViewerTexture> sNullImagep; // Null texture for non-textured objects.
 	static LLPointer<LLViewerTexture> sBlackImagep;	// Texture to show NOTHING (pure black)
+	static LLPointer<LLViewerTexture> sCheckerBoardImagep;	// Texture to show NOTHING (pure black)
 };
 
 
@@ -420,6 +434,8 @@ class LLViewerFetchedTexture : public LLViewerTexture
 
 	bool updateFetch();
 	
+	void clearFetchedResults(); //clear all fetched results, for debug use.
+
 	// Override the computation of discard levels if we know the exact output
 	// size of the image.  Used for UI textures to not decode, even if we have
 	// more data.
@@ -478,6 +494,7 @@ class LLViewerFetchedTexture : public LLViewerTexture
 	BOOL        hasFetcher() const { return mHasFetcher;}
 	void        setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;}
 
+	void        forceToDeleteRequest();
 protected:
 	/*virtual*/ void switchToCachedImage();
 	S32 getCurrentDiscardLevelForFetching() ;
@@ -687,6 +704,7 @@ class LLViewerTextureManager
 	//"find-texture" just check if the texture exists, if yes, return it, otherwise return null.
 	//
 	static LLViewerTexture*           findTexture(const LLUUID& id) ;
+	static LLViewerFetchedTexture*    findFetchedTexture(const LLUUID& id) ;
 	static LLViewerMediaTexture*      findMediaTexture(const LLUUID& id) ;
 	
 	static LLViewerMediaTexture*      createMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 089f45ca899ae58eea2c7f36a0b4f81bd8d351ed..2008a884db1ebfcc84bb3ac327a94cc600f74fab 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -58,6 +58,7 @@
 #include "pipeline.h"
 #include "llappviewer.h"
 #include "llxuiparser.h"
+#include "llagent.h"
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -597,6 +598,12 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_STATS("Stats");
 
 void LLViewerTextureList::updateImages(F32 max_time)
 {
+	if(gAgent.getTeleportState() != LLAgent::TELEPORT_NONE)
+	{
+		clearFetchingRequests();
+		return;
+	}
+
 	LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec());
 
 	LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages);
@@ -659,6 +666,24 @@ void LLViewerTextureList::updateImages(F32 max_time)
 	}
 }
 
+void LLViewerTextureList::clearFetchingRequests()
+{
+	if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
+	{
+		return;
+	}
+
+	for (image_priority_list_t::iterator iter = mImageList.begin();
+		 iter != mImageList.end(); ++iter)
+	{
+		LLViewerFetchedTexture* image = *iter;
+		if(image->hasFetcher())
+		{
+			image->forceToDeleteRequest() ;
+		}
+	}
+}
+
 void LLViewerTextureList::updateImagesDecodePriorities()
 {
 	// Update the decode priority for N images each frame
@@ -1030,7 +1055,6 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage
 {
 	raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
 	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
-	compressedImage->setRate(0.f);
 	
 	if (gSavedSettings.getBOOL("LosslessJ2CUpload") &&
 		(raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF))
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index b386c73d2a5ef876229f504ca26f884a721ded4b..e89997fe283950870a3da2b4fe2481966f3325eb 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -110,6 +110,8 @@ class LLViewerTextureList
 	void doPreloadImages();
 	void doPrefetchImages();
 
+	void clearFetchingRequests();
+
 	static S32 getMinVideoRamSetting();
 	static S32 getMaxVideoRamSetting(bool get_recommended = false);
 	
@@ -164,7 +166,7 @@ class LLViewerTextureList
 	// Request image from a specific host, used for baked avatar textures.
 	// Implemented in header in case someone changes default params above. JC
 	LLViewerFetchedTexture* getImageFromHost(const LLUUID& image_id, LLHost host)
-	{ return getImage(image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host); }
+	{ return getImage(image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host); }	
 
 public:
 	typedef std::set<LLPointer<LLViewerFetchedTexture> > image_list_t;	
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
old mode 100644
new mode 100755
index a7a4281860da8b1e5de1f67397f70dda29a98227..eada77156e477aa9c4cc1640995537c8acaeeaa4
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -101,6 +101,8 @@
 #include "llvoicevisualizer.h" // Ventrella
 
 #include "lldebugmessagebox.h"
+#include "llsdutil.h"
+
 extern F32 SPEED_ADJUST_MAX;
 extern F32 SPEED_ADJUST_MAX_SEC;
 extern F32 ANIM_SPEED_MAX;
@@ -627,7 +629,6 @@ F32 LLVOAvatar::sLODFactor = 1.f;
 F32 LLVOAvatar::sPhysicsLODFactor = 1.f;
 BOOL LLVOAvatar::sUseImpostors = FALSE;
 BOOL LLVOAvatar::sJointDebug = FALSE;
-
 F32 LLVOAvatar::sUnbakedTime = 0.f;
 F32 LLVOAvatar::sUnbakedUpdateTime = 0.f;
 F32 LLVOAvatar::sGreyTime = 0.f;
@@ -683,13 +684,16 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mNeedsSkin(FALSE),
 	mLastSkinTime(0.f),
 	mUpdatePeriod(1),
+	mFirstFullyVisible(TRUE),
 	mFullyLoaded(FALSE),
 	mPreviousFullyLoaded(FALSE),
 	mFullyLoadedInitialized(FALSE),
 	mSupportsAlphaLayers(FALSE),
 	mLoadedCallbacksPaused(FALSE),
 	mHasPelvisOffset( FALSE ),
-	mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar"))
+	mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar")),
+	mLastRezzedStatus(-1)
+
 {
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 	//VTResume();  // VTune
@@ -770,32 +774,46 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mLastPelvisFixup = 0.0f;
 }
 
+std::string LLVOAvatar::avString() const
+{
+	std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
+	return " Avatar '" + getFullname() + "' " + viz_string + " ";
+}
+
+void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string comment)
+{
+	LL_INFOS("Avatar") << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32()
+					   << "sec ]"
+					   << avString() 
+					   << "RuthTimer " << (U32)mRuthDebugTimer.getElapsedTimeF32()
+					   << " Notification " << notification_name
+					   << " : " << comment
+					   << llendl;
+
+	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	{
+		LLSD args;
+		args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
+		args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
+		args["NAME"] = getFullname();
+		LLNotificationsUtil::add(notification_name,args);
+	}
+}
+
 //------------------------------------------------------------------------
 // LLVOAvatar::~LLVOAvatar()
 //------------------------------------------------------------------------
 LLVOAvatar::~LLVOAvatar()
 {
-	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	if (!mFullyLoaded)
 	{
-		if (!mFullyLoaded)
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left after " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds as cloud." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezLeftCloudNotification",args);
-		}
-		else
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezLeftNotification",args);
-		}
-
+		debugAvatarRezTime("AvatarRezLeftCloudNotification","left after ruth seconds as cloud");
 	}
+	else
+	{
+		debugAvatarRezTime("AvatarRezLeftNotification","left sometime after declouding");
+	}
+
 	lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl;
 
 	mRoot.removeAllChildren();
@@ -844,6 +862,8 @@ LLVOAvatar::~LLVOAvatar()
 	mAnimationSources.clear();
 	LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ;
 
+	getPhases().clearPhases();
+	
 	lldebugs << "LLVOAvatar Destructor end" << llendl;
 }
 
@@ -877,6 +897,55 @@ BOOL LLVOAvatar::isFullyBaked()
 	return TRUE;
 }
 
+BOOL LLVOAvatar::isFullyTextured() const
+{
+	for (S32 i = 0; i < mMeshLOD.size(); i++)
+	{
+		LLViewerJoint* joint = (LLViewerJoint*) mMeshLOD[i];
+		if (i==MESH_ID_SKIRT && !isWearingWearableType(LLWearableType::WT_SKIRT))
+		{
+			continue; // don't care about skirt textures if we're not wearing one.
+		}
+		if (!joint)
+		{
+			continue; // nonexistent LOD OK.
+		}
+		std::vector<LLViewerJointMesh*>::iterator meshIter = joint->mMeshParts.begin();
+		if (meshIter != joint->mMeshParts.end())
+		{
+			LLViewerJointMesh *mesh = (LLViewerJointMesh *) *meshIter;
+			if (!mesh)
+			{
+				continue; // nonexistent mesh OK
+			}
+			if (mesh->mTexture.notNull() && mesh->mTexture->hasGLTexture())
+			{
+				continue; // Mesh exists and has a baked texture.
+			}
+			if (mesh->mLayerSet && mesh->mLayerSet->hasComposite())
+			{
+				continue; // Mesh exists and has a composite texture.
+			}
+			// Fail
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+BOOL LLVOAvatar::hasGray() const
+{
+	return !getIsCloud() && !isFullyTextured();
+}
+
+S32 LLVOAvatar::getRezzedStatus() const
+{
+	if (getIsCloud()) return 0;
+	if (isFullyTextured()) return 2;
+	llassert(hasGray());
+	return 1; // gray
+}
+
 void LLVOAvatar::deleteLayerSetCaches(bool clearAll)
 {
 	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
@@ -923,6 +992,31 @@ BOOL LLVOAvatar::areAllNearbyInstancesBaked(S32& grey_avatars)
 	return res;
 }
 
+// static
+void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts)
+{
+	counts.clear();
+	counts.resize(3);
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		 iter != LLCharacter::sInstances.end(); ++iter)
+	{
+		LLVOAvatar* inst = (LLVOAvatar*) *iter;
+		if (!inst)
+			continue;
+		S32 rez_status = inst->getRezzedStatus();
+		counts[rez_status]++;
+	}
+}
+
+// static
+std::string LLVOAvatar::rezStatusToString(S32 rez_status)
+{
+	if (rez_status==0) return "cloud";
+	if (rez_status==1) return "gray";
+	if (rez_status==2) return "textured";
+	return "unknown";
+}
+
 // static
 void LLVOAvatar::dumpBakedStatus()
 {
@@ -2248,18 +2342,12 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys,
 	U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
 
 	// Print out arrival information once we have name of avatar.
-	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	if (has_name && getNVPair("FirstName"))
 	{
-		if (has_name && getNVPair("FirstName"))
-		{
-			mDebugExistenceTimer.reset();
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezArrivedNotification",args);
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' arrived." << llendl;
-		}
+		mDebugExistenceTimer.reset();
+		debugAvatarRezTime("AvatarRezArrivedNotification","avatar arrived");
 	}
+
 	if(retval & LLViewerObject::INVALID_UPDATE)
 	{
 		if (isSelf())
@@ -2765,16 +2853,16 @@ void LLVOAvatar::idleUpdateLoadingEffect()
 	// update visibility when avatar is partially loaded
 	if (updateIsFullyLoaded()) // changed?
 	{
-		if (isFullyLoaded() && isSelf())
+		if (isFullyLoaded() && mFirstFullyVisible && isSelf())
 		{
-			static bool first_fully_visible = true;
-			if (first_fully_visible)
-			{
-				llinfos << "self isFullyLoaded, first_fully_visible" << llendl;
-
-				first_fully_visible = false;
-				LLAppearanceMgr::instance().onFirstFullyVisible();
-			}
+			LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL;
+			mFirstFullyVisible = FALSE;
+			LLAppearanceMgr::instance().onFirstFullyVisible();
+		}
+		if (isFullyLoaded() && mFirstFullyVisible && !isSelf())
+		{
+			LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL;
+			mFirstFullyVisible = FALSE;
 		}
 		if (isFullyLoaded())
 		{
@@ -2918,43 +3006,43 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 		return;
 	}
 
-		BOOL new_name = FALSE;
-		if (visible_chat != mVisibleChat)
+	BOOL new_name = FALSE;
+	if (visible_chat != mVisibleChat)
+	{
+		mVisibleChat = visible_chat;
+		new_name = TRUE;
+	}
+		
+	if (sRenderGroupTitles != mRenderGroupTitles)
+	{
+		mRenderGroupTitles = sRenderGroupTitles;
+		new_name = TRUE;
+	}
+
+	// First Calculate Alpha
+	// If alpha > 0, create mNameText if necessary, otherwise delete it
+	F32 alpha = 0.f;
+	if (mAppAngle > 5.f)
+	{
+		const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION;
+		if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME)
 		{
-			mVisibleChat = visible_chat;
-			new_name = TRUE;
+			alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION;
 		}
-		
-		if (sRenderGroupTitles != mRenderGroupTitles)
+		else
 		{
-			mRenderGroupTitles = sRenderGroupTitles;
-			new_name = TRUE;
+			// ...not fading, full alpha
+			alpha = 1.f;
 		}
-
-		// First Calculate Alpha
-		// If alpha > 0, create mNameText if necessary, otherwise delete it
-			F32 alpha = 0.f;
-			if (mAppAngle > 5.f)
-			{
-				const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION;
-				if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME)
-				{
-					alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION;
-				}
-				else
-				{
-					// ...not fading, full alpha
-					alpha = 1.f;
-				}
-			}
-			else if (mAppAngle > 2.f)
-			{
-				// far away is faded out also
-				alpha = (mAppAngle-2.f)/3.f;
-			}
+	}
+	else if (mAppAngle > 2.f)
+	{
+		// far away is faded out also
+		alpha = (mAppAngle-2.f)/3.f;
+	}
 
 	if (alpha <= 0.f)
-			{
+	{
 		if (mNameText)
 		{
 			mNameText->markDead();
@@ -2964,19 +3052,19 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 		return;
 	}
 
-				if (!mNameText)
-				{
+	if (!mNameText)
+	{
 		mNameText = static_cast<LLHUDNameTag*>( LLHUDObject::addHUDObject(
-			LLHUDObject::LL_HUD_NAME_TAG) );
+													LLHUDObject::LL_HUD_NAME_TAG) );
 		//mNameText->setMass(10.f);
-					mNameText->setSourceObject(this);
+		mNameText->setSourceObject(this);
 		mNameText->setVertAlignment(LLHUDNameTag::ALIGN_VERT_TOP);
-					mNameText->setVisibleOffScreen(TRUE);
-					mNameText->setMaxLines(11);
-					mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
-					sNumVisibleChatBubbles++;
-					new_name = TRUE;
-				}
+		mNameText->setVisibleOffScreen(TRUE);
+		mNameText->setMaxLines(11);
+		mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+		sNumVisibleChatBubbles++;
+		new_name = TRUE;
+	}
 				
 	LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last);
 	mNameText->setPositionAgent(name_position);				
@@ -2985,10 +3073,10 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 }
 
 void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
-			{
-		LLNameValue *title = getNVPair("Title");
-		LLNameValue* firstname = getNVPair("FirstName");
-		LLNameValue* lastname = getNVPair("LastName");
+{
+	LLNameValue *title = getNVPair("Title");
+	LLNameValue* firstname = getNVPair("FirstName");
+	LLNameValue* lastname = getNVPair("LastName");
 
 	// Avatars must have a first and last name
 	if (!firstname || !lastname) return;
@@ -3002,34 +3090,23 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
 		is_muted = false;
 	}
 	else
-		{
+	{
 		is_muted = LLMuteList::getInstance()->isMuted(getID());
 	}
 	bool is_friend = LLAvatarTracker::instance().isBuddy(getID());
 	bool is_cloud = getIsCloud();
 
-			if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
-			{
-				if (is_appearance != mNameAppearance)
-				{
-					if (is_appearance)
-					{
-						LLSD args;
-						args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-						args["NAME"] = getFullname();
-						LLNotificationsUtil::add("AvatarRezEnteredAppearanceNotification",args);
-						llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' entered appearance mode." << llendl;
-					}
-					else
-					{
-						LLSD args;
-						args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-						args["NAME"] = getFullname();
-						LLNotificationsUtil::add("AvatarRezLeftAppearanceNotification",args);
-						llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left appearance mode." << llendl;
-					}
-				}
-			}
+	if (is_appearance != mNameAppearance)
+	{
+		if (is_appearance)
+		{
+			debugAvatarRezTime("AvatarRezEnteredAppearanceNotification","entered appearance mode");
+		}
+		else
+		{
+			debugAvatarRezTime("AvatarRezLeftAppearanceNotification","left appearance mode");
+		}
+	}
 
 	// Rebuild name tag if state change detected
 	if (mNameString.empty()
@@ -3039,56 +3116,56 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
 		|| is_away != mNameAway 
 		|| is_busy != mNameBusy 
 		|| is_muted != mNameMute
-				|| is_appearance != mNameAppearance 
+		|| is_appearance != mNameAppearance 
 		|| is_friend != mNameFriend
 		|| is_cloud != mNameCloud)
-				{
+	{
 		LLColor4 name_tag_color = getNameTagColor(is_friend);
 
 		clearNameTag();
 
 		if (is_away || is_muted || is_busy || is_appearance)
-				{
+		{
 			std::string line;
-					if (is_away)
-					{
-						line += LLTrans::getString("AvatarAway");
+			if (is_away)
+			{
+				line += LLTrans::getString("AvatarAway");
 				line += ", ";
-					}
-					if (is_busy)
-					{
+			}
+			if (is_busy)
+			{
 				line += LLTrans::getString("AvatarBusy");
 				line += ", ";
 			}
 			if (is_muted)
-						{
+			{
 				line += LLTrans::getString("AvatarMuted");
-							line += ", ";
-						}
+				line += ", ";
+			}
 			if (is_appearance)
 			{
 				line += LLTrans::getString("AvatarEditingAppearance");
 				line += ", ";
-					}
+			}
 			if (is_cloud)
-					{
+			{
 				line += LLTrans::getString("LoadingData");
 				line += ", ";
 			}
 			// trim last ", "
 			line.resize( line.length() - 2 );
 			addNameTagLine(line, name_tag_color, LLFontGL::NORMAL,
-				LLFontGL::getFontSansSerifSmall());
+						   LLFontGL::getFontSansSerifSmall());
 		}
 
 		if (sRenderGroupTitles
 			&& title && title->getString() && title->getString()[0] != '\0')
-						{
+		{
 			std::string title_str = title->getString();
 			LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR);
 			addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL,
-				LLFontGL::getFontSansSerifSmall());
-						}
+						   LLFontGL::getFontSansSerifSmall());
+		}
 
 		static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames");
 		static LLUICachedControl<bool> show_usernames("NameTagShowUsernames");
@@ -3101,120 +3178,120 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
 				// ...call this function back when the name arrives
 				// and force a rebuild
 				LLAvatarNameCache::get(getID(),
-					boost::bind(&LLVOAvatar::clearNameTag, this));
-					}
+									   boost::bind(&LLVOAvatar::clearNameTag, this));
+			}
 
 			// Might be blank if name not available yet, that's OK
 			if (show_display_names)
 			{
 				addNameTagLine(av_name.mDisplayName, name_tag_color, LLFontGL::NORMAL,
-					LLFontGL::getFontSansSerif());
-				}
+							   LLFontGL::getFontSansSerif());
+			}
 			// Suppress SLID display if display name matches exactly (ugh)
 			if (show_usernames && !av_name.mIsDisplayNameDefault)
-				{
+			{
 				// *HACK: Desaturate the color
 				LLColor4 username_color = name_tag_color * 0.83f;
 				addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL,
-					LLFontGL::getFontSansSerifSmall());
+							   LLFontGL::getFontSansSerifSmall());
 			}
-				}
+		}
 		else
-				{
+		{
 			const LLFontGL* font = LLFontGL::getFontSansSerif();
 			std::string full_name =
 				LLCacheName::buildFullName( firstname->getString(), lastname->getString() );
 			addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font);
-				}
+		}
 
-				mNameAway = is_away;
-				mNameBusy = is_busy;
-				mNameMute = is_muted;
-				mNameAppearance = is_appearance;
+		mNameAway = is_away;
+		mNameBusy = is_busy;
+		mNameMute = is_muted;
+		mNameAppearance = is_appearance;
 		mNameFriend = is_friend;
-				mNameCloud = is_cloud;
-				mTitle = title ? title->getString() : "";
-				LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR);
-				new_name = TRUE;
-			}
+		mNameCloud = is_cloud;
+		mTitle = title ? title->getString() : "";
+		LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR);
+		new_name = TRUE;
+	}
 
 	if (mVisibleChat)
-			{
-				mNameText->setFont(LLFontGL::getFontSansSerif());
+	{
+		mNameText->setFont(LLFontGL::getFontSansSerif());
 		mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_LEFT);
-				mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f);
+		mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f);
 			
-				char line[MAX_STRING];		/* Flawfinder: ignore */
-				line[0] = '\0';
-				std::deque<LLChat>::iterator chat_iter = mChats.begin();
-				mNameText->clearString();
-
-				LLColor4 new_chat = LLUIColorTable::instance().getColor( isSelf() ? "UserChatColor" : "AgentChatColor" );
-				LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f);
-				LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f);
-				if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) 
-				{
-					++chat_iter;
-				}
+		char line[MAX_STRING];		/* Flawfinder: ignore */
+		line[0] = '\0';
+		std::deque<LLChat>::iterator chat_iter = mChats.begin();
+		mNameText->clearString();
 
-				for(; chat_iter != mChats.end(); ++chat_iter)
-				{
-					F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f);
-					LLFontGL::StyleFlags style;
-					switch(chat_iter->mChatType)
-					{
-						case CHAT_TYPE_WHISPER:
-							style = LLFontGL::ITALIC;
-							break;
-						case CHAT_TYPE_SHOUT:
-							style = LLFontGL::BOLD;
-							break;
-						default:
-							style = LLFontGL::NORMAL;
-							break;
-					}
-					if (chat_fade_amt < 1.f)
-					{
-						F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f);
-						mNameText->addLine(chat_iter->mText, lerp(new_chat, normal_chat, u), style);
-					}
-					else if (chat_fade_amt < 2.f)
-					{
-						F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f);
-						mNameText->addLine(chat_iter->mText, lerp(normal_chat, old_chat, u), style);
-					}
-					else if (chat_fade_amt < 3.f)
-					{
-						// *NOTE: only remove lines down to minimum number
-						mNameText->addLine(chat_iter->mText, old_chat, style);
-					}
-				}
-				mNameText->setVisibleOffScreen(TRUE);
-
-				if (mTyping)
-				{
-					S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1;
-					switch(dot_count)
-					{
-						case 1:
-							mNameText->addLine(".", new_chat);
-							break;
-						case 2:
-							mNameText->addLine("..", new_chat);
-							break;
-						case 3:
-							mNameText->addLine("...", new_chat);
-							break;
-					}
+		LLColor4 new_chat = LLUIColorTable::instance().getColor( isSelf() ? "UserChatColor" : "AgentChatColor" );
+		LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f);
+		LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f);
+		if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) 
+		{
+			++chat_iter;
+		}
 
-				}
+		for(; chat_iter != mChats.end(); ++chat_iter)
+		{
+			F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f);
+			LLFontGL::StyleFlags style;
+			switch(chat_iter->mChatType)
+			{
+				case CHAT_TYPE_WHISPER:
+				style = LLFontGL::ITALIC;
+				break;
+				case CHAT_TYPE_SHOUT:
+				style = LLFontGL::BOLD;
+				break;
+				default:
+				style = LLFontGL::NORMAL;
+				break;
 			}
-			else
+			if (chat_fade_amt < 1.f)
+			{
+				F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f);
+				mNameText->addLine(chat_iter->mText, lerp(new_chat, normal_chat, u), style);
+			}
+			else if (chat_fade_amt < 2.f)
+			{
+				F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f);
+				mNameText->addLine(chat_iter->mText, lerp(normal_chat, old_chat, u), style);
+			}
+			else if (chat_fade_amt < 3.f)
+			{
+				// *NOTE: only remove lines down to minimum number
+				mNameText->addLine(chat_iter->mText, old_chat, style);
+			}
+		}
+		mNameText->setVisibleOffScreen(TRUE);
+
+		if (mTyping)
+		{
+			S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1;
+			switch(dot_count)
 			{
+				case 1:
+				mNameText->addLine(".", new_chat);
+				break;
+				case 2:
+				mNameText->addLine("..", new_chat);
+				break;
+				case 3:
+				mNameText->addLine("...", new_chat);
+				break;
+			}
+
+		}
+	}
+	else
+	{
 		// ...not using chat bubbles, just names
 		mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_CENTER);
-				mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
-				mNameText->setVisibleOffScreen(FALSE);
+		mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+		mNameText->setVisibleOffScreen(FALSE);
 	}
 }
 
@@ -3237,8 +3314,8 @@ void LLVOAvatar::clearNameTag()
 {
 	mNameString.clear();
 	if (mNameText)
-				{
-					mNameText->setLabel("");
+	{
+		mNameText->setLabel("");
 		mNameText->setString( "" );
 	}
 }
@@ -3964,7 +4041,7 @@ void LLVOAvatar::updateVisibility()
 			LLNameValue* firstname = getNVPair("FirstName");
 			if (firstname)
 			{
-				llinfos << "Avatar " << firstname->getString() << " updating visiblity" << llendl;
+				LL_DEBUGS("Avatar") << avString() << " updating visibility" << LL_ENDL;
 			}
 			else
 			{
@@ -4130,11 +4207,11 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
 		if (needs_rebuild || mDirtyMesh >= 2 || mVisibilityRank <= 4)
 		{
-		updateMeshData();
+			updateMeshData();
 			mDirtyMesh = 0;
-		mNeedsSkin = TRUE;
-		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
-	}
+			mNeedsSkin = TRUE;
+			mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+		}
 	}
 
 	if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) <= 0)
@@ -4176,7 +4253,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 		LLNameValue* firstname = getNVPair("FirstName");
 		if (firstname)
 		{
-			llinfos << "Avatar " << firstname->getString() << " in render" << llendl;
+			LL_DEBUGS("Avatar") << avString() << " in render" << LL_ENDL;
 		}
 		else
 		{
@@ -6393,10 +6470,10 @@ BOOL LLVOAvatar::isVisible() const
 }
 
 // Determine if we have enough avatar data to render
-BOOL LLVOAvatar::getIsCloud()
+BOOL LLVOAvatar::getIsCloud() const
 {
 	// Do we have a shape?
-	if (visualParamWeightsAreDefault())
+	if ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault())
 	{
 		return TRUE;
 	}
@@ -6415,11 +6492,53 @@ BOOL LLVOAvatar::getIsCloud()
 	return FALSE;
 }
 
+void LLVOAvatar::updateRezzedStatusTimers()
+{
+	// State machine for rezzed status. Statuses are 0 = cloud, 1 = gray, 2 = textured.
+	// Purpose is to collect time data for each period of cloud or cloud+gray.
+	S32 rez_status = getRezzedStatus();
+	if (rez_status != mLastRezzedStatus)
+	{
+		LL_DEBUGS("Avatar") << avString() << "rez state change: " << mLastRezzedStatus << " -> " << rez_status << LL_ENDL;
+		bool is_cloud_or_gray = (rez_status==0 || rez_status==1);
+		bool was_cloud_or_gray = (mLastRezzedStatus==0 || mLastRezzedStatus==1);
+		bool is_cloud = (rez_status==0);
+		bool was_cloud = (mLastRezzedStatus==0);
+
+		// Non-cloud to cloud
+		if (is_cloud && !was_cloud)
+		{
+			// start cloud timer.
+			getPhases().startPhase("cloud");
+		}
+		else if (was_cloud && !is_cloud)
+		{
+			// stop cloud timer, which will capture stats.
+			getPhases().stopPhase("cloud");
+		}
+
+		// Non-cloud-or-gray to cloud-or-gray
+		if (is_cloud_or_gray && !was_cloud_or_gray)
+		{
+			// start cloud-or-gray timer.
+			getPhases().startPhase("cloud-or-gray");
+		}
+		else if (was_cloud_or_gray && !is_cloud_or_gray)
+		{
+			// stop cloud-or-gray timer, which will capture stats.
+			getPhases().stopPhase("cloud-or-gray");
+		}
+		
+		mLastRezzedStatus = rez_status;
+	}
+}
+
 // call periodically to keep isFullyLoaded up to date.
 // returns true if the value has changed.
 BOOL LLVOAvatar::updateIsFullyLoaded()
 {
 	const BOOL loading = getIsCloud();
+	updateRezzedStatusTimers();
 	updateRuthTimer(loading);
 	return processFullyLoadedChange(loading);
 }
@@ -6434,27 +6553,19 @@ void LLVOAvatar::updateRuthTimer(bool loading)
 	if (mPreviousFullyLoaded)
 	{
 		mRuthTimer.reset();
-		if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' became cloud." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezCloudNotification",args);
-		}
-		mRuthDebugTimer.reset();
+		debugAvatarRezTime("AvatarRezCloudNotification","became cloud");
 	}
 	
 	const F32 LOADING_TIMEOUT__SECONDS = 120.f;
 	if (mRuthTimer.getElapsedTimeF32() > LOADING_TIMEOUT__SECONDS)
 	{
-		llinfos << "Ruth Timer timeout: Missing texture data for '" << getFullname() << "' "
+		LL_DEBUGS("Avatar") << avString()
+				<< "Ruth Timer timeout: Missing texture data for '" << getFullname() << "' "
 				<< "( Params loaded : " << !visualParamWeightsAreDefault() << " ) "
 				<< "( Lower : " << isTextureDefined(TEX_LOWER_BAKED) << " ) "
 				<< "( Upper : " << isTextureDefined(TEX_UPPER_BAKED) << " ) "
 				<< "( Head : " << isTextureDefined(TEX_HEAD_BAKED) << " )."
-				<< llendl;
+				<< LL_ENDL;
 		
 		LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID());
 		mRuthTimer.reset();
@@ -6471,20 +6582,13 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading)
 	
 	mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > PAUSE);
 
-	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	if (!mPreviousFullyLoaded && !loading && mFullyLoaded)
 	{
-		if (!mPreviousFullyLoaded && !loading && mFullyLoaded)
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' resolved in " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezNotification",args);
-		}
+		debugAvatarRezTime("AvatarRezNotification","fully loaded");
 	}
 
 	// did our loading state "change" from last call?
+	// runway - why are we updating every 30 calls even if nothing has changed?
 	const S32 UPDATE_RATE = 30;
 	BOOL changed =
 		((mFullyLoaded != mPreviousFullyLoaded) ||         // if the value is different from the previous call
@@ -6924,7 +7028,7 @@ LLColor4 LLVOAvatar::getDummyColor()
 
 void LLVOAvatar::dumpAvatarTEs( const std::string& context ) const
 {	
-	llinfos << (isSelf() ? "Self: " : "Other: ") << context << llendl;
+	LL_DEBUGS("Avatar") << avString() << (isSelf() ? "Self: " : "Other: ") << context << LL_ENDL;
 	for (LLVOAvatarDictionary::Textures::const_iterator iter = LLVOAvatarDictionary::getInstance()->getTextures().begin();
 		 iter != LLVOAvatarDictionary::getInstance()->getTextures().end();
 		 ++iter)
@@ -6934,23 +7038,23 @@ void LLVOAvatar::dumpAvatarTEs( const std::string& context ) const
 		const LLViewerTexture* te_image = getImage(iter->first,0);
 		if( !te_image )
 		{
-			llinfos << "       " << texture_dict->mName << ": null ptr" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": null ptr" << LL_ENDL;
 		}
 		else if( te_image->getID().isNull() )
 		{
-			llinfos << "       " << texture_dict->mName << ": null UUID" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": null UUID" << LL_ENDL;
 		}
 		else if( te_image->getID() == IMG_DEFAULT )
 		{
-			llinfos << "       " << texture_dict->mName << ": IMG_DEFAULT" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": IMG_DEFAULT" << LL_ENDL;
 		}
 		else if( te_image->getID() == IMG_DEFAULT_AVATAR )
 		{
-			llinfos << "       " << texture_dict->mName << ": IMG_DEFAULT_AVATAR" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": IMG_DEFAULT_AVATAR" << LL_ENDL;
 		}
 		else
 		{
-			llinfos << "       " << texture_dict->mName << ": " << te_image->getID() << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": " << te_image->getID() << LL_ENDL;
 		}
 	}
 }
@@ -7081,6 +7185,7 @@ void LLVOAvatar::rebuildHUD()
 //-----------------------------------------------------------------------------
 void LLVOAvatar::onFirstTEMessageReceived()
 {
+	LL_INFOS("Avatar") << avString() << LL_ENDL;
 	if( !mFirstTEMessageReceived )
 	{
 		mFirstTEMessageReceived = TRUE;
@@ -7109,6 +7214,7 @@ void LLVOAvatar::onFirstTEMessageReceived()
 					image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, TRUE, new LLTextureMaskData( mID ), 
 						src_callback_list, paused);
 				}
+				LL_DEBUGS("Avatar") << avString() << "layer_baked, setting onInitialBakedTextureLoaded as callback" << LL_ENDL;
 				image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, FALSE, new LLUUID( mID ), 
 					src_callback_list, paused );
 			}
@@ -7167,14 +7273,16 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 
-//	llinfos << "processAvatarAppearance start " << mID << llendl;
 	BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived;
-
 	mFirstAppearanceMessageReceived = TRUE;
 
+	LL_INFOS("Avatar") << avString() << "processAvatarAppearance start " << mID
+			<< " first? " << is_first_appearance_message << " self? " << isSelf() << LL_ENDL;
+
+
 	if( isSelf() )
 	{
-		llwarns << "Received AvatarAppearance for self" << llendl;
+		llwarns << avString() << "Received AvatarAppearance for self" << llendl;
 		if( mFirstTEMessageReceived )
 		{
 //			llinfos << "processAvatarAppearance end  " << mID << llendl;
@@ -7202,7 +7310,10 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	}
 
 
-	if( !is_first_appearance_message )
+	// runway - was
+	// if (!is_first_appearance_message )
+	// which means it would be called on second appearance message - probably wrong.
+	if (is_first_appearance_message )
 	{
 		onFirstTEMessageReceived();
 	}
@@ -7223,6 +7334,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	bool drop_visual_params_debug = gSavedSettings.getBOOL("BlockSomeAvatarAppearanceVisualParams") && (ll_rand(2) == 0); // pretend that ~12% of AvatarAppearance messages arrived without a VisualParam block, for testing
 	if( num_blocks > 1 && !drop_visual_params_debug)
 	{
+		LL_DEBUGS("Avatar") << avString() << " handle visual params, num_blocks " << num_blocks << LL_ENDL;
 		BOOL params_changed = FALSE;
 		BOOL interp_params = FALSE;
 		
@@ -7295,6 +7407,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	else
 	{
 		// AvatarAppearance message arrived without visual params
+		LL_DEBUGS("Avatar") << avString() << "no visual params" << LL_ENDL;
 		if (drop_visual_params_debug)
 		{
 			llinfos << "Debug-faked lack of parameters on AvatarAppearance for object: "  << getID() << llendl;
@@ -7447,8 +7560,15 @@ void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerFetchedTexture
 // static
 void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
 {
+
+	
 	LLUUID *avatar_idp = (LLUUID *)userdata;
 	LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+	
+	if (selfp)
+	{
+		LL_DEBUGS("Avatar") << selfp->avString() << "discard_level " << discard_level << " success " << success << " final " << final << LL_ENDL;
+	}
 
 	if (!success && selfp)
 	{
@@ -7460,13 +7580,20 @@ void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerFetchedTextu
 	}
 }
 
-void LLVOAvatar::onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata)
+// Static
+void LLVOAvatar::onBakedTextureLoaded(BOOL success,
+									  LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src,
+									  S32 discard_level, BOOL final, void* userdata)
 {
 	//llinfos << "onBakedTextureLoaded: " << src_vi->getID() << llendl;
 
 	LLUUID id = src_vi->getID();
 	LLUUID *avatar_idp = (LLUUID *)userdata;
 	LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+	if (selfp)
+	{	
+		LL_DEBUGS("Avatar") << selfp->avString() << "discard_level " << discard_level << " success " << success << " final " << final << " id " << src_vi->getID() << LL_ENDL;
+	}
 
 	if (selfp && !success)
 	{
@@ -7488,6 +7615,8 @@ void LLVOAvatar::onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_
 // Called when baked texture is loaded and also when we start up with a baked texture
 void LLVOAvatar::useBakedTexture( const LLUUID& id )
 {
+
+	
 	/* if(id == head_baked->getID())
 		 mHeadBakedLoaded = TRUE;
 		 mLastHeadBakedID = id;
@@ -7498,6 +7627,7 @@ void LLVOAvatar::useBakedTexture( const LLUUID& id )
 		LLViewerTexture* image_baked = getImage( mBakedTextureDatas[i].mTextureIndex, 0 );
 		if (id == image_baked->getID())
 		{
+			LL_DEBUGS("Avatar") << avString() << " i " << i << " id " << id << LL_ENDL;
 			mBakedTextureDatas[i].mIsLoaded = true;
 			mBakedTextureDatas[i].mLastTextureIndex = id;
 			mBakedTextureDatas[i].mIsUsed = true;
@@ -7672,6 +7802,9 @@ void LLVOAvatar::cullAvatarsByPixelArea()
 		}
 	}
 
+	// runway - this doesn't detect gray/grey state.
+	// think we just need to be checking self av since it's the only
+	// one with lltexlayer stuff.
 	S32 grey_avatars = 0;
 	if (LLVOAvatar::areAllNearbyInstancesBaked(grey_avatars))
 	{
@@ -8475,7 +8608,9 @@ void LLVOAvatar::idleUpdateRenderCost()
 		}
 	}
 
-	setDebugText(llformat("%d", cost));
+	
+	std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
+	setDebugText(llformat("%s %d", viz_string.c_str(), cost));
 	mVisualComplexity = cost;
 	F32 green = 1.f-llclamp(((F32) cost-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
 	F32 red = llmin((F32) cost/(F32)ARC_LIMIT, 1.f);
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
old mode 100644
new mode 100755
index 6a4e09593c1dc22335efb0f65feb263089c63a2a..6fb56a4c0bb2bf70a0134746170612c59e665079
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -48,6 +48,7 @@
 #include "lltexglobalcolor.h"
 #include "lldriverparam.h"
 #include "material_codes.h"		// LL_MCODE_END
+#include "llviewerstats.h"
 
 extern const LLUUID ANIM_AGENT_BODY_NOISE;
 extern const LLUUID ANIM_AGENT_BREATHE_ROT;
@@ -277,14 +278,27 @@ class LLVOAvatar :
 public:
 	BOOL			isFullyLoaded() const;
 	bool			isTooComplex() const;
-	bool visualParamWeightsAreDefault();
+	bool 			visualParamWeightsAreDefault();
+	virtual BOOL	getIsCloud() const;
+	BOOL			isFullyTextured() const;
+	BOOL			hasGray() const; 
+	S32				getRezzedStatus() const; // 0 = cloud, 1 = gray, 2 = fully textured.
+	void			updateRezzedStatusTimers();
+
+	S32				mLastRezzedStatus;
+
+	LLViewerStats::PhaseMap& getPhases()
+	{
+		return mPhases;
+	}
+
 protected:
-	virtual BOOL	getIsCloud();
 	BOOL			updateIsFullyLoaded();
 	BOOL			processFullyLoadedChange(bool loading);
 	void			updateRuthTimer(bool loading);
 	F32 			calcMorphAmount();
 private:
+	BOOL			mFirstFullyVisible;
 	BOOL			mFullyLoaded;
 	BOOL			mPreviousFullyLoaded;
 	BOOL			mFullyLoadedInitialized;
@@ -292,6 +306,28 @@ class LLVOAvatar :
 	S32				mVisualComplexity;
 	LLFrameTimer	mFullyLoadedTimer;
 	LLFrameTimer	mRuthTimer;
+
+public:
+	class ScopedPhaseSetter
+	{
+	public:
+		ScopedPhaseSetter(LLVOAvatar *avatarp, std::string phase_name):
+			mAvatar(avatarp), mPhaseName(phase_name)
+		{
+			if (mAvatar) { mAvatar->getPhases().startPhase(mPhaseName); }
+		}
+		~ScopedPhaseSetter()
+		{
+			if (mAvatar) { mAvatar->getPhases().stopPhase(mPhaseName); }
+		}
+	private:
+		std::string mPhaseName;
+		LLVOAvatar* mAvatar;
+	};
+
+private:
+	LLViewerStats::PhaseMap mPhases;
+
 protected:
 	LLFrameTimer    mInvisibleTimer;
 	
@@ -518,9 +554,10 @@ class LLVOAvatar :
 	virtual BOOL	isTextureVisible(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
 	virtual BOOL	isTextureVisible(LLVOAvatarDefines::ETextureIndex type, LLWearable *wearable) const;
 
-protected:
 	BOOL			isFullyBaked();
 	static BOOL		areAllNearbyInstancesBaked(S32& grey_avatars);
+	static void		getNearbyRezzedStats(std::vector<S32>& counts);
+	static std::string rezStatusToString(S32 status);
 
 	//--------------------------------------------------------------------
 	// Baked textures
@@ -882,6 +919,7 @@ class LLVOAvatar :
 
 public:
 	std::string		getFullname() const; // Returns "FirstName LastName"
+	std::string		avString() const; // Frequently used string in log messages "Avatar '<full name'"
 protected:
 	static void		getAnimLabels(LLDynamicArray<std::string>* labels);
 	static void		getAnimNames(LLDynamicArray<std::string>* names);	
@@ -983,7 +1021,9 @@ class LLVOAvatar :
 	// Avatar Rez Metrics
 	//--------------------------------------------------------------------
 public:
+	void 			debugAvatarRezTime(std::string notification_name, std::string comment = "");
 	F32				debugGetExistenceTimeElapsedF32() const { return mDebugExistenceTimer.getElapsedTimeF32(); }
+
 protected:
 	LLFrameTimer	mRuthDebugTimer; // For tracking how long it takes for av to rez
 	LLFrameTimer	mDebugExistenceTimer; // Debugging for how long the avatar has been in memory.
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
old mode 100644
new mode 100755
index f063653cc5555ab84f3ee05f2317e5a1d00b67b0..d2609e5587226a5103b08d3deefc4491e3951506
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -58,6 +58,8 @@
 #include "llappearancemgr.h"
 #include "llmeshrepository.h"
 #include "llvovolume.h"
+#include "llsdutil.h"
+#include "llstartup.h"
 
 #if LL_MSVC
 // disable boost::lexical_cast warning
@@ -75,6 +77,39 @@ BOOL isAgentAvatarValid()
 			(!gAgentAvatarp->isDead()));
 }
 
+void selfStartPhase(const std::string& phase_name)
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().startPhase(phase_name);
+	}
+}
+
+void selfStopPhase(const std::string& phase_name)
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().stopPhase(phase_name);
+	}
+}
+
+void selfClearPhases()
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().clearPhases();
+		gAgentAvatarp->mLastRezzedStatus = -1;
+	}
+}
+
+void selfStopAllPhases()
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().stopAllPhases();
+	}
+}
+
 using namespace LLVOAvatarDefines;
 
 /*********************************************************************************
@@ -131,7 +166,8 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id,
 	LLVOAvatar(id, pcode, regionp),
 	mScreenp(NULL),
 	mLastRegionHandle(0),
-	mRegionCrossingCount(0)
+	mRegionCrossingCount(0),
+	mInitialBakesLoaded(false)
 {
 	gAgentWearables.setAvatarObject(this);
 
@@ -164,6 +200,7 @@ void LLVOAvatarSelf::initInstance()
 	{
 		mDebugBakedTextureTimes[i][0] = -1.0f;
 		mDebugBakedTextureTimes[i][1] = -1.0f;
+		mInitialBakeIDs[i] = LLUUID::null;
 	}
 
 	status &= buildMenus();
@@ -762,6 +799,41 @@ void LLVOAvatarSelf::stopMotionFromSource(const LLUUID& source_id)
 	}
 }
 
+//virtual
+U32  LLVOAvatarSelf::processUpdateMessage(LLMessageSystem *mesgsys,
+													 void **user_data,
+													 U32 block_num,
+													 const EObjectUpdateType update_type,
+													 LLDataPacker *dp)
+{
+	U32 retval = LLVOAvatar::processUpdateMessage(mesgsys,user_data,block_num,update_type,dp);
+
+	if (mInitialBakesLoaded == false && retval == 0x0)
+	{
+		// call update textures to force the images to be created
+		updateMeshTextures();
+
+		// unpack the texture UUIDs to the texture slots
+		retval = unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
+
+		// need to trigger a few operations to get the avatar to use the new bakes
+		for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
+		{
+			const LLVOAvatarDefines::ETextureIndex te = mBakedTextureDatas[i].mTextureIndex;
+			LLUUID texture_id = getTEImage(te)->getID();
+			setNewBakedTexture(te, texture_id);
+			mInitialBakeIDs[i] = texture_id;
+		}
+
+		onFirstTEMessageReceived();
+
+		mInitialBakesLoaded = true;
+	}
+
+	return retval;
+}
+
+
 void LLVOAvatarSelf::setLocalTextureTE(U8 te, LLViewerTexture* image, U32 index)
 {
 	if (te >= TEX_NUM_INDICES)
@@ -1889,7 +1961,7 @@ void LLVOAvatarSelf::dumpTotalLocalTextureByteCount()
 	llinfos << "Total Avatar LocTex GL:" << (gl_bytes/1024) << "KB" << llendl;
 }
 
-BOOL LLVOAvatarSelf::getIsCloud()
+BOOL LLVOAvatarSelf::getIsCloud() const
 {
 	// do we have our body parts?
 	if (gAgentWearables.getWearableCount(LLWearableType::WT_SHAPE) == 0 ||
@@ -2055,6 +2127,80 @@ const std::string LLVOAvatarSelf::debugDumpAllLocalTextureDataInfo() const
 	return text;
 }
 
+// Dump avatar metrics data.
+LLSD LLVOAvatarSelf::metricsData()
+{
+	// runway - add region info
+	LLSD result;
+	result["id"] = getID();
+	result["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus());
+	result["is_self"] = isSelf();
+	std::vector<S32> rez_counts;
+	LLVOAvatar::getNearbyRezzedStats(rez_counts);
+	result["nearby"] = LLSD::emptyMap();
+	for (S32 i=0; i<rez_counts.size(); ++i)
+	{
+		std::string rez_status_name = LLVOAvatar::rezStatusToString(i);
+		result["nearby"][rez_status_name] = rez_counts[i];
+	}
+	result["timers"]["debug_existence"] = mDebugExistenceTimer.getElapsedTimeF32();
+	result["timers"]["ruth_debug"] = mRuthDebugTimer.getElapsedTimeF32();
+	result["timers"]["ruth"] = mRuthTimer.getElapsedTimeF32();
+	result["timers"]["invisible"] = mInvisibleTimer.getElapsedTimeF32();
+	result["timers"]["fully_loaded"] = mFullyLoadedTimer.getElapsedTimeF32();
+	result["phases"] = getPhases().dumpPhases();
+	result["startup"] = LLStartUp::getPhases().dumpPhases();
+	
+	return result;
+}
+
+class ViewerAppearanceChangeMetricsResponder: public LLCurl::Responder
+{
+public:
+	ViewerAppearanceChangeMetricsResponder()
+	{
+	}
+
+	virtual void completed(U32 status,
+						   const std::string& reason,
+						   const LLSD& content)
+	{
+		if (isGoodStatus(status))
+		{
+			LL_DEBUGS("Avatar") << "OK" << LL_ENDL;
+			result(content);
+		}
+		else
+		{
+			LL_WARNS("Avatar") << "Failed " << status << " reason " << reason << LL_ENDL;
+			error(status,reason);
+		}
+	}
+};
+
+void LLVOAvatarSelf::sendAppearanceChangeMetrics()
+{
+	// gAgentAvatarp->stopAllPhases();
+
+	LLSD msg = metricsData();
+	msg["message"] = "ViewerAppearanceChangeMetrics";
+
+	LL_DEBUGS("Avatar") << avString() << "message: " << ll_pretty_print_sd(msg) << LL_ENDL;
+	std::string	caps_url;
+	if (getRegion())
+	{
+		// runway - change here to activate.
+		caps_url = getRegion()->getCapability("ViewerMetrics");
+	}
+	if (!caps_url.empty())
+	{
+		LLCurlRequest::headers_t headers;
+		LLHTTPClient::post(caps_url,
+							msg,
+							new ViewerAppearanceChangeMetricsResponder);
+	}
+}
+
 const LLUUID& LLVOAvatarSelf::grabBakedTexture(EBakedTextureIndex baked_index) const
 {
 	if (canGrabBakedTexture(baked_index))
@@ -2253,11 +2399,25 @@ void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
 			if (isAllLocalTextureDataFinal())
 			{
 				LLNotificationsUtil::add("AvatarRezSelfBakedDoneNotification",args);
+				LL_DEBUGS("Avatar") << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32()
+						<< "sec ]"
+						<< avString() 
+						<< "RuthTimer " << (U32)mRuthDebugTimer.getElapsedTimeF32()
+						<< " SelfLoadTimer " << (U32)mDebugSelfLoadTimer.getElapsedTimeF32()
+						<< " Notification " << "AvatarRezSelfBakedDoneNotification"
+						<< llendl;
 			}
 			else
 			{
 				args["STATUS"] = debugDumpAllLocalTextureDataInfo();
 				LLNotificationsUtil::add("AvatarRezSelfBakedUpdateNotification",args);
+				LL_DEBUGS("Avatar") << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32()
+						<< "sec ]"
+						<< avString() 
+						<< "RuthTimer " << (U32)mRuthDebugTimer.getElapsedTimeF32()
+						<< " SelfLoadTimer " << (U32)mDebugSelfLoadTimer.getElapsedTimeF32()
+						<< " Notification " << "AvatarRezSelfBakedUpdateNotification"
+						<< llendl;
 			}
 		}
 
@@ -2265,7 +2425,7 @@ void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
 	}
 }
 
-// FIXME: This is never called. Something may be broken.
+// FIXME: This is not called consistently. Something may be broken.
 void LLVOAvatarSelf::outputRezDiagnostics() const
 {
 	if(!gSavedSettings.getBOOL("DebugAvatarLocalTexLoadedTime"))
@@ -2274,11 +2434,11 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
 	}
 
 	const F32 final_time = mDebugSelfLoadTimer.getElapsedTimeF32();
-	llinfos << "REZTIME: Myself rez stats:" << llendl;
-	llinfos << "\t Time from avatar creation to load wearables: " << (S32)mDebugTimeWearablesLoaded << llendl;
-	llinfos << "\t Time from avatar creation to de-cloud: " << (S32)mDebugTimeAvatarVisible << llendl;
-	llinfos << "\t Time from avatar creation to de-cloud for others: " << (S32)final_time << llendl;
-	llinfos << "\t Load time for each texture: " << llendl;
+	LL_DEBUGS("Avatar") << "REZTIME: Myself rez stats:" << llendl;
+	LL_DEBUGS("Avatar") << "\t Time from avatar creation to load wearables: " << (S32)mDebugTimeWearablesLoaded << llendl;
+	LL_DEBUGS("Avatar") << "\t Time from avatar creation to de-cloud: " << (S32)mDebugTimeAvatarVisible << llendl;
+	LL_DEBUGS("Avatar") << "\t Time from avatar creation to de-cloud for others: " << (S32)final_time << llendl;
+	LL_DEBUGS("Avatar") << "\t Load time for each texture: " << llendl;
 	for (U32 i = 0; i < LLVOAvatarDefines::TEX_NUM_INDICES; ++i)
 	{
 		std::stringstream out;
@@ -2302,12 +2462,14 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
 
 		// Don't print out non-existent textures.
 		if (j != 0)
-			llinfos << out.str() << llendl;
+		{
+			LL_DEBUGS("Avatar") << out.str() << LL_ENDL;
+		}
 	}
-	llinfos << "\t Time points for each upload (start / finish)" << llendl;
+	LL_DEBUGS("Avatar") << "\t Time points for each upload (start / finish)" << llendl;
 	for (U32 i = 0; i < LLVOAvatarDefines::BAKED_NUM_INDICES; ++i)
 	{
-		llinfos << "\t\t (" << i << ") \t" << (S32)mDebugBakedTextureTimes[i][0] << " / " << (S32)mDebugBakedTextureTimes[i][1] << llendl;
+		LL_DEBUGS("Avatar") << "\t\t (" << i << ") \t" << (S32)mDebugBakedTextureTimes[i][0] << " / " << (S32)mDebugBakedTextureTimes[i][1] << llendl;
 	}
 
 	for (LLVOAvatarDefines::LLVOAvatarDictionary::BakedTextures::const_iterator baked_iter = LLVOAvatarDefines::LLVOAvatarDictionary::getInstance()->getBakedTextures().begin();
@@ -2319,15 +2481,16 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
 		if (!layerset) continue;
 		const LLTexLayerSetBuffer *layerset_buffer = layerset->getComposite();
 		if (!layerset_buffer) continue;
-		llinfos << layerset_buffer->dumpTextureInfo() << llendl;
+		LL_DEBUGS("Avatar") << layerset_buffer->dumpTextureInfo() << llendl;
 	}
 }
 
 void LLVOAvatarSelf::outputRezTiming(const std::string& msg) const
 {
-	LL_DEBUGS("Avatar Rez")
+	LL_INFOS("Avatar")
+		<< avString()
 		<< llformat("%s. Time from avatar creation: %.2f", msg.c_str(), mDebugSelfLoadTimer.getElapsedTimeF32())
-		<< llendl;
+		<< LL_ENDL;
 }
 
 void LLVOAvatarSelf::reportAvatarRezTime() const
@@ -2351,6 +2514,18 @@ void LLVOAvatarSelf::setCachedBakedTexture( ETextureIndex te, const LLUUID& uuid
 	{
 		if ( mBakedTextureDatas[i].mTextureIndex == te && mBakedTextureDatas[i].mTexLayerSet)
 		{
+			if (mInitialBakeIDs[i] != LLUUID::null)
+			{
+				if (mInitialBakeIDs[i] == uuid)
+				{
+					llinfos << "baked texture correctly loaded at login! " << i << llendl;
+				}
+				else
+				{
+					llwarns << "baked texture does not match id loaded at login!" << i << llendl;
+				}
+				mInitialBakeIDs[i] = LLUUID::null;
+			}
 			mBakedTextureDatas[i].mTexLayerSet->cancelUpload();
 		}
 	}
@@ -2478,6 +2653,20 @@ LLTexLayerSet* LLVOAvatarSelf::getLayerSet(ETextureIndex index) const
 	return NULL;
 }
 
+LLTexLayerSet* LLVOAvatarSelf::getLayerSet(EBakedTextureIndex baked_index) const
+{
+       /* switch(index)
+               case TEX_HEAD_BAKED:
+               case TEX_HEAD_BODYPAINT:
+                       return mHeadLayerSet; */
+       if (baked_index >= 0 && baked_index < BAKED_NUM_INDICES)
+       {
+                       return mBakedTextureDatas[baked_index].mTexLayerSet;
+       }
+       return NULL;
+}
+
+
 // static
 void LLVOAvatarSelf::onCustomizeStart()
 {
@@ -2558,49 +2747,6 @@ BOOL LLVOAvatarSelf::needsRenderBeam()
 // static
 void LLVOAvatarSelf::deleteScratchTextures()
 {
-	if(gAuditTexture)
-	{
-		S32 total_tex_size = sScratchTexBytes ;
-		S32 tex_size = SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT ;
-
-		if( sScratchTexNames.checkData( GL_LUMINANCE ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_ALPHA ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_COLOR_INDEX ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= tex_size ;
-		}
-		if( sScratchTexNames.checkData( LLRender::sGLCoreProfile ? GL_RG : GL_LUMINANCE_ALPHA ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 2, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 2 * tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_RGB ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 3, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 3 * tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_RGBA ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 4 * tex_size ;
-		}
-		//others
-		while(total_tex_size > 0)
-		{
-			LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 4 * tex_size ;
-		}
-	}
-
 	for( LLGLuint* namep = sScratchTexNames.getFirstData(); 
 		 namep; 
 		 namep = sScratchTexNames.getNextData() )
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
old mode 100644
new mode 100755
index 655fb3a012a03832aee569f8456738c17ee495e6..543891ca632366a759fdcaa83b12e60449c330b7
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -93,15 +93,27 @@ class LLVOAvatarSelf :
 	/*virtual*/ void updateVisualParams();
 	/*virtual*/ void idleUpdateAppearanceAnimation();
 
+	/*virtual*/ U32  processUpdateMessage(LLMessageSystem *mesgsys,
+													 void **user_data,
+													 U32 block_num,
+													 const EObjectUpdateType update_type,
+													 LLDataPacker *dp);
+
 private:
 	// helper function. Passed in param is assumed to be in avatar's parameter list.
 	BOOL setParamWeight(LLViewerVisualParam *param, F32 weight, BOOL upload_bake = FALSE );
 
 
+
 /**                    Initialization
  **                                                                            **
  *******************************************************************************/
 
+private:
+	LLUUID mInitialBakeIDs[6];
+	bool mInitialBakesLoaded;
+
+
 /********************************************************************************
  **                                                                            **
  **                    STATE
@@ -121,7 +133,7 @@ class LLVOAvatarSelf :
 	// Loading state
 	//--------------------------------------------------------------------
 public:
-	/*virtual*/ BOOL    getIsCloud();
+	/*virtual*/ BOOL    getIsCloud() const;
 
 	//--------------------------------------------------------------------
 	// Region state
@@ -229,6 +241,7 @@ class LLVOAvatarSelf :
 	void				requestLayerSetUpload(LLVOAvatarDefines::EBakedTextureIndex i);
 	void				requestLayerSetUpdate(LLVOAvatarDefines::ETextureIndex i);
 	LLTexLayerSet*		getLayerSet(LLVOAvatarDefines::ETextureIndex index) const;
+	LLTexLayerSet* 		getLayerSet(LLVOAvatarDefines::EBakedTextureIndex baked_index) const;
 	
 	//--------------------------------------------------------------------
 	// Composites
@@ -369,6 +382,8 @@ class LLVOAvatarSelf :
 	const LLTexLayerSet*  	debugGetLayerSet(LLVOAvatarDefines::EBakedTextureIndex index) const { return mBakedTextureDatas[index].mTexLayerSet; }
 	const std::string		debugDumpLocalTextureDataInfo(const LLTexLayerSet* layerset) const; // Lists out state of this particular baked texture layer
 	const std::string		debugDumpAllLocalTextureDataInfo() const; // Lists out which baked textures are at highest LOD
+	LLSD					metricsData();
+	void					sendAppearanceChangeMetrics(); // send data associated with completing a change.
 private:
 	LLFrameTimer    		mDebugSelfLoadTimer;
 	F32						mDebugTimeWearablesLoaded;
@@ -387,4 +402,9 @@ extern LLPointer<LLVOAvatarSelf> gAgentAvatarp;
 
 BOOL isAgentAvatarValid();
 
+void selfStartPhase(const std::string& phase_name);
+void selfStopPhase(const std::string& phase_name);
+void selfStopAllPhases();
+void selfClearPhases();
+
 #endif // LL_VO_AVATARSELF_H
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 201952876fd91d8e03619a3c0418363b4a021f47..f922e3b4f3c11bc1327da781ed74f9285b64049a 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -464,6 +464,7 @@ class LLPipeline
 		RENDER_DEBUG_LOD_INFO	        = 0x04000000,
 		RENDER_DEBUG_RENDER_COMPLEXITY  = 0x08000000,
 		RENDER_DEBUG_ATTACHMENT_BYTES	= 0x10000000,
+		RENDER_DEBUG_TEXEL_DENSITY		= 0x20000000
 	};
 
 public:
diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml
index 2fd932786bdf9e897bbcf0f06bfd740bd5aa751d..9400f7b94ffa3774c29f6a609f47f6a2b5687097 100644
--- a/indra/newview/skins/default/xui/en/floater_stats.xml
+++ b/indra/newview/skins/default/xui/en/floater_stats.xml
@@ -149,13 +149,52 @@
 				 show_per_sec="true"
 				 show_bar="false">
 			  </stat_bar>
+       <stat_bar
+				 name="object_cache_hits"
+				 label="Object Cache Hit Rate"
+				 stat="object_cache_hits"
+				 bar_min="0"
+				 bar_max="100"
+         unit_label="%"
+				 tick_spacing="20"
+				 label_spacing="20"
+				 show_history="true"
+         show_per_sec="false"
+				 show_bar="false">
+        </stat_bar>
 			</stat_view>
         <!--Texture Stats-->
 			<stat_view
 			   name="texture"
 			   label="Texture"
 			   show_label="true">
-			  <stat_bar
+        <stat_bar
+				 name="texture_cache_hits"
+				 label="Cache Hit Rate"
+				 stat="texture_cache_hits"
+				 bar_min="0.f"
+				 bar_max="100.f"
+         unit_label="%"
+				 tick_spacing="20"
+				 label_spacing="20"
+				 show_history="true"
+         show_per_sec="false"
+				 show_bar="false">
+        </stat_bar>
+        <stat_bar
+				 name="texture_cache_read_latency"
+				 label="Cache Read Latency"
+         unit_label="msec"
+				 stat="texture_cache_read_latency"
+				 bar_min="0.f"
+				 bar_max="1000.f"
+				 tick_spacing="100"
+				 label_spacing="200"
+				 show_history="true"
+         show_per_sec="false"
+				 show_bar="false">
+        </stat_bar>
+        <stat_bar
 				 name="numimagesstat"
 				 label="Count"
 				 stat="numimagesstat" 
diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
new file mode 100644
index 0000000000000000000000000000000000000000..44b6a63bcae5a98b07899f2cbdd4c27ac1b3b655
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
@@ -0,0 +1,341 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_minimize="false"
+ height="550"
+ layout="topleft"
+ name="TexFetchDebugger"
+ help_topic="texfetchdebugger"
+ title="Texture Fetching Debugger"
+ width="540">
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left="10"
+   name="total_num_fetched_label"
+   top="30"
+   width="400">
+    1, Total number of fetched textures: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_fetching_requests_label"
+   top_delta="25"
+   width="400">
+    2, Total number of fetching requests: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_cache_hits_label"
+   top_delta="25"
+   width="400">
+    3, Total number of cache hits: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_visible_tex_label"
+   top_delta="25"
+   width="400">
+    4, Total number of visible textures: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_visible_tex_fetch_req_label"
+   top_delta="25"
+   width="450">
+    5, Total number of visible texture fetching requests: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_data_label"
+   top_delta="25"
+   width="530">
+    6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_vis_data_label"
+   top_delta="25"
+   width="480">
+    7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_rendered_data_label"
+   top_delta="25"
+   width="530">
+    8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_cache_read_label"
+   top_delta="25"
+   width="400">
+    9, Total time on cache readings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_cache_write_label"
+   top_delta="25"
+   width="400">
+    10, Total time on cache writings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_decode_label"
+   top_delta="25"
+   width="400">
+    11, Total time on decodings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_gl_label"
+   top_delta="25"
+   width="400">
+    12, Total time on gl texture creation: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_http_label"
+   top_delta="25"
+   width="400">
+    13, Total time on HTTP fetching: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_fetch_label"
+   top_delta="25"
+   width="400">
+    14, Total time on entire fetching: [TIME] seconds
+  </text>
+  <text
+  type="string"
+  length="1"
+  follows="left|top"
+  height="25"
+  layout="topleft"
+  left_delta="0"
+  name="total_time_refetch_vis_cache_label"
+  top_delta="25"
+  width="540">
+    15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels
+  </text>
+  <text
+  type="string"
+  length="1"
+  follows="left|top"
+  height="25"
+  layout="topleft"
+  left_delta="0"
+  name="total_time_refetch_vis_http_label"
+  top_delta="25"
+  width="540">
+    16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels
+  </text>
+  <spinner
+     decimal_digits="2"
+     follows="left|top"
+     height="20"
+     increment="0.01"
+     initial_value="1.0"
+     label="17, Ratio of Texel/Pixel:"
+     label_width="130"
+     layout="topleft"
+     left_delta="0"
+     max_val="10.0"
+     min_val="0.01"
+     name="texel_pixel_ratio"
+     top_delta="30"
+     width="200">
+    <spinner.commit_callback
+		function="TexFetchDebugger.ChangeTexelPixelRatio" />
+  </spinner>
+  <button
+   follows="left|top"
+   height="20"
+   label="Start"
+   layout="topleft"
+   left_delta="0"
+   name="start_btn"
+   top_delta="30"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Start" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Reset"
+   layout="topleft"
+   left_pad="7"
+   name="clear_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Clear" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Close"
+   layout="topleft"
+   left_pad="7"
+   name="close_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Close" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Cache Read"
+   layout="topleft"
+   left="10"
+   name="cacheread_btn"
+   top_delta="30"
+   width="80">
+    <button.commit_callback
+		function="TexFetchDebugger.CacheRead" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Cache Write"
+   layout="topleft"
+   left_pad="7"
+   name="cachewrite_btn"
+   top_delta="0"
+   width="80">
+    <button.commit_callback
+		function="TexFetchDebugger.CacheWrite" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="HTTP"
+   layout="topleft"
+   left_pad="7"
+   name="http_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.HTTPLoad" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Decode"
+   layout="topleft"
+   left_pad="7"
+   name="decode_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Decode" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="GL Texture"
+   layout="topleft"
+   left_pad="7"
+   name="gl_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.GLTexture" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Refetch Vis Cache"
+   layout="topleft"
+   left="10"
+   name="refetchviscache_btn"
+   top_delta="30"
+   width="120">
+    <button.commit_callback
+		function="TexFetchDebugger.RefetchVisCache" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Refetch Vis HTTP"
+   layout="topleft"
+   left_pad="7"
+   name="refetchvishttp_btn"
+   top_delta="0"
+   width="120">
+    <button.commit_callback
+		function="TexFetchDebugger.RefetchVisHTTP" />
+  </button>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 5ba566b175e59a113a0abc154e62f0748e39f9fd..a6898c554fcb6a88654f18a8e13759ca1b51d807 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1876,7 +1876,7 @@
                 <menu_item_check.on_click
                  function="Advanced.ToggleConsole"
                  parameter="texture" />
-            </menu_item_check>
+            </menu_item_check>            
             <menu_item_check
              label="Debug Console"
              name="Debug Console"
@@ -1897,28 +1897,6 @@
                function="Floater.Toggle"
                parameter="notifications_console" />
             </menu_item_call>
-            <menu_item_check
-               label="Texture Size Console"
-               name="Texture Size"
-               shortcut="control|shift|6">
-              <menu_item_check.on_check
-               function="Advanced.CheckConsole"
-               parameter="texture size" />
-              <menu_item_check.on_click
-               function="Advanced.ToggleConsole"
-               parameter="texture size" />
-            </menu_item_check>
-            <menu_item_check
-               label="Texture Category Console"
-               name="Texture Category"
-               shortcut="control|shift|7">
-              <menu_item_check.on_check
-               function="Advanced.CheckConsole"
-               parameter="texture category" />
-              <menu_item_check.on_click
-               function="Advanced.ToggleConsole"
-               parameter="texture category" />
-            </menu_item_check>
             <menu_item_check
              label="Fast Timers"
              name="Fast Timers"
@@ -1953,7 +1931,20 @@
                function="Advanced.ToggleConsole"
                parameter="scene view" />
             </menu_item_check>
-
+            <menu_item_call
+              enabled="false"
+              visible="false"
+              label="Texture Fetch Debug Console"
+              name="Texture Fetch Debug Console">
+              <menu_item_call.on_click
+                function="Floater.Show"
+                parameter="tex_fetch_debugger" />
+              <on_enable
+                function="Develop.SetTexFetchDebugger" />
+              <on_visible
+                function="Develop.SetTexFetchDebugger" />
+            </menu_item_call>
+          
             <menu_item_separator/>
 
             <menu_item_call
@@ -2439,6 +2430,52 @@
            function="Advanced.ToggleInfoDisplay"
            parameter="sculpt" />
 		</menu_item_check>
+       <menu
+         create_jump_keys="true"
+         label="Texture Density"
+         name="Texture Density"
+         tear_off="true">
+          <menu_item_check
+           label="None"
+           name="None">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="none" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="none" />
+          </menu_item_check>
+          <menu_item_check
+           label="Current"
+           name="Current">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="current" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="current" />
+          </menu_item_check>
+          <menu_item_check
+           label="Desired"
+           name="Desired">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="desired" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="desired" />
+          </menu_item_check>
+          <menu_item_check
+           label="Full"
+           name="Full">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="full" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="full" />
+          </menu_item_check>
+        </menu>
       </menu>
         <menu
          create_jump_keys="true"
@@ -2605,16 +2642,6 @@
                  function="ToggleControl"
                  parameter="TextureLoadFullRes" />
             </menu_item_check>
-            <menu_item_check
-               label="Audit Textures"
-               name="Audit Textures">
-              <menu_item_check.on_check
-               function="CheckControl"
-               parameter="AuditTexture" />
-              <menu_item_check.on_click
-               function="ToggleControl"
-               parameter="AuditTexture" />
-            </menu_item_check>
             <menu_item_check
              label="Texture Atlas (experimental)"
              name="Texture Atlas">
diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp
old mode 100644
new mode 100755
index 3faddc13c1bcf47cf20d86e2060cd8d5a8f3260b..f8923b986814dcd4c80692859e06b6a08d6d7f34
--- a/indra/newview/tests/llviewerassetstats_test.cpp
+++ b/indra/newview/tests/llviewerassetstats_test.cpp
@@ -35,6 +35,31 @@
 #include "lluuid.h"
 #include "llsdutil.h"
 #include "llregionhandle.h"
+#include "../llvoavatar.h"
+
+void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts)
+{
+	counts.resize(3);
+	counts[0] = 0;
+	counts[1] = 0;
+	counts[2] = 1;
+}
+
+// static
+std::string LLVOAvatar::rezStatusToString(S32 rez_status)
+{
+	if (rez_status==0) return "cloud";
+	if (rez_status==1) return "gray";
+	if (rez_status==2) return "textured";
+	return "unknown";
+}
+
+// static
+LLViewerStats::StatsAccumulator& LLViewerStats::PhaseMap::getPhaseStats(const std::string& phase_name)
+{
+	static LLViewerStats::StatsAccumulator junk;
+	return junk;
+}
 
 static const char * all_keys[] = 
 {
@@ -104,18 +129,25 @@ is_single_key_map(const LLSD & sd, const std::string & key)
 {
 	return sd.isMap() && 1 == sd.size() && sd.has(key);
 }
-#endif
 
 static bool
 is_double_key_map(const LLSD & sd, const std::string & key1, const std::string & key2)
 {
 	return sd.isMap() && 2 == sd.size() && sd.has(key1) && sd.has(key2);
 }
+#endif
+
+static bool
+is_triple_key_map(const LLSD & sd, const std::string & key1, const std::string & key2, const std::string& key3)
+{
+	return sd.isMap() && 3 == sd.size() && sd.has(key1) && sd.has(key2) && sd.has(key3);
+}
+
 
 static bool
 is_no_stats_map(const LLSD & sd)
 {
-	return is_double_key_map(sd, "duration", "regions");
+	return is_triple_key_map(sd, "duration", "regions", "avatar");
 }
 
 static bool
@@ -226,7 +258,7 @@ namespace tut
 		// Once the region is set, we will get a response even with no data collection
 		it->setRegion(region1_handle);
 		sd_full = it->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd_full, "duration", "regions"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd_full, "duration", "regions", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd_full["regions"], region1_handle));
 		
 		LLSD sd = sd_full["regions"][0];
@@ -267,7 +299,7 @@ namespace tut
 		it->setRegion(region1_handle);
 		
 		LLSD sd = it->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd[0];
 		
@@ -292,7 +324,7 @@ namespace tut
 		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
 
 		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd["regions"][0];
 		
@@ -332,7 +364,7 @@ namespace tut
 		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
 		ensure("Other collector is empty", is_no_stats_map(sd));
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd["regions"][0];
 		
@@ -382,7 +414,7 @@ namespace tut
 
 		// std::cout << sd << std::endl;
 		
-		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		ensure("Correct double-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle));
 		LLSD sd1 = get_region(sd, region1_handle);
 		LLSD sd2 = get_region(sd, region2_handle);
@@ -405,7 +437,7 @@ namespace tut
 		// Reset leaves current region in place
 		gViewerAssetStatsMain->reset();
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
 		sd2 = sd["regions"][0];
 		
@@ -454,7 +486,7 @@ namespace tut
 
 		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
 
-		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		ensure("Correct double-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle));
 		LLSD sd1 = get_region(sd, region1_handle);
 		LLSD sd2 = get_region(sd, region2_handle);
@@ -477,7 +509,7 @@ namespace tut
 		// Reset leaves current region in place
 		gViewerAssetStatsMain->reset();
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
 		sd2 = get_region(sd, region2_handle);
 		ensure("Region2 is present in results", sd2.isMap());
@@ -523,7 +555,7 @@ namespace tut
 		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
 		ensure("Other collector is empty", is_no_stats_map(sd));
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = get_region(sd, region1_handle);
 		ensure("Region1 is present in results", sd.isMap());