diff -r f04a968c189c -r edc6d6b315e4 indra/newview/llassetuploadresponders.cpp
--- a/indra/newview/llassetuploadresponders.cpp	Tue Jun 14 15:54:03 2011 -0400
+++ b/indra/newview/llassetuploadresponders.cpp	Wed Jun 15 21:19:12 2011 +0300
@@ -446,13 +446,13 @@ void LLSendTexLayerResponder::uploadComp
 {
 	LLUUID item_id = mPostData["item_id"];
 
 	std::string result = content["state"];
 	LLUUID new_id = content["new_asset"];
 
-	llinfos << "result: " << result << "new_id:" << new_id << llendl;
+	llinfos << "result: " << result << " new_id: " << new_id << llendl;
 	if (result == "complete"
 		&& mBakedUploadData != NULL)
 	{	// Invoke 
 		LLTexLayerSetBuffer::onTextureUploadComplete(new_id, (void*) mBakedUploadData, 0, LL_EXSTAT_NONE);
 		mBakedUploadData = NULL;	// deleted in onTextureUploadComplete()
 	}
diff -r f04a968c189c -r edc6d6b315e4 indra/newview/llassetuploadresponders.h
--- a/indra/newview/llassetuploadresponders.h	Tue Jun 14 15:54:03 2011 -0400
+++ b/indra/newview/llassetuploadresponders.h	Wed Jun 15 21:19:12 2011 +0300
@@ -109,12 +109,13 @@ private:
 	Impl* mImpl;
 };
 
 struct LLBakedUploadData;
 class LLSendTexLayerResponder : public LLAssetUploadResponder
 {
+	LOG_CLASS(LLSendTexLayerResponder);
 public:
 	LLSendTexLayerResponder(const LLSD& post_data,
 							const LLUUID& vfile_id,
 							LLAssetType::EType asset_type,
 							LLBakedUploadData * baked_upload_data);
 
diff -r f04a968c189c -r edc6d6b315e4 indra/newview/lltexlayer.cpp
--- a/indra/newview/lltexlayer.cpp	Tue Jun 14 15:54:03 2011 -0400
+++ b/indra/newview/lltexlayer.cpp	Wed Jun 15 21:19:12 2011 +0300
@@ -48,12 +48,15 @@
 #include "llviewervisualparam.h"
 
 //#include "../tools/imdebug/imdebug.h"
 
 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
+
 class LLTexLayerInfo
 {
 	friend class LLTexLayer;
 	friend class LLTexLayerTemplate;
 	friend class LLTexLayerInterface;
 public:
@@ -90,17 +93,19 @@ private:
 
 //-----------------------------------------------------------------------------
 // LLBakedUploadData()
 //-----------------------------------------------------------------------------
 LLBakedUploadData::LLBakedUploadData(const LLVOAvatarSelf* avatar,
 									 LLTexLayerSet* layerset,
-									 const LLUUID& id) : 
+									 const LLUUID& id,
+									 bool highest_res) :
 	mAvatar(avatar),
 	mTexLayerSet(layerset),
 	mID(id),
-	mStartTime(LLFrameTimer::getTotalTime())		// Record starting time
+	mStartTime(LLFrameTimer::getTotalTime()),		// Record starting time
+	mIsHighestRes(highest_res)
 { 
 }
 
 //-----------------------------------------------------------------------------
 // LLTexLayerSetBuffer
 // The composite image that a LLTexLayerSet writes to.  Each LLTexLayerSet has one.
@@ -113,12 +118,13 @@ LLTexLayerSetBuffer::LLTexLayerSetBuffer
 										 S32 width, S32 height) :
 	// ORDER_LAST => must render these after the hints are created.
 	LLViewerDynamicTexture( width, height, 4, LLViewerDynamicTexture::ORDER_LAST, TRUE ), 
 	mUploadPending(FALSE), // Not used for any logic here, just to sync sending of updates
 	mNeedsUpload(FALSE),
 	mNumLowresUploads(0),
+	mUploadFailCount(0),
 	mNeedsUpdate(TRUE),
 	mNumLowresUpdates(0),
 	mTexLayerSet(owner)
 {
 	LLTexLayerSetBuffer::sGLByteCount += getSize();
 	mNeedsUploadTimer.start();
@@ -201,12 +207,13 @@ void LLTexLayerSetBuffer::restartUpdateT
 
 void LLTexLayerSetBuffer::cancelUpload()
 {
 	mNeedsUpload = FALSE;
 	mUploadPending = FALSE;
 	mNeedsUploadTimer.pause();
+	mUploadRetryTimer.reset();
 }
 
 void LLTexLayerSetBuffer::pushProjection() const
 {
 	glMatrixMode(GL_PROJECTION);
 	gGL.pushMatrix();
@@ -353,31 +360,44 @@ BOOL LLTexLayerSetBuffer::uploadInProgre
 
 BOOL LLTexLayerSetBuffer::isReadyToUpload() const
 {
 	if (!gAgentQueryManager.hasNoPendingQueries()) return FALSE; // Can't upload if there are pending queries.
 	if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) return FALSE; // Don't upload if avatar is using composites.
 
-	// If we requested an upload and have the final LOD ready, then upload.
-	if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE;
+	BOOL ready = FALSE;
+	if (mTexLayerSet->isLocalTextureDataFinal())
+	{
+		// If we requested an upload and have the final LOD ready, upload (or wait a while if this is a retry)
+		if (mUploadFailCount == 0)
+		{
+			ready = TRUE;
+		}
+		else
+		{
+			ready = mUploadRetryTimer.getElapsedTimeF32() >= BAKE_UPLOAD_RETRY_DELAY * (1 << (mUploadFailCount - 1));
+		}
+	}
+	else
+	{
+		// Upload if we've hit a timeout.  Upload is a pretty expensive process so we need to make sure
+		// we aren't doing uploads too frequently.
+		const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
+		if (texture_timeout != 0)
+		{
+			// The timeout period increases exponentially between every lowres upload in order to prevent
+			// spamming the server with frequent uploads.
+			const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
 
-	// Upload if we've hit a timeout.  Upload is a pretty expensive process so we need to make sure
-	// we aren't doing uploads too frequently.
-	const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
-	if (texture_timeout != 0)
-	{
-		// The timeout period increases exponentially between every lowres upload in order to prevent
-		// spamming the server with frequent uploads.
-		const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
-
-		// If we hit our timeout and have textures available at even lower resolution, then upload.
-		const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
-		const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
-		if (has_lower_lod && is_upload_textures_timeout) return TRUE; 
+			// If we hit our timeout and have textures available at even lower resolution, then upload.
+			const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
+			const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
+			ready = has_lower_lod && is_upload_textures_timeout;
+		}
 	}
 
-	return FALSE;
+	return ready;
 }
 
 BOOL LLTexLayerSetBuffer::isReadyToUpdate() const
 {
 	// If we requested an update and have the final LOD ready, then update.
 	if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE;
@@ -479,23 +499,26 @@ void LLTexLayerSetBuffer::doUpload()
 			{
 				integrity_test->setLastError("Unable to read entire file");
 			}
 			
 			if (valid)
 			{
+				const bool highest_lod = mTexLayerSet->isLocalTextureDataFinal();
 				// Baked_upload_data is owned by the responder and deleted after the request completes.
 				LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp, 
 																			 this->mTexLayerSet, 
-																			 asset_id);
+																			 asset_id,
+																			 highest_lod);
 				// upload ID is used to avoid overlaps, e.g. when the user rapidly makes two changes outside of Face Edit.
 				mUploadID = asset_id;
 
 				// Upload the image
 				const std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture");
 				if(!url.empty()
-					&& !LLPipeline::sForceOldBakedUpload) // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+					&& !LLPipeline::sForceOldBakedUpload // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+					&& (mUploadFailCount < (BAKE_UPLOAD_ATTEMPTS - 1))) // Try last ditch attempt via asset store if cap upload is failing.
 				{
 					LLSD body = LLSD::emptyMap();
 					// The responder will call LLTexLayerSetBuffer::onTextureUploadComplete()
 					LLHTTPClient::post(url, body, new LLSendTexLayerResponder(body, mUploadID, LLAssetType::AT_TEXTURE, baked_upload_data));
 					llinfos << "Baked texture upload via capability of " << mUploadID << " to " << url << llendl;
 				} 
@@ -508,13 +531,12 @@ void LLTexLayerSetBuffer::doUpload()
 												  TRUE,		// temp_file
 												  TRUE,		// is_priority
 												  TRUE);	// store_local
 					llinfos << "Baked texture upload via Asset Store." <<  llendl;
 				}
 
-				const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal();	
 				if (highest_lod)
 				{
 					// Sending the final LOD for the baked texture.  All done, pause 
 					// the upload timer so we know how long it took.
 					mNeedsUpload = FALSE;
 					mNeedsUploadTimer.pause();
@@ -600,20 +622,21 @@ void LLTexLayerSetBuffer::onTextureUploa
 												  void* userdata,
 												  S32 result,
 												  LLExtStat ext_status) // StoreAssetData callback (not fixed)
 {
 	LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata;
 
-	if ((result == 0) &&
-		isAgentAvatarValid() &&
+	if (isAgentAvatarValid() &&
 		!gAgentAvatarp->isDead() &&
 		(baked_upload_data->mAvatar == gAgentAvatarp) && // Sanity check: only the user's avatar should be uploading textures.
 		(baked_upload_data->mTexLayerSet->hasComposite()))
 	{
 		LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mTexLayerSet->getComposite();
-			
+		S32 failures = layerset_buffer->mUploadFailCount;
+		layerset_buffer->mUploadFailCount = 0;
+
 		if (layerset_buffer->mUploadID.isNull())
 		{
 			// The upload got canceled, we should be in the
 			// process of baking a new texture so request an
 			// upload with the new data
 
@@ -623,26 +646,34 @@ void LLTexLayerSetBuffer::onTextureUploa
 			layerset_buffer->requestUpload();
 		}
 		else if (baked_upload_data->mID == layerset_buffer->mUploadID)
 		{
 			// This is the upload we're currently waiting for.
 			layerset_buffer->mUploadID.setNull();
+			const std::string name(baked_upload_data->mTexLayerSet->getBodyRegionName());
+			const std::string resolution = baked_upload_data->mIsHighestRes ? " full res " : " low res ";
 			if (result >= 0)
 			{
-				layerset_buffer->mUploadPending = FALSE;
+				layerset_buffer->mUploadPending = FALSE; // Allows sending of AgentSetAppearance later
 				LLVOAvatarDefines::ETextureIndex baked_te = gAgentAvatarp->getBakedTE(layerset_buffer->mTexLayerSet);
 				// Update baked texture info with the new UUID
 				U64 now = LLFrameTimer::getTotalTime();		// Record starting time
-				llinfos << "Baked texture upload took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
+				llinfos << "Baked" << resolution << "texture upload for " << name << " took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
 				gAgentAvatarp->setNewBakedTexture(baked_te, uuid);
 			}
 			else
 			{	
-				// Avatar appearance is changing, ignore the upload results
-				llinfos << "Baked upload failed. Reason: " << result << llendl;
-				// *FIX: retry upload after n seconds, asset server could be busy
+				++failures;
+				S32 max_attempts = baked_upload_data->mIsHighestRes ? BAKE_UPLOAD_ATTEMPTS : 1; // only retry final bakes
+				llwarns << "Baked" << resolution << "texture upload for " << name << " failed (attempt " << failures << "/" << max_attempts << ")" << llendl;
+				if (failures < max_attempts)
+				{
+					layerset_buffer->mUploadFailCount = failures;
+					layerset_buffer->mUploadRetryTimer.start();
+					layerset_buffer->requestUpload();
+				}
 			}
 		}
 		else
 		{
 			llinfos << "Received baked texture out of date, ignored." << llendl;
 		}
diff -r f04a968c189c -r edc6d6b315e4 indra/newview/lltexlayer.h
--- a/indra/newview/lltexlayer.h	Tue Jun 14 15:54:03 2011 -0400
+++ b/indra/newview/lltexlayer.h	Wed Jun 15 21:19:12 2011 +0300
@@ -309,12 +309,14 @@ protected:
 private:
 	BOOL					mNeedsUpload; 					// Whether we need to send our baked textures to the server
 	U32						mNumLowresUploads; 				// Number of times we've sent a lowres version of our baked textures to the server
 	BOOL					mUploadPending; 				// Whether we have received back the new baked textures
 	LLUUID					mUploadID; 						// The current upload process (null if none).
 	LLFrameTimer    		mNeedsUploadTimer; 				// Tracks time since upload was requested and performed.
+	S32						mUploadFailCount;				// Number of consecutive upload failures
+	LLFrameTimer    		mUploadRetryTimer; 				// Tracks time since last upload failure.
 
 	//--------------------------------------------------------------------
 	// Updates
 	//--------------------------------------------------------------------
 public:
 	void					requestUpdate();
@@ -360,15 +362,17 @@ private:
 // Used by LLTexLayerSetBuffer for a callback.
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 struct LLBakedUploadData
 {
 	LLBakedUploadData(const LLVOAvatarSelf* avatar, 
 					  LLTexLayerSet* layerset, 
-					  const LLUUID& id);
+					  const LLUUID& id,
+					  bool highest_res);
 	~LLBakedUploadData() {}
 	const LLUUID				mID;
 	const LLVOAvatarSelf*		mAvatar; // note: backlink only; don't LLPointer 
 	LLTexLayerSet*				mTexLayerSet;
    	const U64					mStartTime;	// for measuring baked texture upload time
+   	const bool					mIsHighestRes; // whether this is a "final" bake, or intermediate low res
 };
 
 #endif  // LL_LLTEXLAYER_H

